Tipos de índice no Cloud Firestore

Os índices são um fator importante no desempenho de um banco de dados. Muito parecido com o índice de um livro que mapeia tópicos de um livro para números de páginas, um índice de banco de dados mapeia os itens de um banco de dados para suas localizações no banco de dados. Quando você envia uma consulta a um banco de dados, o banco de dados pode usar um índice para procurar rapidamente os locais dos itens solicitados.

Esta página descreve os dois tipos de índices usados ​​pelo Cloud Firestore: índices de campo único e índices compostos .

Um índice por trás de cada consulta

Se não existir nenhum índice para uma consulta, a maioria dos bancos de dados rastreia seu conteúdo item por item, um processo lento que fica ainda mais lento à medida que o banco de dados cresce. O Cloud Firestore garante alto desempenho de consulta usando índices para todas as consultas. Como resultado, o desempenho da consulta depende do tamanho do conjunto de resultados e não do número de itens no banco de dados.

Menos gerenciamento de índices, mais desenvolvimento de aplicativos

O Cloud Firestore inclui recursos que reduzem o tempo gasto no gerenciamento de índices. Os índices necessários para as consultas mais básicas são criados automaticamente para você. À medida que você usa e testa seu aplicativo, o Cloud Firestore ajuda a identificar e criar índices adicionais exigidos pelo seu aplicativo.

Tipos de índice

O Cloud Firestore usa dois tipos de índices: campo único e composto . Além do número de campos indexados, os índices de campo único e compostos diferem na forma como você os gerencia.

Índices de campo único

Um índice de campo único armazena um mapeamento classificado de todos os documentos em uma coleção que contém um campo específico. Cada entrada em um índice de campo único registra o valor de um documento para um campo específico e a localização do documento no banco de dados. O Cloud Firestore usa esses índices para realizar muitas consultas básicas. Você gerencia índices de campo único definindo as configurações de indexação automática e isenções de índice do seu banco de dados.

Indexação automática

Por padrão, o Cloud Firestore mantém automaticamente índices de campo único para cada campo em um documento e cada subcampo em um mapa. O Cloud Firestore usa as seguintes configurações padrão para índices de campo único:

  • Para cada campo não array e não mapa, o Cloud Firestore define dois índices de campo único com escopo de coleção , um em modo ascendente e outro em modo descendente.

  • Para cada campo do mapa, o Cloud Firestore cria o seguinte:

    • Um índice ascendente de escopo de coleção para cada subcampo não-matriz e não-mapa.
    • Um índice descendente de escopo de coleção para cada subcampo não-matriz e não-mapa.
    • Uma matriz de escopo de coleção contém índice para cada subcampo da matriz.
    • O Cloud Firestore indexa recursivamente cada subcampo do mapa.
  • Para cada campo de matriz em um documento, o Cloud Firestore cria e mantém um índice contendo matriz com escopo de 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

Você pode isentar um campo das 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 indexação automática em todo o banco de dados. Uma isenção pode habilitar um índice de campo único que suas configurações de indexação automática desabilitariam ou desabilitar um índice de campo único que a indexação automática habilitaria. Para casos em que isenções podem ser úteis, consulte as melhores práticas 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 comments do grupo de coleções, defina o caminho do campo como * para corresponder a todos os campos no grupo de coleções comments e desative a indexação de todos os campos no grupo de coleções. Você pode então adicionar isenções para indexar apenas os campos necessários para suas consultas. A redução do número de campos indexados reduz os custos de armazenamento e pode melhorar o desempenho de gravação.

Se você criar uma isenção de índice de campo único para um campo de mapa, os subcampos do mapa herdarão essas configurações. No entanto, você pode definir isenções de índice de campo único para subcampos específicos. Se você excluir uma isenção de um subcampo, o subcampo herdará as configurações de isenção do pai, se existirem, ou as configurações de todo o banco de dados, se não existirem isenções pai.

Para criar e gerenciar isenções de índice de campo único, consulte Gerenciar índices no Cloud Firestore .

Índices compostos

Um índice composto armazena um mapeamento ordenado de todos os documentos de uma coleção, com base em uma lista ordenada de campos a serem indexados.

