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

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

Фильтры диапазонов и неравенств по нескольким полям

В следующем запросе используются фильтры диапазона по численности и плотности населения,чтобы получить все города,в которых численность населения превышает 1 000 000 человек,а плотность населения составляет менее 10 000 человек на единицу площади.

Веб-версия 9 (модульный)

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)

Проложить маршрут

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

Особенности индексирования

Прежде чем выполнять запросы, ознакомьтесь с информацией о [запросах][1] и [модели данных][2] Cloud Firestore.

В Cloud Firestore условие ORDER BY запроса определяет, какие индексы можно использовать для его обработки. Например, для запроса ORDER BY a ASC, b ASC требуется составной индекс для полей a ASC, b ASC.

Чтобы оптимизировать производительность и стоимость запросов Cloud Firestore, оптимизируйте порядок полей в индексе. Для этого убедитесь, что индекс упорядочен слева направо, чтобы запрос сводился к набору данных, который предотвращает сканирование ненужных записей в индексе.

Предположим, вы хотите выполнить поиск по коллекции сотрудников и найти сотрудников из США, чья зарплата превышает 100 000 долларов США, а стаж работы превышает 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");

Рекомендации по оптимизации индексов

При оптимизации индексов учитывайте следующие рекомендации.

Упорядочивайте поля индекса по равенствам, а затем по наиболее избирательному диапазону или полю неравенства.

Cloud Firestore использует крайние левые поля составного индекса, чтобы удовлетворить ограничения равенства и диапазона или неравенства (если есть) в первом поле запроса orderBy(). Эти ограничения могут уменьшить количество записей в индексе, которые сканирует Cloud Firestore. Cloud Firestore использует оставшиеся поля индекса, чтобы удовлетворить другие ограничения диапазона или неравенства запроса. Эти ограничения не уменьшают количество записей в индексе, которые сканирует Cloud Firestore, но отфильтровывают несоответствующие документы, чтобы уменьшить количество документов, возвращаемых клиентам.

Дополнительную информацию о создании эффективных индексов можно найти в разделе [Свойства индекса][4].

Упорядочивайте поля по убыванию избирательности ограничений запроса.

Чтобы Cloud Firestore выбрал оптимальный индекс для вашего запроса, укажите предложение orderBy(), которое упорядочивает поля в порядке убывания избирательности ограничений запроса. Чем выше избирательность, тем меньше документов будет соответствовать запросу, и наоборот. Убедитесь, что вы выбираете поля диапазона или неравенства с более высокой селективностью раньше в порядке индекса, чем поля с более низкой селективностью.

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

Например, предположим, что вы хотите выполнить поиск по коллекции сотрудников, чтобы найти сотрудников из США, чья зарплата превышает 100 000 долларов США, и упорядочить результаты по году опыта работы сотрудника. Если вы ожидаете,что только у небольшого числа сотрудников зарплата будет превышать 100 000 долларов, то наиболее эффективный способ написать запрос будет следующим:

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, будет получен тот же набор документов и не потребуется сортировать результаты на клиентах, но запрос может прочитать гораздо больше посторонних записей в индексе, чем предыдущий. Это связано с тем, что Cloud Firestore всегда предпочитает индекс, поля которого соответствуют предложению order by запроса. Если в предложение order by добавить experience, то Cloud Firestore выберет индекс (experience [...], salary [...]) для вычисления результатов запроса. Поскольку других ограничений для experience нет, Cloud Firestore прочитает все записи в индексе коллекции employees, прежде чем применить фильтр salary, чтобы найти окончательный набор результатов. Это означает, что записи в индексе, которые не соответствуют фильтру salary, все равно считываются, что увеличивает задержку и стоимость запроса.

Цены

Запросы с фильтрами по диапазону и неравенству в нескольких полях оплачиваются на основе прочитанных документов и записей в индексе.

Подробную информацию можно найти на странице [Цены][5].

Ограничения

Помимо [ограничений на запросы][6], перед использованием запросов с фильтрами по диапазону и неравенству в нескольких полях обратите внимание на следующие ограничения:

  • Запросы с фильтрами диапазона или неравенства в полях документа и только ограничениями равенства в ключе документа (__name__) не поддерживаются.
  • В Cloud Firestore количество полей с диапазонами или неравенствами ограничено 10. Это позволяет избежать слишком высоких расходов на выполнение запросов.

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

  • Подробнее [об оптимизации запросов][3]…
  • Подробнее [о выполнении простых и составных запросов][8]…
  • Узнайте, [как Cloud Firestore использует индексы][9].

[1]: /docs/firestore/query-data/get-data [2]: /docs/firestore/data-model [3]: /docs/firestore/query-data/multiple-range-optimize-indexes [4]: /docs/firestore/concepts/index-overview#index_properties [5]: /docs/firestore/pricing [6]: /docs/firestore/query-data/queries#query_limitations [7]: https://en.wikipedia.org/wiki/Disjunctive_normal_form [8]: /docs/firestore/query-data/queries [9]: /docs/firestore/concepts/index-overview [10]: /docs/firestore/query-data/multiple-range-optimize-indexes