Google App Engine 的排序问题

1 投票
4 回答
2802 浏览
提问于 2025-04-15 17:33

我正在使用以下这个类来存储一些数据:

class NewsArticle(db.Model):
    score = db.FloatProperty(default=0.0)
    date_scored = db.DateTimeProperty()
    ...

我需要做的是获取在某个时间段内得分最高的新闻文章(比如说获取今天或者上周得分最高的数据)。

我尝试了以下方法:

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)

但是这样不行,因为数据存储要求

第一个排序的属性必须和不等式过滤的属性相同

我考虑过获取特定时间段内的所有新闻文章,然后在我的应用程序中进行得分排序,但我预计结果会非常多,所以在内存中排序效率不高。

还有什么其他解决方案可以解决我的问题呢?

4 个回答

0

试试这个:

import datetime
...
query = Data.gql('SELECT * FROM NewsArticle WHERE date_created >= :today ORDER BY score DESC', today=datetime.date.today())
1

我预计会有很多结果,所以在内存中排序会很低效。

你从查询中最多只能得到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

AppEngine的处理器比Air快得多,所以用700微秒来排序1000个结果是一个非常保守的估计;而获取数据的时间大约是几十毫秒。所以,关于排序你完全不必担心:只要你能获取到想要的结果,就没问题。

顺便提一下,要评估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个。

当然,具体的数字取决于你的score字段的分布(更可能是钟形曲线而不是平坦的均匀分布),以及你需要多少“高分候选者”,但这是一个通常有效的方法来绕过1000个结果的限制。

5

你可以选择以下两种方法:

  1. 只根据时间范围进行筛选,然后在内存中按分数排序,或者,

  2. 如果你能把时间范围限制在整天和整周,可以在你的模型中添加额外的属性,把周保存为一个整数,把天保存为一个 DateProperty,然后进行简单的相等性检查。

编辑:想了解更多,可以查看 查询的限制

撰写回答