Consulta com filtros de intervalo e desigualdade na visão geral de vários campos

O Firestore é compatível com o uso de filtros de intervalo e desigualdade em vários campos em uma única consulta. É possível ter condições de intervalo e desigualdade em vários campos e simplificar o desenvolvimento de aplicativos delegando a implementação de lógica pós-filtragem para o Firestore.

Filtros de intervalo e desigualdade em vários campos

A consulta a seguir usa filtros de intervalo de população e densidade para retornar todas as cidades em que a população é maior que 1.000.000 e a densidade populacional é menor que 10.000 pessoas por unidade de área.

Modular versão 9 para a Web

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

Considerações sobre indexação

Antes de executar as consultas, leia sobre [consultas][1] e o [modelo de dados][2] do Firestore.

No Firestore, a cláusula ORDER BY de uma consulta determina quais índices podem ser usados para atender à consulta. Por exemplo, uma consulta ORDER BY a ASC, b ASC requer um índice composto nos campos a ASC, b ASC.

Para otimizar o desempenho e o custo das consultas do Firestore, otimize a ordem dos campos no índice. Para isso, você deve garantir que seu índice seja ordenado da esquerda para a direita para que a consulta extraia um conjunto de dados que impede a verificação de entradas de índice irrelevantes.

Suponha que você queira pesquisar em um grupo de funcionários e encontrar funcionários dos Estados Unidos com salário superior a US$ 100.000 e número de anos de experiência maior do que zero. Com base em sua compreensão do conjunto de dados, você sabe que a restrição salarial é mais seletiva do que a restrição de experiência. O índice ideal que reduziria o número de verificações de índice seria o (salary [...], experience [...]): Assim, a consulta que seria rápida e mais econômica pediria salary antes de experience e ficaria assim:

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

Práticas recomendadas para otimizar índices

Ao otimizar os índices, observe as seguintes práticas recomendadas.

Ordenar campos de índice por igualdades seguidas pelo intervalo mais seletivo ou campo de desigualdade

O Firestore usa os campos mais à esquerda de um índice composto para satisfazer as restrições de igualdade e a restrição de intervalo ou desigualdade, se houver, no primeiro campo da consulta orderBy(). Essas restrições podem reduzir o número de entradas de índice verificadas pelo Firestore. O Firestore usa os campos restantes do índice para satisfazer outras restrições de intervalo ou desigualdade da consulta. Essas restrições não reduzem o número de entradas de índice que o Firestore verifica, mas filtra documentos sem correspondência para que o número de documentos que estejam retornados aos clientes sejam reduzidos.

Para mais informações sobre como criar índices eficientes, consulte [propriedades de índice][4].

Ordenar campos em ordem decrescente de seletividade de restrição de consulta

Para garantir que o Firestore selecione o índice ideal para sua consulta, especifique uma cláusula orderBy() que ordene os campos em ordem decrescente de seletividade de restrição. Maior seletividade corresponde a um subconjunto menor de documentos, enquanto a menor seletividade corresponde a um subconjunto maior de documentos. Verifique se você selecionou campos de intervalo ou desigualdade com maior seletividade no início do índice do que campos com menor seletividade.

Para minimizar o número de documentos verificados e retornados pelo Firestore na rede, você sempre deve ordenar os campos na ordem decrescente de consulta de seletividade de restrição. Se o conjunto de resultados não estiver na ordem exigida e o resultado for pequeno, você pode implementar a lógica do lado do cliente para reordená-lo de acordo com sua expectativa de ordem.

Por exemplo, suponha que você queira pesquisar um conjunto de funcionários para encontrar funcionários dos Estados Unidos com salário superior a US$ 100.000 e ordenar os resultados pelo tempo de experiência do funcionário. Se você espera que apenas um pequeno número de funcionários tenha salários superiores a US$ 100.000, a maneira mais eficiente de escrever a consulta é a seguinte:

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`

Adicionar uma ordenação em experience à consulta produzirá o mesmo conjunto de documentos e evita a reordenação dos resultados nos clientes, a consulta poderá ler muito mais entradas de índice irrelevantes do que a consulta anterior. Isso acontece porque o Firestore sempre prefere um índice cujo prefixo de campos de índice corresponda à cláusula "order by" da consulta. Se experience forem adicionados à ordem por uma cláusula, o Firestore vai selecionar o índice (experience [...], salary [...]) para calcular os resultados da consulta. Como não há outras restrições em experience, o Firestore vai ler todas as entradas de índice da coleção employees antes de aplicar o filtro salary para encontrar o resultado final de conjunto de resultados. Isso significa que as entradas de índice que não atendem aos critérios salary são lidos, aumentando assim a latência e o custo da consulta.

Preços

As consultas com filtros de intervalo e desigualdade em vários campos são faturadas com base em documentos lidos e entradas de índice lidas.

Para informações detalhadas, consulte a página [Preços][5].

Limitações

Além das [limitações de consulta][6], observe as seguintes limitações antes de usar consultas com filtros de intervalo e desigualdade em vários campos:

  • Consultas com filtros de intervalo ou desigualdade nos campos do documento e apenas restrições de igualdade na chave de documento no (__name__) não têm suporte.
  • O Firestore limita a 10 o número de campos de intervalo ou desigualdade. Isso é para evitar que as consultas fiquem muito caras para execução.

A seguir

  • Saiba como [otimizar suas consultas][3].
  • Saiba mais sobre [como realizar consultas simples e compostas][8].
  • Entenda como o [Firestore usa índices][9].

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