Índices

Em todas as consultas do Firestore no modo Datastore, os cálculos dos resultados são feitos usando um ou mais índices. Neles, há chaves de entidade em uma sequência especificada pelas propriedades do índice e, opcionalmente, pelos ancestrais da entidade. Os índices são atualizados para refletir as alterações feitas pelo aplicativo nas entidades. Assim, os resultados corretos de todas as consultas ficam disponíveis sem precisar computar mais nada.

Há dois tipos de índice:

Índices integrados
Por padrão, o modo Datastore predefine automaticamente um índice para cada propriedade de cada tipo de entidade. Esses índices de propriedade única são adequados para tipos de consulta simples.
Índices compostos
Ordenam valores de diversas propriedades por entidade indexada. Os índices compostos são compatíveis com consultas complexas e são definidos em um arquivo de configuração de índice (index.yaml).

Mais abaixo neste tópico há mais detalhes sobre os tipos de índice.

Definição e estrutura dos índices

Um índice é definido em uma lista de propriedades de um determinado tipo de entidade, com uma ordem correspondente (crescente ou decrescente) para cada propriedade. Para o uso com consultas de ancestral, é possível que o índice também inclua opcionalmente os ancestrais de uma entidade.

Um índice contém entradas para cada propriedade nomeada na definição do índice. Cada entrada do índice representa uma entidade que gera um potencial resultado para consultas baseadas no índice. Uma entidade só é incluída no índice quando tem um valor indexado definido para cada propriedade usada no índice. Se a definição do índice se referir a uma propriedade para a qual a entidade não tem valor, essa entidade não vai aparecer no índice e, portanto, nunca será retornada como resultado de nenhuma consulta baseada no índice.

O índice composto é classificado primeiro pelo ancestral e depois pelos valores da propriedade, na ordem especificada na definição do índice. Com base nesse entendimento, você pode criar o índice perfeito que permite consultas eficientes.

Configuração de índice

O Firestore no modo Datastore fornece índices integrados, ou automáticos, para consultas dos seguintes formulários:

  • consultas sem tipo usando apenas filtros de chave e ancestral
  • consultas usando apenas filtros de igualdade e ancestral
  • consultas usando apenas filtros de desigualdade (que são limitados a uma única propriedade)
  • consultas usando apenas filtros de ancestral, filtros de igualdade nas propriedades e filtros de desigualdade nas chaves
  • consultas sem filtros e apenas uma ordem de classificação em uma propriedade, crescente ou decrescente.

Como exemplo, por padrão, os bancos de dados do modo Datastore predefinem automaticamente dois índices de uma propriedade para cada propriedade de cada tipo de entidade, um em ordem crescente e o outro em ordem decrescente. Se você não quiser que o banco de dados mantenha um índice para uma propriedade, exclua a propriedade dos índices. A exclusão de uma propriedade a remove de qualquer índice composto.

Os índices integrados são suficientes para realizar muitas consultas, como consultas simples de desigualdade e só de igualdade.

Os índices integrados não aparecem na página Índices do console do Google Cloud.

Para fazer consultas mais complexas, um aplicativo precisa definir índices compostos ou manuais. Os índices compostos são obrigatórios nas consultas dos seguintes formulários:

  • consultas com filtros de desigualdade e ancestral
  • consultas com um ou mais filtros de desigualdade em uma propriedade e um ou mais filtros de igualdade nas outras propriedades
  • consultas com uma ordem de classificação nas chaves, em ordem decrescente
  • consultas com várias ordens de classificação
  • consultas com um ou mais filtros e uma ou mais ordens de classificação

Os índices compostos são definidos no arquivo de configuração de índice (index.yaml) do aplicativo. Os índices integrados não estão contidos no arquivo de configuração de índice.

Os índices compostos são constituídos por várias propriedades e exigem que cada propriedade não seja excluída dos índices.

Os índices compostos podem ser visualizados na página Índices do console do Google Cloud. Não é possível usar o console do Google Cloud para criar ou atualizar índices compostos.

Se o aplicativo tentar fazer uma consulta que não possa ser executada com os índices disponíveis (sejam integrados ou especificados no arquivo de configuração de índice), a consulta falhará.

A API do modo Datastore sugere automaticamente os índices apropriados para a maioria dos aplicativos. Dependendo do uso que o aplicativo faz do modo Datastore e do tamanho e formato dos dados, é possível que os índices sejam ajustados manualmente. Por exemplo, gravar entidades com diversos valores de propriedade pode resultar em um índice em explosão com altos custos de armazenamento e maior latência de gravação.

