Descripción general de la indexación
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 realizas una consulta en una base de datos, esta puede usar un índice para identificar rápidamente las ubicaciones de los artículos que solicitaste.
En esta página, se describen los dos tipos de índices que usa Firestore: los de campo único y los compuestos.
Definición y estructura de los índices
Un índice se define en una lista de campos de un documento determinado, con un modo de índice correspondiente para cada campo.
Un índice contiene una entrada para cada campo nombrado en la definición del índice. El índice incluye todos los documentos que son los posibles resultados de las consultas basadas en el índice. Un documento solo se incluye en el índice si tiene un valor indexado configurado para cada campo utilizado en el índice. Si la definición del índice hace referencia a un campo para el cual el documento no tiene un valor establecido, ese documento no aparecerá en el índice. En este caso, nunca se mostrará el documento como resultado de ninguna consulta basada en el índice.
El índice compuesto se ordena por los valores de campo, en el orden especificado en la definición del índice.
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. 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
Firestore incluye funciones que reducen el tiempo que necesitas dedicar a la administración de índices. Los índices necesarios para las consultas más básicas se crean automáticamente para ti. A medida que usas y pruebas tu app, Firestore te ayudará a identificar y crear los índices adicionales que la app necesita.
Tipos de índices
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. 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, Firestore mantiene de forma automática un índice de campo único para cada campo de un documento y cada subcampo de un mapa. Firestore usa la siguiente configuración predeterminada para los índices de campo único:
En los campos que no son de arreglo ni de mapa, 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, 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
- Firestore indexa recursivamente cada subcampo de mapa.
En cada campo de arreglo de un documento, Firestore crea y mantiene un índice de contenido de arreglo 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 exenciones de índice de campo único, consulta Administra los índices.
Í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.
Firestore usa índices compuestos para admitir consultas que no se pueden realizar en un índice de campo único.
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, Firestore te ayuda a identificar y crear los índices compuestos necesarios mientras compilas tu app.
Cada vez que intentes realizar una consulta no compatible con un índice, Firestore muestra un mensaje de error con un vínculo que puedes seguir para crear el índice que falta.
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 permisos 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. |
Vector | Admite cláusulas de consulta FindNearest en el campo. |
Permisos 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
- De forma predeterminada, Firestore crea índices con alcance de colección. Estos índices admiten consultas que muestran resultados de una sola colección.
- Alcance del grupo de colección
- 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 | __name__ |
nombre, Colección |
cities | __name__ |
estado, Colección |
cities | __name__ |
país, población, Colección |
Para ordenar los resultados según la dirección __name__
no predeterminada, tienes que
crear ese índice.
Propiedades del índice
Un índice que permite que la consulta se ejecute de la manera más eficiente se define con las siguientes propiedades:
- Campos usados en los filtros de igualdad
- Campos usados en órdenes de clasificación
- Campos usados en el rango y filtros de desigualdad (que aún no están incluidos en los órdenes de clasificación)
- Campos usados en agregaciones (que aún no están incluidos en órdenes de clasificación ni en filtros de rango y desigualdad)
Firestore calcula los resultados de las consultas de la siguiente manera:
- Identifica el índice correspondiente a la colección, las propiedades de filtro, los operadores de filtro y los órdenes de clasificación de la consulta.
- Identifica la posición del índice desde la que comienza el análisis. La posición de inicio tiene el prefijo de los filtros de igualdad de la consulta y termina con los filtros de rango y desigualdad del primer campo
orderBy
. - Comienza a analizar el índice y muestra cada documento que satisface todos los filtros hasta que el proceso de análisis realice una de las siguientes acciones:
- Encuentra un documento que no cumple con las condiciones del filtro y confirma que cualquier documento posterior nunca cumplirá por completo las condiciones del filtro.
- Llega al final del índice.
- Recopila la cantidad máxima de resultados que solicita la consulta.
Ejemplo de indexación
Firestore crea los índices de campo único de forma automática, lo que permite que tu aplicación admita con rapidez 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"] });
Con la configuración de indexación automática predeterminada como base, Firestore actualiza un índice de campo único ascendente y uno descendente por cada campo que no sea de arreglo y un índice de campo único con contenido del arreglo para el campo del arreglo. 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
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 |
Para evitar pérdidas de rendimiento a causa de la combinación de índices, te recomendamos crear
un índice compuesto para combinar una consulta 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 ) capitalColección |
Consultas compatibles con los índices del grupo de colecciones
Para demostrar un índice con alcance del grupo de colecciones, agrega una
subcolección landmarks
en algunos de los 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"])
Si quieres consultar los puntos de referencia de todas las ciudades, por ejemplo,
ejecuta esta consulta en el grupo de colecciones que consta de todas las colecciones de landmarks
. También debes habilitar un índice de campo único landmarks
con
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 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 calcular el tamaño de almacenamiento de los índices, consulta Tamaño de una entrada del índice.
Usa la combinación de índices
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
, Firestore puede volver a usar los índices existentes. 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.
Puedes reducir los costos de indexación si identificas situaciones en las que puedes usar la combinación de índices. Por ejemplo, en 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
Esta app usa consultas como las siguientes. La app usa cláusulas de combinaciones de igualdad
para category
, city
y editors_pick
, y siempre se organiza por
star_rating
en orden ascendente:
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 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. Para obtener más información sobre las cuotas y los límites, consulta Cuotas y límites.
En esta página, se indican los límites y las cuotas de solicitudes de Firestore.
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:
Para ver cómo Firestore convierte un documento y un conjunto de índices en entradas en un í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 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: |
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 estás usando consultas con operadores de desigualdad en varios campos, consulta las consideraciones de indexación que debes tener en cuenta para optimizar el rendimiento y costo de las consultas de Firestore
Para 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.