在使用 Google App Engine 存储数据的过程中,遇到以下问题:
- 应用程序中存在如下的数据类:
class NewsArticle(db.Model): score = db.FloatProperty(default=0.0) date_scored = db.DateTimeProperty() ...
- 希望从 NewsArticle 中获取在特定时间框架内得分最高的数据实体,例如获取当天或上周得分最高的数据实体。
- 尝试使用以下查询语句:
query = db.GqlQuery('SELECT * FROM NewsArticle WHERE date_created > DATETIME(:year, :month, :day, 0, 0, 0) ORDER BY score DESC', year=date.selected_year, month=date.selected_month, day=date.selected_day)
但该查询语句无法正常工作,因为数据存储要求第一个排序属性必须与不等式过滤属性相同。
- 考虑在应用程序中获取特定时间框架内的所有 NewsArticle 实体,然后进行分数排序,但预期结果数量非常大,因此内存中排序效率低下。
2、解决方案 方法一:
- 过滤仅限时间框架并在内存中按分数排序,或
- 如果可以将时间框架限制为整天和整周,可以在模型中包含其他属性,以将周保存为整数并将日期保存为 DateProperty,并对其进行简单的等式检查。
- 编辑:有关更多信息,请参阅查询限制。
方法二:
- 预计会有大量结果,因此内存中排序效率低下。
- 最多可以从查询中获取 1000 个结果,因此以任何方式对它们进行排序都会非常有效——例如,考虑在我的 Macbook Air(第一天,最慢型号)上:
$ python -mtimeit -s'import random; x=range(1000); random.shuffle(x)' 'y=sorted(x)' 1000 loops, best of 3: 714 usec per loop
App Engine 的 CPU 明显快于 Air,因此对 1000 个结果进行排序需要 700 微秒将是一个非常悲观的估计;这与获取数据所需的数十毫秒相比——因此,完全不必担心排序:只要能够获取所需的结果,就完全没问题。
- 顺便说一下,要评估 App Engine 在任务上的可能性能,请参阅 Guido van Rossum 在此处的演讲——他声称“典型的 db.get()”需要 10-50 毫秒(put 等需要 50-100 毫秒)。
- 如果预期从查询中得到 1000 个以上的结果,通常意味着需要对表进行非规范化,以便将查询结果修剪到 1000 个以下。例如,在你的情况下,假设你每天期望有 500-700 个条目——在这种情况下,获取今天的全部结果不成问题,但一周肯定是个问题:你需要将查询修剪到“正常”结果的 20% 或更少。
- 例如,假设你的分数在 0-100 范围内,分布均匀。在这种情况下,你可以向实体添加一个布尔字段“topcandidate”:保存实体时,仅当分数在 85-100 的范围内时,才将该字段设置为 True(如你所见,这意味着表被非规范化了,因为该字段表示逻辑上冗余的信息)。
- 当你获取每周的最高结果时,添加等式条件使 topcandidate 为 True。这应该会将结果从 3500-4900 减少到 500-900——得分最高的 15% 左右,然后你可以在内存中对它们进行排序并选取前 100 个。
- 当然,具体数字取决于你的分数字段的分布(更可能是一个钟形曲线,而不是一个平坦的均匀分布)以及你需要的“最高分数候选人”数量,但这是一个通常有用的方法,可以解决 1000 个结果的限制。