Í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, se você quiser, 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 calcular 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 aos 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.

Uma tabela de índice contém uma coluna para cada propriedade nomeada na definição do índice. Cada linha representa uma entidade que gera um potencial resultado para consultas baseadas no índice. Uma entidade só será incluída no índice se tiver um conjunto de valores indexado a cada propriedade usada nele. Se a definição do índice se referir a uma propriedade em que a entidade não tem valor, essa entidade não será exibida e, por isso, nunca será retornada como resultado de uma consulta baseada no índice.

As linhas de uma tabela de índice são classificadas primeiro pelo ancestral e depois pelos valores da propriedade, na ordem especificada na definição do índice. O índice perfeito de uma consulta, que permite a realização mais eficiente dela, é definido nas seguintes propriedades, em ordem:

  1. Propriedades usadas nos filtros de igualdade
  2. Propriedade usada em um filtro de desigualdade (das quais não pode haver mais de uma)
  3. propriedades usadas em ordens de classificação

Isso garante que todos os resultados de cada execução de consulta possível apareçam nas linhas consecutivas da tabela. Os bancos de dados do modo Datastore executam uma consulta usando um índice perfeito seguindo as etapas a seguir:

  1. Identifica o índice correspondente ao tipo de consulta, às propriedades do filtro, aos operadores do filtro e às ordens de classificação.
  2. Verifica desde o início do índice até a primeira entidade que atenda a todas as condições do filtro da consulta.
  3. Continua verificando o índice, retornando uma entidade por vez até
    • encontrar uma entidade que não atenda às condições do filtro ou;
    • chegar ao fim do índice ou;
    • coletar o número máximo de resultados solicitados pela consulta.

Por exemplo, pense na seguinte consulta:

SELECT * FROM Task
    WHERE category = 'Personal'
      AND priority < 3
    ORDER BY priority DESC
    

O índice perfeito para esta consulta é uma tabela de chaves para entidades do tipo Task, com colunas para os valores das propriedades category e priority. O índice é classificado primeiro em ordem crescente por category e depois em ordem decrescente por priority:

indexes:
    - kind: Task
      properties:
      - name: category
        direction: asc
      - name: priority
        direction: desc
    

Duas consultas de formulários iguais, mas com valores de filtro diferentes, usam o mesmo índice. Por exemplo, a consulta a seguir usa o mesmo índice da consulta acima:

SELECT * FROM Task
    WHERE category = 'Work'
      AND priority < 5
    ORDER BY priority DESC
    

Para este índice

indexes:
    - kind: Task
      properties:
      - name: category
        direction: asc
      - name: priority
        direction: asc
      - name: created
        direction: asc
    

as duas consultas a seguir também usam o mesmo índice, apesar de terem formulários diferentes:

SELECT * FROM Task
    WHERE category = 'Personal'
      AND priority = 5
    ORDER BY created ASC
    

e

SELECT * FROM Task
    WHERE category = 'Work'
    ORDER BY priority ASC, created ASC
    

É possível atender a essas duas consultas com o índice criado acima.

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 Google Cloud Console.

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 Cloud. Não é possível usar o Console do 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, é possível que a gravação de entidades com diversos valores de propriedade resulte em um índice em explosão com altos custos de armazenamento.

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 o sucesso da consulta. 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.

Í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 souber que nunca precisará filtrar ou classificar uma determinada propriedade, você pode configurar o banco de dados no 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. 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para C#.

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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Go.

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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Java.

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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Node.js.

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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para PHP.

$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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Python.

task = datastore.Entity(
        key,
        exclude_from_indexes=['description'])
    task.update({
        'category': 'Personal',
        'description': 'Learn Cloud Datastore',
        'created': datetime.datetime.utcnow(),
        'done': False,
        'priority': 4,
        'percent_complete': 10.5,
    })

Ruby

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Ruby.

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 relevante

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

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para C#.

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

Go

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Go.

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

Java

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Java.

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

Node.js

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Node.js.

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para PHP.

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

Python

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Python.

query = client.query(kind='Task')
    query.add_filter('description', '=', 'Learn Cloud Datastore')

Ruby

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Ruby.

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 deixar as entidades acessíveis pelas próximas consultas, você precisa gravá-las novamente no banco de dados para que sejam inseridas nos índices correspondentes. Ou seja, você precisa seguir estas etapas para cada uma dessas entidades:

  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 requer uma entrada de índice individual para cada valor. Repetindo, se o número de valores possíveis for grande, essa entidade excederá o limite de entradas.

A situação piora no caso de entidades com várias propriedades: cada uma 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 facilmente.

Considere o seguinte código:

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para C#.

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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Go.

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

Java

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Java.

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

Node.js

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Node.js.

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para PHP.

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

Python

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Python.

task = datastore.Entity(client.key('Task'))
    task.update({
        'tags': [
            'fun',
            'programming',
            'learn'
        ],
        'collaborators': [
            'alice',
            'bob',
            'charlie'
        ],
        'created': datetime.datetime.utcnow()
    })

Ruby

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para saber mais, veja a documentação de referência da API do Cloud Datastore para Ruby.

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

GQL

Não relevante

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 no índice falharão, e o índice aparecerá no estado Error no Console do Cloud. Para lidar com índices Error,

  1. Remova o índice do arquivo de configuração de índice (index.yaml).
  2. Limpe o índice usando a ferramenta de linha de comando gcloud com a opção vacuum_indexes, conforme descrito em Como excluir índices não utilizados.
  3. Siga uma destas 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. Atualize o índice usando a ferramenta de linha de comando gcloud com a opção update_indexes, conforme descrito em Atualização de í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 acima, isso inclui as 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.