Assinar
Voltar
Backend

MongoDB para RAG: quais índices criar no Atlas Vector Search

Um guia prático de backend para montar a camada de recuperação de um sistema RAG com MongoDB Atlas: como armazenar embeddings e, principalmente, quais índices criar — o índice vectorSearch, os campos de filtro para pré-filtragem de metadados e o índice de texto do Atlas Search para busca híbrida — com exemplos reais da definição JSON e do estágio $vectorSearch.

{}

Quando falamos em RAG (Retrieval-Augmented Generation), a parte mais glamurosa costuma ser o LLM que gera a resposta. Mas o segredo de um RAG que funciona está na etapa de recuperação: encontrar, entre milhares de documentos, os trechos realmente relevantes para a pergunta do usuário. É aqui que o MongoDB entra com o Atlas Vector Search, permitindo guardar embeddings ao lado dos seus dados e consultá-los por similaridade semântica. Neste artigo o foco é bem concreto: quais índices você precisa criar e como escrever a consulta.

O fluxo de RAG com MongoDB Atlas

Antes dos índices, vale fixar o fluxo. Ele tem duas fases distintas:

  • Ingestão (offline): você quebra seus documentos em pedaços (chunks), gera um embedding para cada chunk usando um modelo (OpenAI, Voyage AI, Cohere etc.) e grava esse vetor dentro do documento no MongoDB, junto com o texto e metadados.
  • Consulta (online): a pergunta do usuário também vira um embedding; o Atlas busca os vetores mais próximos (vizinhos mais próximos), e os trechos recuperados são injetados no prompt do LLM como contexto.

A busca vetorial não substitui o LLM — ela alimenta o LLM com o contexto certo. Índice bom significa contexto bom; contexto bom significa resposta confiável.

Como armazenar embeddings

No MongoDB, um embedding é simplesmente um array de números (double) em um campo do documento. Um documento típico da coleção fica assim:

{
  "_id": ObjectId("..."),
  "texto": "O MongoDB Atlas Vector Search permite...",
  "embedding": [0.0123, -0.0456, 0.0789, ...],  // ex.: 1536 dimensões
  "categoria": "backend",
  "idioma": "pt-BR",
  "publicado_em": ISODate("2026-01-10")
}

Duas regras de ouro: o número de dimensões do array precisa bater exatamente com o modelo usado (por exemplo, 1536 para text-embedding-3-small da OpenAI), e todo documento indexado precisa ter esse campo preenchido.

Os índices que você precisa criar

Aqui está o coração do artigo. Para um RAG completo você pode combinar até três tipos de índice, mas apenas o primeiro é obrigatório.

1. Índice do tipo vectorSearch (obrigatório)

É o índice que habilita a busca por similaridade. Ele é definido em JSON e tem dois tipos de campo: vector (o campo do embedding) e filter (campos de metadados usados na pré-filtragem). Os parâmetros essenciais do campo vector são:

  • path: o caminho do campo que guarda o embedding.
  • numDimensions: quantas dimensões o vetor tem — precisa ser igual ao modelo de embedding.
  • similarity: a métrica de distância. Os valores válidos são euclidean, cosine e dotProduct. Para embeddings de texto normalizados, cosine é a escolha mais comum; dotProduct tende a ser mais eficiente quando os vetores já estão normalizados.

A definição do índice fica assim:

{
  "fields": [
    {
      "type": "vector",
      "path": "embedding",
      "numDimensions": 1536,
      "similarity": "cosine"
    },
    {
      "type": "filter",
      "path": "categoria"
    },
    {
      "type": "filter",
      "path": "idioma"
    }
  ]
}

Para coleções grandes, você ainda pode ativar quantização (por exemplo "quantization": { "type": "scalar" } ou "int8") para reduzir o uso de memória do índice sem perder muita precisão.

2. Índices de filtro (para pré-filtragem de metadados)

Note, no exemplo acima, que categoria e idioma aparecem como campos filter dentro do próprio índice vectorSearch. Isso é fundamental: no Atlas Vector Search, a pré-filtragem de metadados não usa índices B-tree tradicionais — os campos precisam ser declarados como filter na definição do índice vetorial para poderem ser usados no operador filter da consulta.

A pré-filtragem acontece antes da busca vetorial, restringindo o espaço de candidatos. Isso é ótimo em RAG multi-tenant ou multi-idioma: você garante que o usuário só recupere trechos da sua organização, do idioma certo ou dentro de uma janela de data, melhorando a precisão e o desempenho.

3. Índice de busca textual (Atlas Search) para busca híbrida

A busca puramente vetorial é excelente para semântica, mas às vezes o usuário procura um termo exato — um código de produto, um nome próprio, uma sigla. Nesses casos vale combinar com um índice do Atlas Search (busca full-text, baseada em Lucene) sobre o campo de texto. Isso é a chamada busca híbrida.

São dois índices separados e independentes: um índice vectorSearch sobre embedding e um índice search (full-text) sobre texto. Na consulta, você executa os dois estágios ($vectorSearch e $search) e combina os resultados com fusão de rankings — o Atlas oferece o estágio $rankFusion (Reciprocal Rank Fusion), que mescla as listas ordenadas dando peso a cada abordagem.

Exemplo de consulta com $vectorSearch

Com os índices no lugar, a recuperação é feita com um aggregation pipeline. O estágio $vectorSearch precisa ser o primeiro do pipeline. Seus campos principais:

  • index: o nome do índice vectorSearch criado.
  • path e queryVector: o campo do embedding e o vetor da pergunta.
  • numCandidates: quantos candidatos considerar na busca aproximada (ANN); a doc recomenda pelo menos 20x o limit.
  • limit: quantos documentos retornar.
  • filter: a pré-filtragem de metadados (usa os campos filter do índice).
db.artigos.aggregate([
  {
    "$vectorSearch": {
      "index": "idx_embedding",
      "path": "embedding",
      "queryVector": [0.0021, -0.0339, 0.0517, ...],
      "numCandidates": 150,
      "limit": 5,
      "filter": {
        "$and": [
          { "categoria": "backend" },
          { "idioma": "pt-BR" }
        ]
      }
    }
  },
  {
    "$project": {
      "_id": 0,
      "texto": 1,
      "categoria": 1,
      "score": { "$meta": "vectorSearchScore" }
    }
  }
])

O estágio $project traz o vectorSearchScore via $meta — a pontuação de similaridade (quanto mais perto de 1, mais similar). É esse score que você pode usar para descartar trechos fracos antes de montar o prompt.

Regra prática: comece com numCandidates em torno de 10x a 20x o limit. Se a recall estiver baixa, aumente; se a latência incomodar, reduza. Para bases pequenas, o campo exact: true faz busca exata (ENN) sem numCandidates.

Juntando tudo

Um RAG de produção com MongoDB costuma ter: um índice vectorSearch com o campo do embedding e os metadados relevantes declarados como filter; opcionalmente um índice search full-text para busca híbrida; e um pipeline que pré-filtra por tenant/idioma, recupera por similaridade e projeta o score. Os trechos retornados viram o contexto do LLM. Simples de descrever, poderoso quando bem indexado.

Fontes

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *