Ottimizza le query con filtri di intervallo e disuguaglianza su più campi

Questa pagina fornisce esempi della strategia di indicizzazione da utilizzare per le query con filtri di intervallo e disuguaglianza su più campi al fine di creare un'esperienza di query efficiente.

Scopri i concetti correlati prima di ottimizzare le query.

Ottimizza le query con la spiegazione delle query

Per determinare se la query e gli indici utilizzati sono ottimali, puoi utilizzare Query Explain per ottenere il riepilogo del piano di query e le statistiche di esecuzione della query:

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

L'esempio seguente mostra in che modo l'utilizzo di un ordinamento corretto dell'indice riduce il numero di voci di indice analizzate da Firestore.

Query semplici

Con l'esempio precedente di una raccolta di dipendenti, la semplice query eseguita con l'indice (experience ASC, salary ASC) è la seguente:

Java

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

La query analizza 95.000 voci di indice solo per restituire cinque documenti. Poiché il predicato della query non è soddisfatto, un numero elevato di voci di indice viene lette, ma filtrate.

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

Dall'esperienza in campo, si può dedurre che la maggior parte dei dipendenti avrà almeno un po' di esperienza, ma pochi avranno uno stipendio superiore a 100.000. Dato questo insight, possiamo concludere che il vincolo salary è più selettivo del vincolo experience. Per influenzare l'indice utilizzato da Firestore per eseguire la query, specifica una clausola orderBy che ordina il vincolo salary prima del vincolo experience.

Java

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

Quando usi esplicitamente la clausola orderBy() per aggiungere i predicati, Firestore utilizza l'indice (salary ASC, experience ASC) per eseguire la query. Di conseguenza, poiché la selettività del filtro del primo intervallo è più elevata in questa query rispetto alla query precedente, la query viene eseguita più velocemente ed è più conveniente.

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

Passaggi successivi