Pular para o conteúdo

Por Que Sua Busca Não Retorna Nada — E Como o MongoDB Vector Search Resolve Isso

A busca por palavras-chave só encontra o que está literalmente lá. Quando usuários buscam por 'laptop bag' e seus documentos dizem 'notebook carrying case', regex não vai ajudar. A busca vetorial entende o significado — e o MongoDB Atlas a suporta nativamente.

Durval Pereira
Durval Pereira
10 min

O problema dos resultados ocultos

Sua busca funciona. Ou, pelo menos, parece funcionar. Usuários digitam uma consulta, resultados são exibidos, ninguém reclama muito alto. Mas existe uma classe de falhas que é quase invisível: os resultados que deveriam ter aparecido, mas não apareceram.

Considere um catálogo de produtos para uma loja online. Um usuário digita "laptop bag" na barra de busca. O sistema retorna dois produtos que contêm a palavra "laptop bag" em seu título ou descrição. Parece bom.

Mas há uma dúzia de outros produtos relevantes no catálogo. Uma "notebook carrying case", uma "padded sleeve for 15-inch computers", uma "tech commuter backpack with device compartment". Nenhum deles contém a string literal "laptop bag". Então, nenhum deles aparece.

O usuário vê dois resultados e assume que é tudo o que você tem. O sistema ocultou silenciosamente os produtos mais relevantes porque o mecanismo de busca é estruturalmente incapaz de entender o que o usuário quis dizer.

Como a busca por palavras-chave realmente funciona

A maioria das implementações de busca interna que encontrei usa alguma forma de regex ou correspondência de substring. A string de consulta é verificada em um conjunto de campos indexados — productName, description, category, specifications — e qualquer documento que contenha essa sequência exata de caracteres é retornado.

db.products.find({
  $or: [
    { productName: { $regex: "laptop bag", $options: "i" } },
    { description: { $regex: "laptop bag", $options: "i" } },
    { category: { $regex: "laptop bag", $options: "i" } },
    { specifications: { $regex: "laptop bag", $options: "i" } },
  ]
})

Isso funciona quando a linguagem do usuário corresponde exatamente à linguagem do documento. Buscar "laptop" encontra documentos que dizem "laptop". Buscar "bag" também funciona porque é uma substring de "bags".

Mas a abordagem é puramente lexical. Ela não tem conceito de significado.

Onde falha

Os modos de falha são sistemáticos, não casos de exceção:

Usuário busca porEspera encontrarPor que a busca por palavras-chave falha
"laptop bag"Sleeves para notebooks, mochilas techO produto diz "carrying case", não "bag"
"winter jacket"Parkas, casacos puffer, jaquetas isoladasO produto diz "thermal outerwear"
"kids tablet"Dispositivos educacionais, tablets de aprendizagemO produto diz "children's interactive screen"
"presente para um corredor"Tênis de corrida, monitores de fitness, equipamentos de hidrataçãoNenhum campo contém o conceito de "presente para um corredor"
"algo para uma road trip"Coolers, carregadores veiculares, travesseiros de viagemConsultas conceituais não têm correspondência literal

Nenhuma quantidade de indexação de campos pode antecipar todas as formas como um usuário pode expressar sua intenção. A limitação não está na implementação — está no paradigma.

O paliativo da desnormalização

Uma reação comum é desnormalizar: puxar dados relacionados de outras collections para o documento pesquisável. Digamos que seu catálogo tenha uma collection products com metadados básicos, mas as descrições ricas e amigáveis a palavras-chave vivam em uma collection productDetails separada, vinculada por SKU.

// Before: lean product document with references
{
  "_id": "prod_2241",
  "productName": "TechShield Commuter Pack",
  "brand": "TechShield",
  "skus": ["TS-441", "TS-442", "TS-443"]
}

// After: enriched with detail metadata
{
  "_id": "prod_2241",
  "productName": "TechShield Commuter Pack",
  "brand": "TechShield",
  "skus": ["TS-441", "TS-442", "TS-443"],
  "variantNames": [
    "TechShield Padded Laptop Bag 15-inch, Black",
    "TechShield Padded Laptop Bag 15-inch, Navy",
    "TechShield Padded Laptop Sleeve 13-inch, Gray"
  ]
}

Agora, uma busca por "laptop bag" corresponderá a este produto porque a string aparece em variantNames. Isso funciona como uma correção tática. Mas introduz uma desvantagem: cada documento de produto deve ser atualizado sempre que os dados da variante mudam, a redundância deve ser mantida ao longo do tempo, e você ainda está correndo atrás do vocabulário do usuário.

