Tipos de índices de Cloud Firestore

Los índices son un factor importante del rendimiento de las bases de datos. Al igual que el índice de un libro que indica en qué página se encuentra cada tema, el índice de una base de datos se encarga de ordenar todos los elementos en su ubicación respectiva. Cuando envías una consulta a una base de datos, esta puede usar el índice para buscar rápidamente los elementos que solicitaste.

En esta página, se describen los dos tipos de índices que usa Cloud Firestore: los de campo único y los compuestos.

Un índice para cada consulta

Si se hace una consulta sin índice, la mayoría de las bases de datos rastreará su contenido elemento por elemento. Este proceso es muy lento y empeora a medida que aumenta el tamaño de la base de datos. Cloud Firestore garantiza un alto rendimiento, ya que usa índices en todas las consultas. Por lo tanto, el rendimiento de las consultas depende del tamaño del conjunto de resultados y no de la cantidad de elementos que se encuentran en la base de datos.

Dedica menos tiempo a la administración de índices y más al desarrollo de apps

Cloud Firestore tiene funciones que reducen el tiempo requerido para administrar los índices, y crea automáticamente los índices necesarios para las consultas más básicas. A medida que usas y pruebas tu app, Cloud Firestore te ayudará a identificar y crear los índices adicionales que necesitas.

Tipos de índices

Cloud Firestore usa dos tipos de índices: los de campo único y los compuestos. Además de la cantidad de campos indexados, los índices de campo único y compuestos se administran de manera distinta.

Índices de campo único

Un índice de campo único almacena el orden de todos los documentos en una colección con un campo específico. Todas las entradas de un índice de campo único registran el valor de un documento en un campo específico y su ubicación en la base de datos. Cloud Firestore usa estos índices para realizar muchas consultas básicas. Para administrar los índices de campo único, puedes configurar la indexación automática de tu base de datos y las exenciones de índices.

Indexación automática

Según la configuración predeterminada, Cloud Firestore mantiene automáticamente un índice de campo único para cada campo de un documento y cada subcampo de un mapa. Cloud Firestore usa la siguiente configuración predeterminada para los índices de campo único:

  • En los campos que no son de array ni de mapa, Cloud Firestore define dos índices de campo único con alcance de colección: uno en modo ascendente y otro en modo descendente.

  • Para cada campo de mapa, Cloud Firestore crea lo siguiente:

    • Un índice ascendente con alcance de colección para cada subcampo que no sea de mapa ni de array.
    • Un índice descendente de alcance de colección para cada subcampo que no sea de mapa ni de array.
    • Un índice de contenido de array con alcance de colección para cada subcampo de array
    • Cloud Firestore indexa recursivamente cada subcampo de mapa.
  • En cada campo de array de un documento, Cloud Firestore crea y mantiene un índice de contenido de array con alcance de colección.

  • Según la configuración predeterminada, no se mantienen los índices de campo único con alcance del grupo de colección.

Exenciones de índices de campo único

Para eximir un campo de tu configuración de indexación automática, crea una exención del índice de campo único. Esta opción anula la configuración de índice automático de la base de datos. Una exención puede habilitar un índice de campo único que, de lo contrario, se inhabilitaría por tu configuración de indexación automática, o viceversa. Consulta las prácticas recomendadas de indexación para saber en qué casos podrían ser útiles las exenciones.

Usa el valor de ruta del campo * para agregar exenciones de índices a nivel de colección en todos los campos de un grupo de colecciones. Por ejemplo, para el grupo de colecciones comments, establece la ruta de acceso del campo en * a fin de que coincida con todos los campos del grupo de colecciones comments y, luego, inhabilita la indexación de todos los campos del grupo de colecciones. Luego, puedes agregar exenciones a fin de indexar solo los campos obligatorios para tus consultas. Reducir la cantidad de campos indexados reduce los costos de almacenamiento y puede mejorar el rendimiento de la escritura.

Si creas una exención de un índice de campo único para un campo de mapa, sus subcampos heredarán esta configuración. Sin embargo, puedes definir exenciones de índices de campo único para subcampos específicos. Si borras la exención de un subcampo, este heredará la configuración de la exención principal (si tienes una) o, de lo contrario, heredará la de la base de datos.

Para crear y administrar las exenciones de índice de campo único, consulta Administra índices en Cloud Firestore.

Índices compuestos

Un índice compuesto almacena el orden de todos los documentos en una colección, según una lista ordenada de los campos que se indexarán.