O emulador do Datastore pode ajudar a facilitar o gerenciamento do arquivo de configuração de índice. Em vez de falhar ao executar uma consulta que requer um índice e não tem um, o emulador do Datastore pode gerar uma configuração de índice que permita que a consulta seja realizada com sucesso. Se o teste local de um aplicativo verificar todas as consultas possíveis que o aplicativo fará usando todas as combinações de filtro e ordem de classificação, as entradas geradas representarão um conjunto de índices completo. Se o teste não verificar todos os formulários de consulta possíveis, você poderá analisar e ajustar o arquivo de configuração de índice antes de atualizar os índices.

Saiba mais sobre index.yaml em Configuração de índice.

Como implantar ou excluir índices

Quando terminar de alterar o arquivo de configuração de índice, execute o comando gcloud datastore indexes create para que os índices funcionem. Saiba mais na seção Como atualizar índices.

Se você implantou índices que não são mais necessários, exclua-os.

Custos de armazenamento e latência de gravação

Os índices são contabilizados nos custos de armazenamento. Em Tamanho da entrada de índice, descrevemos como os índices integrados e compostos contribuem para o tamanho do armazenamento do banco de dados. Use as estatísticas do Firestore no modo Datastore para ver mais informações sobre entradas e tamanho de armazenamento de índices.

Os índices também contribuem para a latência de gravação. Ao atualizar o valor de uma propriedade, o banco de dados também atualiza todos os índices relacionados. Quanto mais índices o banco de dados precisar atualizar, mais tempo levará a operação.

É possível reduzir os custos de armazenamento e melhorar o desempenho de gravação ao excluir índices não utilizados e excluir as propriedades da indexação. Isso também impede que as operações falhem devido aos limites do índice.

Índices e propriedades

Veja a seguir algumas considerações especiais sobre os índices e a relação deles com as propriedades de entidades:

Propriedades com tipos de valor mistos

Quando duas entidades têm propriedades com o mesmo nome, mas tipos de valor diferentes, um índice da propriedade classifica as entidades primeiro por tipo de valor e depois por uma ordenação secundária apropriada a cada tipo. Por exemplo, se duas entidades tiverem uma propriedade chamada age, uma com valor inteiro e outra com valor de string, a entidade com o valor inteiro sempre precederá aquela com o valor de string quando classificada pela propriedade age, independentemente dos próprios valores da propriedade.

Isso é especialmente válido no caso de números inteiros e de ponto flutuante, que são tratados como tipos separados pelo modo Datastore. Como todos os números inteiros são classificados antes dos flutuantes, uma propriedade com o valor inteiro 38 é classificada antes de outra com valor de ponto flutuante 37.5.

Propriedades excluídas

Se você souber que nunca precisará filtrar ou classificar uma determinada propriedade, é possível configurar o modo Datastore para não manter as entradas de índice dessa propriedade excluindo-as dos índices. Isso diminui o custo de execução do seu aplicativo reduzindo o tamanho do armazenamento necessário para as entradas de índice. Isso também pode melhorar a latência da gravação. Uma entidade com uma propriedade excluída se comporta como se a propriedade não estivesse definida: as consultas com um filtro ou ordem de classificação na propriedade excluída nunca corresponderão a essa entidade.

A propriedade description no exemplo a seguir é excluída dos índices:

C#

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API C# do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Entity task = new Entity()
{
    Key = _db.CreateKeyFactory("Task").CreateKey("sampleTask"),
    ["category"] = "Personal",
    ["created"] = new DateTime(1999, 01, 01, 0, 0, 0, DateTimeKind.Utc),
    ["done"] = false,
    ["priority"] = 4,
    ["percent_complete"] = 10.0,
    ["description"] = new Value()
    {
        StringValue = "Learn Cloud Datastore",
        ExcludeFromIndexes = true
    },
};

Go

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Go do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

type Task struct {
	Category        string
	Done            bool
	Priority        int
	Description     string `datastore:",noindex"`
	PercentComplete float64
	Created         time.Time
}
task := &Task{
	Category:        "Personal",
	Done:            false,
	Priority:        4,
	Description:     "Learn Cloud Datastore",
	PercentComplete: 10.0,
	Created:         time.Now(),
}