O Cloud Firestore usa índices compostos para dar suporte a consultas que ainda não são suportadas por índices de campo único.

O Cloud Firestore não cria índices compostos automaticamente como faz para índices de campo único devido ao grande número de combinações de campos possíveis. Em vez disso, o Cloud Firestore ajuda a identificar e criar os índices compostos necessários à medida que você cria seu aplicativo.

Se você tentar a consulta acima sem primeiro criar o índice necessário, o Cloud Firestore retornará uma mensagem de erro contendo um link que você pode seguir para criar o índice ausente. Isso acontece sempre que você tenta uma consulta não suportada por um índice. Você também pode definir e gerenciar índices compostos manualmente usando o console ou a CLI do Firebase . Para obter mais informações sobre como criar e gerenciar índices compostos, consulte Gerenciando índices .

Modos de índice e escopos de consulta

Você configura índices de campo único e compostos 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. Você pode selecionar um dos seguintes modos de índice:

Modo de índice Descrição
ascendente_para cima Suporta < , <= , == , >= , > , != , in e not-in , cláusulas de consulta no campo e suporta a classificação de resultados em ordem crescente com base neste valor de campo.
Seta baixo Suporta cláusulas de consulta < , <= , == , >= , > , != , in e not-in no campo e suporta a classificação de resultados em ordem decrescente com base neste valor de campo.
Matriz contém Suporta cláusulas de consulta array-contains e array-contains-any no campo.

Escopos de consulta

Cada índice tem como escopo uma coleção ou um grupo de coleções. Isso é conhecido como escopo de consulta do índice:

Escopo da coleção
O Cloud Firestore cria índices com escopo de coleção por padrão. Esses índices suportam 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 de coleção. Para executar uma consulta de grupo de coleções que retorne resultados filtrados ou ordenados de um grupo de coleções, você deve criar um índice correspondente com escopo de grupo de coleções.

Ordem padrão e o campo __name__

Além de ordenar os documentos pelos modos de índice especificados para cada campo (crescente ou decrescente), os índices aplicam uma ordenaçã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 do documento.

Por padrão, o campo __name__ é classificado na mesma direção do último campo classificado na definição do índice. Por exemplo:

Coleção Campos indexados Escopo da consulta
cidades nome, __name__ Coleção
cidades estado , __name__ Coleção
cidades país, população, __name__ Coleção

Para classificar os resultados pela direção __name__ não padrão, você precisa criar esse índice.

Exemplo de indexação

Ao criar automaticamente índices de campo único para você, o Cloud Firestore permite que seu aplicativo ofereça suporte rapidamente às consultas mais básicas do banco de dados. Os índices de campo único permitem realizar consultas simples com base nos valores dos campos e nos comparadores < , <= , == , >= , > , e in . Para campos de array, eles permitem que você execute consultas array-contains e array-contains-any .

Para ilustrar, examine os exemplos a seguir do ponto de vista da criação de índices. O trecho a seguir cria alguns documentos city em uma coleção cities e define campos name , state , country , capital , population e tags para cada documento:

Rede
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"] });

Supondo as configurações de indexação automática padrão, o Cloud Firestore atualiza um índice de campo único ascendente por campo não-matriz, um índice de campo único descendente por campo não-matriz e um índice de campo único contendo matriz para o campo da matriz. Cada linha na tabela a seguir representa uma entrada em um índice de campo único:

Coleção Campo indexado Escopo da consulta
cidades nome Coleção
cidades estado Coleção
cidades país Coleção
cidades capital Coleção
cidades população Coleção
cidades nome Coleção
cidades estado Coleção
cidades país Coleção
cidades capital Coleção
cidades população Coleção
cidades array-contains regiões Coleção

Consultas suportadas por índices de campo único

Usando esses índices de campo único criados automaticamente, você pode executar consultas simples como as seguintes:

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

Você também pode criar in de igualdade ( == ) e compostas:

Rede
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 use uma comparação de intervalo ( < , <= , > ou >= ) ou se precisar classificar por um campo diferente, deverá criar um índice composto para essa consulta.

