“对多个字段使用范围和不等式过滤条件的查询”概览

Firestore 支持在单个查询中对多个字段使用范围和不等式过滤条件。您可以对多个字段使用范围和不等式条件,并可通过将后过滤逻辑的实现委托给 Firestore 来简化应用开发。

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

以下查询对人口和人口密度使用范围过滤条件,返回人口超过 1,000,000 人且人口密度低于每单位面积 10,000 人的所有城市。

Web v9 模块化库

const q = query(
    collection(db, "cities"),
    where('population', '>', 1000000),
    where('density', '<', 10000),
  );

Swift

let query = db.collection("cities")
  .whereField("population", isGreaterThan: 1000000)
  .whereField("density", isLessThan: 10000)

Objective-C

FIRQuery *query =
 [[[[self.db collectionWithPath:@"cities"]
queryWhereField:@"population" isGreaterThan:@1000000]
   queryWhereField:@"density" isLessThan:@10000];

Java Android

Query query = db.collection("cities")
 .whereGreaterThan("population", 1000000)
 .whereLessThan("density", 10000);

Kotlin+KTX Android

val query = db.collection("cities")
 .whereGreaterThan("population", 1000000)
 .whereLessThan("density", 10000)

Go

   query := client.Collection("cities").
      Where("population", ">", 1000000).
      Where("density", "<", 10000)

Java

db.collection("cities")
  .whereGreaterThan("population", 1000000)
  .whereLessThan("density", 10000);

Node.js

db.collection("cities")
  .where('population', '>', 1000000),
  .where('density', '<', 10000)

Python

from google.cloud import firestore

db = firestore.Client()
query = db.collection("cities")
.where("population", ">", 1000000)
.where("density", "<", 10000)

PHP

$collection = $db->collection('samples/php/cities');
$chainedQuery = $collection
    ->where('population', '>', 1000000)
    ->where('density', '<', 10000);

C#

CollectionReference citiesRef = db.Collection("cities");
Query query = citiesRef
    .WhereGreaterThan("Population", 1000000)
    .WhereLessThan("Density", 10000);
QuerySnapshot querySnapshot = await query.GetSnapshotAsync();
foreach (DocumentSnapshot documentSnapshot in querySnapshot)
{
    var name = documentSnapshot.GetValue<string>("Name");
    var population = documentSnapshot.GetValue<int>("Population");
    var density = documentSnapshot.GetValue<int>("Density");
    Console.WriteLine($"City '{name}' returned by query. Population={population}; Density={density}");
}

Ruby

query = cities_ref.where("population", ">", "1000000")
                  .where("density", "<", 10000)

C++

CollectionReference cities_ref = db->Collection("cities");
Query query = cities_ref.WhereGreaterThan("population", FieldValue::Integer(1000000))
                       .WhereLessThan("density", FieldValue::Integer(10000));

Unity

CollectionReference citiesRef = db.Collection("cities");
Query query = citiesRef.WhereGreaterThan("population", 1000000)
                      .WhereLessThan("density", 10000);

Dart

final citiesRef = FirebaseFirestore.instance.collection('cities')
final query = citiesRef.where("population", isGreaterThan: 1000000)
                  .where("density", isLessThan: 10000);

索引编制注意事项

在运行查询之前,请阅读有关查询和 Firestore 数据模型的内容。

在 Firestore 中,查询的 ORDER BY 子句决定了哪些索引可以用来提供查询。例如,ORDER BY a ASC, b ASC 查询需要使用一个基于 a ASC, b ASC 字段的复合索引。

如需优化 Firestore 查询的性能和费用,请优化索引中字段的顺序。为此,请确保按如下方式将索引从左到右排序:即要使得查询能够在提取到某个数据集后,便不再继续扫描不必要的索引条目。

假设您想搜索一组员工,并找到薪水超过 10 万美元且工作经验年限超过 0 年的美国员工。基于您对该数据集的了解,您知道该薪水限制条件要比经验限制条件更为严苛。那么,理想的索引将是 (salary [...], experience [...]),因为它可以减少索引扫描次数。因此,为了提高查询速度并使其更加经济高效,应将 salary 排在 experience 之前,如下所示:

Java

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

Node.js

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

Python

db.collection("employees")
  .where("salary", ">", 100000)
  .where("experience", ">", 0)
  .order_by("salary")
  .order_by("experience");

索引优化最佳实践

优化索引时,请遵循以下最佳实践。

先按等式过滤条件对索引字段进行排序,然后按严苛程度最高的范围或不等式字段对索引字段进行排序

Firestore 会使用复合索引最左侧的字段来满足对 orderBy() 查询的第一个字段使用的等式限制条件及范围或不等式限制条件(如果有)。这些限制条件可以减少 Firestore 扫描的索引条目数。Firestore 会使用索引的其余字段来满足查询的其他范围或不等式限制条件。虽然这些限制条件不会减少 Firestore 扫描的索引条目数,但会滤除不匹配的文档,从而减少返回给客户端的文档数量。

如需详细了解如何创建高效索引,请参阅索引属性

将字段按查询限制条件的严苛程度降序排列

为确保 Firestore 为您的查询选择最佳索引,请指定一个 orderBy() 子句,将字段按查询限制条件的严苛程度降序排列。严苛程度越高,匹配的文档子集就越小;相反,严苛程度越低,匹配的文档子集就越大。确保在索引排序中,先选择严苛程度更高的范围或不等式字段,后选择严苛程度更低的字段。

为了尽可能减少 Firestore 通过网络扫描和返回的文档数量,您应始终将字段按查询限制条件的严苛程度降序排列。如果结果集未按所需顺序排序,并且预计结果集较小,则您可以实现客户端逻辑来按所需顺序对其重新排序。

例如,假设您想搜索一组员工,找到薪水超过 10 万美元的美国员工,并将结果按员工的工作经验年限进行排序。如果您预计只有少数员工的薪水会超过 10 万美元,那么可按如下所示编写最高效的查询:

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .orderBy("salary")
  .get()
  .addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
        @Override
        public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
          // Order results by `experience`
        }
    });;

Node.js

const querySnapshot = await db.collection('employees')
                              .where("salary", ">", 100000)
                              .orderBy("salary")
                              .get();

// Order results by `experience`

Python

results = db.collection("employees")
            .where("salary", ">", 100000)
            .order_by("salary")
            .stream()

// Order results by `experience`

虽然向查询添加按 experience 排序会产生相同的文档集,并且无需在客户端对结果进行重新排序,但该查询可能会比之前的查询读取更多无关的索引条目。这是因为 Firestore 始终会优先执行其索引字段前缀与查询的排序依据子句匹配的索引。如果将 experience 添加到排序依据子句,则 Firestore 会选择使用 (experience [...], salary [...]) 索引来计算查询结果。由于没有对 experience 使用其他限制条件,因此,Firestore 会先读取 employees 集合的所有索引条目,然后才会应用 salary 过滤条件来查找最终的结果集。这意味着,不满足 salary 过滤条件的索引条目仍会被读取,从而导致查询的延迟时间和费用增加。

价格

对多个字段使用范围和不等式过滤条件的查询是根据所读取的文档数和索引条目数来收费的。

如需了解详细信息,请参阅价格页面。

限制

除了查询限制之外,在使用对多个字段使用范围和不等式过滤条件的查询之前,还应注意以下限制:

  • 不支持对文档字段使用范围或不等式过滤条件的查询,也不支持对文档键 (__name__) 仅使用等式限制条件的查询。
  • Firestore 将范围或不等式字段的数量上限设为 10;这样做是为了防止查询的运行开销过高。

后续步骤