Dia 05 de 15

Fundamentos de Indexação

Olá, dev! Seja bem-vindo ao Dia 05 do seu plano de preparação para a certificação MongoDB Associate Developer. Hoje estudaremos a base de performance do MongoDB: os Índices. Compreender como as estruturas B-tree funcionam e como escolher as propriedades corretas é essencial tanto para o exame quanto para o ambiente de produção.

1. O que são Índices e por que usá-los?

Sem índices, uma consulta precisa realizar um COLLSCAN (Collection Scan), varrendo cada documento da coleção do início ao fim no disco. Os índices armazenam uma estrutura ordenada em árvore B (B-tree) em memória RAM contendo uma porção menor dos dados, permitindo localizar rapidamente a posição exata do documento no arquivo de dados através de um IXSCAN (Index Scan).

2. Tipos Fundamentais de Índices

A. Índice de Campo Único (Single Field Index)

// Criando um índice de campo único no campo 'email'
db.usuarios.createIndex({ email: 1 })

O valor 1 indica ordem ascendente e -1 indica ordem descendente. Em índices de campo único, a direção não faz diferença para a ordenação, pois o MongoDB pode navegar na B-tree em ambas as direções com a mesma eficiência.

B. Índice Composto (Compound Index)

Índice formado por múltiplos campos. A ordem dos campos e a direção no índice composto são críticas.

db.pedidos.createIndex({ status: 1, dataCriacao: -1 })

A Regra dos Prefixos do Índice (Index Prefixes):

Um índice composto em { a: 1, b: 1, c: 1 } possui os seguintes prefixos:

Consultas que filtram por a, ou por a e b, utilizarão o índice. Porém, uma consulta que filtra apenas por `b` ou `c` não poderá utilizar o índice.

C. Índice Multikey (Indexação de Arrays)

Quando você cria um índice em um campo cujo valor é um array, o MongoDB cria automaticamente um Multikey Index, adicionando uma chave na B-tree para cada elemento individual do array.

// 'habilidades' é um array de strings
db.developers.createIndex({ habilidades: 1 })

3. Propriedades Especiais de Índices (Index Properties)

A. Unique Index (Índice Único)

Garante que a coleção não contenha valores duplicados para o campo indexado.

db.usuarios.createIndex({ cpf: 1 }, { unique: true })

Armadilha do Exame: Se múltiplos documentos forem inseridos sem o campo cpf, o MongoDB atribuirá null a todos eles, o que causará erro de duplicidade no segundo documento. Para evitar isso, combina-se com sparse ou partialFilterExpression.

B. TTL Index (Time-To-Live)

Remove automaticamente documentos da coleção após um determinado período. Muito utilizado para sessões, tokens temporários e logs.

// Expira documentos 3600 segundos (1 hora) após a data informada em 'dataCriacao'
db.sessoes.createIndex(
  { dataCriacao: 1 },
  { expireAfterSeconds: 3600 }
)

Regra de Validação: O campo indexado por TTL deve obrigatoriamente ser do tipo BSON Date. O thread de expiração do MongoDB executa a cada 60 segundos.

C. Sparse Index (Índice Esparso)

Indexa apenas os documentos que possuem o campo especificado. Documentos que não contêm o campo são completamente omitidos da B-tree, economizando espaço.

db.usuarios.createIndex({ telefone: 1 }, { sparse: true })

D. Partial Index (Índice Parcial)

Mais flexível e poderoso que o Sparse Index. Permite definir uma expressão de filtro para determinar exatamente quais documentos devem ser indexados.

// Indexa o campo email apenas para usuários ativos
db.usuarios.createIndex(
  { email: 1 },
  { partialFilterExpression: { status: "ATIVO" } }
)

4. Comandos Utilitários em `mongosh`

// Listar todos os índices da coleção
db.usuarios.getIndexes()

// Remover um índice específico pelo nome
db.usuarios.dropIndex("email_1")

// Verificar o tamanho consumido pelos índices
db.usuarios.totalIndexSize()

5. Questões no Estilo Exame de Certificação

Questão 1: Uma aplicação possui uma coleção com o índice composto { categoria: 1, preco: -1, marca: 1 }. Qual das consultas a seguir NÃO conseguirá utilizar este índice?

A) db.produtos.find({ categoria: "Eletrônicos" })
B) db.produtos.find({ categoria: "Eletrônicos", preco: { $lt: 1000 } })
C) db.produtos.find({ preco: 500, marca: "Sony" })
D) db.produtos.find({ categoria: "Eletrônicos", preco: 500, marca: "Sony" })

Resposta Correta: C. A consulta C tenta filtrar por preco e marca sem incluir o primeiro campo do prefixo (categoria). Sem o prefixo inicial, o MongoDB não pode utilizar o índice composto.

Questão 2: Um desenvolvedor tenta criar o índice { "enderecos.cep": 1, "telefones.numero": 1 }. Onde enderecos é um array de objetos e telefones também é um array de objetos. O que acontecerá ao executar o comando de criação de índice?

A) O índice será criado normalmente como um Multikey Index.
B) O MongoDB retornará um erro informando que um índice composto não pode conter mais de um campo do tipo array.
C) O índice será criado, mas consultas que buscam ambos os campos falharão em runtime.
D) O MongoDB converterá automaticamente o segundo campo para uma string.

Resposta Correta: B. O MongoDB proíbe estritamente a criação de índices compostos Multikey em que mais de um campo seja um array.