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

Questa pagina fornisce esempi di strategie di indicizzazione che puoi utilizzare per le query con filtri di intervallo e di disuguaglianza su più campi per creare un'esperienza di query efficiente.

Prima di ottimizzare le query, leggi i concetti correlati.

Ottimizzare le query con Query Explain

Per determinare se la query e gli indici 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 come l'utilizzo dell'ordinamento corretto dell'indice riduce il numero di voci dell'indice sottoposte a scansione da Cloud 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 esegue la scansione di 95.000 voci dell'indice solo per restituire cinque documenti. Poiché il predicato della query non è soddisfatto, un numero elevato di voci dell'indice viene letto, ma viene filtrato.

// 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 nel dominio puoi dedurre che la maggior parte dei dipendenti avrà almeno un po' di esperienza, ma pochi avranno uno stipendio superiore a 100.000. In base a questa informazione, puoi vedere che il vincolo salary è più selettivo del vincolo experience. Per influenzare l'indice utilizzato da Cloud Firestore per eseguire la query, specifica una clausola orderBy che ordini la limitazione salary prima della limitazione experience.

Java

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

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

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