← Back to home

Busca Lexical

By Pscodium · 8/4/2026 · 6 views

Busca Lexical na Prática com BM25

No artigo anterior vimos como funciona a busca semântica, que tenta encontrar resultados com base no significado dos textos.

Agora vamos entender a busca lexical.

A busca lexical trabalha de uma maneira mais direta:

Ela procura correspondências entre os termos da pesquisa e os termos presentes nos documentos.

Imagine que temos:

Documento 1:
"Como configurar PostgreSQL utilizando Docker"

Documento 2:
"Como criar uma API utilizando Node.js"

Documento 3:
"Como configurar Redis utilizando Docker"

O usuário pesquisa:

PostgreSQL Docker

A busca lexical identifica que o Documento 1 possui os termos:

PostgreSQL
Docker

Portanto, ele provavelmente será o resultado mais relevante.


1. O conceito de índice invertido

Um dos principais conceitos por trás de mecanismos de busca lexicais é o:

Índice invertido (Inverted Index)

Imagine que temos:

Documento 1
"PostgreSQL Docker"

Documento 2
"Node.js API"

Documento 3
"PostgreSQL Redis"

Podemos criar um índice:

PostgreSQL
  ↓
Documento 1
Documento 3

Docker
  ↓
Documento 1

Node.js
  ↓
Documento 2

API
  ↓
Documento 2

Redis
  ↓
Documento 3

Quando o usuário pesquisa:

PostgreSQL Docker

o sistema consulta o índice:

PostgreSQL
  ↓
1, 3

Docker
  ↓
1

O Documento 1 possui os dois termos.

Portanto:

Documento 1

é provavelmente o melhor resultado.

Esse conceito permite realizar buscas muito mais rapidamente do que analisar todos os documentos do zero.


2. Tokenização

Antes de criar o índice, precisamos transformar o texto em termos.

Por exemplo:

"Como configurar PostgreSQL utilizando Docker"

Pode se tornar:

[
  "como",
  "configurar",
  "postgresql",
  "utilizando",
  "docker"
]

Esse processo é chamado de:

Tokenização

Dependendo do mecanismo de busca, podemos realizar outras operações, como:

  • Converter para lowercase
  • Remover pontuação
  • Remover stopwords
  • Stemming
  • Normalização

Por exemplo:

"PostgreSQL, PostgreSQL!"

pode ser normalizado para:

postgresql
postgresql

3. Stopwords

Algumas palavras aparecem com tanta frequência que normalmente não ajudam muito na busca.

Por exemplo:

o
a
de
do
da
para
com
em

Essas palavras podem ser chamadas de:

Stopwords

Uma frase:

"Como configurar o PostgreSQL no Docker"

pode ser simplificada para:

configurar
postgresql
docker

Isso pode reduzir o tamanho do índice e melhorar a eficiência.

Porém, stopwords devem ser utilizadas com cuidado.

Em algumas aplicações, palavras aparentemente comuns podem ser importantes para o significado da consulta.


4. Term Frequency

Uma das ideias fundamentais da busca lexical é:

Term Frequency (TF)

Ela representa a frequência com que um termo aparece em um documento.

Imagine:

Documento A:

PostgreSQL é um banco de dados.
PostgreSQL pode ser executado com Docker.
PostgreSQL possui muitas extensões.

O termo:

PostgreSQL

aparece várias vezes.

Isso indica que o documento provavelmente está relacionado ao termo.

Porém, existe um problema.

Imagine:

PostgreSQL PostgreSQL PostgreSQL PostgreSQL

Só porque uma palavra aparece muitas vezes não significa que o documento seja melhor.

Por isso, mecanismos modernos utilizam algoritmos de ranking mais sofisticados.

Um dos mais conhecidos é o:

BM25


5. O que é BM25?

O BM25 é um algoritmo utilizado para calcular a relevância de um documento em relação a uma consulta.

De maneira simplificada, ele considera fatores como:

1. Frequência do termo no documento
2. Frequência do termo em toda a coleção
3. Tamanho do documento

A ideia é:

Query
  ↓
Termos
  ↓
Documentos que possuem os termos
  ↓
BM25 calcula relevância
  ↓
Ranking

O resultado pode ser:

Documento A
Score: 12.4

Documento B
Score: 8.7

Documento C
Score: 1.2

Os documentos são então retornados nessa ordem.


6. Um exemplo simples

Imagine os seguintes documentos:

const documents = [
  {
    id: 1,
    text: "Como configurar PostgreSQL utilizando Docker",
  },
  {
    id: 2,
    text: "Como criar uma API utilizando Node.js",
  },
  {
    id: 3,
    text: "Como configurar Redis utilizando Docker",
  },
];

