如何从数据存储中获取超过 1000 条记录并将所有记录放在一个列表中以传递给 django?
16 回答
仅作记录 - 1000 个条目的获取限制现在已经消失:
http://googleappengine.blogspot.com/2010/02/app-engine-sdk-131-include-major.html
引述:
不再有 1000 个结果限制 - 没错:随着游标的添加以及过去几个月中许多较小的数据存储稳定性和性能改进达到顶峰,我们现在有足够的信心完全取消最大结果限制。无论您是进行获取、迭代还是使用游标,结果的数量都没有限制。
App Engine 为您提供了一种通过对 Key 进行排序并使用最后一个键作为下一个偏移量来将结果“分页”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
每次遇到这个限制时,我总是想知道“为什么需要超过 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 talk 中讨论过)是动态计算汇总数据,当它进入时,并保存它。
你不能。
部分常见问题解答指出,您无法访问超出查询的第 1000 行,增加“OFFSET”只会导致更短的结果集,
即:OFFSET 999 --> 1 结果回来了。
来自维基百科:
App Engine 将从实体返回的最大行数限制为每次 Datastore 调用 1000 行。大多数 Web 数据库应用程序使用分页和缓存,因此一次不需要这么多数据,因此在大多数情况下这不是问题。[需要引用] 如果应用程序每次操作需要超过 1,000 条记录,它可以使用它的拥有客户端软件或 Ajax 页面以对无限数量的行执行操作。
来自http://code.google.com/appengine/docs/whatisgoogleappengine.html
服务限制的另一个示例是查询返回的结果数。一个查询最多可以返回 1,000 个结果。将返回更多结果的查询仅返回最大值。在这种情况下,执行此类查询的请求不太可能在超时之前返回请求,但存在限制以节省数据存储上的资源。
来自http://code.google.com/appengine/docs/datastore/gqlreference.html
注意:LIMIT 子句的最大值为 1000。如果指定的限制大于最大值,则使用最大值。同样的最大值适用于 GqlQuery 类的 fetch() 方法。
注意:与 fetch() 方法的 offset 参数一样,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”硬编码标识符,那么您就不走运了
此 1K 限制问题已解决。
query = MyModel.all()
for doc in query:
print doc.title
通过将 Query 对象视为可迭代对象:迭代器以小批量从数据存储中检索结果,允许应用程序停止对结果进行迭代以避免获取超出需要的数据。当检索到与查询匹配的所有结果时,迭代停止。与 fetch() 一样,迭代器接口不缓存结果,因此从 Query 对象创建新的迭代器将重新执行查询。
最大批量大小为 1K。而且您仍然拥有自动数据存储配额。
但是在 1.3.1 SDK 计划中,他们引入了可以序列化和保存的游标,以便将来的调用可以从上次停止的地方开始查询。
1000 条记录限制是 Google AppEngine 中的硬限制。
此演示文稿http://sites.google.com/site/io/building-scalable-web-applications-with-google-app-engine解释了如何使用 AppEngine 有效地对数据进行分页。
(基本上通过使用数字 id 作为键并在 id 上指定 WHERE 子句。)
当超过 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
我们在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 个查询限制。我想密钥版本同样容易实现。
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
entities = []
for entity in Entity.all():
entities.append(entity)
就那么简单。请注意,每个实体都有一个 RPC,它比以块的形式获取要慢得多。因此,如果您担心性能,请执行以下操作:
如果您的商品少于 100 万件:
entities = Entity.all().fetch(999999)
否则,使用游标。
还应该注意的是:
Entity.all().fetch(Entity.all().count())
返回最大值 1000,不应使用。
JJG:您上面的解决方案很棒,除了如果您有 0 条记录,它会导致无限循环。(我在本地测试我的一些报告时发现了这一点)。
我修改了 while 循环的开始,如下所示:
while count % 1000 == 0:
current_count = query.count()
if current_count == 0:
break
要将两个查询的内容加在一起:
list1 = first query
list2 = second query
list1 += list2
列表 1 现在包含所有 2000 个结果。
建议的解决方案仅在条目按键排序时才有效...如果您首先按另一列排序,您仍然必须使用 limit(offset, count) 子句,那么 1000 个条目的限制仍然适用。如果您使用两个请求也是一样的:一个用于检索索引(带有条件和排序),另一个使用 where index in () 和来自第一个结果的索引子集,因为第一个请求不能返回超过 1000 个键?(Google Queries on Keys部分没有明确说明我们是否必须按键排序以消除 1000 个结果的限制)
这接近 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”或“!=”运算符的查询)
如果您使用 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