Visão geral da indexação
Os índices são um fator importante no desempenho de um banco de dados. Semelhantes aos índices de livros que correlacionam os tópicos às páginas, os índices de bancos de dados referenciam os itens aos locais deles no banco de dados. Ao fazer uma consulta a um banco de dados, o banco de dados pode usar um índice para identificar rapidamente os locais dos itens solicitados.
Veja nesta página os dois tipos de índice usados pelo Firestore: índices de campo único e índices compostos.
Definição e estrutura dos índices
Um índice é definido em uma lista de campos de um determinado documento, com um modo de índice correspondente para cada campo.
Um índice contém uma entrada para cada campo nomeado na definição do índice. O índice inclui todos os documentos que são os possíveis resultados para consultas baseadas no índice. Um documento só é incluído no índice quando tem um valor indexado definido para cada campo usado no índice. Se a definição do índice se referir a um campo para o qual o documento não tenha um valor definido, ele não aparecerá no índice. Nesse caso, o documento nunca será retornado como um resultado para qualquer consulta baseada no índice.
O índice composto é classificado por valores de campo, na ordem especificada na definição do índice.
Um índice para cada consulta
Caso não haja um índice em uma consulta, a maioria dos bancos de dados rastreará o conteúdo item por item, um processo demorado que fica ainda mais lento à medida que o banco de dados cresce. O Firestore garante alto desempenho de consulta usando índices em todas as consultas. Como resultado, o desempenho da consulta depende do tamanho do conjunto de resultados e não do número de itens inclusos no banco de dados.
Menos gerenciamento de índices, mais desenvolvimento de apps
O Firestore inclui recursos que reduzem a quantidade de tempo que você precisam gastar com gerenciamento de índices. Os índices necessários para as consultas mais básicas são criados automaticamente. À medida que você usa e testa seu app, o Firestore ajuda a identificar e criar outros índices que sejam necessários para ele.
Tipos de índice
O Firestore usa dois tipos de índices, de campo único e compostos. O número de campos indexados não é a única diferença entre esses índices, o gerenciamento deles também é diferente.
Índices de campo único
Um índice de campo único armazena um mapeamento ordenado de todos os documentos em um conjunto com um campo específico. Cada entrada em um índice de campo único registra o valor de um documento para um campo específico e o local do documento no banco de dados. O Firestore usa esses índices para realizar muitas consultas básicas. É preciso definir as configurações de indexação automática e a isenção de índice para gerenciar índices de campo único.
Indexação automática
Por padrão, o Firestore mantém automaticamente os índices de campo único para cada campo em um documento e cada subcampo em um mapa. O Firestore usa as seguintes configurações para índices de campo único:
Para cada campo que não seja de matriz ou de mapa, o Firestore define dois índices de campo único do escopo da coleção: um em modo crescente e o outro em modo decrescente.
Para cada campo de mapa, o Firestore cria o seguinte:
- Um índice em ordem crescente do escopo da coleção para cada subcampo que não é de matriz ou mapa.
- Um índice em ordem decrescente do escopo da coleção para cada subcampo que não é da matriz ou mapa.
- Um índice de conteúdo da matriz do escopo da coleção para cada subcampo da matriz.
- O Firestore indexa recursivamente cada subcampo do mapa.
Para cada campo de matriz de um documento, o Firestore cria e mantém um índice "array-contains" do escopo da coleção.
Índices de campo único com escopo de grupo de coleções não são mantidos por padrão.
Isenções de índice de campo único
É possível isentar um campo a partir das suas configurações de indexação automática criando uma isenção de índice de campo único. Uma isenção de indexação substitui as configurações de índice automático em todo o banco de dados. Uma isenção pode ativar um índice de campo único que suas configurações de indexação automática geralmente desativariam, ou desativar um índice de campo único que a indexação automática geralmente ativaria. Para ver casos em que as isenções podem ser úteis, consulte as práticas recomendadas de indexação.
Use o valor do caminho do campo *
para adicionar isenções de índice no nível da coleção em todos
os campos de um grupo de coleções. Por exemplo, para o grupo de coleções comments
, defina
o caminho do campo como *
para corresponder a todos os campos no grupo de coleções de comments
e
desativar a indexação de todos os campos no grupo de coleções. Em seguida, é possível adicionar
isenções para indexar somente os campos obrigatórios para suas consultas. A redução do
número de campos indexados reduz os custos de armazenamento e pode melhorar o desempenho
da gravação.
Se você criar uma isenção para um índice de campo único no campo do mapa, os subcampos do mapa herdarão essas configurações. No entanto, é possível definir as isenções para um índice de campo único em subcampos específicos. Se você excluir a isenção de um subcampo, ele herdará as configurações de isenção do campo pai. Se essas configurações não existirem, ele herdará as configurações gerais do banco de dados.
Para criar e gerenciar isenções de índice de campo único, consulte Gerenciar índices.
Índices compostos
Um índice composto armazena um mapeamento ordenado de todos os documentos em uma coleção, com base em uma lista ordenada de campos a serem indexados.
O Firestore usa índices compostos para oferecer suporte às consultas que ainda não aceitam índices de campo único.
O Firestore não cria índices compostos automaticamente para índices de campo único devido ao grande número de possíveis campos diferentes. Em vez disso, ele ajuda a identificar e criar os índices compostos necessários à medida que você cria seu app.
Sempre que você tenta fazer uma consulta sem suporte de um índice, o Firestore retorna uma mensagem de erro com um link que pode ser acessado para criar a índice.
Também é possível criar e gerenciar índices compostos manualmente usando o console ou a CLI do Firebase. Para mais informações sobre esses processos, consulte Como gerenciar índices.
Modos de índice e escopos de consulta
A configuração de índices compostos e de campo único é feita de maneira diferente, mas ambos exigem que você configure modos de índice e escopos de consulta para seus índices.
Modos de índice
Ao definir um índice, você seleciona um modo de índice para cada campo indexado. O modo de índice de cada campo oferece suporte a cláusulas de consulta específicas nesse campo. É possível selecionar os seguintes modos de índice:
Modo de índice | Descrição |
---|---|
Crescente | Aceita as cláusulas de consulta < , <= , == , >= , > , != , in e not-in no campo e aceita classificar os resultados em ordem crescente com base no valor desse campo. |
Decrescente | Aceita as cláusulas de consulta < , <= , == , >= , > , != , in e not-in no campo e aceita classificar os resultados em ordem decrescente com base no valor desse campo. |
Matriz contém | Aceita as cláusulas de consulta array-contains e array-contains-any no campo. |
Vetor | Compatível com cláusulas de consulta FindNearest no campo. |
Escopos da consulta
Cada índice tem um escopo delimitado a uma coleção ou a um grupo de coleções. Isso é conhecido como o escopo da consulta do índice:
- Escopo da coleção
- O Firestore cria índices com um escopo de coleção por padrão. Esses índices são compatíveis com consultas que retornam resultados de uma única coleção.
- Escopo do grupo de coleções
- Um grupo de coleções inclui todas as coleções com o mesmo ID. Para executar uma consulta do grupo de coleções que retorne resultados filtrados ou ordenados de um desses grupos, é necessário criar um índice correspondente com o escopo de grupo de coleções.
Ordem padrão e o campo __name__
Além de classificar os documentos pelos modos de índice
especificados para cada campo (crescente ou decrescente), os índices aplicam uma classificação
final pelo campo __name__
de cada documento. O valor do campo __name__
é definido como o caminho completo do documento. Isso significa que os documentos
no conjunto de resultados com os mesmos valores de campo são classificados por caminho.
Por padrão, o campo __name__
é classificado na mesma direção do último
campo classificado na definição do índice. Exemplo:
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
cities | __name__ |
nome, Coleção |
cities | __name__ |
estado, Coleção |
cities | __name__ |
país, população, Coleção |
Para classificar os resultados pela direção __name__
não padrão,
crie esse índice.
Propriedades do índice
Um índice que permite que a consulta seja executada com mais eficiência é definido pelas seguintes propriedades:
- Campos usados em filtros de igualdade
- Campos usados em ordens de classificação
- Os campos usados em filtros de intervalo e desigualdade (que ainda não estão incluídos em ordens de classificação)
- Campos usados em agregações (que ainda não estão incluídos nas ordens de classificação e nos filtros de intervalo e desigualdade)
O Firestore calcula os resultados das consultas da seguinte maneira:
- Identifica o índice correspondente à coleção da consulta, às propriedades do filtro, aos operadores do filtro e às ordens de classificação.
- Identifica a posição do índice a partir da qual a verificação é iniciada. A posição inicial é prefixada com os filtros de igualdade da consulta e termina com os filtros de intervalo e desigualdade no primeiro campo
orderBy
. - Começa a verificar o índice, retornando cada documento que satisfaça todos os filtros, até que o processo de verificação realize uma das seguintes ações:
- Encontra um documento que não atende às condições do filtro e confirma que qualquer documento subsequente nunca atenderá totalmente às condições do filtro.
- Chega ao fim do índice.
- Coleta o número máximo de resultados solicitados pela consulta.
Exemplo de indexação
Ao criar índices de campo único automaticamente para você, o Firestore
permite que seu aplicativo dê suporte rapidamente às consultas mais básicas no banco de dados.
Com os índices de campo único, é possível realizar consultas simples com base nos valores de campo e nos comparadores <
, <=
, ==
, >=
, >
e in
. Quando se trata de campos de matriz, eles permitem
que você realize as consultas array-contains
e array-contains-any
.
Para entender melhor, examine os exemplos a seguir do ponto de vista da
criação do índice. O snippet a seguir cria alguns documentoscity
em uma coleção cities
e define name
, state
, country
, capital
, population
e tags
campos 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"] });
Considerando as configurações de indexação automática padrão, o Firestore atualiza um índice de campo único crescente por campo que não seja de matriz, um índice de campo único decrescente por campo que não seja de matriz e um índice de campo único "array-contains" para o campo de matriz. Cada linha na tabela a seguir representa uma entrada em um índice de campo único:
Coleção | Campo indexado | Escopo da consulta |
---|---|---|
cities | Nome | Coleção |
cities | estado | Coleção |
cities | country | Coleção |
cities | capital | Coleção |
cities | population | Coleção |
cities | Nome | Coleção |
cities | estado | Coleção |
cities | country | Coleção |
cities | capital | Coleção |
cities | population | Coleção |
cities | array-contains regions |
Coleção |
Consultas compatíveis com índices de campo único
Ao usar esses índices de campo único criados automaticamente, é possível executar consultas simples como as seguintes:
Web
const stateQuery = citiesRef.where("state", "==", "CA"); const populationQuery = citiesRef.where("population", "<", 100000); const nameQuery = citiesRef.where("name", ">=", "San Francisco");
Também é possível criar consultas in
e de igualdade composta (==
):
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)
Se você precisar executar uma consulta composta que usa uma comparação de intervalo (<
, <=
,
>
ou >=
) ou classificar por um campo diferente, crie um
índice composto para essa consulta.
O índice array-contains
permite consultar o campo de matriz 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 compatíveis com índices compostos
O Firestore usa índices compostos para oferecer suporte a consultas compostas que ainda não aceitam índices de campo único. Por exemplo, você precisaria de um índice composto para as seguintes 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)
Essas consultas exigem o índice composto abaixo. Como a consulta usa
uma igualdade (==
ou in
) para o campo country
, é possível usar
um modo de índice crescente ou decrescente para esse campo. Por padrão,
as cláusulas de desigualdade usam uma ordem de classificação crescente com base no
campo na cláusula de desigualdade.
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
cities | (ou ) país, população | Coleção |
Para executar as mesmas consultas, mas com uma ordem de classificação decrescente, você
precisa de um índice composto adicional na direção decrescente para 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")
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
cities | país, população | Coleção |
cities | país, população | Coleção |
Para evitar a perda de desempenho causada pela mesclagem de índices, recomendamos criar
um índice composto para combinar uma consulta array-contains
ou array-contains-any
com cláusulas adicionais:
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)
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
cities | A matriz contém tags, | (ou ) capitalColeção |
Consultas compatíveis com índices de grupo de coleções
Para demonstrar um índice com escopo de grupo de coleções, adicione uma
subcoleção landmarks
a alguns dos documentos 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" });
Ao usar o seguinte índice de campo único com escopo de coleção, é possível consultar
uma coleção de landmarks
de uma única cidade com base no campo category
:
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
pontos de referência | (ou ) categoria | Coleção |
Web
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park") citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])
Se você tiver interesse em consultar os pontos de referência de todas as cidades, por exemplo,
você executa essa consulta no grupo de coleções que consiste em todas as condições landmarks
. Você também precisa ativar um índice de campo único landmarks
com
escopo do grupo de coleções:
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
pontos de referência | (ou ) categoria | Grupo de coleta |
Com esse índice ativado, é possível consultar o grupo de coleçõeslandmarks
:
Web
var landmarksGroupRef = db.collectionGroup("landmarks"); landmarksGroupRef.where("category", "==", "park") landmarksGroupRef.where("category", "in", ["park", "museum"])
Para executar uma consulta de grupo de coleções que retorne resultados filtrados ou ordenados, você precisa ativar um índice correspondente composto ou de campo único com o escopo do grupo de coleções. No entanto, as consultas de grupo de coleções que não filtram ou ordenam os resultados não exigem definições de índice adicionais.
Por exemplo, é possível executar a seguinte consulta de grupo de coleções sem ativar um índice adicional:
Web
db.collectionGroup("landmarks").get()
Entradas de índice
Os índices configurados do projeto e a estrutura de um documento determinam o número de entradas de índice de um documento. As entradas do índice são contabilizadas no limite de contagem de entradas do índice.
O exemplo a seguir demonstra as entradas de índice de um documento.
Documentos
/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 e DESC)
Índices compostos
- city_name ASC, neighborhoods ARRAY
- city_name DESC, neighborhoods ARRAY
Entradas de índice
Essa configuração de indexação resulta nas seguintes entradas de índice para o documento:
Índice | Dados 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 composto | |
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 e preços
Os índices são contabilizados nos custos de armazenamento do seu aplicativo. Para mais informações sobre como calcular o tamanho de armazenamento para índices, consulte Tamanho da entrada do índice.
Usar a mesclagem de índices
Embora o Firestore use um índice para cada consulta, ele não
exigem necessariamente um índice por consulta. Para consultas com múltiplas cláusulas
de igualdade (==
) e, opcionalmente, uma cláusula orderBy
, o Firestore pode
reutilizar índices existentes. O Firestore pode mesclar os índices de filtros de igualdade simples
para criar os índices compostos necessários a consultas
de igualdade maiores.
É possível reduzir os custos de indexação ao identificar situações em que é possível usar a mesclagem de índices. Por exemplo, em uma coleção restaurants
para um app de classificação de restaurantes:
restaurants
burgerthyme
name : "Burger Thyme"
category : "burgers"
city : "San Francisco"
editors_pick : true
star_rating : 4
Este app usa consultas como as mostradas abaixo. O app usa combinações de cláusulas
de igualdade para category
, city
e editors_pick
enquanto sempre classifica por
star_rating
crescente:
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")
Você poderia criar um índice para cada consulta:
Coleção | Campos indexados | Escopo da consulta |
---|---|---|
restaurantes | categoria, avaliação com estrelas | Coleção |
restaurantes | cidade, avaliação com estrelas | Coleção |
restaurantes | categoria, cidade, avaliação com estrelas | Coleção |
restaurantes | categoria, cidade, sugestões dos editores, avaliação com estrelas | Coleta |
Uma solução melhor seria reduzir o número de índices aproveitando o recurso de mesclagem de cláusulas de igualdade do Firestore:
Coleta | Campos indexados | Escopo da consulta |
---|---|---|
restaurantes | categoria, avaliação com estrelas | Coleção |
restaurantes | cidade, avaliação com estrelas | Coleção |
restaurantes | sugestões dos editores, avaliação com estrelas | Coleção |
Esse conjunto de índices é menor e também aceita uma consulta adicional:
Web
db.collection("restaurants").where("editors_pick", "==", true) .orderBy("star_rating")
Limites de indexação
Os limites a seguir são aplicáveis aos índices. Para mais informações sobre o assunto, consulte Cotas e limites.
Nesta página, identificamos as cotas de solicitação e os limites do Firestore.
Limite | Detalhes |
---|---|
Número máximo de índices compostos para um banco de dados |
|
Número máximo de configurações de campo único para um banco de dados |
Uma configuração no nível do campo pode conter várias configurações para o mesmo campo. Por exemplo, uma isenção de indexação de campo único e uma política de TTL no mesmo campo contam como uma configuração de campo para o limite. |
Número máximo de entradas de índice para cada documento |
40.000 O número de entradas de índice é a soma do valor a seguir para um documento:
Para ver como o Firestore transforma um documento e um conjunto de índices em entradas do índice, veja este exemplo de contagem de entradas do índice. |
Número máximo de campos em um índice composto | 100 |
Tamanho máximo de uma entrada de índice |
7,5 KiB Para ver como o Firestore calcula o tamanho da entrada do índice, consulte a seção tamanho da entrada do índice. |
Soma máxima dos tamanhos das entradas de índice de um documento |
8 MiB O tamanho total é a soma dos seguintes itens para um documento: |
Tamanho máximo de um valor de campo indexado |
1.500 bytes Os valores de campo acima de 1.500 bytes são truncados. Consultas que envolvem valores de índice truncados podem retornar resultados inconsistentes. |
Práticas recomendadas de indexação
Para a maioria dos aplicativos, você pode confiar na indexação automática e nos links de mensagens de erro para gerenciar seus índices. No entanto, você pode querer adicionar isenções de campo único nos seguintes casos:
Caso | Descrição |
---|---|
Campos de string grandes | Se você tiver um campo de string que geralmente armazena valores de string longos que você não usa para consulta, é possível reduzir os custos de armazenamento com a isenção da indexação no campo. |
Taxas de gravação altas em uma coleção que contém documentos com valores sequenciais | Se você indexar um campo que aumenta ou diminui sequencialmente entre documentos em uma coleção, como um carimbo de data/hora, a taxa máxima de gravação para a coleção será de 500 gravações por segundo. Se você não fizer consultas com base no campo com valores sequenciais, poderá isentar o campo da indexação para ignorar esse limite. Em um caso de uso de IoT com uma alta taxa de gravação, por exemplo, uma coleção que contém documentos com um campo de carimbo de data/hora pode se aproximar do limite de 500 gravações por segundo. |
Campos TTL |
Se você usa políticas de TTL, observe que o campo TTL precisa ser um carimbo de data/hora. A indexação em campos TTL é ativada por padrão e pode afetar o desempenho em taxas de tráfego mais altas. Como prática recomendada, adicione isenções de campo único para seus campos TTL. |
Campos grandes de matriz ou de mapa | Campos grandes de matriz ou de mapa podem se aproximar do limite de 40.000 entradas de índice por documento. Se você não estiver fazendo consultas com base em um campo grande de matriz ou de mapa, recomendamos isentá-lo da indexação. |
Se você estiver usando consultas com operadores de intervalo e desigualdade em vários campos, consulte a documentação de indexação considerações a serem consideradas para otimizar desempenho e custo das consultas do Firestore
Para mais informações sobre como resolver problemas de indexação (divergência de índice, erros INVALID_ARGUMENT
), consulte a página de solução de problemas.