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

Firestore では、1 つのクエリで複数のフィールドに対する範囲フィルタと不等式フィルタの使用をサポートします。複数のフィールドに範囲条件と不等式条件を使用し、フィルタリング後のロジックの実装を Firestore に委任することで、アプリケーション開発を簡素化できます。

複数のフィールドに対する範囲フィルタと不等式フィルタ

次のクエリでは、人口と密度に対する範囲フィルタを使用して、人口が 1,000,000 人を超え、かつ人口密度が単位面積あたり 10,000 人未満の都市を返します。

ウェブ バージョン 9 のモジュラー

const q = query(
    collection(db, "cities"),
    where('population', '>', 1000000),
    where('density', '<', 10000),
  );

Swift

let query = db.collection("cities")
  .whereField("population", isGreaterThan: 1000000)
  .whereField("density", isLessThan: 10000)

Objective-C

FIRQuery *query =
 [[[[self.db collectionWithPath:@"cities"]
queryWhereField:@"population" isGreaterThan:@1000000]
   queryWhereField:@"density" isLessThan:@10000];

Java Android

Query query = db.collection("cities")
 .whereGreaterThan("population", 1000000)
 .whereLessThan("density", 10000);

Kotlin+KTX Android

val query = db.collection("cities")
 .whereGreaterThan("population", 1000000)
 .whereLessThan("density", 10000)

Go

   query := client.Collection("cities").
      Where("population", ">", 1000000).
      Where("density", "<", 10000)

Java

db.collection("cities")
  .whereGreaterThan("population", 1000000)
  .whereLessThan("density", 10000);

Node.js

db.collection("cities")
  .where('population', '>', 1000000),
  .where('density', '<', 10000)

Python

from google.cloud import firestore

db = firestore.Client()
query = db.collection("cities")
.where("population", ">", 1000000)
.where("density", "<", 10000)

PHP

$collection = $db->collection('samples/php/cities');
$chainedQuery = $collection
    ->where('population', '>', 1000000)
    ->where('density', '<', 10000);

C#

CollectionReference citiesRef = db.Collection("cities");
Query query = citiesRef
    .WhereGreaterThan("Population", 1000000)
    .WhereLessThan("Density", 10000);
QuerySnapshot querySnapshot = await query.GetSnapshotAsync();
foreach (DocumentSnapshot documentSnapshot in querySnapshot)
{
    var name = documentSnapshot.GetValue<string>("Name");
    var population = documentSnapshot.GetValue<int>("Population");
    var density = documentSnapshot.GetValue<int>("Density");
    Console.WriteLine($"City '{name}' returned by query. Population={population}; Density={density}");
}

Ruby

query = cities_ref.where("population", ">", "1000000")
                  .where("density", "<", 10000)

C++

CollectionReference cities_ref = db->Collection("cities");
Query query = cities_ref.WhereGreaterThan("population", FieldValue::Integer(1000000))
                       .WhereLessThan("density", FieldValue::Integer(10000));

Unity

CollectionReference citiesRef = db.Collection("cities");
Query query = citiesRef.WhereGreaterThan("population", 1000000)
                      .WhereLessThan("density", 10000);

Dart

final citiesRef = FirebaseFirestore.instance.collection('cities')
final query = citiesRef.where("population", isGreaterThan: 1000000)
                  .where("density", isLessThan: 10000);

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

クエリを実行する前に、クエリと Firestore のデータモデルについて読んでおいてください。

Firestore では、クエリの ORDER BY 句によって、どのインデックスをクエリの処理に使用できるかが決まります。たとえば、ORDER BY a ASC, b ASC クエリでは、a ASC, b ASC フィールドに対する複合インデックスが必要です。

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

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

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .whereGreaterThan("experience", 0)
  .orderBy("salary")
  .orderBy("experience");

Node.js

db.collection("employees")
  .where("salary", ">", 100000)
  .where("experience", ">", 0)
  .orderBy("salary")
  .orderBy("experience");

Python

db.collection("employees")
  .where("salary", ">", 100000)
  .where("experience", ">", 0)
  .order_by("salary")
  .order_by("experience");

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

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

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

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

効率的なインデックスの作成の詳細については、インデックス プロパティをご覧ください。

クエリ制約の選択性の降順でフィールドを並べ替える

Firestore がクエリに最適なインデックスを選択するようにするには、クエリ制約の選択性の降順でフィールドを並べ替える orderBy() 句を指定します。選択性が高いほど、一致するドキュメントのサブセットは小さくなりますが、選択性が低いほど、一致するドキュメントのサブセットは大きくなります。選択性が低いフィールドよりも、インデックスの順序の早い段階で高い選択性を持つ範囲または不等式フィールドを選択するようにしてください。

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

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

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .orderBy("salary")
  .get()
  .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
          // Order results by `experience`
        }
    });;

Node.js

const querySnapshot = await db.collection('employees')
                              .where("salary", ">", 100000)
                              .orderBy("salary")
                              .get();

// Order results by `experience`

Python

results = db.collection("employees")
            .where("salary", ">", 100000)
            .order_by("salary")
            .stream()

// Order results by `experience`

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

料金

複数のフィールドに対して範囲フィルタと不等式フィルタを使用するクエリは、ドキュメントの読み取りとインデックス エントリの読み取りに基づいて請求されます。

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

制限事項

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

  • ドキュメント フィールドに対する範囲フィルタまたは不等式フィルタがあり、ドキュメント キー (__name__) に対する等式制約のみがあるクエリは、サポートされていません。
  • Firestore では、範囲フィールドまたは不等式フィールドの数が 10 に制限されています。これは、クエリの実行に費用がかかりすぎないようにするためです。

次のステップ