Esta página oferece exemplos de estratégias de indexação que pode usar para consultas com filtros de intervalo e desigualdade em vários campos para criar uma experiência de consulta eficiente.
Antes de otimizar as suas consultas, leia sobre os conceitos de filtros de intervalo e de desigualdade em várias propriedades .
Otimize consultas com a funcionalidade Explicação de consultas
Para determinar se a consulta e os índices usados são ideais, pode criar uma consulta usando Query Explain e rever o resumo da execução.
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));
O exemplo seguinte mostra como a utilização da ordem de índice correta poupa o número de entidades que o Firestore no modo Datastore analisa.
Consultas simples
Com o exemplo anterior de uma coleção de funcionários, a consulta simples que é executada com o índice (salary, experience)
é a seguinte:
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();
A consulta analisa 95 000 entradas de índice apenas para devolver 5 entidades. Foi lido um grande número de entradas de índice, mas foram filtradas porque não satisfaziam o predicado de consulta.
// 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" } } }
Conforme o exemplo anterior, podemos inferir que a restrição salary
é mais seletiva do que a restrição 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 usa explicitamente a cláusula orderBy()
para adicionar os predicados na ordem anterior, o Firestore no modo Datastore usa o índice (salary, experience)
para executar a consulta. Uma vez que a seleção do primeiro filtro de intervalo é melhor do que a consulta anterior, a consulta é executada mais rapidamente e é rentável.
// 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" } } }
O que se segue?
- Saiba mais sobre a explicação de consultas.