O usuário pesquisa:

PostgreSQL Docker

O resultado ideal seria:

Documento 1

porque contém os dois termos.

O Documento 3 contém:

Docker

mas não contém:

PostgreSQL

Portanto, ele pode aparecer depois.


7. Uma implementação simples

Antes de implementar BM25, podemos começar com uma busca lexical extremamente simples.

function tokenize(text: string): string[] {
  return text
    .toLowerCase()
    .replace(/[^\w\s]/g, "")
    .split(/\s+/);
}

Agora podemos calcular quantos termos da pesquisa aparecem em cada documento:

function lexicalScore(
  query: string,
  document: string
): number {
  const queryTerms = tokenize(query);
  const documentTerms = tokenize(document);

  let score = 0;

  for (const term of queryTerms) {
    if (documentTerms.includes(term)) {
      score++;
    }
  }

  return score;
}

Podemos executar:

const query = "PostgreSQL Docker";

for (const document of documents) {
  const score = lexicalScore(
    query,
    document.text
  );

  console.log(
    document.id,
    score
  );
}

Teríamos algo semelhante a:

Documento 1 → 2

Documento 2 → 0

Documento 3 → 1

Então podemos ordenar:

1. Documento 1
2. Documento 3
3. Documento 2

Isso já é uma forma extremamente simples de busca lexical.

Porém, mecanismos reais utilizam algoritmos mais sofisticados.


8. Implementando uma versão simplificada de BM25

Uma implementação simplificada pode ser feita assim:

function bm25(
  query: string,
  document: string,
  allDocuments: string[],
  k1 = 1.5,
  b = 0.75
): number {
  const queryTerms = tokenize(query);
  const documentTerms = tokenize(document);

  const documentLength =
    documentTerms.length;

  const averageDocumentLength =
    allDocuments.reduce(
      (sum, doc) =>
        sum + tokenize(doc).length,
      0
    ) / allDocuments.length;

  let score = 0;

  for (const term of queryTerms) {
    const termFrequency =
      documentTerms.filter(
        (item) => item === term
      ).length;

    if (termFrequency === 0) {
      continue;
    }

    const documentsContainingTerm =
      allDocuments.filter((doc) =>
        tokenize(doc).includes(term)
      ).length;

    const idf = Math.log(
      1 +
        (allDocuments.length -
          documentsContainingTerm +
          0.5) /
        (documentsContainingTerm + 0.5)
    );

    const numerator =
      termFrequency * (k1 + 1);

    const denominator =
      termFrequency +
      k1 *
        (1 -
          b +
          b *
            (documentLength /
              averageDocumentLength));

    score +=
      idf *
      (numerator / denominator);
  }

  return score;
}

Agora podemos criar nossa busca:

function search(
  query: string,
  documents: {
    id: number;
    text: string;
  }[]
) {
  const allTexts =
    documents.map(
      (document) => document.text
    );

  return documents
    .map((document) => ({
      ...document,

      score: bm25(
        query,
        document.text,
        allTexts
      ),
    }))
    .sort(
      (a, b) =>
        b.score - a.score
    );
}

Podemos executar:

const results = search(
  "PostgreSQL Docker",
  documents
);

console.log(results);

O resultado seria aproximadamente:

[
  {
    id: 1,
    text: "Como configurar PostgreSQL utilizando Docker",
    score: 1.8
  },
  {
    id: 3,
    text: "Como configurar Redis utilizando Docker",
    score: 0.7
  },
  {
    id: 2,
    text: "Como criar uma API utilizando Node.js",
    score: 0
  }
]

O Documento 1 ficou em primeiro porque possui:

PostgreSQL
+
Docker

Enquanto o Documento 3 possui apenas:

Docker

9. O problema da busca simples

Nossa implementação funciona para demonstrar o conceito, mas existem vários problemas.

Por exemplo, o usuário pesquisa:

PC lento

O documento contém:

Computador com baixo desempenho

A busca lexical provavelmente não encontrará uma boa correspondência.

Isso acontece porque:

PC ≠ computador

lento ≠ baixo desempenho

Mesmo que os significados sejam próximos.

Esse é justamente um dos principais problemas que a busca semântica tenta resolver.


10. Busca Lexical vs Semântica

Podemos comparar:

Busca Lexical

Query:

"PostgreSQL Docker"

Procura:

PostgreSQL
Docker

Busca Semântica

Query:

"Como executar um banco em containers"

Pode encontrar:

"Como configurar PostgreSQL utilizando Docker"

mesmo que as palavras sejam diferentes.


