优化对多个字段使用范围和不等式过滤条件的查询

本页面提供了一些索引编制策略的示例,可在对多个字段使用范围和不等式过滤条件的查询中使用这些策略来打造高效的查询体验。

在优化您的查询之前,请先了解一些相关概念

使用查询解释优化查询

如需确定所使用的查询和索引是否最优,您可以使用查询解释获取查询计划摘要和查询执行统计信息:

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 个索引条目,仅返回了 5 个文档。由于查询谓词不满足条件,因此,虽然系统读取了大量索引条目,但滤除了绝大部分。

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

我们可以通过相关领域专业知识推断,大多数员工至少会有一些经验,但很少有人会有超过 10 万美元的薪水。鉴于这一分析洞见,我们可以得出结论,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"
        }
    }
}

后续步骤