Descripción general de consultas con filtros de rango y desigualdad en varios campos

Firestore admite el uso de filtros de rango y desigualdad en varios campos de una sola consulta. Puedes tener condiciones de rango y desigualdad en varios campos y simplificar tu desarrollo de aplicaciones delegando la implementación de la lógica posterior al filtrado a Firestore.

Filtros de rango y desigualdad en varios campos

La siguiente consulta usa filtros de rango para la población y la densidad para mostrar todas las ciudades en las que la población es superior a 1,000,000 personas y la densidad de población es inferior a 10,000 personas por unidad de área.

Modular de la versión web 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)

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

Consideraciones sobre la indexación

Antes de ejecutar tus consultas, lee sobre las consultas y el modelo de datos de Firestore.

En Firestore, la cláusula ORDER BY de una consulta determina qué índices se pueden usar para entregar la consulta. Por ejemplo, una consulta ORDER BY a ASC, b ASC requiere un índice compuesto en los campos a ASC, b ASC.

Para optimizar el rendimiento y el costo de las consultas de Firestore, optimiza el orden de los campos en el índice. Para ello, asegúrate de que tu índice esté ordenado de izquierda a derecha para que la consulta se extraiga en un conjunto de datos que impida el análisis de entradas de índice innecesarias.

Supongamos que quieres buscar en una colección de empleados y encontrar empleados de Estados Unidos cuyo salario es superior a $100,000 y cuya cantidad de años de experiencia es mayor que 0. En función de tu comprensión del conjunto de datos, sabes que la restricción salarial es más selectiva que la limitación de la experiencia. El índice ideal que reduciría la cantidad de análisis del índice sería (salary [...], experience [...]). Por lo tanto, la consulta que sería rápida y rentable ordenaría salary antes del experience y se vería de la siguiente manera:

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ácticas recomendadas para optimizar índices

Cuando optimices índices, ten en cuenta las siguientes prácticas recomendadas.

Ordena los campos de índice por igualdades seguidas del rango más selectivo o el campo de desigualdad

Firestore usa los campos más a la izquierda de un índice compuesto para satisfacer las restricciones de igualdad y la restricción de rango o desigualdad, si las hay, en el primer campo de la consulta orderBy(). Estas restricciones pueden reducir la cantidad de registros que Firestore analiza. Firestore usa los campos restantes del índice para satisfacer otras restricciones de rango o desigualdad de la consulta. Estas restricciones no reducen la cantidad de entradas de índice que analiza Firestore, pero filtra los documentos no coincidentes para que se reduzca la cantidad de documentos que se devuelven a los clientes.

Para obtener más información sobre cómo crear índices eficientes, consulta Propiedades de los índices.

Ordena los campos en orden descendente de selectividad de la restricción de consulta

Para asegurarte de que Firestore seleccione el índice óptimo para tu consulta, especifica una cláusula orderBy() que ordene los campos en orden descendente de la selectividad de las restricciones de consulta. Una mayor selectividad coincide con un subconjunto más pequeño de documentos, mientras que la selectividad más baja coincide con un subconjunto más grande de documentos. Asegúrate de seleccionar campos de rango o de desigualdad con una selectividad más alta en el ordenamiento del índice antes que los campos con una selectividad más baja.

Para minimizar la cantidad de documentos que Firestore analiza y devuelve a la red, siempre debes ordenar los campos en orden descendente según la selectividad de las restricciones de la consulta. Si el conjunto de resultados no está en el orden requerido y el conjunto de resultados esperados es pequeño, puedes implementar la lógica del cliente y volver a hacer el pedido según lo previsto.

Por ejemplo, supongamos que deseas buscar en una colección de empleados para encontrar empleados de Estados Unidos cuyo salario es superior a $100,000 y ordenan los resultados por años de experiencia del empleado. Si esperas que solo una pequeña cantidad de empleados tenga salarios superiores a $100,000, entonces la forma más eficiente de escribir la consulta es la siguiente:

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`

Aunque agregar un orden en experience a la consulta generará el mismo conjunto de documentos y omitirá el reordenamiento de los resultados en los clientes, la consulta leerá muchas más entradas de índice extrañas que la consulta anterior. Esto se debe a que Firestore siempre prefiere un índice cuyo prefijo de campos de índice coincida con el orden por cláusula de la consulta. Si experience se agregara al orden por una cláusula, entonces Firestore seleccionará el índice (experience [...], salary [...]) para procesar los resultados de las consultas. Como no hay otras restricciones en experience, Firestore leerá todas las entradas de índice de la colección employees antes de aplicar el filtro salary para encontrar el conjunto de resultados. Esto significa que las entradas de índice que no satisfacen el filtro salary aún se leen, lo que aumenta la latencia y el costo de la consulta.

Precios

Las consultas con filtros de rango y desigualdad en varios campos se facturan en función de documentos y entradas de índice leídas.

Para obtener más detalles, consulta la página de precios.

Limitaciones

Además de las limitaciones de las consultas, ten en cuenta las siguientes limitaciones antes de usar las consultas con filtros de rango y desigualdad en varios campos:

  • Consultas con filtros de rango o desigualdad en campos de documentos y solo igualdad no se admiten restricciones en la clave del documento (__name__).
  • Firestore limita la cantidad de rangos o campos de desigualdad a 10. Esto sirve para evitar que las consultas sean demasiado costosas cuando se ejecuten.

¿Qué sigue?