11. Onde a busca lexical é melhor?

A busca lexical é especialmente útil quando a correspondência exata é importante.

Por exemplo:

"RTX 5090"
"iPhone 17 Pro"
"ERR_CONNECTION_REFUSED"
"PostgreSQL 18"

Nesses casos, você provavelmente não quer que o sistema retorne apenas algo "semanticamente parecido".

Se o usuário pesquisou:

RTX 5090

um resultado sobre:

RTX 4090

pode não ser relevante, mesmo que os produtos sejam semanticamente relacionados.


12. Busca Lexical em aplicações reais

Um sistema de busca real normalmente não implementa BM25 manualmente.

É comum utilizar mecanismos especializados.

Algumas opções populares são:

Elasticsearch
OpenSearch
Solr
Lucene
PostgreSQL Full Text Search

O fluxo seria:

Documentos
    ↓
Indexação
    ↓
Índice Invertido
    ↓
BM25

Quando o usuário pesquisa:

"PostgreSQL Docker"

temos:

Query
  ↓
Tokenização
  ↓
Índice Invertido
  ↓
Documentos candidatos
  ↓
BM25
  ↓
Ranking
  ↓
Resultados

13. Exemplo com Elasticsearch

Em um sistema real, poderíamos ter um índice:

{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "content": {
        "type": "text"
      }
    }
  }
}

Podemos então pesquisar:

{
  "query": {
    "multi_match": {
      "query": "PostgreSQL Docker",
      "fields": [
        "title",
        "content"
      ]
    }
  }
}

O Elasticsearch pode utilizar BM25 para calcular a relevância.

O resultado pode ser:

Documento A
Score: 8.2

Documento B
Score: 4.7

Documento C
Score: 1.1

Os resultados são retornados já ordenados pela relevância.


14. O conceito de Ranking

Um dos pontos mais importantes da busca lexical é:

Encontrar documentos é apenas uma parte do problema.

Também precisamos decidir:

Qual documento deve aparecer primeiro?

Imagine:

Query:

"PostgreSQL Docker"

Temos:

Documento A
Título: PostgreSQL com Docker
Conteúdo: Guia completo...
Documento B
Título: Docker
Conteúdo: PostgreSQL é mencionado uma vez...

Mesmo que ambos possuam os termos, o Documento A provavelmente deve aparecer primeiro.

O ranking considera fatores como:

Frequência dos termos
Relevância dos termos
Tamanho do documento
Raridade dos termos
Campo onde o termo aparece

Por isso, mecanismos de busca são muito mais sofisticados do que:

if (document.includes(query))

15. Busca Lexical e Busca Híbrida

Em sistemas modernos, a busca lexical frequentemente é combinada com a busca semântica.

Podemos ter:

             Query
                ↓
       ┌────────┴────────┐
       ↓                 ↓
   Busca Lexical     Busca Semântica
       ↓                 ↓
      BM25          Vector Search
       ↓                 ↓
       └────────┬────────┘
                ↓
        Combinação dos Rankings
                ↓
             Reranking
                ↓
            Resultado

Por exemplo:

Busca Lexical
→ Excelente para termos exatos

Busca Semântica
→ Excelente para significado

Busca Híbrida
→ Combina os dois

Isso é especialmente útil em:

  • RAG
  • E-commerce
  • Documentação
  • Sistemas de busca internos
  • Bases de conhecimento
  • Sistemas de recomendação

Conclusão

A busca lexical é uma das bases dos sistemas modernos de recuperação de informação.

Seu funcionamento pode ser resumido em:

Documento
    ↓
Tokenização
    ↓
Indexação
    ↓
Índice Invertido
    ↓
Query
    ↓
Busca pelos termos
    ↓
BM25
    ↓
Ranking
    ↓
Resultados

O BM25 é especialmente importante porque permite calcular a relevância de cada documento levando em consideração não apenas se um termo aparece, mas também fatores como sua frequência, sua raridade e o tamanho do documento.

A principal vantagem da busca lexical é a precisão em termos específicos.

Ela é excelente para encontrar:

Códigos
IDs
SKUs
Nomes
Versões
Termos técnicos

Por outro lado, ela possui dificuldade para compreender que:

PC
≈
Computador

ou:

carro econômico
≈
veículo com baixo consumo

É por isso que sistemas modernos frequentemente combinam:

Busca Lexical
      +
Busca Semântica
      ↓
Busca Híbrida

A busca lexical responde "quais documentos possuem termos relacionados à minha pesquisa?", enquanto a busca semântica tenta responder "quais documentos possuem significado relacionado à minha pesquisa?".



Comments

No comments yet.