Full-Text Search no PostgreSQL

tsvector, tsquery e idioma

to_tsvector/to_tsquery com configuração portuguese e busca composta.

Intermediário 42 min 33 pontos Leitura 0%

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_tsvector e variantes de tsquery em 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/websearch para humanos; to_tsquery para operadores controlados
  • Coluna search_vector persistida prepara o terreno para o índice GIN