データストアから1000を超えるレコードをフェッチし、すべてを1つのリストに入れてdjangoに渡すにはどうすればよいですか?
バージョン1.3.6(2010年8月17日リリース)以降、あなた[〜#〜]できます[〜#〜]
データストアcount()クエリの結果およびすべてのデータストアクエリのオフセットは、1000で制限されなくなりました。
記録のためだけに-1000エントリのフェッチ制限がなくなりました。
http://googleappengine.blogspot.com/2010/02/app-engine-sdk-131-include-major.html
引用:
1000件の結果の制限はありません-そうです。カーソルの追加と、過去数か月にわたる多くの小さなDatastoreの安定性とパフォーマンスの向上の集大成により、最大の結果の制限を完全に取り除くのに十分です。フェッチ、反復、またはCursorを使用している場合でも、結果の数に制限はありません。
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
これが制限として現れるたびに、「なぜ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を超える結果をユーザーに返している場合は、データストアで許可されていないという事実よりも大きな問題があると思います。
多くの結果が必要になる可能性のある(正当な)理由の1つは、データに対して大規模な操作を実行し、要約(たとえば、このすべてのデータの平均値)を提示している場合です。この問題(Google I/Oトークで説明されています)の解決策は、要約データを即座に計算し、それを保存することです。
できません。
FAQの一部は、クエリの行1000を超えてアクセスできる方法はないと述べています。「OFFSET」を増やすと、結果セットが短くなります。
つまり、OFFSET 999-> 1つの結果が返されます。
ウィキペディアから:
App Engineは、エンティティから返される最大行数を、データストア呼び出しあたり1000行に制限します。ほとんどのWebデータベースアプリケーションはページングとキャッシングを使用しているため、一度にこれほど多くのデータを必要としないため、ほとんどのシナリオでこれは問題になりません。独自のクライアント側ソフトウェアまたはAjaxページを使用して、無制限の行数で操作を実行できます。
http://code.google.com/appengine/docs/whatisgoogleappengine.html から
サービス制限の別の例は、クエリによって返される結果の数です。クエリは最大1,000件の結果を返すことができます。より多くの結果を返すクエリは、最大値のみを返します。この場合、そのようなクエリを実行するリクエストは、タイムアウト前にリクエストを返す可能性は低いですが、データストア上のリソースを節約するために制限が設けられています。
From http://code.google.com/appengine/docs/datastore/gqlreference.html から
注:LIMIT句の最大値は1000です。最大値より大きい制限が指定されている場合、最大値が使用されます。これと同じ最大値がGqlQueryクラスのfetch()メソッドに適用されます。
注:fetch()メソッドのオフセットパラメータと同様に、GQLクエリ文字列のOFFSETは、データストアからフェッチされるエンティティの数を減らしません。 fetch()メソッドによって返される結果にのみ影響します。オフセットのあるクエリには、オフセットサイズに線形に対応するパフォーマンス特性があります。
http://code.google.com/appengine/docs/datastore/queryclass.html から
Limitおよびoffset引数は、データストアからフェッチされる結果の数、およびfetch()メソッドによって返される結果の数を制御します。
データストアは、オフセット+制限の結果をアプリケーションにフェッチします。最初のオフセット結果は、notであり、データストア自体によってスキップされます。
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クエリの制限を回避できます。鍵バージョンの実装も同じくらい簡単だと思います。
entities = []
for entity in Entity.all():
entities.append(entity)
そのような単純な。チャンクでフェッチするよりもはるかに遅いすべてのエンティティに対して作成されたRPCがあることに注意してください。したがって、パフォーマンスが気になる場合は、次の手順を実行します。
アイテムが100万個未満の場合:
entities = Entity.all().fetch(999999)
それ以外の場合は、カーソルを使用します。
次の点にも注意してください。
Entity.all().fetch(Entity.all().count())
最大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
JJG:上記のソリューションは素晴らしいですが、レコードが0の場合に無限ループが発生する点が異なります。 (一部のレポートをローカルでテストしているときにこれがわかりました)。
Whileループの開始を次のように変更しました。
while count % 1000 == 0:
current_count = query.count()
if current_count == 0:
break
2つのクエリの内容を一緒に追加するには:
list1 = first query
list2 = second query
list1 += list2
リスト1には2000件の結果がすべて含まれています。
これは、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)
私のクエリに対して完全に機能し、高速でもあります(67秒のエンティティをカウントするのに1.1秒)
クエリが不等式フィルターまたはセットであってはなりません。そうでないと、カーソルが機能せず、次の例外が発生します。
AssertionError:マルチクエリで使用できるカーソルがありません( "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
提案されたソリューションは、エントリがキーでソートされている場合にのみ機能します...最初に別の列でソートしている場合でも、limit(offset、count)句を使用する必要がありますが、1000エントリの制限が引き続き適用されます。最初のリクエストは1000個を超えるキーを返すことができないため、2つのリクエストを使用する場合も同じです。 (Google キーのクエリセクションでは、keyでソートして1000件の結果の制限を削除する必要があるかどうかを明確に述べていません)