9 pontos por GN⁺ 2025-11-18 | 1 comentários | Compartilhar no WhatsApp
  • Implementa uma arquitetura de mecanismo de busca que funciona sem serviços externos usando o banco de dados existente, com foco em tokenização, pesos e scoring
  • A ideia central é tokenizar e armazenar todo o texto e, na busca, combinar os tokens da mesma forma para calcular a relevância
  • Combina tokenizadores Word, Prefix e N-Gram para lidar com correspondência exata, correspondência parcial e tolerância a erros de digitação, com pesos próprios para cada tokenizador
  • Por meio de um sistema de pesos e algoritmo de scoring baseado em SQL, avalia em conjunto comprimento do documento, diversidade de tokens e qualidade média
  • Tem alta escalabilidade e transparência, permitindo adicionar novos tokenizadores ou tipos de documento, ajustar pesos e modificar o scoring com liberdade

Por que criar seu próprio mecanismo de busca

  • Serviços externos como Elasticsearch e Algolia são poderosos, mas trazem a carga de aprender APIs complexas e gerenciar infraestrutura
  • Quando o que se precisa é apenas de uma função de busca integrada ao banco de dados existente e fácil de depurar, construir internamente pode ser útil
  • O objetivo é um mecanismo de busca simples que retorne resultados relevantes sem dependências externas

Conceito central: tokenização e correspondência

  • O princípio básico é tokenizar (tokenize) e armazenar todo o texto e, na busca, gerar tokens da mesma forma para fazer a correspondência
  • Na etapa de indexação, o conteúdo é separado em unidades de token e armazenado com seus pesos
  • Na etapa de busca, a consulta é tokenizada da mesma maneira para encontrar tokens correspondentes e calcular a pontuação
  • Na etapa de scoring, os pesos armazenados são usados para calcular a relevância

Projeto do esquema do banco de dados

  • Usa duas tabelas: index_tokens e index_entries
    • index_tokens: armazena tokens únicos e pesos por tokenizador
    • index_entries: conecta tokens e documentos, armazenando a pontuação final que reflete os pesos de campo, documento e tokenizador
  • Fórmula de cálculo do peso final:
    field_weight × tokenizer_weight × ceil(sqrt(token_length))
  • Os índices são configurados para consulta de documentos, consulta de tokens, queries por campo e filtragem por peso

Sistema de tokenização

  • WordTokenizer: separação por palavra, remoção de palavras curtas, para correspondência exata (peso 20)
  • PrefixTokenizer: gera prefixos de palavras, para autocomplete e correspondência parcial (peso 5)
  • NGramsTokenizer: gera combinações de caracteres de tamanho fixo, para lidar com erros de digitação e correspondência parcial (peso 1)
  • Todos os tokenizadores executam em comum conversão para minúsculas, remoção de caracteres especiais e normalização de espaços

Sistema de pesos

  • Peso de campo: reflete a importância de título, corpo, palavras-chave etc.
  • Peso do tokenizador: na ordem Word > Prefix > N-Gram
  • Peso do documento: calculado combinando os dois fatores acima com o comprimento do token
  • A função ceil(sqrt()) é usada para reduzir a influência de tokens longos, podendo ser ajustada para uma função logarítmica ou linear se necessário

Serviço de indexação

  • Só documentos que implementam IndexableDocumentInterface podem ser indexados
  • Na criação ou modificação do documento, a indexação é executada por listeners de eventos ou comandos (app:index-document, app:reindex-documents)
  • Procedimento:
    • Remove o índice existente e gera novos tokens
    • Executa todos os tokenizadores para cada campo
    • Verifica a existência do token e o cria se necessário (findOrCreateToken)
    • Faz inserção em lote (batch insert) em index_entries com os pesos calculados
  • Estrutura voltada para evitar duplicação, melhorar desempenho e lidar com atualizações

Serviço de busca

  • A query é processada com os mesmos tokenizadores para obter o mesmo conjunto de tokens da indexação
  • Após remover tokens duplicados, eles são ordenados por comprimento em ordem decrescente (tokens mais longos primeiro), com limite de 300
  • Uma query SQL faz o join entre tokens e documentos e calcula e ordena a pontuação de relevância
  • O resultado é retornado no formato SearchResult(documentId, score)