Java

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Java do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Entity task =
    Entity.newBuilder(taskKey)
        .set("category", "Personal")
        .set("created", Timestamp.now())
        .set("done", false)
        .set("priority", 4)
        .set("percent_complete", 10.0)
        .set(
            "description",
            StringValue.newBuilder("Learn Cloud Datastore").setExcludeFromIndexes(true).build())
        .build();

Node.js

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Node.js do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

const task = [
  {
    name: 'category',
    value: 'Personal',
  },
  {
    name: 'created',
    value: new Date(),
  },
  {
    name: 'done',
    value: false,
  },
  {
    name: 'priority',
    value: 4,
  },
  {
    name: 'percent_complete',
    value: 10.0,
  },
  {
    name: 'description',
    value: 'Learn Cloud Datastore',
    excludeFromIndexes: true,
  },
];

PHP

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API PHP do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

$task = $datastore->entity(
    $key,
    [
        'category' => 'Personal',
        'created' => new DateTime(),
        'done' => false,
        'priority' => 4,
        'percent_complete' => 10.0,
        'description' => 'Learn Cloud Datastore'
    ],
    ['excludeFromIndexes' => ['description']]
);

Python

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Python do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

import datetime

key = client.key("Task")
task = datastore.Entity(key, exclude_from_indexes=("description",))
task.update(
    {
        "category": "Personal",
        "description": "Learn Cloud Datastore",
        "created": datetime.datetime.now(tz=datetime.timezone.utc),
        "done": False,
        "priority": 4,
        "percent_complete": 10.5,
    }
)
client.put(task)

Ruby

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Ruby do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

task = datastore.entity "Task" do |t|
  t["category"] = "Personal"
  t["created"] = Time.now
  t["done"] = false
  t["priority"] = 4
  t["percent_complete"] = 10.0
  t["description"] = "Learn Cloud Datastore"
  t.exclude_from_indexes! "description", true
end

GQL

Não aplicável

A consulta no exemplo a seguir não vai retornar resultados se a propriedade description tiver sido excluída:

C#

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API C# do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Query query = new Query("Task")
{
    Filter = Filter.Equal("description", "Learn Cloud Datastore")
};

Go

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Go do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

query := datastore.NewQuery("Tasks").
	FilterField("Description", "=", "A task description")

Java

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Java do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Query<Entity> query =
    Query.newEntityQueryBuilder()
        .setKind("Task")
        .setFilter(PropertyFilter.eq("description", "A task description"))
        .build();

Node.js

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Node.js do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

const query = datastore
  .createQuery('Task')
  .filter(new PropertyFilter('description', '=', 'A task description.'));

PHP

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API PHP do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

$query = $datastore->query()
    ->kind('Task')
    ->filter('description', '=', 'A task description.');

Python

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Python do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

query = client.query(kind="Task")
query.add_filter(
    filter=datastore.query.PropertyFilter(
        "description", "=", "Learn Cloud Datastore"
    )
)

Ruby

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Ruby do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

query = datastore.query("Task")
                 .where("description", "=", "A task description.")

GQL

# Will not return any results!
SELECT * FROM Task WHERE description = 'A task description.'

Posteriormente, é possível alterar a propriedade de volta para indexada.

No entanto, a alteração de uma propriedade de excluída para indexada não afeta qualquer entidade existente que tenha sido criada antes da alteração. A filtragem de consultas quanto à propriedade não retornará essas entidades atuais porque elas não foram gravadas no índice da consulta quando criadas. Para tornar as entidades acessíveis pelas próximas consultas, você precisa gravá-las novamente no banco de dados para que sejam inseridas nos índices apropriados. Ou seja, você precisa seguir estas etapas para cada uma dessas entidades atuais:

  1. Pesquisar (conseguir) a entidade.
  2. Gravar (colocar) a entidade de volta no banco de dados.

Da mesma forma, a alteração de uma propriedade de indexada para excluída só afeta as entidades gravadas posteriormente no banco de dados. As entradas de índice de qualquer entidade atual com essa propriedade continuarão existindo até que as entidades sejam atualizadas ou excluídas. Para evitar resultados indesejados, você precisa limpar todas as consultas do código que filtram ou classificam pela propriedade (agora excluída).

Limites dos índices

O Firestore no modo Datastore impõe limites ao número e ao tamanho geral das entradas de índice que podem ser associadas a uma única entidade. Esses limites são amplos e a maioria dos aplicativos não é afetada. No entanto, há circunstâncias em que talvez você encontre limites.