Cloud Firestore usa índices compuestos para admitir consultas que no se pueden realizar en un índice de campo único.

Cloud Firestore no crea automáticamente los índices compuestos, como sí lo hace con los de campo único, debido a la gran cantidad de combinaciones posibles de los campos. En lugar de eso, Cloud Firestore te ayuda a identificar y crear los índices compuestos necesarios mientras compilas tu app.

Si intentas realizar la consulta anterior sin haber creado el índice necesario, Cloud Firestore mostrará un mensaje de error con un vínculo que podrás usar para crear el índice. Esto ocurre cada vez que intentas realizar una consulta que no es compatible con un índice. También puedes definir y administrar los índices compuestos de forma manual con la consola o Firebase CLI. Para obtener más información sobre cómo crear y administrar índices compuestos, consulta este artículo.

Modos de índice y alcances de las consultas

Los índices de campo único y los compuestos se configuran de manera distinta, pero ambos requieren que ajustes los modos de índice y alcances de las consultas.

Modos de índice

Cuando se define un índice, se selecciona un modo de índice para cada campo indexado. El modo de índice de cada campo admite cláusulas específicas de consultas en ese campo. Puedes seleccionar las siguientes opciones:

Modo de índice Descripción
Ascendente Admite cláusulas de consulta <, <=, ==, >=, >, !=, in y not-in en el campo, así como el orden ascendente según el valor del campo.
Descendente Admite cláusulas de consulta <, <=, ==, >=, >, !=, in y not-in en el campo, así como el orden descendente según el valor del campo.
Contenido del array Admite cláusulas de consulta array-contains y array-contains-any en el campo.

Alcances de las consultas

Cada índice se limita a una colección o a un grupo de colecciones. Esto se conoce como el alcance de la consulta del índice:

Alcance de la colección
Según la configuración predeterminada, Cloud Firestore crea índices con alcance de colección. Estos índices admiten consultas que muestran resultados de una sola colección.

Alcance del grupo de colecciones
Un grupo de colecciones incluye todas las colecciones con el mismo ID. Para ejecutar una consulta del grupo de colecciones que muestre resultados ordenados o filtrados de un grupo de colecciones, debes crear un índice correspondiente con el alcance del grupo de colecciones.

Orden predeterminado y campo __name__

Además de ordenar los documentos según los modos de índice especificados para cada campo (ascendente o descendente), los índices aplican un ordenamiento final por el campo __name__ de cada documento. El valor del campo __name__ se establece en la ruta de acceso completa del documento. Esto significa que los documentos en el conjunto de resultados con los mismos valores de campo se ordenan por su ruta de acceso.

De forma predeterminada, el campo __name__ se ordena en la misma dirección del último campo ordenado de la definición del índice. Por ejemplo:

Colección Campos indexados Alcance de la consulta
cities nombre, __name__ Colección
cities estado, __name__ Colección
cities país, población, __name__ Colección

Para ordenar los resultados según la dirección __name__ no predeterminada, tienes que crear ese índice.

Ejemplo de indexación

Cloud Firestore crea los índices de campo único de forma automática, lo que permite que tu aplicación admita rápidamente los tipos de consultas de base de datos más básicos. Los índices de campo único te permiten ejecutar consultas simples basadas en los valores de campo y los comparadores <, <=, ==, >=, > y in. En los campos de array, te permiten ejecutar consultas de array-contains y array-contains-any.

A modo de referencia, consulta los siguientes ejemplos desde la perspectiva de la creación de índices. En el siguiente ejemplo, se crean algunos documentos de city en una colección de cities y se configuran los campos name, state, country, capital, population y tags para cada documento:

Web
var citiesRef = db.collection("cities");

citiesRef.doc("SF").set({
    name: "San Francisco", state: "CA", country: "USA",
    capital: false, population: 860000,
    regions: ["west_coast", "norcal"] });
citiesRef.doc("LA").set({
    name: "Los Angeles", state: "CA", country: "USA",
    capital: false, population: 3900000,
    regions: ["west_coast", "socal"] });
citiesRef.doc("DC").set({
    name: "Washington, D.C.", state: null, country: "USA",
    capital: true, population: 680000,
    regions: ["east_coast"] });
citiesRef.doc("TOK").set({
    name: "Tokyo", state: null, country: "Japan",
    capital: true, population: 9000000,
    regions: ["kanto", "honshu"] });
