一連のキーのどれがデータストアに存在するかを判断する最適な方法
-
05-07-2019 - |
質問
事前に計算した同じモデルのすべてのキーが数百個あります:
candidate_keys = [db.Key(...), db.Key(...), db.Key(...), ...]
これらのキーの一部はデータストア内の実際のエンティティを参照し、一部は参照しません。どのキーがエンティティに対応しているかを判断したい。
エンティティ内のデータを知る必要はありません。存在するかどうかだけです。
1つの解決策は、db.get()を使用することです。
keys_with_entities = set()
for entity in db.get(candidate_keys):
if entity:
keys_with_entities.add(entity.key())
ただし、このプロシージャはストアからすべてのエンティティデータをフェッチするため、不要でコストがかかります。
2番目のアイデアは、 IN
フィルターで key_name
を使用してクエリを使用し、 IN コード>擬似フィルター。ただし、キーのみのクエリは
IN
フィルターでは許可されていません。
もっと良い方法はありますか?
解決
INフィルターはApp Engineデータストアで直接サポートされていません。クライアントライブラリに実装されている便利さです。 30個の値を持つINクエリは、それぞれ1つの値に対して30個の等式クエリに変換され、30個の通常クエリになります!
ラウンドトリップ時間とキーのみのクエリの費用のため、1回のバッチフェッチですべてのエンティティをフェッチしようとするのが最も効率的であることがわかると思います。ただし、エンティティが大きい場合は、さらに最適化を行うことができます。挿入するすべてのエンティティに対して、空の「存在」エンティティをそのエンティティの子として挿入し、クエリで使用します。例:
foo = AnEntity(...)
foo.put()
presence = PresenceEntity(key_name='x', parent=foo)
presence.put()
...
def exists(keys):
test_keys = [db.Key.from_path('PresenceEntity', 'x', parent=x) for x in keys)
return [x is not None for x in db.get(test_keys)]
他のヒント
この時点で、唯一の解決策は、キーごとに1回、 keys_only = True
を使用してキーで手動でクエリを実行することです。
for key in candidate_keys:
if MyModel.all(keys_only=True).filter('__key__ =', key).count():
keys_with_entities.add(key)
これは実際には、エンティティをバッチでロードして破棄するよりも遅い可能性がありますが、バッチロードは APIから受信したデータ
クォータも破壊します。
それをしない方法(Nick Johnsonの回答に基づいて更新):
また、 IN
フィルターでスキャンできるようにするために、パラメーターを追加することも検討しています。
class MyModel(db.Model):
"""Some model"""
# ... all the old stuff
the_key = db.StringProperty(required=True) # just a duplicate of the key_name
#... meanwhile back in the example
for key_batch in batches_of_30(candidate_keys):
key_names = [x.name() for x in key_batch]
found_keys = MyModel.all(keys_only=True).filter('the_key IN', key_names)
keys_with_entities.update(found_keys)
これを避けるべき理由は、プロパティのINフィルターがインデックススキャンを順次実行し、さらに IN
セット内のアイテムごとに1回検索するためです。各検索には160〜200ミリ秒かかるため、非常に迅速に非常に遅い操作になります。