Conforme descrito acima, o banco de dados do modo Datastore cria uma entrada em um índice predefinido para cada propriedade de cada entidade, exceto para aquelas explicitamente declaradas como excluídas dos índices. A propriedade também pode ser incluída em outros índices personalizados declarados em seu arquivo de configuração de índice (index.yaml). Uma entidade terá no máximo uma entrada em cada índice personalizado (para índices não ancestrais) ou uma para cada ancestral da entidade (para índices ancestrais), desde que ela não tenha propriedades de lista. Cada uma dessas entradas de índice precisa ser atualizada sempre que o valor da propriedade for alterado.

Para uma propriedade com um único valor para cada entidade, cada valor possível só precisa ser armazenado uma vez por entidade no índice predefinido da propriedade. Mesmo assim, é possível que uma entidade com um grande número de propriedades com um único valor exceda o limite de tamanho ou de entradas do índice. Da mesma forma, uma entidade com diversos valores para a mesma propriedade exige uma entrada de índice separada para cada valor. Novamente, se houver um grande número de valores possíveis, essa entidade poderá exceder o limite de entradas.

A situação piora no caso de entidades com várias propriedades: cada uma delas pode aceitar diversos valores. Para acomodar esse tipo de entidade, o índice precisa incluir uma entrada para cada combinação possível de valores de propriedade. Os índices personalizados que se referem a várias propriedades, cada uma com diversos valores, podem "explodir" de forma combinatória, exigindo grandes números de entrada para uma entidade com apenas um número relativamente pequeno de valores de propriedade possíveis. Esses índices em explosão tendem a aumentar significativamente o tamanho do armazenamento de uma entidade devido ao grande número de entradas de índice que precisam ser armazenadas. Os índices em explosão também podem fazer com que uma entidade exceda o limite de tamanho ou de contagem de entradas de índice.

Pense no seguinte código:

C#

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API C# do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Entity task = new Entity()
{
    Key = _db.CreateKeyFactory("Task").CreateKey("sampleTask"),
    ["tags"] = new ArrayValue() { Values = { "fun", "programming", "learn" } },
    ["collaborators"] = new ArrayValue() { Values = { "alice", "bob", "charlie" } },
    ["created"] = DateTime.UtcNow
};

Go

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Go do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

task := &Task{
	Tags:          []string{"fun", "programming", "learn"},
	Collaborators: []string{"alice", "bob", "charlie"},
	Created:       time.Now(),
}

Java

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Java do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

Entity task =
    Entity.newBuilder(taskKey)
        .set("tags", "fun", "programming", "learn")
        .set("collaborators", "alice", "bob", "charlie")
        .set("created", Timestamp.now())
        .build();

Node.js

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Node.js do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

const task = {
  method: 'insert',
  key: datastore.key('Task'),
  data: {
    tags: ['fun', 'programming', 'learn'],
    collaborators: ['alice', 'bob', 'charlie'],
    created: new Date(),
  },
};

PHP

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API PHP do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

$task = $datastore->entity(
    $datastore->key('Task'),
    [
        'tags' => ['fun', 'programming', 'learn'],
        'collaborators' => ['alice', 'bob', 'charlie'],
        'created' => new DateTime(),
    ]
);

Python

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Python do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

import datetime

task = datastore.Entity(client.key("Task"))
task.update(
    {
        "tags": ["fun", "programming", "learn"],
        "collaborators": ["alice", "bob", "charlie"],
        "created": datetime.datetime.now(tz=datetime.timezone.utc),
    }
)

Ruby

Para saber como instalar e usar a biblioteca de cliente do Cloud Datastore, consulte as Bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API Ruby do Cloud Datastore.

Para autenticar no Cloud Datastore, configure o Application Default Credentials. Para mais informações, consulte Configurar a autenticação para um ambiente de desenvolvimento local.

task = datastore.entity "Task" do |t|
  t["tags"] = ["fun", "programming", "learn"]
  t["collaborators"] = ["alice", "bob", "charlie"]
  t["created"] = Time.now
end

GQL

Não aplicável

Ele cria uma entidade Task com três valores para a propriedade tags, três valores para a propriedade collaborators, e created definido para a data atual. Isso exige nove entradas de índice, uma para cada combinação possível de valores de propriedade:

('fun', 'alice', NOW())
('fun', 'bob', NOW())
('fun', 'charlie', NOW())

('programming', 'alice', NOW())
('programming', 'bob', NOW())
('programming', 'charlie', NOW())