Algoritmo de scoring

  • Pontuação base: SUM(sd.weight)
  • Correção por diversidade de tokens: LOG(1 + COUNT(DISTINCT token_id))
  • Correção por peso médio: LOG(1 + AVG(weight))
  • Penalidade por tamanho do documento: 1 / (1 + LOG(1 + token_count))
  • Normalização (normalization): divide pelo valor máximo para ajustar ao intervalo de 0 a 1
  • Remove correspondências irrelevantes de baixo peso com o filtro de peso mínimo de token (st2.weight >= ?)

Tratamento dos resultados

  • Os resultados da busca são retornados como ID do documento e pontuação, e convertidos em documentos reais via repositório (repository)
  • Usa a função FIELD() para manter a ordem dos resultados da busca durante a consulta dos documentos

Escalabilidade do sistema

  • Novos tokenizadores podem ser adicionados implementando TokenizerInterface
  • Novos tipos de documento podem ser registrados implementando IndexableDocumentInterface
  • A lógica de pesos e scoring pode ser ajustada apenas com modificações no SQL

Conclusão

  • Essa estrutura é um mecanismo de busca simples, mas que realmente funciona, oferecendo desempenho suficiente sem infraestrutura externa
  • As vantagens são lógica clara, controle total e depuração fácil
  • Reforça a ideia de que, mais do que sistemas complexos, código que você mesmo entende e controla pode ter mais valor

