複数のフィールドに対する範囲フィルタと不等式フィルタを使用してクエリを最適化する

このページでは、効率的なクエリ エクスペリエンスを創出するために、複数のフィールドに対して範囲フィルタと不等式フィルタを使用したクエリに使用できるインデックス戦略の例を紹介します。

クエリを最適化する前に、関連するコンセプトについてお読みください。

クエリ Explain を使用してクエリを最適化する

クエリとインデックスが最適かどうかを判断するには、Query Explain を使用して、クエリプランの概要とクエリの実行の統計情報を取得します。

Java

Query q = db.collection("employees").whereGreaterThan("salary",
100000).whereGreaterThan("experience", 0);

ExplainResults<QuerySnapshot> explainResults = q.explain(ExplainOptions.builder().analyze(true).build()).get();
ExplainMetrics metrics = explainResults.getMetrics();

PlanSummary planSummary = metrics.getPlanSummary();
ExecutionStats executionStats = metrics.getExecutionStats();

System.out.println(planSummary.getIndexesUsed());
System.out.println(stats.getResultsReturned());
System.out.println(stats.getExecutionDuration());
System.out.println(stats.getReadOperations());
System.out.println(stats.getDebugStats());

Node.js

let q = db.collection("employees")
      .where("salary", ">", 100000)
      .where("experience", ">",0);

let options = { analyze : 'true' };
let explainResults = await q.explain(options);

let planSummary = explainResults.metrics.planSummary;
let stats = explainResults.metrics.executionStats;

console.log(planSummary);
console.log(stats);

以下の例は、正しいインデックス順序を使用することで、Firestore がスキャンするインデックス エントリの数がどの程度減少するかを示しています。

単純なクエリ

従業員のコレクションに関する前述の例では、(experience ASC, salary ASC) インデックスで実行するシンプルなクエリが次のようになります。

Java

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

このクエリは 95,000 件のインデックス エントリをスキャンしますが、返されるドキュメントは 5 件にすぎません。クエリ述部が満たされていないため、多数のインデックス エントリが読み取られますが、除外されます。

// Output query planning info
{
    "indexesUsed": [
        {
            "properties": "(experience ASC, salary ASC, __name__ ASC)",
            "query_scope": "Collection"
        }
    ],

    // Output Query Execution Stats
    "resultsReturned": "5",
    "executionDuration": "2.5s",
    "readOperations": "100",
    "debugStats": {
        "index_entries_scanned": "95000",
        "documents_scanned": "5",
        "billing_details": {
            "documents_billable": "5",
            "index_entries_billable": "95000",
            "small_ops": "0",
            "min_query_cost": "0"
        }
    }
}

ドメインの専門知識から、ほとんどの従業員にある程度以上の経験があるものの、給与が 100,000 を超える従業員はほとんどいないと推測できます。この分析結果から、salary 制約は experience 制約よりも選択性が高いことがわかります。Firestore がクエリの実行に使用するインデックスに影響を与えるようにするには、salary 制約が experience 制約の前になるように並べ替える orderBy 句を指定します。

Java

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

orderBy() 句を明示的に使用して述部を追加すると、Firestore は (salary ASC, experience ASC) インデックスを使用してクエリを実行します。したがって、このクエリでは最初の範囲フィルタの選択性は前のクエリと比較して高いため、クエリの実行速度が速くなり、費用対効果が向上します。

// Output query planning info
{
    "indexesUsed": [
        {
            "properties": "(salary ASC, experience ASC, __name__ ASC)",
            "query_scope": "Collection"
        }
    ],

    // Output Query Execution Stats
    "resultsReturned": "5",
    "executionDuration": "0.2s",
    "readOperations": "6",
    "debugStats": {
        "index_entries_scanned": "1000",
        "documents_scanned": "5",
        "billing_details": {
            "documents_billable": "5",
            "index_entries_billable": "1000",
            "small_ops": "0",
            "min_query_cost": "0"
        }
    }
}

次のステップ