クエリは複数の不等式フィルタをかけることができない

Google App Engine のデータストアを利用していると、次のようなフィルタに関するエラーに直面することがあります。

BadFilterError: BadFilterError: invalid filter: Only one property per query may have inequality filters (<=, >=, <, >)..

これはあるクエリオブジェクトに対して、複数の不等式フィルタ<<=>=>!=)を掛けてしまったために発生する例外です。
例えば以下のような例です。

query.filter('rating >=', 5)
query.filter('view_count >=', 1000)  # BadFilterError
query.fetch(100)

この件について、リファレンスの解説を引用します。

クエリ メカニズムは、結果を得るためにテーブル全体をスキャンする必要のないように、クエリのすべての結果がインデックス テーブル内で隣接し合うことで成り立っています。単一のインデックス テーブルは、すべての結果をテーブル内で連続させながら、複数のプロパティに対する複数の不等式フィルタを表すことはできません。

早い話、2つ目のフィルタリング処理は fetch() 後の list を自前でどうにかするしかないということです。

  • 解決策

上記の場合、クエリの order(property) を使って以下のように書くことができます。

query.filter('rating >=', 5)
query.order('-rating')  # 他の並び替え順序より先に、不等式フィルタのプロパティを並び替える
query.order('-view_count')
results = []
for obj in query:
  if obj.view_count < 1000:
    continue
  results.append(obj)
  if len(results) >= 10:
    break

ここで注意しなければならないのが、「他の並び替え順序より先に、不等式フィルタのプロパティを並び替える必要がある」というクエリ制限です。
以下、リファレンスからの引用。

クエリに不等式比較のフィルタと 1 つ以上の並び替え順序がある場合、クエリは不等式に使用されるプロパティの並び替え順序を含んでいる必要があり、この並び替え順序は他のプロパティに対する並び替え順序より先に出現する必要があります。

さもなければ、以下のような例外が発生するでしょう。

BadArgumentError: First ordering property must be the same as inequality filter property, if specified for this query; received view_count, expected rating

要するに、不等式フィルタを用いる rating プロパティに対する order() メソッドは、view_count プロパティに対する order() メソッドより前になければならないということです。

query.filter('rating >=', 5)
query.order('-view_count')  # BadArgumentError
query.order('-rating')

ちなみに、filter() メソッドは order() メソッドの後に実行しても構いません。

query.order('-rating')
query.order('-view_count')
query.filter('rating >=', 5)  # ok

このようなクエリに対する注意点を説明したリファレンスページはこちら