【问题标题】:How to fetch more than 1000?如何获取超过1000?
【发布时间】:2010-09-20 19:39:53
【问题描述】:

如何从数据存储中获取超过 1000 条记录并将所有记录放在一个列表中以传递给 django?

【问题讨论】:

    标签: python google-app-engine google-cloud-datastore


    【解决方案1】:

    从版本 1.3.6(2010 年 8 月 17 日发布)开始,您 可以

    From the changelog:

    数据存储区 count() 查询的结果所有数据存储区查询的偏移量不再上限为 1000

    【讨论】:

    • 我仍然只得到了 1000,而应该是 3600。如何实施?谢谢
    • @Ivan 一次获取只能返回 1000,但您可以遍历查询一次获取 1000 并将偏移量设置为下一个 1000。我将很快发布代码。
    • 这适用于例如: numTotalUsers = User.all(keys_only=True).count(999999) # 999999 是最大限制。否则我得到 1000 作为我的计数。
    • 出于性能和成本问题,实际上不建议使用偏移量。您应该使用光标,如下所示:developers.google.com/appengine/docs/python/datastore/…
    • 不建议这样做。您应该查看分片计数器:blog.svpino.com/2015/03/08/…
    【解决方案2】:

    仅作记录 - 1000 个条目的获取限制现已取消:

    http://googleappengine.blogspot.com/2010/02/app-engine-sdk-131-including-major.html

    引用:

    不再有 1000 个结果限制 - 就是这样 右:添加光标和 许多较小的高潮 数据存储稳定性和性能 过去几个月的改进, 我们现在有足够的信心移除 最大结果限制。 无论你是在取货, 迭代,或使用游标,有 结果数量没有限制。

    【讨论】:

      【解决方案3】:

      App Engine 为您提供了一种以 1000 为单位“分页”结果的好方法,方法是对键进行排序并使用最后一个键作为下一个偏移量。他们甚至在这里提供了一些示例代码:

      http://code.google.com/appengine/docs/python/datastore/queriesandindexes.html#Queries_on_Keys

      尽管他们的示例将查询分散到许多请求中,但您可以将页面大小从 20 更改为 1000,并在循环中查询,结合查询集。此外,您可以使用 itertools 链接查询,而无需在需要之前对其进行评估。

      例如,计算超过 1000 行的行数:

      class MyModel(db.Expando):
          @classmethod
          def count_all(cls):
              """
              Count *all* of the rows (without maxing out at 1000)
              """
              count = 0
              query = cls.all().order('__key__')
      
              while count % 1000 == 0:
                  current_count = query.count()
                  if current_count == 0:
                      break
      
                  count += current_count
      
                  if current_count == 1000:
                      last_key = query.fetch(1, 999)[0].key()
                      query = query.filter('__key__ > ', last_key)
      
              return count
      

      【讨论】:

      • 如果实际计数恰好是 1000 的精确倍数,这将永远循环——错误的退出条件!-) 否则很好......
      • 这行不通。 while 循环永远不会进入 b/c 计数被初始化为零。
      【解决方案4】:

      每次遇到这种限制时,我总是想知道“为什么您需要超过 1,000 个结果?”您知道 Google 本身提供的结果不超过 1,000 个吗?试试这个搜索:http://www.google.ca/search?hl=en&client=firefox-a&rls=org.mozilla:en-US:official&hs=qhu&q=1000+results&start=1000&sa=N 直到最近我才知道,因为我从来没有花时间点击查询的第 100 页搜索结果。

      如果您实际上向用户返回了 1,000 多个结果,那么我认为手头的问题比数据存储不允许您这样做更大。

      需要这么多结果的一个可能(合理)原因是,如果您对数据执行大型操作并提供摘要(例如,所有这些数据的平均值是多少)。这个问题的解决方案(在 Google I/O 演讲中讨论过)是即时计算汇总数据,并保存它。

      【讨论】:

      • 同意。在一个页面中向用户返回数千个结果是没有意义的。
      • 从那里可以看出,从数据存储中提取 1000 条记录是没有意义的,除非您要将它们全部返回给用户。
      • 如果我想对存储在数据存储中的 1000 多个实体的属性求和,我需要以某种方式解决这个限制。 jgeewax 有我正在寻找的解决方案。
      【解决方案5】:

      你不能。

      部分常见问题解答指出,您无法访问超出查询的第 1000 行,增加“OFFSET”只会导致更短的结果集,

      ie: OFFSET 999 --> 1 个结果返回。

      来自维基百科:

      App Engine 限制了最大行数 从实体返回到 1000 每个 Datastore 调用的行数。大多数网络 数据库应用程序使用分页和 缓存,因此不需要这个 一次有很多数据,所以这是一个 在大多数情况下都不是问题。[引文 需要]如果应用程序需要更多 每次操作超过 1,000 条记录,它 可以使用自己的客户端软件 或 Ajax 页面来执行 对无限数量的操作 行。

      来自http://code.google.com/appengine/docs/whatisgoogleappengine.html

      服务限制的另一个例子是 a 返回的结果数 询问。一个查询最多可以返回 1,000 个结果。查询会 返回更多结果只返回 最大。在这种情况下,请求 执行这样的查询是不可能的 在超时之前返回请求, 但限制到位以保存 数据存储上的资源。

      来自http://code.google.com/appengine/docs/datastore/gqlreference.html

      注意:LIMIT 子句最多有 1000. 如果指定了大于最大值的限制,则最大值为 用过的。同样的最大值适用于 GqlQuery 类的 fetch() 方法。

      注意:像 offset 参数一样 fetch() 方法,GQL 中的一个 OFFSET 查询字符串不减少 从 数据存储。它只影响哪个 结果由 fetch() 返回 方法。带有偏移量的查询有 性能特点 与偏移量线性对应 大小。

      来自http://code.google.com/appengine/docs/datastore/queryclass.html

      limit 和 offset 参数控制 有多少结果是从 数据存储,以及返回的数量 通过 fetch() 方法:

      • 数据存储将偏移量 + 限制结果提取到应用程序。数据存储区本身不会跳过第一个偏移量结果。

      • fetch() 方法跳过第一个偏移量结果,然后返回其余的(限制结果)。

      • 查询具有对应的性能特征 与偏移量加上限制成线性关系。

      这是什么意思

      如果您有一个单数查询,则无法请求 0-1000 范围之外的任何内容。

      增加偏移量只会增加0,所以

      LIMIT 1000  OFFSET 0    
      

      将返回 1000 行,

      LIMIT 1000 OFFSET 1000 
      

      将返回 0 行,因此无法通过单一查询语法手动或使用 API 获取 2000 个结果。

      唯一可能的例外

      是在表上创建数字索引,即:

       SELECT * FROM Foo  WHERE ID > 0 AND ID < 1000 
      
       SELECT * FROM Foo WHERE ID >= 1000 AND ID < 2000
      

      如果您的数据或查询不能有这个“ID”硬编码标识符,那么您运气不佳

      【讨论】:

      • 我知道。但是我如何才能获取 1000 乘以 1000 并创建 1 个 2000 的列表?
      • list1 = 第一个查询 list2 = 第二个查询 list1 += list2
      • Tom:如果第二次查询由于数据库限制,保证返回 0 行,则毫无意义。
      • 请注意,这个答案现在有点过时了:key 伪属性现在可用于排序和过滤,它允许您分段遍历任意大的结果集.
      • 可以使用jgeewax的方法。
      【解决方案6】:

      此 1K 限制问题已解决。

      query = MyModel.all()
      for doc in query:
          print doc.title
      

      通过将 Query 对象视为可迭代对象:迭代器以小批量的方式从数据存储中检索结果,从而允许应用停止对结果进行迭代以避免获取超出需要的数据。当检索到与查询匹配的所有结果时,迭代停止。与 fetch() 一样,迭代器接口不缓存结果,因此从 Query 对象创建新的迭代器将重新执行查询。

      最大批量大小为 1K。而且您仍然拥有自动数据存储配额。

      但是在 1.3.1 SDK 计划中,他们引入了可以序列化和保存的游标,以便将来的调用可以从上次停止的地方开始查询。

      【讨论】:

        【解决方案7】:

        1000 条记录限制是 Google AppEngine 中的硬性限制。

        此演示文稿http://sites.google.com/site/io/building-scalable-web-applications-with-google-app-engine 解释了如何使用 AppEngine 有效地对数据进行分页。

        (基本上通过使用数字 id 作为键并在 id 上指定 WHERE 子句。)

        【讨论】:

          【解决方案8】:

          当超过 1000 条记录时,通过远程 api 获取仍然存在问题。我们编写了这个小函数来逐块迭代表:

          def _iterate_table(table, chunk_size = 200):
              offset = 0
              while True:
                  results = table.all().order('__key__').fetch(chunk_size+1, offset = offset)
                  if not results:
                      break
                  for result in results[:chunk_size]:
                      yield result
                  if len(results) < chunk_size+1:
                      break
                  offset += chunk_size
          

          【讨论】:

            【解决方案9】:

            我们在 ModelBase 类中使用了一些东西:

            @classmethod
            def get_all(cls):
              q = cls.all()
              holder = q.fetch(1000)
              result = holder
              while len(holder) == 1000:
                holder = q.with_cursor(q.cursor()).fetch(1000)
                result += holder
              return result
            

            这无需考虑就可以解决每个模型的 1000 个查询限制。我想密钥版本会很容易实现。

            【讨论】:

              【解决方案10】:
              class Count(object):
              def getCount(self,cls):
                  class Count(object):
              def getCount(self,cls):
                  """
                  Count *all* of the rows (without maxing out at 1000)
                  """
                  count = 0
                  query = cls.all().order('__key__')
              
              
                  while 1:
                      current_count = query.count()
                      count += current_count
                      if current_count == 0:
                          break
              
                      last_key = query.fetch(1, current_count-1)[0].key()
                      query = query.filter('__key__ > ', last_key)
              
                  return count
              

              【讨论】:

                【解决方案11】:
                entities = []
                for entity in Entity.all():
                    entities.append(entity)
                

                就这么简单。请注意,每个实体都有一个 RPC,它比以块的形式获取要慢得多。因此,如果您担心性能,请执行以下操作:

                如果您的商品少于 1M:

                entities = Entity.all().fetch(999999)
                

                否则,请使用光标。

                还需要注意的是:

                Entity.all().fetch(Entity.all().count())
                

                最大返回 1000,不应使用。

                【讨论】:

                • 因此,如果您遍历 Entity.all() 查询,您将继续获得结果,直到您找到与查询匹配的最后一项,即使它是 #100,000?当您在#999、#1999、#2999 时,GAE 是否准备好下一批?
                【解决方案12】:

                JJG:您上面的解决方案很棒,除了如果您有 0 条记录,它会导致无限循环。 (我在本地测试我的一些报告时发现了这一点)。

                我修改了 while 循环的开始,如下所示:

                while count % 1000 == 0:
                    current_count = query.count()
                    if current_count == 0:
                        break
                

                【讨论】:

                  【解决方案13】:

                  将两个查询的内容相加:

                  list1 = first query
                  list2 = second query
                  list1 += list2
                  

                  列表 1 现在包含所有 2000 个结果。

                  【讨论】:

                  • 这在 普通 数据库中很好,但在带有 GQL 的 GAE 中则不行。 GQL 有一个硬性限制。基于 LIMIT/OFFSET 的增加对您没有帮助,它们必须是不同的查询,即:不同的 WHERE 条件。
                  • 我同意(并赞成)您的回答。我的重点是关注 python 问题。一旦你有两个列表(使用不同的 where 条件),你需要合并它们。这非常简单,但你错过了他的一部分问题。
                  • 最好警告他任何 GAE 对象的大小也可能受到限制。在测试期间,它是 1 兆字节。
                  【解决方案14】:

                  仅当条目按键排序时,建议的解决方案才有效...如果您首先按另一列排序,您仍然必须使用 limit(offset, count) 子句,那么 1000 个条目的限制仍然适用。如果您使用两个请求也是一样的:一个用于检索索引(带有条件和排序),另一个使用 where index in () 和来自第一个结果的索引子集,因为第一个请求不能返回超过 1000 个键? (Google Query on Keys 部分没有明确说明我们是否必须按 key 排序以消除 1000 个结果的限制)

                  【讨论】:

                    【解决方案15】:

                    这与 Gabriel 提供的解决方案很接近,但不获取它只是计算它们的结果:

                    count = 0
                    q = YourEntityClass.all().filter('myval = ', 2)
                    countBatch = q.count()
                    while countBatch > 0:
                        count += countBatch
                        countBatch = q.with_cursor(q.cursor()).count()
                    
                    logging.info('Count=%d' % count)
                    

                    非常适合我的查询,而且速度也很快(1.1 秒即可计算 67,000 个实体)

                    请注意,查询不能是不等式过滤器或集合,否则光标将不起作用,您将收到此异常:

                    AssertionError: 没有可用于 MultiQuery 的游标(使用“IN”或“!=" 运算符的查询)

                    【讨论】:

                      【解决方案16】:

                      如果您使用的是 NDB:

                      @staticmethod
                      def _iterate_table(table, chunk_size=200):
                          offset = 0
                          while True:
                              results = table.query().order(table.key).fetch(chunk_size + 1, offset=offset)
                              if not results:
                                  break
                              for result in results[:chunk_size]:
                                  yield result
                              if len(results) < chunk_size + 1:
                                  break
                              offset += chunk_size
                      

                      【讨论】:

                        猜你喜欢
                        • 2019-06-16
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2021-03-16
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多