Amazon SimpleDB查询查找“按朋友发布”

时间:2010-08-06 01:18:47

标签: database amazon-simpledb

我一直在开发一款iPhone应用程序,用于查询中继我存储在Amazon SimpleDB中的数据的服务器。我有一个由各种用户提交的“提交”数据库表。我正在与Facebook接口以检索Facebook好友,并希望查询“提交内容”以查找朋友的帖子 - 例如:

SELECT * FROM submissions WHERE userID = '00123' OR userID = '00124' OR .... 

(通过完整的朋友列表)

我认为这会使用这种选择语句进入亚马逊查询限制 -

[每个选择表达式的最大比较次数:20]

你能想到用SimpleDB优雅地解决这个问题的方法吗? 我宁愿不必做一堆20人的查询。 或者,我是否需要移动到不同的数据库包然后执行跨表查询?

谢谢!

3 个答案:

答案 0 :(得分:4)

有一种方法可以使用SimpleDB,但它不优雅,它更像是一个黑客,因为它要求你人为地复制提交项目中的userid属性。

这是基于以下事实:尽管每个IN谓词只能进行20次比较,但你可以有20个IN谓词 如果 它们各自命名不同的属性。因此,请在表单的提交项目中添加其他合成属性:

userID ='00123'userID_2 ='00123'userID_3 ='00123'userID_4 ='00123'... userID_20 ='00123'

它们对于给定的提交都具有相同的值。然后,您可以通过一个查询获取最多400位朋友的提交内容:

SELECT * FROM submissions 
WHERE userID IN('00120','00121',...,'00139') OR
    `userID_2` IN('00140','00141',...,'00159') OR
    `userID_3` IN('00160','00161',...,'00179') OR
    `userID_4` IN('00180','00181',...,'00199') OR
    ...
    `userID_20` IN('00300','00301',...,'00319')

您可以在创建提交时填充19个额外属性(如果您有备用属性),并且听起来不像提交的用户会改变。此外,您可能希望显式命名要返回的属性(而不是使用*),因为您现在将有19个在退货数据集中不关心的属性。

从数据模型的角度来看,这显然是一个黑客行为。但话说回来,对于拥有400或更少朋友的用户,它可以为您提供您想要的内容:单个查询,因此您可以按日期或其他条件进行限制,按最近时间排序,按页面排序结果等。不幸的是,容量400不会容纳所有Facebook用户的好友列表。因此,您可能仍需要为大型朋友列表实现多查询解决方案。

我的建议是,如果SimpleDB满足您的应用程序的需求,除了这个问题,那么考虑使用hack。但是如果你需要反复做这样的事情,那么SimpleDB可能不是最好的选择。

答案 1 :(得分:2)

您需要IN子句或临时表的连接。不幸的是,AmazonSimpleDB有其局限性。出于这个原因,我们放弃了一个很有希望的项目。在我们切换齿轮之前,我们走了多线程的道路并使用了NextToken功能。

您可以对SimpleDB执行并行(多线程)查询以获取提交,每个查询最多可查找20个用户ID,然后将结果合并到一个列表中。不过,现在可能是时候考虑切换到MySQL或SQL Server,以便能够将ID列表作为临时表上传,然后进行简单的连接以获得结果。

答案 2 :(得分:1)

我创建了Simple Savant .NET library for SimpleDB,我碰巧有一些实用程序代码用于分割和并行运行多个选择查询,同时将每个选择的IN子句限制为20个值。我可能会将此代码推送到下一个Savant版本中,但这里适用于任何发现它有用的人:

    /// <summary>
    /// Invokes select queries that use parameter lists (with IN clauses) by splitting the parameter list
    /// across multiple invocations that are invoked in parallel.
    /// </summary>
    /// <typeparam name="T">The item type</typeparam>
    /// <typeparam name="P">The select parameter type</typeparam>
    /// <param name="savant">The savant instance.</param>
    /// <param name="command">The command.</param>
    /// <param name="paramValues">The param values.</param>
    /// <param name="paramName">Name of the param.</param>
    /// <returns></returns>
    public static List<T> SelectWithList<T,P>(ISimpleSavantU savant, SelectCommand<T> command, List<P> paramValues, string paramName)
    {
        var allValues = SelectAttributesWithList(savant, command, paramValues, paramName);
        var typedValues = new List<T>();
        foreach (var values in allValues)
        {
            typedValues.Add((T)PropertyValues.CreateItem(typeof (T), values));
        }
        return typedValues;
    }

    /// <summary>
    /// Invokes select queries that use parameter lists (with IN clauses) by splitting the parameter list
    /// across multiple invocations that are invoked in parallel.
    /// </summary>
    /// <typeparam name="P">The select parameter type</typeparam>
    /// <param name="savant">The savant instance.</param>
    /// <param name="command">The command.</param>
    /// <param name="paramValues">The param values.</param>
    /// <param name="paramName">Name of the param.</param>
    /// <returns></returns>
    public static List<PropertyValues> SelectAttributesWithList<P>(ISimpleSavantU savant, SelectCommand command, List<P> paramValues, string paramName)
    {
        Arg.CheckNull("savant", savant);
        Arg.CheckNull("command", command);
        Arg.CheckNull("paramValues", paramValues);
        Arg.CheckNullOrEmpty("paramName", paramName);

        var allValues = new List<PropertyValues>();
        if (paramValues.Count == 0)
        {
            return allValues;
        }

        var results = new List<IAsyncResult>();
        do
        {
            var currentParams = paramValues.Skip(results.Count * MaxValueTestsPerSimpleDbQuery).Take(MaxValueTestsPerSimpleDbQuery).ToList();
            if (!currentParams.Any())
            {
                break;
            }
            var currentCommand = Clone(command);
            currentCommand.Reset();
            var parameter = currentCommand.GetParameter(paramName);
            parameter.Values.Clear();
            parameter.Values.AddRange(currentParams.Select(e => (object)e));
            var result = savant.BeginSelectAttributes(currentCommand, null, null);
            results.Add(result);
        } while (true);

        foreach (var result in results)
        {
            var values = ((ISimpleSavant2)savant).EndSelectAttributes(result);
            allValues.AddRange(values);
        }

        return allValues;
    }

    private static SelectCommand Clone(SelectCommand command)
    {
        var newParameters = new List<CommandParameter>();
        foreach (var parameter in command.Parameters)
        {
            var newParameter = new CommandParameter(parameter.Name, parameter.PropertyName, null);
            newParameter.Values.Clear();
            newParameters.Add(newParameter);
        }
        var newCommand = new SelectCommand(command.Mapping, command.CommandText, newParameters.ToArray())
            {
                MaxResultPages = command.MaxResultPages
            };
        return newCommand;
    }