Busca textual
Índice invertido, relevância e os motores de busca como Elasticsearch e OpenSearch.
As decisões de busca são quatro: onde buscar, como analisar o texto, como lidar com erros de digitação e como manter o índice igual aos dados.
Onde buscar
| Opção | Bom para | Custos |
|---|---|---|
PostgreSQL com tsvector e GIN | Busca por palavras com acentos, radicais, pesos e frases, sem sistema a mais | Relevância simples (ts_rank); sem busca aproximada embutida |
PostgreSQL com pg_trgm | Busca por trechos e ILIKE rápidos; nomes curtos com um erro de digitação | Fraco com vários erros (0,40 para o tênis e para a mochila, no teste) |
| OpenSearch ou Elasticsearch | Busca como produto: relevância BM25, busca aproximada, sugestões, filtros com contagens, sinônimos | Outro sistema para operar, com índice que precisa ser sincronizado |
| Meilisearch, Typesense | Busca para o usuário final, com tolerância a erros já ajustada, mais simples de operar | Menos flexíveis em consultas complexas |
| Busca gerenciada (Algolia, OpenSearch Service, Elastic Cloud) | Sem operar servidores | Custo por uso; os dados saem do seu ambiente |
| Necessidade | Escolha |
|---|---|
| Achar registros por palavras numa tela administrativa | PostgreSQL com tsvector |
| Autocompletar nomes de cidades ou clientes | PostgreSQL com pg_trgm, ou índice de prefixo |
| Busca da loja, com erros de digitação, filtros e relevância ajustada | OpenSearch, Elasticsearch ou Meilisearch |
| Busca em logs | Ferramentas de logs (ver o tópico de logs) |
| Busca por significado ("algo para correr na chuva") | Busca vetorial (ver o tópico de embeddings e RAG), muitas vezes combinada com a textual |
Como analisar o texto
| Decisão | Recomendação |
|---|---|
| Acentos | Remover na análise (unaccent no PostgreSQL, asciifolding no OpenSearch) |
| Radicalização | Usar, mas testar com as palavras do domínio: o radicalizador leve transformou "tênis" em tenil |
| Palavras vazias | Remover as da língua; cuidado com as que importam no domínio ("sem fio", "não tóxico") |
| Sinônimos | Uma lista do domínio: "celular" e "smartphone", "tênis" e "sapatilha" |
| Campos e pesos | Nome com peso maior que a descrição |
| Vários campos para o mesmo texto | Um com radicalização, para relevância; outro sem, para busca aproximada e frases exatas |
Erros de digitação
| Técnica | Como funciona | Cuidados |
|---|---|---|
| Busca aproximada (fuzziness) | Aceita 1 ou 2 letras diferentes por palavra | Rodar num campo sem radicalização; no teste, sobre os radicais, não achou nada |
| Trigramas | Semelhança por trechos de três letras | Bom para nomes curtos e um erro; ruim com vários erros e palavras comuns |
| "Você quis dizer" (suggest) | Sugere a palavra mais próxima que existe no índice | Mostrar a sugestão em vez de trocar a busca em silêncio |
| Fonética | Agrupa palavras que soam parecido | Útil para nomes de pessoas |
Uma combinação comum: buscar primeiro sem tolerância a erros, no campo com radicalização; se vier pouco ou nada, buscar com tolerância no campo sem radicalização. No teste, essa combinação devolveu os tênis de corrida para "tenys corida".
Manter o índice igual aos dados
Com o PostgreSQL, a coluna tsvector gerada pelo próprio banco nunca diverge da linha. Com um motor separado, o índice é uma cópia:
| Forma | Atraso | Riscos |
|---|---|---|
| A aplicação grava no banco e no índice | Quase nenhum | Uma das duas gravações falha, e os dois divergem |
| Fila de saída (outbox) e um consumidor | Segundos | Nenhuma perda, se o consumidor for idempotente |
| Captura de mudanças (CDC) do banco | Segundos | Infraestrutura a mais (Debezium, Kafka) |
| Reindexação periódica | Horas | Simples; serve para catálogos que mudam pouco |
Mesmo com a cópia em dia, o motor demora para mostrar o que foi gravado: no teste, um produto novo só apareceu na busca 1,5 segundo depois de gravado. Telas que gravam e logo em seguida buscam o próprio registro precisam ler do banco principal, ou pedir que o índice seja atualizado (refresh) nessa gravação específica.
E tenha sempre um caminho para reconstruir o índice do zero a partir do banco: mudanças de analisador exigem reindexar tudo. No teste, a reindexação dos 500 mil produtos com um campo novo levou 9 segundos.
Resumo da decisão
- PostgreSQL com
tsvector,unaccente GIN para a maioria das buscas; motor dedicado quando a busca é o centro do produto. - Análise testada com as palavras do domínio: acentos, radicais, palavras vazias e sinônimos.
- Pesos por campo e ordenação por relevância.
- Tolerância a erros num campo sem radicalização, como segunda tentativa.
- Índice separado como cópia, sincronizado por fila de saída ou CDC, e reconstruível do zero.
O próximo nível mostra a busca no PostgreSQL nas cinco linguagens e a configuração do OpenSearch testada.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Escolher a busca do meu sistema
Quero decidir como implementar a busca no meu sistema. Pergunte o que é buscado, o volume, quem busca (equipe interna ou clientes), se há erros de digitação, filtros e sugestões, e qual banco eu uso. Compare para o meu caso: PostgreSQL com tsvector, PostgreSQL com pg_trgm, OpenSearch ou Elasticsearch, Meilisearch e um serviço gerenciado. Recomende uma opção e explique o que eu ganharia e perderia com as outras.
Melhorar a qualidade da busca
A busca do meu sistema funciona, mas os usuários reclamam dos resultados. Pergunte qual tecnologia eu uso, peça exemplos de buscas com resultados ruins e como os campos estão configurados. Me guie: 1. Montar um conjunto de buscas de teste com os resultados esperados. 2. Conferir a análise do texto com as palavras do domínio. 3. Ajustar pesos, sinônimos e a tolerância a erros. 4. Registrar as buscas sem resultado e as buscas abandonadas. 5. Medir antes e depois de cada mudança.
Sincronizar um motor de busca
Vou usar um motor de busca separado do banco principal. Pergunte qual banco e qual motor eu uso, quantos registros mudam por dia e com que atraso a busca pode ficar. Compare gravação dupla, fila de saída, captura de mudanças (CDC) e reindexação periódica. Monte comigo a opção escolhida, com o tratamento de falhas, a reconstrução do índice do zero e a troca de analisador sem parar a busca (índice novo e um apelido).