2

我正在尝试在数据存储中查询得分最高的 100 个用户,这些用户在过去一周登录(日期字段)。

List<User> users = ofy().load().type(User.class)
   .filter("date >", date).order("date")
   .order("-points").limit(100).list();

它似乎忽略了按点的最终排序,而是返回按日期排序的列表。如果我删除日期过滤器并排序,那么我会得到按点很好地排序的列表,但包括超过一周前登录的用户。

我已经仔细阅读了文档,它似乎允许同时包含不等式过滤器和多种排序的查询。

任何想法我做错了什么?

以下是文档中的一些相关说明:

由于 App Engine 数据存储区执行查询的方式,如果查询指定属性的不等式过滤器和其他属性的排序顺序,则不等式过滤器中使用的属性必须在其他属性之前排序。

...如果查询指定了一个或多个不等式过滤器以及一个或多个排序顺序,则第一个排序顺序必须引用不等式过滤器中命名的相同属性。

4

3 回答 3

4

您观察到的是应用引擎使用的基于索引的查询的标准预期行为。在过滤时,如果您应用不等式过滤器(只能在查询中的一个属性上使用),那么当您有多个排序顺序时,第一个排序将针对该属性,然后可以基于其他属性进行进一步排序。对于基于日期不等式过滤器的查询和按点排序,数据存储将使用如下索引,其中日期属性按升序或降序排列:

day 1 -  100
day 2 -  30
day 2 - 90
day 2- 10
day 3 - 50
day 4 - 40
day 5 - 60 

现在,如果您使用 day > 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。

于 2013-07-05T06:05:38.503 回答
2

您可能需要考虑另一种方法。这是很多索引开销,这将导致您的成本更高,执行此功能的处理程序的响应时间要慢一个数量级,并且您将有时索引更新的最终一致性会影响此维护数据。如果您有一个繁忙的站点,您肯定不会对与此方法相关的延迟和成本感到满意。

有许多替代方法。您预期的每秒站点事务数会影响您的选择。这是一个非常简单的替代方案。使用 TextProperty 创建一个 ndb 实体。使用诸如 score_userid 之类的字符串对得分最高的条目进行序列化。通过将它们与唯一字符连接起来,将它们存储在文本字段中。当一个新的分数进来时,使用 get_by_id 来检索这个记录(ndb 会自动为你处理 memcaching)。将其拆分为一个数组。拆分数组的最后一个元素,并检查新分数。如果它小于分数,则删除它,并将新的 score_userid 字符串附加到数组中。对数组进行排序,加入它,然后 put() 新的 TextProperty。如果您愿意,您可以设置一天结束的 cron 来扫描您当天的分数,以检查您的流程是否受到两个分数几乎同时到达导致一个分数覆盖另一个分数的极小可能性的影响。HTH。-史蒂夫

于 2013-07-05T15:21:46.427 回答
2

感谢托尼花时间为您的精彩解释。

第三个更复杂但“更清洁”的选项也是可能的

(是的,因为您假设在 300 个最佳结果中,将有 100 个新结果,因此除了使用一些带宽(如果在 gwt 中)和资源这一事实之外,它在数学上是不连贯的)。

第三种解决方案是有一个单独的表,其中始终按分数排序最多 100 个结果。

您可以将这 100 行始终保存在内存中。

在保存任何新的条目检查之前

  • 如果 100 个内存行在日期限制内并删除旧行(假设您只有一个日期选项:例如最佳月份)
  • 如果要保存的当前分数优于“最新”条目中的最低分数,请将其添加到内存缓存中。

现在,如果您有多个日期选项:例如:最佳日期、最佳星期、最佳月份 添加一列以指定前 100 个元素属于哪个类别(并且有 300 个元素而不是 100 个元素,或者具有多个值列并保留 100 个条目(代码复杂度更高))

此致,

于 2013-11-29T07:59:56.270 回答