Оптимизация запросов с фильтрами по диапазону и неравенству в нескольких полях

На этой странице приведены примеры стратегии индексирования, которую можно использовать для запросов с фильтрами диапазона и неравенства в нескольких полях, чтобы обеспечить эффективную работу запросов.

Прежде чем оптимизировать запросы, ознакомьтесь с сопутствующими понятиями.

Оптимизация запросов с помощью Query Explain

Чтобы определить, оптимальны ли ваши запросы и индексы, вы можете использовать Query Explain, чтобы получить сводку плана запроса и статистику его выполнения:

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);

В следующем примере показано, как использование правильного порядка индексов уменьшает количество записей индекса, которые сканирует Cloud Firestore.

Простые запросы

В примере с коллекцией сотрудников простой запрос, который выполняется с индексом (experience ASC, salary ASC), выглядит следующим образом:

Java

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

Запрос сканирует 95 000 записей индекса, чтобы вернуть пять документов. Поскольку предикат запроса не удовлетворен, считывается большое количество записей индекса, но они отфильтровываются.

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

Вы можете сделать вывод из опыта в предметной области, что у большинства сотрудников будет хотя бы некоторый опыт, но у немногих будет зарплата, превышающая 100 000. Таким образом, ограничение salary является более строгим, чем experience. Чтобы повлиять на индекс, который Cloud Firestore использует для выполнения запроса, укажите предложение orderBy, которое упорядочивает ограничение salary перед ограничением experience.

Java

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

Если вы явно используете предложение orderBy() для добавления предикатов, Cloud Firestore использует индекс (salary ASC, experience ASC) для выполнения запроса. Поскольку селективность первого фильтра диапазона в этом запросе выше, чем в предыдущем, запрос выполняется быстрее и эффективнее.

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

Дальнейшие действия