Abfragen mit Bereichs- und Ungleichheitsfiltern für mehrere Felder optimieren

Auf dieser Seite finden Sie Beispiele für Indexierungsstrategien, die Sie für Abfragen mit Bereichs- und Ungleichheitsfiltern auf mehreren Feldern verwenden können, um Abfragen effizienter zu gestalten.

Bevor Sie Ihre Abfragen optimieren, sollten Sie sich mit den zugehörigen Konzepten vertraut machen.

Abfragen mit „Abfrage erläutern“ optimieren

Mit Abfrage – Erläuterung können Sie die Zusammenfassung des Abfrageplans und die Ausführungsstatistiken der Abfrage abrufen, um festzustellen, ob Ihre Abfrage und Ihre Indexe optimal sind:

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);

Das folgende Beispiel zeigt, wie durch die richtige Indexreihenfolge die Anzahl der Indexeinträge reduziert wird, die von Firestore gescannt werden.

Einfache Abfragen

Für das vorherige Beispiel mit einer Sammlung von Mitarbeitern sieht die einfache Abfrage, die mit dem Index (experience ASC, salary ASC) ausgeführt wird, so aus:

Java

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

Bei der Abfrage werden 95.000 Indexeinträge gescannt, um nur fünf Dokumente zurückzugeben. Da das Abfrageprädikat nicht erfüllt ist, werden viele Indexeinträge gelesen, aber herausgefiltert.

// 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"
        }
    }
}

Aus dem Fachwissen können Sie schließen, dass die meisten Mitarbeiter mindestens etwas Erfahrung haben, aber nur wenige ein Gehalt von mehr als 100.000 € erhalten. Anhand dieser Informationen können Sie sehen, dass die Einschränkung salary selektiver ist als die Einschränkung experience. Wenn Sie beeinflussen möchten, welcher Index von Firestore für die Ausführung der Abfrage verwendet wird, geben Sie eine orderBy-Klausel an, mit der die salary-Einschränkung vor der experience-Einschränkung sortiert wird.

Java

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

Wenn Sie die Prädikate explizit mit der orderBy()-Klausel hinzufügen, verwendet Firestore den (salary ASC, experience ASC)-Index, um die Abfrage auszuführen. Da die Selektivität des ersten Bereichsfilters in dieser Abfrage im Vergleich zur vorherigen Abfrage höher ist, wird sie schneller ausgeführt und ist kosteneffizienter.

// 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"
        }
    }
}

Nächste Schritte