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

O Firestore oferece suporte ao 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 da 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 e o modelo de dados do Firestore.

No Firestore, a cláusula ORDER BY de uma consulta determina quais índices podem ser usados para exibir a 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 filtram documentos sem correspondência para que o número de documentos retornados aos clientes seja reduzido.

Para mais informações sobre como criar índices eficientes, consulte Propriedades de índice.

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 ordena 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 à ordem por cláusula 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.

Limitações

Além das limitações de consulta, 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