Optimiser les requêtes avec des filtres de plage et d'inégalité sur plusieurs propriétés

Cette page fournit des exemples de stratégie d'indexation à utiliser pour les requêtes comportant des filtres de plage et d'inégalité sur plusieurs champs afin de créer une expérience de requête efficace.

Lisez les concepts liés aux filtres de plage et d'inégalité sur plusieurs propriétés avant d'optimiser vos requêtes.

Optimiser les requêtes avec Query Explain

Pour déterminer si la requête et les index utilisés sont optimaux, vous pouvez créer une requête à l'aide de Query Explain (Expliquer la requête) et consulter le résumé de l'exécution.

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
ExplainResults<Entity> explainResults = 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'exemple suivant montre comment le bon ordre des index permet d'enregistrer le nombre d'entités analysées par Firestore en mode Datastore.

Requêtes simples

Pour l'exemple précédent d'un ensemble d'employés, la requête simple exécutée avec l'index (salary, experience) est la suivante:

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 requête analyse 95 000 entrées d'index pour ne renvoyer que 5 entités. Un grand nombre d'entrées d'index ont été lues, mais filtrées, car elles ne respectaient pas le prédicat de requête.

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

Comme dans l'exemple précédent, nous pouvons déduire que la contrainte salary est plus sélective que la contrainte 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();

Lorsque vous utilisez explicitement la clause orderBy() pour ajouter les prédicats dans l'ordre précédent, Firestore en mode Datastore utilise l'index (salary, experience) pour exécuter la requête. Ainsi, comme la sélection du premier filtre de plage est meilleure que la requête précédente, la requête s'exécute plus rapidement et est rentable.

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

Étape suivante