Full-Text Search no PostgreSQL
tsvector, tsquery e idioma
to_tsvector/to_tsquery com configuração portuguese e busca composta.
Nesta aula você vai
- Gerar tsvector e tsquery com configuração portuguese
- Escrever predicados @@ para busca composta
- Escolher entre to_tsquery, plainto_tsquery e websearch_to_tsquery
tsvector, tsquery e idioma
Objetivos
Nesta aula você vai:
- Usar
to_tsvectore variantes detsqueryem português - Montar buscas compostas com o operador
@@ - Entender por que a configuração de idioma precisa ser a mesma no documento e na consulta
Introdução
No PostgreSQL, full-text gira em torno de dois tipos: tsvector (documento analisado) e tsquery (consulta analisada). O operador @@ pergunta se o documento satisfaz a consulta. A configuração 'portuguese' define stopwords e stemming — e deve ser consistente nos dois lados.
Usaremos a Estante Norte (obras) e o TicketFlow (tickets) como corpora.
Conteúdo
Documento: tsvector
SELECT to_tsvector(
'portuguese',
'Manual avançado de PostgreSQL para times de plataforma'
);
Resultado ilustrativo:
'avanc':2 'manual':1 'plataforma':7 'postgresql':4 'tim':6
Criar coluna persistida (recomendado em produção):
ALTER TABLE obras
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('portuguese', coalesce(titulo, '')), 'A') ||
setweight(to_tsvector('portuguese', coalesce(subtitulo, '')), 'B') ||
setweight(to_tsvector('portuguese', coalesce(sinopse, '')), 'C')
) STORED;
(Pesos setweight serão aprofundados em aula própria; aqui já adiantamos o hábito de separar campos.)
Consulta: tsquery
-- Operadores explícitos
SELECT to_tsquery('portuguese', 'postgresql & plataforma');
-- Texto livre do usuário (AND implícito entre termos)
SELECT plainto_tsquery('portuguese', 'postgresql para plataforma');
-- Estilo caixa de busca web (aspas, OR com or, -exclusão) em versões recentes
SELECT websearch_to_tsquery('portuguese', 'postgresql plataforma -mysql');
| Função | Quando usar |
|---|---|
to_tsquery |
Você controla operadores (&, |, !, <->) |
plainto_tsquery |
Input de usuário simples → AND de termos |
phraseto_tsquery |
Frase com posições adjacentes |
websearch_to_tsquery |
UX parecida com buscadores web |
Predicado @@
SELECT id, titulo
FROM obras
WHERE search_vector @@ plainto_tsquery('portuguese', 'redes neurais')
LIMIT 20;
Busca composta com proximidade (frase):
SELECT id, titulo
FROM obras
WHERE search_vector @@ phraseto_tsquery('portuguese', 'aprendizado de máquina')
LIMIT 20;
TicketFlow:
SELECT id, assunto
FROM tickets
WHERE search_vector @@ to_tsquery('portuguese', 'timeout & gateway & pagamento')
AND status = 'aberto'
ORDER BY aberto_em DESC
LIMIT 25;
Idioma inconsistente: o bug silencioso
-- ERRADO: documento em portuguese, consulta em english (stemming/stopwords divergem)
SELECT to_tsvector('portuguese', 'ferramentas elétricas')
@@ to_tsquery('english', 'ferramentas');
Sempre alinhe a regconfig. Em aplicações, centralize a constante 'portuguese' (ou a regconfig do tenant) num único módulo.
-- Inspecionar configurações disponíveis
SELECT cfgname FROM pg_ts_config WHERE cfgname IN ('portuguese', 'simple', 'english');
Problema comum e solução
Problema: to_tsquery('portuguese', 'aprendizado de máquina') falha ou se comporta mal porque de é stopword e a sintaxe de operadores fica inválida.
Solução: para input humano, prefira plainto_tsquery ou websearch_to_tsquery. Reserve to_tsquery para strings montadas pela aplicação com operadores já validados.
Como analisar
SELECT * FROM ts_debug('portuguese', 'Redes neurais artificiais aplicadas');
EXPLAIN (ANALYZE, BUFFERS)
SELECT id FROM obras
WHERE search_vector @@ plainto_tsquery('portuguese', 'redes neurais artificiais')
LIMIT 20;
Se ainda não houver índice GIN, espere Seq Scan — tema da próxima aula. Mesmo assim, valide que tokens e query fazem sentido antes de culpar o índice.
Resumo
tsvector= documento analisado;tsquery= consulta analisada;@@conecta os dois- Use a mesma regconfig (
portuguese) nos dois lados plainto/websearchpara humanos;to_tsquerypara operadores controlados- Coluna
search_vectorpersistida prepara o terreno para o índice GIN