Consultar com filtros de intervalo e desigualdade em vários campos: vista geral

O Firestore suporta a utilização de filtros de intervalo e de desigualdade em vários campos numa única consulta. Pode ter condições de intervalo e desigualdade em vários campos e simplificar o desenvolvimento de aplicações delegando a implementação da lógica de pós-filtragem no Firestore.

Filtros de intervalo e desigualdade em vários campos

A seguinte consulta usa filtros de intervalo na população e na densidade para devolver todas as cidades onde a população é superior a 1 000 000 de pessoas e a densidade populacional é inferior a 10 000 pessoas por unidade de área.

Versão Web 9 modular

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 a indexação

Antes de executar as consultas, leia acerca das consultas e do modelo de dados do Firestore.

No Firestore, a cláusula ORDER BY de uma consulta determina que índices podem ser usados para publicar 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 o fazer, certifique-se de que o índice está ordenado da esquerda para a direita de modo que a consulta destile para um conjunto de dados que impeça a análise de entradas de índice desnecessárias.

Suponhamos que quer pesquisar numa coleção de funcionários e encontrar funcionários dos Estados Unidos cujo salário seja superior a 100 000 € e cujo número de anos de experiência seja superior a 0. Com base na sua compreensão do conjunto de dados, sabe que a restrição de salário é 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 rentável ordenaria salary antes de experience e teria o seguinte aspeto:

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 os índices

Quando otimizar os índices, tenha em atenção as seguintes práticas recomendadas.

Ordene os campos de índice por igualdades, seguidos do campo de intervalo ou desigualdade mais seletivo

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 existir, no primeiro campo da consulta orderBy(). Estas restrições podem reduzir o número de entradas de índice que o Firestore analisa. O Firestore usa os campos restantes do índice para satisfazer outras restrições de intervalo ou desigualdade da consulta. Estas restrições não reduzem o número de entradas de índice que o Firestore analisa, mas filtram os documentos que não correspondem, para que o número de documentos devolvidos aos clientes seja reduzido.

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

Ordene os campos por ordem decrescente de seletividade da restrição de consulta

Para garantir que o Firestore seleciona o índice ideal para a sua consulta, especifique uma cláusula orderBy() que ordene os campos por ordem decrescente de seletividade da restrição de consulta. Uma seletividade mais elevada corresponde a um subconjunto mais pequeno de documentos, enquanto uma seletividade mais baixa corresponde a um subconjunto maior de documentos. Certifique-se de que seleciona campos de intervalo ou de desigualdade com maior seletividade mais cedo na ordenação do índice do que os campos com menor seletividade.

Para minimizar o número de documentos que o Firestore analisa e devolve através da rede, deve ordenar sempre os campos por ordem decrescente da seletividade da restrição da consulta. Se o conjunto de resultados não estiver na ordem necessária e o conjunto de resultados for pequeno, pode implementar uma lógica do lado do cliente para reordená-lo de acordo com a sua expetativa de ordenação.

Por exemplo, suponha que quer pesquisar numa coleção de funcionários para encontrar funcionários dos Estados Unidos cujo salário seja superior a 100 000 $e ordenar os resultados pelo ano de experiência do funcionário. Se esperar que apenas um pequeno número de funcionários tenha salários superiores a 100 000 USD, a forma 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`

Embora a adição de uma ordenação em experience à consulta produza o mesmo conjunto de documentos e evite a reordenação dos resultados nos clientes, a consulta pode ler muitas mais entradas de índice estranhas do que a consulta anterior. Isto acontece porque o Firestore prefere sempre um índice cujos campos de índice correspondam ao prefixo da cláusula order by da consulta. Se experience tiver sido adicionado à cláusula ORDER BY, o Firestore seleciona o índice (experience [...], salary [...]) para calcular os resultados da consulta. Uma vez que não existem outras restrições em experience, o Firestore lê todas as entradas de índice da coleção employees antes de aplicar o filtro salary para encontrar o conjunto de resultados final. Isto significa que as entradas de índice que não satisfazem o filtro salary são lidas na mesma, o que aumenta 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 nos documentos lidos e nas entradas de índice lidas.

Para obter informações detalhadas, consulte a página Preços.

Limitações

Além das limitações de consultas, tenha em atenção as seguintes limitações antes de usar consultas com filtros de intervalo e desigualdade em vários campos:

  • As consultas com filtros de intervalo ou de desigualdade em campos de documentos e apenas restrições de igualdade na chave do documento (__name__) não são suportadas.
  • O Firestore limita o número de campos de intervalo ou de desigualdade a 10. Isto serve para evitar que as consultas se tornem demasiado caras para executar.

O que se segue?