Voltar ao blog
BuscaPostgreSQLElasticsearchFull-text searchSaaS

Busca que encontra: full-text search com PostgreSQL e Elasticsearch

2026-09-23
Busca que encontra: full-text search com PostgreSQL e Elasticsearch

Busca é uma daquelas funcionalidades que parecem triviais até você tentar implementar direito. O usuário digita "camisa azul" e espera encontrar "camiseta na cor azul", tolerando que ele tenha escrito "camissa", e quer os resultados mais relevantes no topo, não numa ordem aleatória. Um LIKE '%termo%' no banco entrega o oposto de tudo isso: é lento, ignora relevância, não entende variações de palavra e não perdoa um erro de digitação. Busca de verdade é um problema de linguagem, não de comparação de strings.

Este guia trata de full-text search a sério. Como o PostgreSQL resolve busca textual bem mais do que a maioria imagina, quando isso é suficiente, e em que ponto vale migrar para um motor dedicado como o Elasticsearch ou o OpenSearch. O foco aqui é busca e relevância, um assunto distinto de escalar o banco com índices e particionamento.

Por que LIKE não é busca

O LIKE '%palavra%' é a primeira coisa que todo mundo tenta, e a primeira que quebra em produção. Ele tem três defeitos fatais como mecanismo de busca:

  • É lento. Um curinga no início do padrão impede o uso de índices comuns, então o banco varre a tabela inteira.
  • É literal. "correndo" não casa com "corre", "ratos" não casa com "rato". Ele não entende que são a mesma palavra em formas diferentes.
  • Não ordena por relevância. Um resultado que menciona o termo uma vez vem junto com outro que o menciona dez vezes, sem distinção.

Busca precisa entender palavras e suas variações, e precisa rankear. É aí que entra o full-text search.

Full-text search no PostgreSQL

O que muita gente não sabe é que o PostgreSQL tem um motor de busca textual embutido, capaz e maduro. Ele funciona convertendo texto em uma forma otimizada para busca, o tsvector, e consultas em uma forma estruturada, o tsquery.

tsvector e tsquery

O tsvector transforma um texto em uma lista de lexemas, as palavras reduzidas à sua raiz, sem ruído. O processo, chamado stemming, faz "correndo", "correu" e "corre" virarem o mesmo lexema, e descarta stop words (palavras vazias como "de", "a", "o"). O tsquery representa os termos buscados, também normalizados, com operadores lógicos.

-- Documento vira tsvector; busca vira tsquery
SELECT titulo
FROM artigos
WHERE to_tsvector('portuguese', titulo || ' ' || corpo)
      @@ to_tsquery('portuguese', 'escalabilidade & banco');

O operador @@ verifica se o documento casa com a consulta. Como os dois lados são normalizados pelo mesmo dicionário de idioma, "escalabilidade" encontra "escalável", e a busca entende português de verdade, não apenas caracteres.

Ranking de relevância

Encontrar não basta; é preciso ordenar por relevância. O PostgreSQL oferece ts_rank, que pontua cada resultado conforme a frequência e a proximidade dos termos no documento. Você ordena por essa pontuação e entrega os melhores primeiro, exatamente o que o LIKE nunca fez.

Índice GIN: o que torna tudo rápido

Sem índice, o banco recalcularia o tsvector de cada linha a cada busca. O índice GIN (Generalized Inverted Index) resolve isso guardando um mapa invertido de lexema para documentos, a mesma ideia que motores de busca dedicados usam. Com um GIN sobre a coluna de busca, consultas full-text ficam rápidas mesmo em milhões de registros. O padrão é materializar o tsvector em uma coluna própria e indexá-la com GIN.

Quando o PostgreSQL basta

Para uma parcela enorme dos produtos, o full-text search do PostgreSQL é mais que suficiente, e tem uma vantagem decisiva: os dados já estão lá. Você não precisa manter um segundo sistema sincronizado. O Postgres resolve bem quando:

  • O volume de documentos está na casa de milhares a poucos milhões.
  • A busca é sobre dados que já vivem no banco.
  • Você precisa de stemming, ranking e busca por idioma, mas não de recursos avançados de relevância.
  • Simplicidade operacional importa mais que sofisticação de busca.

Adotar um motor de busca dedicado cedo demais é um erro clássico: você dobra a complexidade da infraestrutura para resolver um problema que o banco que você já opera resolveria.

Quando migrar para Elasticsearch ou OpenSearch

Há um ponto em que a busca vira um produto dentro do produto, e um motor dedicado como o Elasticsearch ou o OpenSearch passa a compensar. Esses sistemas são construídos do zero para busca e trazem recursos que o Postgres não entrega bem:

  • Tolerância a erros de digitação (fuzzy search). "camissa" encontra "camisa" por distância de edição. Fundamental em busca voltada ao usuário final.
  • Relevância avançada e ajustável. Controle fino de pesos por campo, sinônimos, boosting e modelos de pontuação sofisticados.
  • Facetas e agregações. Aqueles filtros de "marca", "cor", "faixa de preço" que se atualizam com a busca, calculados em tempo real.
  • Autocomplete e sugestões enquanto o usuário digita.
  • Escala horizontal para dezenas de milhões de documentos e alto volume de consultas, distribuída entre nós.

O custo é real: você passa a operar um sistema distribuído à parte e precisa sincronizar os dados do banco para o índice de busca, lidando com o atraso e a consistência dessa replicação.

Postgres vs. motor dedicado, lado a lado

Critério PostgreSQL full-text Elasticsearch / OpenSearch
Infraestrutura Já existe (o próprio banco) Sistema distribuído à parte
Sincronização de dados Nenhuma Necessária, do banco ao índice
Typo tolerance (fuzzy) Limitada Nativa e forte
Facetas e agregações Básicas Avançadas
Autocomplete e sugestões Manual Nativo
Escala de documentos Milhares a milhões Dezenas de milhões e além
Complexidade operacional Baixa Alta

Um caminho de evolução saudável

A trajetória mais sensata não começa com Elasticsearch. Ela começa com o que você já tem e sobe de degrau quando a dor aparece:

  • Comece no PostgreSQL com tsvector, tsquery, ts_rank e índice GIN. Resolve a maioria dos casos com zero infraestrutura nova.
  • Meça a experiência de busca real. Os usuários reclamam de resultados irrelevantes? Erram a digitação e não encontram nada? Pedem filtros que se atualizam?
  • Migre para um motor dedicado quando o produto exigir relevância fina, fuzzy, facetas ou escala que o Postgres não acompanha, e não antes disso.

Busca é uma funcionalidade que os usuários julgam em segundos: ou encontra o que eles querem, ou eles vão embora. A boa notícia é que você raramente precisa de um sistema complexo para começar bem. O PostgreSQL leva você surpreendentemente longe, e o motor dedicado entra quando a busca deixa de ser um detalhe e vira um diferencial competitivo.


Sua busca frustra os usuários com resultados fracos ou lentos? Na ConstructorTI, desenhamos busca do jeito certo para o seu estágio, do full-text nativo do PostgreSQL bem indexado a um motor dedicado com relevância, fuzzy e facetas quando o produto pede. Se o seu campo de busca hoje entrega menos do que deveria, fale com a nossa engenharia e receba um diagnóstico da sua camada de busca.