O índice array-contains permite consultar o campo da matriz regions :

Rede
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 suportadas por índices compostos

O Cloud Firestore usa índices compostos para oferecer suporte a consultas compostas ainda não suportadas por índices de campo único. Por exemplo, você precisaria de um índice composto para as seguintes consultas:

Rede
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 , você pode usar um modo de índice crescente ou decrescente para esse campo. Por padrão, as cláusulas de desigualdade aplicam uma ordem de classificação crescente com base no campo da cláusula de desigualdade.

Coleção Campos indexados Escopo da consulta
cidades (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 :

Rede
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
cidades país , população Coleção
cidades país , população Coleção

Você também precisa criar um índice composto para combinar uma consulta array-contains ou array-contains-any com cláusulas adicionais.

Rede
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
cidades array contém tags, (ou ) maiúscula Coleção

Consultas suportadas por índices de grupos de coleções

Para demonstrar um índice com escopo de grupo de coleção, imagine que você adicione uma subcoleção landmarks a alguns dos documentos city :

Rede
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" });

Usando o seguinte índice de campo único com escopo de coleção, você pode consultar a coleção landmarks de uma única cidade com base no campo category :

Coleção Campos indexados Escopo da consulta
pontos de referência categoria (ou ) Coleção
Rede
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")
citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])

Agora, imagine que você esteja interessado em consultar os pontos de referência de todas as cidades. Para executar esta consulta no grupo de coleções que consiste em todas as coleções landmarks , você deve ativar um índice de campo único landmarks com escopo de grupo de coleções:

Coleção Campos indexados Escopo da consulta
pontos de referência categoria (ou ) Grupo de coleta

Com este índice habilitado, você pode consultar o grupo de coleta landmarks :

Rede
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ê deve ativar um campo único correspondente ou índice composto com escopo de grupo de coleções. Consultas de grupo de coleções que não filtram nem ordenam resultados, no entanto, não requerem definições de índice adicionais.

Por exemplo, você pode executar a seguinte consulta de grupo de coleções sem ativar um índice adicional:

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

Entradas de índice

Os índices configurados do seu projeto e a estrutura de um documento determinam o número de entradas de índice de um documento. As entradas do índice contam para o limite de contagem de entradas do índice .

O exemplo a seguir demonstra as entradas de índice de um documento.

Documento

/cities/SF

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

Índices de campo único

  • nome_cidade ASC
  • nome_cidade DESC
  • temperaturas.verão ASC
  • temperaturas.verão DESC
  • temperaturas.inverno ASC
  • temperaturas.inverno DESC
  • Matriz de bairros contém (ASC e DESC)

Índices compostos

  • city_name ASC, bairros ARRAY
  • cidade_nome DESC, bairros ARRAY

Entradas de índice

Essa configuração de indexação resulta nas 18 entradas de índice a seguir para o documento:

Índice Dados indexados
Entradas de índice de campo único
nome_cidade ASC nome_cidade: "São Francisco"
nome_cidade DESC nome_cidade: "São Francisco"
temperaturas.verão ASC temperaturas.verão: 67
temperaturas.verão DESC temperaturas.verão: 67
temperaturas.inverno ASC temperaturas.inverno: 55
temperaturas.inverno DESC temperaturas.inverno: 55
Matriz de bairros contém ASC bairros: "Missão"
Matriz de bairros Contém DESC bairros: "Missão"
Matriz de bairros contém ASC bairros: "Centro"
Matriz de bairros Contém DESC bairros: "Centro"
Matriz de bairros contém ASC bairros: "Marina"
Matriz de bairros Contém DESC bairros: "Marina"
Entradas de índice composto
city_name ASC, bairros ARRAY city_name: "São Francisco", bairros: "Missão"
city_name ASC, bairros ARRAY city_name: "São Francisco", bairros: "Downtown"
city_name ASC, bairros ARRAY city_name: "São Francisco", bairros: "Marina"
city_name DESC, bairros ARRAY city_name: "São Francisco", bairros: "Missão"
cidade_nome DESC, bairros ARRAY city_name: "São Francisco", bairros: "Downtown"
cidade_nome DESC, bairros ARRAY city_name: "São Francisco", bairros: "Marina"

