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

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

Prima di ottimizzare le query, leggi i concetti relativi ai filtri di intervallo e di disuguaglianza su più proprietà .

Ottimizzare le query con Spiegazione query

Per determinare se la query e gli indici utilizzati sono ottimali, puoi creare una query utilizzando Query Explain ed esaminare il riepilogo dell'esecuzione.

Java

...
// Build the query
Query<Entity> query =
    Query.newEntityQueryBuilder()
        .setKind("employees")
        .setFilter(
            CompositeFilter.and(
                PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
        .setOrderBy(OrderBy("experience"), OrderBy("salary"))
        .build();

// Set the explain options to get back *only* the plan summary
QueryResults<Entity> results = datastore.run(query, ExplainOptions.newBuilder().build());

// Get the explain metrics
Optional<ExplainMetrics> explainMetrics = results.getExplainMetrics();
if (!explainMetrics.isPresent()) {
  throw new Exception("No explain metrics returned");
}

// Get the plan summary
PlanSummary planSummary = explainMetrics.get().getPlanSummary();
List<Map<String, Object>> indexesUsed = planSummary.getIndexesUsed();
System.out.println("----- Indexes Used -----");
indexesUsed.forEach(map -> map.forEach((s, o) -> System.out.println(s + ": " + o)));

// Get the execution stats
if (!explainMetrics.getExecutionStats().isPresent()) {
  throw new Exception("No execution stats returned");
}

ExecutionStats queryStats = explainMetrics.getExecutionStats().get();
Map<String, Object> debugStats = queryStats.getDebugStats();
System.out.println("----- Debug Stats -----");
debugStats.forEach((s, o) -> System.out.println(s + ": " + o));

L'esempio seguente mostra come l'utilizzo dell'ordinamento corretto degli indici consente di risparmiare il numero di entità analizzate da Firestore in modalità Datastore.

Query semplici

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

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY experience, salary;

Java

Query<Entity> query =
   Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("experience"), OrderBy("salary"))
    .build();

La query analizza 95.000 voci di indice solo per restituire 5 entità. È stato letto un numero elevato di voci di indice, ma sono state filtrate perché non soddisfacevano il predicato della query.

// Output query planning info
{
        "indexesUsed": [
            {
                "query_scope": "Collection Group",
                "properties": "(experience ASC, salary ASC, __name__ ASC)"
            }
        ]
    },
    // 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"
            }
        }
    }

Come nell'esempio precedente, possiamo dedurre che il vincolo salary sia più selettivo del vincolo experience.

GQL

SELECT *
FROM /employees
WHERE salary > 100000 AND experience > 0
ORDER BY salary, experience;

Java

Query<Entity> query =
   Query.newEntityQueryBuilder()
    .setKind("employees")
    .setFilter(
        CompositeFilter.and(
            PropertyFilter.gt("salary", 100000), PropertyFilter.gt("experience", 0)))
    .setOrderBy(OrderBy("salary"), OrderBy("experience"))
    .build();

Quando utilizzi esplicitamente la clausola orderBy() per aggiungere i predicati nell'ordine precedente, Firestore in modalità Datastore utilizza l'indice (salary, experience) per eseguire la query. Poiché la selezione del primo filtro intervallo è migliore rispetto alla query precedente, la query viene eseguita più velocemente ed è conveniente.

    // Output query planning info
{
    "indexesUsed": [
        {
            "query_scope": "Collection Group",
            "properties": "(salary ASC, experience ASC, __name__ ASC)"
        }
    ],
    // 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