citiesRef.doc("BJ").set({
    name: "Beijing", state: null, country: "China",
    capital: true, population: 21500000,
    regions: ["jingjinji", "hebei"] });

Suponiendo que se tiene la configuración de indexación automática predeterminada, Cloud Firestore actualiza un índice de campo único ascendente y uno descendente por cada campo que no sea de array y un índice de campo único con contenido del array para el campo del array. Cada fila de la siguiente tabla representa una entrada en un índice de campo único:

Colección Campo indexado Alcance de la consulta
cities name Colección
cities state Colección
cities country Colección
cities capital Colección
cities population Colección
cities name Colección
cities state Colección
cities country Colección
cities capital Colección
cities population Colección
cities array-contains regions Colección

Consultas compatibles con los índices de campo único

Puedes usar estos índices de campo único creados automáticamente para ejecutar consultas simples, como esta:

Web
const stateQuery = citiesRef.where("state", "==", "CA");
const populationQuery = citiesRef.where("population", "<", 100000);
const nameQuery = citiesRef.where("name", ">=", "San Francisco");

También puedes crear consultas in y de igualdad compuesta (==):

Web
citiesRef.where('country', 'in', ["USA", "Japan", "China"])

// Compound equality queries
citiesRef.where("state", "==", "CO").where("name", "==", "Denver")
citiesRef.where("country", "==", "USA")
         .where("capital", "==", false)
         .where("state", "==", "CA")
         .where("population", "==", 860000)

Si necesitas ejecutar una consulta compuesta que use una comparación de rangos (<, <=, > o >=) o necesitas ordenar según otro campo, debes crear un índice compuesto para la consulta.

El índice de array-contains te permite realizar una consulta en el campo de array regions:

Web
citiesRef.where("regions", "array-contains", "west_coast")
// array-contains-any and array-contains use the same indexes
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])

Consultas compatibles con los índices compuestos

Cloud Firestore usa índices compuestos para admitir consultas compuestas que no se pueden realizar en un índice de campo único. Por ejemplo, debes tener un índice compuesto para realizar las siguientes consultas:

Web
citiesRef.where("country", "==", "USA").orderBy("population", "asc")
citiesRef.where("country", "==", "USA").where("population", "<", 3800000)
citiesRef.where("country", "==", "USA").where("population", ">", 690000)
// in and == clauses use the same index
citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)

Estas requieren el índice compuesto que se indica a continuación. Dado que la consulta utiliza una igualdad (== o in) para el campo country, puedes usar un modo de índice ascendente o descendente para este campo. Según la configuración predeterminada, las cláusulas con desigualdad aplicarán un orden ascendente según el campo que se indique en la cláusula.

Colección Campos indexados Alcance de la consulta
cities (o ) country, population Colección

Para ejecutar las mismas consultas, pero en orden descendente, necesitas otro índice compuesto en orden descendente para el valor population:

Web
citiesRef.where("country", "==", "USA").orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", "<", 3800000)
         .orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", ">", 690000)
         .orderBy("population", "desc")

citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)
         .orderBy("population", "desc")
Colección Campos indexados Alcance de la consulta
cities country, population Colección
cities country, population Colección

También deberás crear un índice compuesto para combinar una consulta de array-contains o array-contains-any con cláusulas adicionales.

Web
citiesRef.where("regions", "array-contains", "east_coast")
         .where("capital", "==", true)

// array-contains-any and array-contains use the same index
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])
         .where("capital", "==", true)
Colección Campos indexados Alcance de la consulta
cities array-contains tags, (o ) capital Colección

Consultas compatibles con los índices del grupo de colecciones

Para demostrar un índice con alcance del grupo de colecciones, imagina que agregas una subcolección landmarks a algunos documentos de city:

Web
var citiesRef = db.collection("cities");

citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Bridge",
    category : "bridge" });
citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Park",
    category : "park" });

citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Gallery of Art",
    category : "museum" });
citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Mall",
    category : "park" });

Si usas el siguiente índice de campo único con alcance de colección, puedes consultar la colección landmarks de una sola ciudad según el campo category:

Colección Campos indexados Alcance de la consulta
landmarks (o ) category Colección
Web
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")
citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])

Ahora imagina que te interesa consultar los puntos de referencia de todas las ciudades. Para realizar esta consulta en un grupo que tenga todas las colecciones de landmarks, debes habilitar el índice de campo único landmarks con el alcance del grupo de colecciones:

