データストアのクエリ

昨日の続きです。

複数のプロパティに関するフィルタ

ここで注目したい内容があります。それはインデックスのプロパティの順番です。先ほどのクエリはこんな感じでした。

SELECT * FROM Player WHERE charclass = 'ヒーロー' AND level < 10


ここでプロパティの出現順序はもちろん、charclass (クラス) → level (レベル) です。でインデックスはこんな感じです。

キー クラス レベル


素直に出現順序のまま並んでいます。では次のクエリだとどうなるでしょうか?

SELECT * FROM Player WHERE level < 10 AND charclass = 'ヒーロー'


実はインデックスのプロパティの出現順序は変わりません。つまり等号フィルタに利用されているプロパティの方がインデックスでは先に出現する、ということです。本書ではこのルールのことを不等号フィルタの第1ルールと書かれています。ちなみに出現順がGQLの出現順と一致していると次のようなインデックスになります。

キー レベル クラス
ゴレンジャーのキー 2 ヒーロー
ウルトラマンのキー 7 ヒーロー
バルタン星人のキー 21 怪獣
仮面ライダーのキー 22 ヒーロー
ショッカーのキー 24 悪者


単純な行のスキャンでは目的の結果が得られないのが分かります。うまく出来てますね。

では次のようなパターンの場合はどうでしょうか?

SELECT * FROM Player WHERE level < 10 ORDER BY score


levelプロパティの値が10未満のエンティティをscoreプロパティの値を昇順にソートするクエリです。この時のインデックスがどうなっているのか?たとえばこんな感じはどうでしょうか。

キー レベル 得点
ゴレンジャー 2 2003
ウルトラマン 7 1366
バルタン星人 21 2003
仮面ライダー 22 1096
ショッカー 24 33311


あれれ?目的の結果が得られませんね。scoreプロパティの値が昇順にソートされていません。かと言ってレベルと得点の出現順序を逆転させても当然うまく行きません。

キー 得点 レベル
仮面ライダー 1096 22
ウルトラマン 1366 7
ゴレンジャー 2003 2
バルタン星人 2003 21
ショッカー 33311 24


通常このようにフィルタで利用しているプロパティ以外のプロパティでソートする場合、明示的にフィルタで利用しているプロパティのソート順序を指定する必要があります (そうしないと例外が発生します) 。こんな感じ。

SELECT * FROM Player WHERE level < 10 ORDER BY level, score


この場合、次のようなインデックスが作られます。

キー レベル 得点
ゴレンジャー 2 2003
ウルトラマン 7 1366
バルタン星人 21 2003
仮面ライダー 22 1096
ショッカー 24 33311


ここで得られる結果はこんな感じ。

キー レベル 得点
ゴレンジャー 2 2003
ウルトラマン 7 1366


う〜ん、やっぱり得点ではソート出来ないのですね。SQLであれば、きっと

キー レベル 得点
ウルトラマン 7 1366
ゴレンジャー 2 2003


という結果が得られるはずです。これはApp Engineの (データストアの) 制約なんでしょうかね。むしろこのような制約があるから、不等号フィルタで利用しているプロパティでソート指定させているんでしょうね。そうしないとこの結果は不正ですからね。

このように不等号フィルタで利用しているプロパティではじめにソートする必要がある、というルールのことを本書では不等号フィルタの第2ルールと呼んでいます。


そしてデータストアの最大の制約と思える項目があります。たとえば以下のクエリを考えてみます。

SELECT * FROM Player WHERE level < 10 AND score < 500


SQLではいかにもありそうなクエリですよね。でもこのクエリはApp Engineでは不正なクエリとなります。たとえば次のようなエンティティの列があったとします。

レベル 得点
8 498
10 498
9 499
8 500


このようなエンティティ列に関しては、どのようにインデックスを作成しても目的のエンティティ列は取得出来ません。

レベル → 得点の順でソートした場合のインデックス

レベル 得点
8 498
8 500
9 499
10 498


得点 → レベルの順でソートした場合のインデックス

得点 レベル
498 8
498 10
499 9
500 8


どちらも目的のエンティティ列を取得することは出来ません。連続した行として表現出来ないのです。

これが本書で不等号フィルタの第3のルールと呼ばれている内容です。つまり一つのクエリで複数のプロパティに対して不等号フィルタは使えないのです。興味深い制約ですよね。

なお、同じプロパティに対して複数の不等号フィルタを使うことは可能です (範囲指定とか) 。