1 comentários

 
GN⁺ 2025-11-18
Comentários no Hacker News
  • A ideia básica de busca é simples e um campo de problemas interessante
    Mas o realmente difícil é lidar com grandes volumes de dados e com consultas ambíguas
    Uma abordagem baseada em DBMS funciona bem no nível de sites pequenos, mas bate no limite rapidamente na escala da Wikipédia em inglês
    Como material introdutório, o e-book SeIRP é um bom recurso gratuito

    • Grandes volumes de dados são obviamente difíceis, mas acho que lidar com consultas ambíguas é um subproblema de “como decidir quais são os resultados mais relevantes”
      O fato de não existir uma resposta claramente correta torna isso especialmente complicado
      O Google às vezes mostra anúncios como “os resultados mais relevantes”, então o Marginalia Search é um bom caso de contraste
      Fico curioso se você já chegou a consultar os artigos do TREC
    • Hoje em dia, acho que um problema ainda maior é evitar spam de SEO
      Motores de busca precisam lutar sem parar contra agentes adversariais tentando ganhar dinheiro com anúncios
      Isso vira um jogo infinito de gato e rato, mudando constantemente as métricas de qualidade para impedir que sejam exploradas
    • Se alguém rodar processos de negócio centrados em texto com SQLite em um único servidor (na faixa de mil dólares), qual seria na prática a escala de repositório de documentos que dá para suportar?
      Queria saber que tamanho de corpus daria para pesquisar assumindo algo como 5 segundos por consulta e cerca de 12 consultas por minuto
    • Gosto muito do Marginalia Search
    • Acho que a dificuldade da busca não está só no tamanho dos dados, mas na decisão sobre quais resultados retornar
      Por exemplo, é difícil julgar se o artigo da wiki de Gilligan’s Island ou um blog de fã é o resultado “melhor”
      Se ainda somar manipulação de ranking ou keyword stuffing, o desafio fica muito mais complexo do que um mero problema de escalabilidade
  • Busca é realmente difícil
    Até empresas com muitos recursos e capacidade técnica, como Apple, Microsoft e OpenAI, têm baixa qualidade nas funcionalidades de busca
    Isso não é só um problema técnico

    • O motivo de a maioria das empresas não implementar busca bem é que a cultura corporativa e o jeito de desenvolver entram em conflito com o desenvolvimento de busca
      Para melhorar a qualidade da busca, é preciso ajustar finamente os parâmetros de ranking, e esse tipo de trabalho é difícil de planejar em estruturas de gestão como sprint ou Jira
      No fim, é uma área que exige confiança e autonomia para os desenvolvedores
    • Mas em algumas empresas a qualidade é baixa simplesmente porque busca não é prioridade
      Investem bilhões em modelos de IA, mas webapps ou busca ficam em segundo plano, e o resultado é esse
  • Há uns 10 anos trabalhei com um colega de doutorado em design de motores de busca
    Ele falava com muito entusiasmo sobre a integração entre busca e banco de dados, e aprendi bastante por causa disso
    Um dia ainda quero explorar a fundo o funcionamento interno de Solr e Lucene da Apache

    • Eu também gosto tanto da minha área que consigo falar dela por horas, mas não tem tanta gente interessada nos detalhes de implementação de sistemas grandes
  • Antigamente não existiam soluções open source de busca, então era preciso fazer tudo na mão
    A lição tirada dessa experiência foi: não construa seu próprio motor de busca
    Muita gente passou anos em cima desse problema, e fazer por conta própria leva a um inferno interminável de manutenção
    Quando começam pedidos como “adiciona correção de typos” ou “ano que vem vamos incluir também uma taxonomia”, acabou

  • Eu gostava muito de uma aula de construção de motores de busca dada no passado pelo professor David Evans, da Virginia University
    Construir um “motor de busca clássico” com as próprias mãos foi um projeto muito divertido
    Dá para consultar o link da disciplina e a playlist no YouTube

    • Eu também fiz essa disciplina, e era uma aula interessante e densa até para programadores iniciantes
  • O que me incomoda nos motores de busca que uso com frequência é que eles ignoram siglas ou palavras de 2 a 3 letras
    É muito ruim quando removem termos curtos como “mp3” ou “PHP” ao pesquisar

  • Li Programming Collective Intelligence, do Toby Segaran, e me inspirei em várias ideias sobre busca, recomendação e classificadores

    • Eu também gostava do livro, mas vi o autor dizer depois no YouTube que “agora está ultrapassado, então não use”
    • Era um livro realmente muito bom, e fico curioso se existe alguma edição atualizada para 2025
  • Foi um texto interessante
    Fiquei curioso sobre o nível de otimização dos tokenizadores usado pelos mecanismos de busca populares

  • Fico me perguntando o quanto esse sistema conseguiria escalar bem
    O Elasticsearch mostra um desempenho bem impressionante mesmo além da escala recomendada

  • Não é tão difícil fazer um motor simples de busca textual
    Mas criar um bom motor de busca é uma história completamente diferente
    Não basta simplesmente implementar um algoritmo como BM25
    A maioria das empresas para as quais fiz consultoria usava soluções próprias e acabou migrando para Elasticsearch ou OpenSearch
    A implementação própria começa simples, mas com o tempo fica complexa por causa de problemas de ranking e queda de desempenho
    Sintomas como “está lento” ou “está trazendo resultados nada a ver” passam a se repetir
    O Elasticsearch já resolve esses problemas há 10 anos, e hoje está muito mais evoluído
    Dizem que “é difícil de configurar”, mas hoje em dia quase tudo é configurado automaticamente, e também há muitos serviços gerenciados
    É até mais fácil de lidar do que o Postgres
    No fim, o que importa mesmo é otimizar o mapeamento do índice
    Algumas pessoas dizem “não precisamos dessas funções avançadas”, mas na prática a qualidade da busca está diretamente ligada à sobrevivência do negócio
    Se você quer uma busca realmente boa, no fim vai ter de aceitar essa complexidade

    • Eu também uso Elasticsearch por padrão
      Alternativas em ascensão como o SeekStorm, que tem aparecido bastante no HN ultimamente, também parecem interessantes, mas ainda não vi casos reais em produção
    • Concordo com a ideia de que “não existem recursos desnecessários”
      Especialmente a dica de desativar o dynamic mapping e impedir a indexação de campos desnecessários foi útil
    • Fico curioso sobre o que você acha do ManticoreSearch
      Pelo que sei, é um projeto mais antigo que o Lucene