1000以上を取得する方法?
-
06-07-2019 - |
質問
データストアから1000を超えるレコードをフェッチし、すべてを1つのリストに入れてdjangoに渡すにはどうすればよいですか?
解決
バージョン1.3.6(2010年8月17日リリース)以降、 CAN
データストアcount()クエリの結果およびすべてのデータストアクエリのオフセットは、1000で制限されなくなりました。
他のヒント
記録のためだけに、1000エントリのフェッチ制限がなくなりました:
http://googleappengine.blogspot。 com / 2010/02 / app-engine-sdk-131-include-major.html
引用:
結果の上限は1000個です-それは 右:カーソルと 多くの小規模の集大成 データストアの安定性とパフォーマンス 過去数か月間の改善、 これで削除できると確信しています 結果の最大制限。 フェッチを行うかどうか、 繰り返し、またはカーソルを使用して、 結果の数に制限はありません。
App Engineは、「ページング」の優れた方法を提供します。 Keysで順序付けし、最後のキーを次のオフセットとして使用して、結果を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=ja&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」を増やしています。結果セットが短くなります
ie:オフセット999-> 1つの結果が返されます。
ウィキペディアから:
App Engineは最大行を制限します エンティティから返された1000 データストア呼び出しごとの行。ほとんどのウェブ データベースアプリケーションはページングと キャッシング、したがって、これを必要としません 一度に多くのデータを取得するため、これは ほとんどのシナリオで問題なし。[引用 必要]アプリケーションがさらに必要な場合 操作ごとに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()メソッド、GQLのOFFSET クエリ文字列は、 から取得されたエンティティの数 データストア。どちらにのみ影響します 結果はfetch()によって返されます 方法。オフセット付きのクエリには パフォーマンス特性 オフセットと線形に対応 サイズ。
http://code.google.com/appengine/docs/からdatastore / queryclass.html
制限およびオフセット引数制御 から取得される結果の数 データストア、および返される数 fetch()メソッドによる:
データストアは、オフセット+制限結果をアプリケーションにフェッチします。最初のオフセット結果は、データストア自体によってスキップされません。
fetch()メソッドは最初のオフセット結果をスキップし、残りを返します(結果の制限)。
クエリには、対応するパフォーマンス特性があります オフセット量に制限を加えた直線的に。
これが意味するもの
単一のクエリがある場合、0〜1000の範囲外のものを要求する方法はありません。
オフセットを増やすと0が上がるだけなので、
LIMIT 1000 OFFSET 0
1000行を返します、
and
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
2つのクエリのコンテンツを一緒に追加するには:
list1 = first query
list2 = second query
list1 += list2
リスト1には2000件の結果がすべて含まれています。
提案されたソリューションは、エントリがキーでソートされている場合にのみ機能します...別の列でソートする場合、limit(offset、count)句を使用する必要がありますが、1000エントリの制限が適用されます。最初のリクエストは1000以上のキーを返すことができないため、2つのリクエストを使用する場合も同じです:1つは(条件およびソートを伴う)インデックスの取得用、もう1つは最初の結果からのインデックスのサブセットを持つwhere index in()を使用するためですか? (Googleのキーに関するクエリセクションには、キーで並べ替えて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)
クエリに対して完全に機能し、高速(67,000エンティティをカウントするのに1.1秒)
クエリが不等式フィルターまたはセットであってはならないことに注意してください。そうでない場合、カーソルは機能せず、この例外が発生します。
AssertionError:MultiQueryで使用可能なカーソルがありません(&quot; IN&quot;または&quot;!=&quot;演算子を使用したクエリ)
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