複数のプロパティに対する範囲フィルタと不等式フィルタを使用したクエリの概要

Datastore モードの Firestore では、単一のクエリで複数のプロパティに対する範囲フィルタと不等式フィルタの使用をサポートします。この機能を使用すると、複数のプロパティに対する範囲条件と不等式条件を含むことができるようになり、フィルタリング後のロジックの実装を Datastore モードの Firestore に委任することで、アプリケーション開発を簡素化できます。

複数のプロパティに対する範囲フィルタと不等式フィルタ

次のクエリは、優先度と日数に範囲フィルタを使用して、優先度が 4 より大きく、完了までに 3 日未満のタスクをすべて返します。

Go

query := datastore.NewQuery("Task").
   FilterField("priority", ">", 4).
   FilterField("days", "<", 3).

GQL

SELECT * FROM /tasks WHERE priority > 4 AND days < 3;

Java

Query<Entity> query =
    Query.newEntityQueryBuilder()
      .setKind("Task")
      .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("priority", 4), PropertyFilter.lt("days", 3)))
    .build();

Node.js

const query = datastore
  .createQuery('Task')
  .filter(
    and([
      new PropertyFilter('priority', '>', 4),
      new PropertyFilter('days', '<', 3),
    ])
  );

Python

from google.cloud import datastore
client = datastore.Client()
query = client.query(kind="Task")
query.add_filter(filter=PropertyFilter("priority", ">", 4))
query.add_filter(filter=PropertyFilter("days", "<", 3))

PHP

$query = $datastore->query()
    ->kind('Task')
    ->filter('priority', '>', 4)
    ->filter('days', '<', 3)

C#

Query query = new Query("Task")
{
  Filter = Filter.And(Filter.GreaterThan("priority", 4),
    Filter.LessThan("days", 3))
};

Ruby

query = datastore.query("Task")
                 .where("priority", ">", 4)
                 .where("days", "<", 3)

検討事項のインデックス登録

クエリの実行を開始する前に、クエリの説明をご確認ください。

ただし、ORDER BY 句が指定されていない場合、Datastore モードの Firestore は、クエリのフィルタ条件を満たすインデックスを使用してクエリを処理します。このアプローチでは、インデックス定義に従って順序付けられた結果セットが生成されます。

Datastore モードの Firestore クエリのパフォーマンスと費用を最適化するには、インデックス内のプロパティの順序を最適化します。これを行うには、不要なインデックス エントリのスキャンを防ぐデータセットにクエリが抽出するようにインデックスを左から右に並べる必要があります。

たとえば、従業員のコレクションを検索して、給与が $100,000 を超え、経験年数が 0 を超える米国の従業員を見つけるとします。データセットに関する知識に基づき、給与の制約は経験の制約よりも選択的であることがわかります。インデックス スキャンの回数を減らすインデックスは (salary [...], experience [...]) インデックスです。したがって、高速で費用対効果の高いクエリは、次の例に示すように、salaryexperience の前に並べ替えます。

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY salary, experience

Java

Query<Entity> query =
  Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("salary"), OrderBy("experience"))
    .build();

Node.js

const query = datastore
  .createQuery("employees")
  .filter(
    and([
      new PropertyFilter("salary", ">", 100000),
      new PropertyFilter("experience", ">", 0),
       ])
    )
  .order("salary")
  .order("experience");

Python

query = client.query(kind="employees")
query.add_filter("salary", ">", 100000)
query.add_filter("experience", ">", 0)
query.order = ["-salary", "-experience"]

インデックスの最適化のベストプラクティス

インデックスを最適化する場合は、次のベスト プラクティスを念頭に置いてください。

等式ごとにクエリを並べ、最も選択的な範囲または不等式フィールドが続く

Datastore モードの Firestore は、複合インデックスの左端のプロパティを使用して、orderBy() クエリの最初のフィールドに対する等式の制約と範囲および不等式の制約(存在する場合)を満たします。これらの制約により、Datastore モードの Firestore がスキャンするインデックス エントリの数を減らすことができます。Datastore モードの Firestore は、インデックスの残りのプロパティを使用して、クエリの他の範囲の制約と不等式の制約を満たします。これらの制約によって、Datastore モードの Firestore がスキャンするインデックス エントリの数は減りませんが、一致しないエンティティは除外されるため、クライアントに返されるエンティティ数が減ります。

効率的なインデックスの作成の詳細については、インデックスの構造と定義インデックスの最適化をご覧ください。

クエリ制約の選択性の降順でプロパティを並べ替える

Datastore モードの Firestore がクエリに最適なインデックスを選択するようにするには、クエリで制約の選択性が最も高い順に、範囲と不等式プロパティを並べ替える orderBy() 句を指定します。選択性が高いほど、一致するエンティティが少なくなり、選択性が低いほど、一致するエンティティが多くなります。インデックスの順序付けでは、選択性が高い範囲と不等式のプロパティを、選択性が低いプロパティの前に配置します。

Datastore モードの Firestore がネットワーク経由でスキャンして返すエンティティの数を最小限に抑えるには、常にクエリ制約の選択性降順でプロパティを並べ替える必要があります。結果セットが必要な順序ではなく、結果セットのサイズが小さいと予想される場合、クライアントサイドのロジックを実装して、順序の想定に沿って順序を並べ替えることができます。

たとえば、従業員のコレクションを検索して、給与が $100,000 を超える米国の従業員を見つけて、結果を従業員の経験年数で並べ替えるとします。少数の従業員のみ給与が 100,000 ドルを超えることが予想される場合は、クエリを作成する効率的な方法は次のとおりです。

Java

Query<Entity> query =
  Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(PropertyFilter.gt("salary", 100000))
    .setOrderBy(OrderBy("salary"))
    .build();
QueryResults<Entity> results = datastore.run(query);
// Order results by `experience`

Node.js

const query = datastore
  .createQuery("employees")
  .filter(new PropertyFilter("salary", ">", 100000))
  .order("salary");
const [entities] = await datastore.runQuery(query);
// Order results by `experience`

Python

query = client.query(kind="employees")
query.add_filter("salary", ">", 100000)
query.order = ["salary"]
results = query.fetch()
// Order results by `experience`

experience に対する順序付けをクエリに追加すると、同じエンティティのセットが生成され、クライアントでの結果の順序の並び替えは不要ですが、クエリは前のクエリよりも多くの不要なインデックス エントリを読み取る場合があります。これは、Datastore モードの Firestore が、インデックス プロパティの接頭辞がクエリの ORDER BY 句と一致するインデックスを常に優先するためです。experience が ORDER BY 句に追加された場合、Datastore モードの Firestore はクエリ結果を計算するために (experience [...], salary [...]) インデックスを選択します。experience には他の制約がないため、Datastore モードの Firestore は、salary フィルタを適用する前に employees コレクションのすべてのインデックス エントリを読み取り、最終結果セットを見つけます。つまり、salary フィルタを満たさないインデックス エントリがまだ読み取られるため、クエリのレイテンシとコストが増加します。

料金

複数のプロパティに対して範囲フィルタと不等式フィルタを使用するクエリは、読み取られたエンティティと読み取られたインデックス エントリに基づいて請求されます。

詳細については、料金ページをご覧ください。

制限事項

クエリの制限とは別に、複数のプロパティに対する範囲フィルタと不等式フィルタでクエリを使用する前に、次の制限事項に注意してください。

  • クエリの実行コストが高くなりすぎないように、Datastore モードの Firestore では、範囲演算子または不等式演算子の数が 10 に制限されています。

次のステップ