Índices e preços

Os índices contribuem para os custos de armazenamento do seu aplicativo. Para obter mais informações sobre como o tamanho de armazenamento dos índices é calculado, consulte Tamanho da entrada do índice .

Aproveitando a fusão de índices

Embora o Cloud Firestore use um índice para cada consulta, ele não exige necessariamente um índice por consulta. Para consultas com múltiplas cláusulas de igualdade ( == ) e, opcionalmente, uma cláusula orderBy , o Cloud Firestore pode reutilizar índices existentes. O Cloud Firestore pode mesclar os índices de filtros de igualdade simples para criar os índices compostos necessários para consultas de igualdade maiores.

Você pode reduzir os custos de indexação identificando situações em que pode aproveitar as vantagens da fusão de índices. Por exemplo, imagine uma coleção restaurants para um aplicativo de classificação de restaurantes:

  • restaurantes

    • hambúrguertomilho

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

Agora, imagine que este aplicativo usa consultas como as abaixo. Observe que o aplicativo usa combinações de cláusulas de igualdade para category , city e editors_pick enquanto sempre classifica por ordem crescente star_rating :

Rede
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 , star_rating Coleção
restaurantes cidade, star_rating Coleção
restaurantes categoria , cidade, star_rating Coleção
restaurantes categoria , cidade , editors_pick, star_rating Coleção

Como solução melhor, você pode reduzir o número de índices aproveitando a capacidade do Cloud Firestore de mesclar índices para cláusulas de igualdade:

Coleção Campos indexados Escopo da consulta
restaurantes categoria , star_rating Coleção
restaurantes cidade, star_rating Coleção
restaurantes editores_pick, star_rating Coleção

Este conjunto de índices não é apenas menor, mas também suporta uma consulta adicional:

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

Limites de indexação

Os limites a seguir se aplicam aos índices. Para todas as cotas e limites, consulte Cotas e Limites .

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 em nível de campo pode conter diversas 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 seguinte para um documento:

  • O número de entradas de índice de campo único
  • O número de entradas de índice composto

Para ver como o Cloud Firestore transforma um documento e um conjunto de índices em entradas de índice, consulte este exemplo de contagem de entradas de í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 Cloud Firestore calcula o tamanho da entrada do índice, consulte tamanho da entrada do índice .

Soma máxima dos tamanhos das entradas de índice de um documento

8 MiB

O tamanho total é a soma do seguinte para um documento:

  • A soma do tamanho das entradas do índice de campo único de um documento
  • A soma do tamanho das entradas do índice composto de um documento
  • Tamanho máximo de um valor de campo indexado

    1.500 bytes

    Valores de campo acima de 1.500 bytes são truncados. Consultas envolvendo valores de campo truncados podem retornar resultados inconsistentes.

    Práticas recomendadas de indexação

    Para a maioria dos aplicativos, você pode contar com a indexação automática e os 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 contém valores de string longos que não são usados ​​para consulta, você poderá reduzir os custos de armazenamento isentando o campo da indexação.

    Altas taxas de gravação em uma coleção contendo 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 na coleção será de 500 gravações por segundo. Se você não fizer a consulta com base no campo com valores sequenciais, poderá isentar o campo da indexação para contornar esse limite.

    Em um caso de uso de IoT com alta taxa de gravação, por exemplo, uma coleção contendo documentos com um campo de carimbo de data/hora pode se aproximar do limite de 500 gravações por segundo.

    Campos TTL

    Se você usar políticas TTL (time-to-live) , observe que o campo TTL deve ser um carimbo de data/hora. A indexação em campos TTL é habilitada 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.

    Grande matriz ou campos de mapa

    Grandes campos de array ou mapa podem atingir o limite de 40.000 entradas de índice por documento. Se você não estiver consultando com base em um array grande ou campo de mapa, deverá isentá-lo da indexação.

    Para obter mais informações sobre como resolver problemas de indexação (fanout de índice, erros INVALID_ARGUMENT ), consulte a página de solução de problemas .