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égies d'indexation que vous pouvez utiliser pour les requêtes avec des filtres de plage et d'inégalité sur plusieurs champs afin de créer une expérience de requête efficace.

Avant d'optimiser vos requêtes, consultez les concepts de filtres de plage et d'inégalité sur plusieurs propriétés .

Optimiser les requêtes avec l'explication des requêtes

Pour déterminer si la requête et les index utilisés sont optimaux, vous pouvez créer une requête à l'aide de Expliquer la requête et consulter le récapitulatif 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 l'utilisation d'un ordre d'index correct permet de réduire le nombre d'entités que Firestore en mode Datastore analyse.

Requêtes simples

Avec l'exemple précédent d'une collection 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 renvoyer seulement cinq entités. Un grand nombre d'entrées d'index ont été lues, mais filtrées, car elles ne satisfaisaient 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 en 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. Étant donné que 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