Colección Campos indexados Alcance de la consulta
landmarks (o ) category Grupo de colecciones

Con este índice habilitado, puedes consultar el grupo de colecciones landmarks:

Web
var landmarksGroupRef = db.collectionGroup("landmarks");

landmarksGroupRef.where("category", "==", "park")
landmarksGroupRef.where("category", "in", ["park", "museum"])

Para realizar una consulta en un grupo de colección que muestre resultados ordenados o filtrados, debes habilitar un índice de campo único o compuesto correspondiente con el alcance del grupo de colección. Sin embargo, las consultas que no ordenan ni filtran los comentarios, no requieren definiciones adicionales del índice.

Por ejemplo, puedes ejecutar la siguiente consulta en el grupo de colección sin habilitar un índice adicional:

Web
db.collectionGroup("landmarks").get()

Entradas de índice

Los índices configurados de tu proyecto y la estructura de un documento determinan la cantidad de entradas de índice de un documento. Las entradas de índice se consideran en el límite de recuento de entradas de índice.

En el siguiente ejemplo, se muestran las entradas de índice de un documento.

Documento

/cities/SF

city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]

Índices de campo único

  • city_name ASC
  • city_name DESC
  • temperatures.summer ASC
  • temperatures.summer DESC
  • temperatures.winter ASC
  • temperatures.winter DESC
  • neighborhoods Array Contains (ASC y DESC)

Índices compuestos

  • city_name ASC, neighborhoods ARRAY
  • city_name DESC, neighborhoods ARRAY

Entradas de índice

Esta configuración de indexación da como resultado las siguientes 18 entradas de índice para el documento:

Índice Datos indexados
Entradas de índice de campo único
city_name ASC city_name: “San Francisco”
city_name DESC city_name: “San Francisco”
temperatures.summer ASC temperatures.summer: 67
temperatures.summer DESC temperatures.summer: 67
temperatures.winter ASC temperatures.winter: 55
temperatures.winter DESC temperatures.winter: 55
neighborhoods Array Contains ASC neighborhoods: “Mission”
neighborhoods Array Contains DESC neighborhoods: “Mission”
neighborhoods Array Contains ASC neighborhoods: “Downtown”
neighborhoods Array Contains DESC neighborhoods: “Downtown”
neighborhoods Array Contains ASC neighborhoods: “Marina”
neighborhoods Array Contains DESC neighborhoods: “Marina”
Entradas de índice compuesto
city_name ASC, neighborhoods ARRAY city_name: “San Francisco”, neighborhoods: “Mission”
city_name ASC, neighborhoods ARRAY city_name: “San Francisco”, neighborhoods: “Downtown”
city_name ASC, neighborhoods ARRAY city_name: “San Francisco”, neighborhoods: “Marina”
city_name DESC, neighborhoods ARRAY city_name: “San Francisco”, neighborhoods: “Mission”
city_name DESC, neighborhoods ARRAY city_name: “San Francisco”, neighborhoods: “Downtown”
city_name DESC, neighborhoods ARRAY city_name: “San Francisco”, neighborhoods: “Marina”

Índices y precios

Los índices contribuyen a los costos de almacenamiento de tu aplicación. Para obtener más información sobre cómo se calcula el tamaño del almacenamiento de los índices, consulta Tamaño de una entrada del índice.

Cómo aprovechar la combinación de índices

Cloud Firestore usa un índice para cada consulta, pero este no es un requisito. En el caso de las consultas con varias cláusulas de igualdad (==) y, de forma opcional, una cláusula orderBy, Cloud Firestore puede volver a usar los índices existentes. Cloud Firestore puede combinar los índices con filtros de igualdad simples a fin de crear los índices compuestos necesarios para realizar consultas con más cláusulas de igualdad.

A fin de reducir los costos de creación de los índices, identifica las situaciones en las que puedes combinar algunos de ellos. Por ejemplo, imagina una colección de restaurants en una app de calificación de restaurantes:

  • restaurants

    • burgerthyme

      name : "Burger Thyme"
      category : "burgers"
      city : "San Francisco"
      editors_pick : true
      star_rating : 4

Ahora imagina que esta app usa consultas como las que se indican a continuación. Ten en cuenta que la app usa combinaciones de cláusulas de igualdad para category, city y editors_pick, y un orden ascendente por star_rating.

