Optymalizuj zapytania za pomocą filtrów zakresów i nierówności w wielu polach

Na tej stronie znajdziesz przykłady strategii indeksowania, których możesz używać w przypadku zapytań z filtrami zakresu i nierówności w wielu polach, aby zapewnić wydajne działanie zapytań.

Zanim zoptymalizujesz zapytania, zapoznaj się z powiązanymi pojęciami.

Optymalizacja zapytań za pomocą funkcji Query Explain

Aby sprawdzić, czy zapytanie i indeksy są optymalne, możesz użyć funkcji Query Explain, która pozwala uzyskać podsumowanie planu zapytania i statystyki wykonania zapytania:

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

Poniższy przykład pokazuje, jak użycie prawidłowej kolejności indeksów zmniejsza liczbę wpisów indeksu skanowanych przez Cloud Firestore.

Proste zapytania

W podanym wcześniej przykładzie kolekcji pracowników proste zapytanie, które działa z indeksem (experience ASC, salary ASC), wygląda tak:

Java

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

Zapytanie skanuje 95 000 wpisów indeksu, aby zwrócić tylko 5 dokumentów. Ponieważ warunek zapytania nie jest spełniony, odczytywana jest duża liczba wpisów indeksu, ale są one odfiltrowywane.

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

Na podstawie wiedzy o domenie można wywnioskować, że większość pracowników będzie miała przynajmniej pewne doświadczenie, ale niewielu będzie miało wynagrodzenie przekraczające 100 000. Na podstawie tych informacji możesz stwierdzić, że ograniczenie salary jest bardziej selektywne niż ograniczenie experience. Aby wpłynąć na indeks, którego Cloud Firestore używa do wykonania zapytania, określ klauzulę orderBy, która porządkuje ograniczenie salary przed ograniczeniem experience.

Java

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

Gdy wyraźnie użyjesz klauzuli orderBy(), aby dodać predykaty, Cloud Firestore użyje indeksu (salary ASC, experience ASC) do uruchomienia zapytania. Ponieważ selektywność pierwszego filtra zakresu jest w tym zapytaniu wyższa niż we wcześniejszym, zapytanie działa szybciej i jest bardziej opłacalne.

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

Co dalej?