ソースは「Building_Scalable_Complex_App_Engines」です。2009年の資料なんですね……完全に見落としてました。
まずよくある多対多のリレーションにおけるデータ検索の正直な実装……例えば複数の送信先のあるメッセージの中から、自分宛てのメッセージを取る、みたいな場合はこんな感じです。
class Message(db.Model): receivers = db.ListProperty(db.Key) message = db.TextProperty() messages_to_me = Message.all().filter("receivers =", me).fetch(100)
でもこれだと該当するデータを探している最中に、message(TextProperty)までメモリにロードすることになるから、オーバヘッドが大きいらしいです。
だから検索を速く軽くするために、検索に関係する部分のモデルだけをIndex Tableとして切り出して、実部分をその親のキーとする、っていうテクニックを使うと、検索が速くなるんだそうな(資料によると十倍ほど速く軽くなるんだそうです)。
from google.appengine.ext import db # メッセージ本体 class Message(db.Model): message = db.TextProperty() # 検索に使われる部分だけ抜き出したモデル class MessageIndex(db.Model): receivers = db.ListProperty(db.Key) message = Message( message = "hello, world" ) message.put() index = MessageIndex( parent = message.key(), # 親にMessageモデルのキーを設定 receivers = [me.key(), person1.key(), person2.key()] ) index.put() index_keys = MessageIndex.all(keys_only=True).filter("receivers =", me).fetch(100) # keyだけ取る message_keys = map(lambda key: key.parent(), index_keys) # 親モデルのキーにマップ messages = db.get(message_keys) # 実データを取る
該当データの検索処理がMessageIndexテーブルへの処理で完結しているのと、keys_onlyを真にしてkeyだけを取得し、Datastoreがプロパティを展開するオーバーヘッドを減らしているのがポイントです。
しかしこのkeys_onlyって、こちらの日本語のドキュメントには記されていないのが不思議ですね(こちらの英語のドキュメントには記されている)。