我正在尝试按照得分,过去一周登录的日期字段(日期字段)查询前100名用户的数据存储区。
List<User> users = ofy().load().type(User.class)
.filter("date >", date).order("date")
.order("-points").limit(100).list();
它似乎忽略了按点的最终排序,而是返回按日期排序的列表。 如果我删除了日期过滤器并进行排序,那么我会按点进行排序,但包括已经在一周以上登录的用户。
我仔细阅读了文档,似乎允许包含不等式过滤器和多种排序的查询。
任何想法我做错了什么?
以下是文档中的一些相关说明:
由于App Engine数据存储区执行查询的方式,如果查询指定属性上的不等式过滤器并对其他属性排序顺序,则必须在其他属性之前对不等式过滤器中使用的属性进行排序。
...如果查询指定了一个或多个不等式过滤器以及一个或多个排序顺序,则第一个排序顺序必须引用不等式过滤器中指定的相同属性。
答案 0 :(得分:4)
您观察到的是app引擎使用的基于索引的查询的标准预期行为。在过滤时,如果应用不等式过滤器(只能在查询中的一个属性上使用),那么当您有多个排序顺序时,第一个排序将是该属性,然后进一步排序可以基于其他属性。对于基于不等式过滤器进行日期查询和按点排序的查询,数据存储区将使用和索引如下,其中date属性按升序或降序排列:
day 1 - 100
day 2 - 30
day 2 - 90
day 2- 10
day 3 - 50
day 4 - 40
day 5 - 60
现在,如果您使用日期的不等式过滤器进行查询&gt; day1,然后查询将搜索上面的索引并返回下面的结果,即使你没有明确提及,也会按日期排序:
day 2 - 30
day 2 - 90
day 2- 10
day 3 - 50
day 4 - 40
day 5 - 60
现在,如果您在日期中使用不等式过滤器进行查询并在点上添加排序顺序,那么它将对上面已按日期排序的结果应用其他排序。这就是为什么你被迫明确提到日期作为第一个排序顺序(因为它默认已存在)然后提到点作为第二个排序顺序。 结果如下。 查看第2天的排序:
day 2 - 10
day 2 - 30
day 2- 90
day 3 - 50
day 4 - 40
day 5 - 60
因此,如果您想要实现逻辑,则需要从应用引擎中检索数据并执行一些额外的排序,如下所示:
1,使用日期不等式过滤器获取,然后在您的客户端中根据点进行适当的排序以获得前100名。
2,根据点的降序索引获取最高结果(大约300),然后根据客户端中的日期过滤它们以获得所需的100。
答案 1 :(得分:2)
您可能需要考虑其他方法。这是很多索引开销会导致您的成本更高,执行此函数的处理程序的响应时间会慢一个数量级,并且您将有时间索引更新的最终一致性将影响此维护数据。如果您有一个繁忙的网站,您肯定不会对与此方法相关的延迟和成本感到满意。
有许多替代方法。您每秒的预期网站交易会影响您选择的内容。这是一个非常简单的选择。使用TextProperty创建一个ndb实体。使用score_userid等字符串序列化最高分数条目。通过将它们与唯一字符连接,将它们存储在文本字段中。当新分数进入时,使用get_by_id检索此记录(ndb会自动为您处理memcaching)。将其拆分为数组。拆分数组的最后一个元素,并检查新的分数。如果它小于分数,则删除它,并将新的score_userid字符串附加到数组。对数组进行排序,连接它,并放入()新的TextProperty。如果你想要你可以设置一天结束时的cron来扫描当天的分数,以检查你的过程是否受到两个分数几乎同时到达而导致一个覆盖另一个分数的极小机会的影响。 HTH。 -stevep
答案 2 :(得分:2)
感谢tony花时间为您提供了很好的解释。
第三种更复杂但更“清洁”的选择也是可能的
(是的,因为你认为在300个最佳结果中,将会有100个新的结果,因此除了使用一些带宽(如果在gwt中)和资源这一事实外,它在物质上是不连贯的。)
第三个解决方案是有一个单独的表,总是最多100个结果按分数排序。
您可以将这100行永远存储在内存中。
在保存任何新条目之前检查
现在,如果您有多个日期选项:例如:最好的一天,最好的一周,最好的一个月 添加一列以指定前100个元素在哪个类别中(并且有300个元素而不是100个,或者具有多个值列并保留100个条目(代码复杂度更高))
致以最诚挚的问候,