【问题标题】:How to get the number of results in an App Engine query before actually iterating through them all如何在实际遍历所有结果之前获取 App Engine 查询中的结果数量
【发布时间】:2013-06-05 18:52:55
【问题描述】:

在我的 Google App Engine 应用中,我需要从数据存储查询中获取并返回可能大量的实体,以响应服务调用 GET 请求。此调用可能会返回数千个实体和 MB 的序列化数据。

响应数据包的第一部分传达序列化结果中有多少实体,然后是所有序列化实体。目前,我正在使用QueryResultIterator 遍历查询中的所有实体,直至达到最大页面大小限制,之后我返回一个游标,可用于继续获取上一个调用停止的位置(如果达到最大值并且查询中仍有结果)。当我遍历结果时,我将它们保存在一个列表中。一旦我用尽了查询结果或达到了最大页面大小,我就可以从这个列表的大小中获取实体的数量。但是我必须再次遍历这个列表来序列化每个实体并将结果写入响应输出流。

我不知道这是执行此操作的最有效方法。有没有一种方法可以在实际遍历所有实体或将它们直接提取到列表中之前获取查询结果中的实体数量? (list方法无论如何都不起作用,因为我使用的是游标,这需要使用QueryResultIterator)。

QueryResultIterator 有一个方法getIndexList()。这是获取查询结果中实体数量的一种成本更低的方法吗?我假设这个列表将只包含查询结果中每个实体的一个索引对象。另外,我需要这个列表只包含插入器当前光标位置之后的实体索引。我的理解是否正确,或者这种方法不会像我想的那样做?

与加载整个实体列表相比,仅包含索引的列表所需的内存要少得多。虽然,我不知道这个列表是否会受到查询的预取或块大小的限制,或者我是否想使用查询的限制参数,因为我只想知道有多少实体在结果达到最大页面大小加一(知道还有更多结果并提供光标继续)。

目前我正在设置预取和块大小(到我的页面限制的大小),但我没有使用限制或偏移参数,因为我使用的是游标。据我了解,游标比偏移/限制更可取。设置限制参数会影响使用游标继续查询吗?

很明显,对于 GAE 数据存储查询的工作原理以及更改参数对它们的影响,我有很多疑问。因此,任何见解都值得赞赏。 App Engine API 的文档通常很少,就像一句话描述的方法一样,几乎可以从方法签名中推断出什么。否则,他们通常不会详细介绍。也许我目前这样做的方式毕竟很好。它按原样工作,但我正在尝试优化服务调用,以便为我的客户端应用程序获得最佳响应时间。

更新:顺便说一句,我在我的应用程序中使用Objectify v3 并执行此查询。我需要在几个地方使用低级数据存储 API,包括执行地理位置查询(使用 geomodel)和投影查询(Objectify v3 不支持)。因此,如果有一个使用 Objectify 的好方法来做到这一点,那将是理想的。否则我可以使用低级 API,但这样总是比较麻烦。

【问题讨论】:

    标签: java google-app-engine objectify


    【解决方案1】:

    低级 api 和 Objectify 都有一个 count() 方法(有关详细信息,请查看 javadocs)。但是,计数可能是一项非常昂贵且耗时的操作 - 每个返回的数字都需要 1 个小操作。例如,返回 5000 的 count() 需要 5000 次小操作(加上 1 次查询读取),并且需要对所有 5000 进行仅键扫描所需的时间(这是 GAE 实际所做的)。

    如果您绝对必须有一个准确的计数,您可能需要自己通过增加/减少一个(可能是分片的)计数器来聚合这个值。当您处理过滤查询时,这会变得非常棘手。

    这里没有一个正确的解决方案。 Google 搜索会为您提供“大约 119,000,000 个结果”之类的总数,这些结果是故意不准确的,几乎可以肯定是预先计算的。对于较小的结果集,使用 count() 是可以接受的 - 但您可能希望应用 limit() 以便永远不会破产。您总是可以说“超过 500 个结果...”

    【讨论】:

    • 感谢您提供有用的信息。似乎最好的方法是用查询结果在内存中填充一个列表,直到最大大小限制。然后从列表中获取计数并遍历它以序列化服务调用响应中的实体。我希望避免在列表中包含所有响应实体所需的大量内存使用,但在 F1 实例上大小仍应在合理范围内(不超过 20-30MB)。我也希望只遍历它们一次,但这需要 2 遍。其他方式需要更多的数据存储操作,但不一定更快。
    【解决方案2】:

    如果您想获取记录,则可以使用以下代码

      com.google.appengine.api.datastore.Query qry = new com.google.appengine.api.datastore.Query("EntityName");
      com.google.appengine.api.datastore.DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
      int totalCount = datastoreService.prepare(qry).countEntities(FetchOptions.Builder.withDefaults());
    

    anf 如果你想要过滤器而不是可以使用

    qry.addFilter("firstName", FilterOperator.EQUAL, firstName);
    

    希望对你有帮助

    【讨论】:

    • 我正在使用 Objectify 来创建我的查询。我可能应该在问题中注意到这一点。是否可以使用 Objectify 做类似的事情?我可以(并且需要在几个地方)使用低级 API,所以如果 Objectify 没有办法,这可能是要走的路。
    • 所以我认为你可以使用这个 int q = ObjectifyService.ofy().load().type(yourClassName.class).count();
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-22
    • 2015-12-16
    • 1970-01-01
    • 1970-01-01
    • 2015-02-09
    • 1970-01-01
    • 2018-06-27
    相关资源
    最近更新 更多