Web
db.collection("restaurants").where("category", "==", "burgers")
                            .orderBy("star_rating")

db.collection("restaurants").where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==" "San Francisco")
                            .where("editors_pick", "==", true )
                            .orderBy("star_rating")

Puedes crear un índice para cada consulta:

Colección Campos indexados Alcance de la consulta
restaurants category, star_rating Colección
restaurants city, star_rating Colección
restaurants category, city, star_rating Colección
restaurants category, city, editors_pick, star_rating Colección

Una mejor opción es aprovechar la función de Cloud Firestore para combinar índices según cláusulas de igualdad a fin de reducir la cantidad de índices:

Colección Campos indexados Alcance de la consulta
restaurants category, star_rating Colección
restaurants city, star_rating Colección
restaurants editors_pick, star_rating Colección

Este conjunto de índices no solo es más pequeño, sino que también admite una consulta adicional:

Web
db.collection("restaurants").where("editors_pick", "==", true)
                            .orderBy("star_rating")

Límites de indexación

Los siguientes límites se aplican a los índices. Consulta Cuotas y límites para obtener más información sobre este tema.

Límite Detalles
Cantidad máxima de índices compuestos que se permiten para una base de datos
Cantidad máxima de parámetros de configuración de campo único para una base de datos

Una configuración a nivel de campo puede contener varios parámetros de configuración para el mismo campo. Por ejemplo, una exención de la indexación de campo único y una política de TTL en el mismo campo se registran como un solo parámetro de configuración para el límite.

Cantidad máxima de entradas de índice permitidas en cada documento

40,000

La cantidad de entradas de índice en cada documento es la suma de los siguientes elementos:

  • Cantidad de entradas en un índice de campo único
  • Cantidad de entradas en un índice compuesto

Para ver cómo Cloud Firestore convierte un documento y un conjunto de índices en entradas de índice, consulta este ejemplo de recuento de entradas de índice.

Cantidad máxima de campos en un índice compuesto 100
Tamaño máximo de una entrada de índice

7.5 KiB

Para saber cómo Cloud Firestore calcula el tamaño de las entradas en el índice, consulta Tamaño de una entrada del índice.

Suma máxima de los tamaños de las entradas en un índice de un documento

8 MiB

El tamaño total corresponde a la suma de los siguientes elementos de un documento:

  • La suma del tamaño de las entradas de índice de campo único del documento
  • La suma del tamaño de las entradas de índice compuesto del documento
  • Tamaño máximo del valor de un campo indexado

    1,500 bytes

    Se reducirán los valores de campo que superen los 1,500 bytes. Es posible que las consultas que tengan valores de campo reducidos muestren resultados incoherentes.

    Recomendaciones sobre indexación

    En la mayoría de las apps, puedes confiar en la indexación automática y en los vínculos de los mensajes de error para administrar tus índices. Sin embargo, es posible que quieras agregar exenciones de campo único en los siguientes casos:

    Caso Descripción
    Campos de string grandes

    Si tienes un campo de string que suele tener valores de string largos que no usas para realizar consultas, exime al campo de la indexación a fin de reducir los costos de almacenamiento.

    Tasas altas de escritura en una colección que tiene documentos con valores secuenciales

    Si indexas un campo que aumenta o disminuye secuencialmente entre los documentos de una colección, como una marca de tiempo, la tasa máxima de escritura en la colección es de 500 operaciones de escritura por segundo. Si no realizas consultas basadas en campos con valores secuenciales, puedes eximir al campo de la indexación para pasar ese límite.

    Por ejemplo, en un caso de uso de IoT con una tasa alta de escritura, es posible que una colección de documentos con un campo de marca de tiempo se acerque al límite de 500 operaciones de escritura por segundo.

    Campos TTL

    Si usas políticas de TTL (tiempo de actividad), ten en cuenta que el campo TTL debe ser una marca de tiempo. La indexación en los campos TTL está habilitada de forma predeterminada y puede afectar el rendimiento con tasas de tráfico más altas. Como práctica recomendada, agrega exenciones de campo único para tus campos TTL.

    Campos grandes de array o de mapa

    Los campos grandes de array o de mapa pueden acercarse al límite de 40,000 entradas de índice por documento. Si no realizas consultas basadas en un campo grande de array o de mapa, debes excluir el campo de la indexación.

    Si quieres obtener más información para resolver los problemas de indexación (fanout de índices, errores INVALID_ARGUMENT), consulta la página de solución de problemas.