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, aby uzyskać podsumowanie planu zapytania i statystyki jego wykonania:

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 prawidłowe uporządkowanie indeksu zmniejsza liczbę pozycji indeksu, które skanuje Cloud Firestore.

Proste zapytania

poprzednim 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 pozycji indeksu, aby zwrócić 5 dokumentów. Ponieważ predykat 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żesz wywnioskować, że większość pracowników ma co najmniej pewne doświadczenie, ale niewielu z nich zarabia więcej niż 100 tys. USD. Z tego wniosku wynika, ż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");

Jeśli do dodania predykatów użyjesz klauzuli orderBy(),Cloud Firestore użyje indeksu (salary ASC, experience ASC) do uruchomienia zapytania. W tym zapytaniu selektywność pierwszego filtra zakresu jest wyższa niż w poprzednim zapytaniu, więc zapytanie działa szybciej i jest bardziej ekonomiczne.

// 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?