Um usuário que busca por "backpack for my MacBook" ainda não corresponderá a "Padded Laptop Bag" a menos que você continue expandindo os campos desnormalizados. Você está remendando um sistema fundamentalmente lexical, um sinônimo por vez.

Busca vetorial: correspondência por significado

A busca vetorial adota uma abordagem completamente diferente. Em vez de comparar sequências de caracteres, ela compara o significado.

A ideia central: converter texto em representações numéricas de alta dimensão chamadas embeddings. Estes são gerados por modelos de machine learning (Voyage AI, text-embedding-3-small da OpenAI, modelos de código aberto como nomic-embed-text) treinados em vastos corpora de texto. Os modelos aprendem relações semânticas entre palavras e conceitos.

No espaço de embeddings:

  • Palavras com significados semelhantes se agrupam próximas (pequena distância vetorial)
  • Palavras com significados diferentes estão distantes (grande distância vetorial)
"laptop bag"     → [0.021, -0.187, 0.443, 0.078, ..., 0.312]  (768 dimensions)
"notebook sleeve" → [0.019, -0.174, 0.451, 0.065, ..., 0.298]  (nearby)
"refrigerator"   → [-0.342, 0.501, -0.113, 0.227, ..., -0.089] (distant)

Quando um usuário busca por "laptop bag", a consulta é convertida em um embedding e comparada com os embeddings pré-computados de todos os documentos. Os resultados são classificados por similaridade de cosseno. A "notebook carrying case" aparece — não por uma correspondência de string, mas porque o modelo entende que carrying cases e bags para laptops habitam a mesma vizinhança semântica.

MongoDB Atlas Vector Search: implementação

O MongoDB Atlas suporta busca vetorial nativamente. Nenhuma infraestrutura de busca separada, nenhum sidecar do Elasticsearch, nenhum pipeline de sincronização de dados. Ele roda no seu cluster existente.

Passo 1: Gerar embeddings

Para cada documento, concatene os campos semanticamente significativos e passe-os por um modelo de embedding:

def build_embedding_text(product):
    parts = [
        product.get("productName", ""),
        product.get("brand", ""),
        product.get("description", ""),
        product.get("category", ""),
        product.get("specifications", ""),
    ]
    return " | ".join(part for part in parts if part)

Para a mochila de viagem, isso produz:

"TechShield Commuter Pack | TechShield | Durable backpack with padded
device compartment and organizer pockets | Bags & Accessories | Water-
resistant nylon, fits up to 15-inch devices"

O embedding resultante captura o conceito — "uma bolsa para carregar dispositivos tech, formato de mochila, acolchoamento protetor." Armazene-o como um novo campo no documento:

{
  "_id": "prod_2241",
  "productName": "TechShield Commuter Pack",
  "embedding": [0.019, -0.174, 0.451, 0.065, "...", 0.298]
}

Passo 2: Criar um índice de busca vetorial

Defina o índice no MongoDB Atlas:

{
  "type": "vectorSearch",
  "fields": [
    {
      "path": "embedding",
      "type": "vector",
      "numDimensions": 768,
      "similarity": "cosine"
    }
  ]
}

O numDimensions deve corresponder ao tamanho de saída do seu modelo de embedding. A similaridade de cosseno é a escolha padrão para embeddings de texto.

Passo 3: Consultar com $vectorSearch

No momento da busca, incorpore a consulta do usuário com o mesmo modelo e passe-a para o estágio de agregação $vectorSearch:

db.products.aggregate([
  {
    $vectorSearch: {
      index: "product_vector_index",
      path: "embedding",
      queryVector: embedQuery("laptop bag"),
      numCandidates: 100,
      limit: 20
    }
  },
  {
    $project: {
      productName: 1,
      brand: 1,
      category: 1,
      score: { $meta: "vectorSearchScore" }
    }
  }
])

Uma busca por "laptop bag" agora retorna:

ClassificaçãoProdutoPontuação
1TechShield Commuter Pack0.92
2SlimGuard Notebook Sleeve 15"0.89
3UrbanGear Padded Carrying Case0.86
4ProTravel Tech Backpack0.81

O produto TechShield aparece — não por uma correspondência de string, mas porque o modelo entende que uma "mochila de viagem com compartimento acolchoado para dispositivos" é semanticamente o que alguém quer dizer ao buscar por "laptop bag".

Por que isso é fundamentalmente melhor