('learn', 'alice', NOW())
('learn', 'bob', NOW())
('learn', 'charlie', NOW())

Quando a mesma propriedade é repetida várias vezes, o Firestore no modo Datastore pode detectar índices explosivos e sugerir um índice alternativo. No entanto, em todas as outras circunstâncias, como na consulta definida neste exemplo, o banco de dados do modo Datastore gera um índice em explosão. Nesse caso, você pode evitar o índice em explosão configurando manualmente um índice no seu arquivo de configuração de índice.

indexes:
- kind: Task
  properties:
  - name: tags
  - name: created
- kind: Task
  properties:
  - name: collaborators
  - name: created

Isso reduz o número de entradas necessárias para apenas (|tags| * |created| + |collaborators| * |created|) ou 6 entradas em vez de 9:

('fun', NOW())
('programming', NOW())
('learn', NOW())

('alice', NOW())
('bob', NOW())
('charlie', NOW())

Qualquer operação commit que faça um índice exceder a entrada do índice ou o limite de tamanho falhará. O texto do erro descreve o limite que foi excedido ("Too many indexed properties" ou "Index entries too large") e o índice personalizado que o causou. Se você criar um novo índice que exceda os limites de qualquer entidade quando criado, as consultas ao índice falharão e o índice aparecerá no estado Error no console do Google Cloud. Para lidar com índices Error,

  1. Remova o índice do arquivo de configuração de índice (index.yaml).
  2. Usando a Google Cloud CLI, remova o índice do banco de dados usando o comando datastore indexes cleanup, conforme descrito em Como excluir índices não utilizados.
  3. Siga uma das seguintes instruções
    • Reformule a definição do índice e as consultas correspondentes
    • Remova as entidades que estão fazendo o índice explodir.
  4. Adicione o índice novamente a index.yaml.
  5. Usando a Google Cloud CLI, adicione o índice ao banco de dados executando o comando datastore indexes create, conforme descrito em Como atualizar índices.

Você poderá evitar os índices em explosão se não fizer consultas que exijam um índice personalizado usando uma propriedade de lista. Conforme descrito anteriormente, isso inclui consultas com várias ordens de classificação ou com uma combinação de filtros de igualdade e desigualdade.

Índices de projeções

As consultas de projeção exigem que todas as propriedades especificadas na projeção sejam incluídas como um índice. O emulador do Datastore gera automaticamente os índices necessários para você no arquivo de configuração de índice, index.yaml, que é carregado com seu aplicativo.

Uma forma de reduzir o número de índices necessários é projetar as mesmas propriedades consistentemente, mesmo quando nem todas são necessárias. Por exemplo, essas consultas exigem dois índices separados:

SELECT priority, percent_complete FROM Task

SELECT priority, percent_complete, created FROM Task

No entanto, se você projetar sempre propriedades priority, percent_complete, created, mesmo quando created não for exigido, somente um índice será necessário.

A conversão de uma consulta em uma projeção talvez exija a criação de um novo índice caso as propriedades dessa projeção não estejam em outra parte da consulta. Por exemplo, suponha que você tenha a seguinte consulta existente:

SELECT * FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

Ela exige este índice:

indexes:
- kind: Task
  properties:
  - name: priority
  - name: percent_complete

A conversão em uma das consultas de projeção

SELECT created FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

SELECT priority, percent_complete, created FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

introduz uma nova propriedade (created) e, portanto, será necessário criar um novo índice:

indexes:
- kind: Task
  properties:
  - name: priority
  - name: percent_complete
  - name: created

No entanto,

SELECT priority, percent_complete FROM Task
WHERE priority > 1
ORDER BY priority, percent_complete

não altera o índice exigido, uma vez que as propriedades projetadas priority e percent_complete já foram incluídas na consulta existente.

Vários bancos de dados

É possível usar gcloud firestore para gerenciar um único índice no modo Datastore ou gcloud datastore com um arquivo index.yaml para gerenciar todos os índices em um banco de dados.

gcloud firestore
gcloud firestore indexes composite create --api-scope=datastore-mode-api  --query-scope=QUERY_SCOPE --database=DATABASE_ID
gcloud datastore
gcloud alpha datastore indexes create index.yaml --database=DATABASE_ID

Substitua:

  • DATABASE_ID: um ID do banco de dados.
  • QUERY_SCOPE: collection-recursive para índices ancestrais ou collection-group para índices não ancestrais.