App Engine - 对包含许多多项选择响应的用户配置文件进行高效查询

时间:2011-12-13 21:35:55

标签: google-app-engine

我正在构建一个应用程序,用户可以通过回答一堆多项选择题来为自己创建配置文件。用户还可以通过指定这些问题的答案标准来搜索其他用户。

让我们说我们有9个问题q1 .. q9,每个问题有6个可能的答案(0到5)。这可以在用户配置文件中表示为:

class UserProfile(db.Model):
    user = db.StringProperty(required=True)
    q1 = db.IntegerProperty()
    ...
    q9 = db.IntegerProperty()

现在,考虑用户想要为回答:

的用户运行查询
  • 0,1或2表示q1
  • 1,2或5 for q2
  • ...
  • 3,4或5 for q9

我们可以写一个查询,例如:

q = UserProfile.all()
q.filter("q1 IN", [0, 1, 2])
q.filter("q2 IN", [1, 2, 5])
...
q.filter("q9 IN", [3, 4, 5])

不幸的是,这将产生近20,000个子查询(假设用户为每个过滤器指定了3个可能的答案),这远远大于允许的30个,更不用说其可怕的低效率了。

是否有设计模式可以有效地完成这项工作?

通过使用二进制编码(例如,[1,2,5] - > b100110 = 38)将每个过滤器表示为整数并存储每个用户答案,我可以设想将这些过滤器中的每一个转换为单个相等过滤器的方法在数据存储区中作为它将匹配的查询列表(例如,1 - > bxxxx1x - > [2,3,6,7,10,11,...,62,63])。然而,这看起来有些麻烦。

如果有人对实施提出更有效的建议,我将不胜感激。

更新(关于建议的二进制编码):

Nick Johnson对上面提到的二进制编码提出了一些有趣的担忧,因此我想更详细地澄清所提出的编码,以便让他和其他人能够对其优点和挑战进行明确的评估。

我认为一个具体的例子最有效。另外,我认为从查询机制开始也更直观。

继续上面的例子,让我们假设有9个问题,每个问题有6个可能的答案(0到5)。我们还定义每个查询将采用多个这些问题的过滤器的形式,以便与多个可能的答案进行匹配(如上所述)。我建议转换形式的每个查询" q2 IN [1,2,5]"使用二进制编码的相等滤波器,如果它是查询响应之一,则每个位位置为1,否则为0。例如," q2 IN [1,2,5]"将转换为" q2 == b100110"或" q2 == 38"。进一步应用此功能,上述复合查询将转换为以下多个相等过滤器:

    q1的
  • 0,1或2 - > q1 == b000111 - > q1 == 7
  • 对于q2,
  • 1,2或5 - > q2 == b100110 - > q2 == 38
  • ...
  • 对于q9,
  • 3,4或5 - > q9 == b111000 - > q9 == 56

启用" IN"过滤到" =="过滤器,我们需要事先确定哪些查询(以二进制编码形式)的配置文件响应将匹配。例如,如果用户选择2(在0到5之间)作为答案,则该响应将匹配其二进制编码在2位中具有1的任何查询,即bxxx1xx形式,其中x可以是0或1 .bxxx1xx定义的整数集是[b000100,b000101,b000110,b000111,b001100,b001101,...,b111100,b111101,b111110,b111111]或十进制形式:[4,5,6,7,12, 13,...,60,61,62,63],它是32个整数的列表。一般来说,这个"查询匹配集"将有2 ^(n-1)个元素用于对具有n个可能答案的问题的响应,因为二进制编码中的n个比特中的1个将固定为1,而其他的可以是0或1。

因此,如果我们有m个问题,每个问题都有n个可能的答案,那么存储这些"查询匹配集的每个实体的索引条目数"对于每个问题将是m x(2 ^(n-1))。如果我有:

  • 9个问题,每个问题有6个可能的答案,这将需要9 x 2 ^ 5 = 288个索引条目
  • 10个问题,每个问题有8个可能的答案,这将需要10 x 2 ^ 7 = 1280个索引条目
  • 15个问题,每个问题有9个可能的答案,这需要15 x 2 ^ 8 = 3840个索引条目
  • 20个问题,每个问题有10个可能的答案,这将需要20 x 2 ^ 9 = 10240索引条目(高于App Engine强加的5000 /实体限制)

因此,我同意对于任意大量的问题,这不是一种合适的方法,特别是如果问题的答案的可能数量也很大。但是,如果要编入索引的问题数量为10-15,并且可能的答案数不超过6,这似乎是可行的,至少对于大多数问题而言。

我将不超过10个需要编入索引的问题。他们中的大多数有3-5个可能的答案。有些人有6-7个可能的答案,所以我预计每个实体的索引条目少于300个(除非我对如何计算上述索引要求有误)。

我并不认为这是一个非常优雅的解决方案,但是:

  • 看起来索引开销可以管理(即远低于5000索引行限制)
  • 它将完全返回我正在过滤的内容(而不是获取部分过滤的实体列表,这些实体都需要通过网络传输,只能由应用程序进一步过滤)
  • 我认为内置的合并连接速度足够快,以使其有效。

我仍然会对以下问题有所了解:

  1. 根据这个更详细的解释,您认为索引要求是否合理?如果您认为这仍然会遇到限制,我真的很感激您对此的见解。
  2. 即使索引要求合理,您认为编写查询规划器会产生更有效的解决方案吗?如果是这样,我将不胜感激(a)简要解释为什么这样做会更有效率,以及(b)指示如何做到这一点。我不确定如何开始使用查询规划器。

1 个答案:

答案 0 :(得分:2)

在您描述查询时,根本没有有效的方法来构建查询数据。唯一的方法是查询您认为最具限制性的标准,然后在内存中手动过滤剩余标准。

如果您告诉我们更多关于人们可能执行的具体查询类型以及原因,我们可能会提供更有效的具体建议。