DimensãoBusca por Palavras-Chave / RegexBusca Vetorial
Mecanismo de correspondênciaCorrespondência exata de substringSimilaridade semântica
Lida com sinônimosNão ("bag" ≠ "case" ≠ "sleeve")Sim (entende equivalência)
Lida com paráfrasesNãoSim ("algo para carregar meu laptop" → bolsas)
Requer desnormalizaçãoSim — deve copiar dados para campos pesquisáveisNão — o significado é capturado no embedding
Carga de manutençãoAlta — manter campos redundantes sincronizadosBaixa — re-embedar apenas quando o texto fonte muda
Tolerância a erros de digitaçãoNão ("laptpo bag" falha)Parcial (embeddings são robustos a pequenas variações)
Consultas conceituaisImpossívelSim ("equipamento para viajantes tech" exibe produtos relevantes)
Qualidade de classificaçãoBinária (correspondência ou não correspondência)Pontuação de relevância contínua

A vantagem mais significativa é a última. A busca por palavras-chave é binária — ou um documento contém a string ou não. A busca vetorial produz uma pontuação de relevância, o que significa que os resultados podem ser classificados por quão próximos eles correspondem à intenção do usuário.

Busca híbrida: a escolha pragmática

A busca vetorial pura tem uma fraqueza: correspondências exatas. Se um usuário digita o nome exato do produto — "TechShield Commuter Pack 15-inch Black" — a busca por palavras-chave o encontrará imediatamente, enquanto a busca vetorial pode classificá-lo bem, mas não necessariamente em primeiro lugar.

O MongoDB Atlas suporta busca híbrida — combinando pontuações de busca full-text com pontuações de similaridade vetorial usando Reciprocal Rank Fusion (RRF):

db.products.aggregate([
  {
    $vectorSearch: {
      index: "product_vector_index",
      path: "embedding",
      queryVector: embedQuery("laptop bag"),
      numCandidates: 100,
      limit: 50
    }
  },
  {
    $unionWith: {
      coll: "products",
      pipeline: [
        {
          $search: {
            index: "product_text_index",
            text: {
              query: "laptop bag",
              path: ["productName", "brand", "description", "category"]
            }
          }
        },
        { $limit: 50 }
      ]
    }
  }
  // Reciprocal Rank Fusion to merge and re-rank results
])

Isso oferece o melhor dos dois mundos:

  • Buscas por nomes exatos de produtos são tratadas de forma nítida pela correspondência de palavras-chave
  • Consultas exploratórias ou conceituais ("algo à prova d'água para caminhar com meu laptop") são tratadas pela similaridade vetorial
  • Ambos os sinais são fundidos em um único conjunto de resultados classificados

Enriquecendo embeddings para melhores resultados

Embora a busca vetorial resolva o problema dos resultados ocultos sem desnormalização, você pode melhorar ainda mais a qualidade incluindo dados relacionados no texto fonte do embedding. Este é um parente mais leve da abordagem de desnormalização — em vez de reestruturar documentos para varredura de palavras-chave, você anexa contexto ao texto que é incorporado:

def build_enriched_embedding_text(product, variant_names):
    base = build_embedding_text(product)
    variants = " | ".join(variant_names)
    return f"{base} | Variants: {variants}"

Isso oferece ao modelo de embedding um contexto mais rico, fortalecendo o sinal semântico para termos que aparecem nos detalhes da variante, mas não na listagem principal do produto. A estrutura do documento permanece inalterada — apenas o embedding se beneficia.

Quando adotar isso

Se sua busca é apoiada por regex ou consultas $text básicas no MongoDB, o caminho a seguir é claro:

  1. Imediato: Audite suas consultas de busca de maior tráfego. Identifique quais delas retornam menos resultados do que deveriam. Isso quantifica o problema dos resultados ocultos em seu sistema.

  2. Curto prazo: Se a adoção da busca vetorial precisar de tempo, considere a desnormalização direcionada para as consultas com pior desempenho. Isso ganha tempo sem mudança arquitetural.

  3. Médio prazo: Implemente o MongoDB Atlas Vector Search. Gere embeddings a partir dos metadados do seu documento, crie o índice e valide com testes A/B contra a busca atual.

  4. Longo prazo: Adote a busca híbrida combinando sinais de palavras-chave e vetoriais. Estenda para superfícies adicionais — descoberta de produtos, recomendações, busca conversacional.

O resultado é uma experiência de busca onde os usuários encontram o que procuram, mesmo quando não usam as palavras exatas que aparecem em seus dados. Isso não é um "bom ter". Para qualquer sistema onde a busca impulsiona o engajamento ou a receita, é a diferença entre um produto que parece inteligente e um que parece quebrado.


Este artigo faz parte de uma série sobre bancos de dados e infraestrutura de dados.

Tagsmongodbvector-searchsearchsemantic-searchembeddings