🌐 Detecting your location…

Como construir um recurso de pesquisa de texto completo com PostgreSQL em 2026

⏱️6 min read  ·  1,251 words

Adicionar pesquisa ao seu aplicativo geralmente faz com que os desenvolvedores recorram ao Elasticsearch, mas o PostgreSQL possui uma poderosa pesquisa de texto completo integrada que atende à maioria das necessidades sem infraestrutura extra. Este guia mostra como construir pesquisa de produção com PostgreSQL, desde o básico até classificação e destaque.

Por que usar o PostgreSQL para pesquisa?

  • Sem infraestrutura extra: Use seu banco de dados existente — nenhum mecanismo de pesquisa separado para executar e sincronizar
  • Bom o suficiente para a maioria dos aplicativos: Lida com milhões de documentos com indexação adequada
  • Consistência transacional: Atualizações de índice de pesquisa com seus dados, sem atraso de sincronização
  • Recursos avançados: Classificação, lematização, destaque, vários idiomas integrados

Noções básicas sobre tsvector e tsquery

-- tsvector: a processed, searchable representation of text
-- tsquery: a search query

SELECT to_tsvector('english', 'The quick brown foxes are jumping');
-- 'brown':3 'fox':4 'jump':6 'quick':2
-- Note: stemming (foxes->fox, jumping->jump) and stopword removal (the, are)

SELECT to_tsvector('english', 'The quick brown foxes')
       @@ to_tsquery('english', 'fox');
-- true - matches because 'foxes' stems to 'fox'

Pesquisa básica de texto completo

-- Search articles by title and content
SELECT id, title
FROM articles
WHERE to_tsvector('english', title || ' ' || content)
      @@ to_tsquery('english', 'postgresql & search');
-- & means AND, | means OR, ! means NOT

-- plainto_tsquery handles user input safely (treats as AND)
SELECT id, title
FROM articles
WHERE to_tsvector('english', title || ' ' || content)
      @@ plainto_tsquery('english', 'postgresql search tutorial');

-- websearch_to_tsquery supports Google-like syntax (quotes, OR, -)
SELECT id, title
FROM articles
WHERE to_tsvector('english', content)
      @@ websearch_to_tsquery('english', '"full text" search -elasticsearch');

Adicionando uma coluna tsvector pré-computada com índice

O cálculo do tsvector em cada consulta é lento. Armazene-o em uma coluna com um índice GIN:

-- Add a generated tsvector column (auto-updates with the data)
ALTER TABLE articles ADD COLUMN search_vector tsvector
  GENERATED ALWAYS AS (
    to_tsvector('english', coalesce(title, '') || ' ' || coalesce(content, ''))
  ) STORED;

-- Create a GIN index for fast searching
CREATE INDEX articles_search_idx ON articles USING GIN (search_vector);

-- Now searches are fast and use the index
SELECT id, title FROM articles
WHERE search_vector @@ plainto_tsquery('english', 'postgresql search');

Classificação dos resultados por relevância

-- ts_rank scores how well each row matches
SELECT id, title,
       ts_rank(search_vector, query) AS rank
FROM articles, plainto_tsquery('english', 'postgresql search') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;

-- Weight title matches higher than content
ALTER TABLE articles ADD COLUMN search_vector tsvector
  GENERATED ALWAYS AS (
    setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
    setweight(to_tsvector('english', coalesce(content, '')), 'B')
  ) STORED;
-- 'A' weight (title) ranks higher than 'B' weight (content)

Destacando termos correspondentes

-- ts_headline returns snippets with matched terms highlighted
SELECT id, title,
       ts_headline('english', content, query,
         'StartSel=, StopSel=, MaxWords=35, MinWords=15') AS snippet
FROM articles, plainto_tsquery('english', 'postgresql') query
WHERE search_vector @@ query
ORDER BY ts_rank(search_vector, query) DESC;
-- snippet contains the relevant excerpt with  around matches

Usando-o no Node.js

app.get('/search', async (req, res) => {
  const q = req.query.q;
  if (!q) return res.json({ results: [] });

  const result = await db.query(`
    SELECT id, title,
           ts_headline('english', content, query,
             'StartSel=,StopSel=,MaxWords=35') AS snippet,
           ts_rank(search_vector, query) AS rank
    FROM articles, plainto_tsquery('english', $1) query
    WHERE search_vector @@ query
    ORDER BY rank DESC
    LIMIT 20
  `, [q]);

  res.json({ results: result.rows });
});

Adicionando pesquisa difusa/tolerante a erros de digitação

-- Enable the pg_trgm extension for fuzzy matching (handles typos)
CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- Trigram index for similarity search
CREATE INDEX articles_title_trgm ON articles USING GIN (title gin_trgm_ops);

-- Find titles similar to a (possibly misspelled) query
SELECT title, similarity(title, 'postgres serch') AS sim
FROM articles
WHERE title % 'postgres serch'   -- % is the similarity operator
ORDER BY sim DESC;
-- Matches 'PostgreSQL search' despite the typo

Quando usar o Elasticsearch

A pesquisa de texto completo do PostgreSQL lida bem com a maioria dos aplicativos. Considere o Elasticsearch quando precisar:

  • Escala muito grande (centenas de milhões de documentos) com ajuste de relevância complexo
  • Recursos avançados: pesquisa facetada, agregações complexas, pesquisa geográfica em escala
  • Pesquise em muitas fontes de dados além do seu banco de dados
  • Análise em tempo real de dados de pesquisa

Para a maioria dos aplicativos – blogs, comércio eletrônico, sites de conteúdo, SaaS – a pesquisa PostgreSQL é mais simples, não requer infraestrutura extra e é mais que suficiente.

Perguntas Frequentes

P: A pesquisa do PostgreSQL é boa o suficiente ou preciso do Elasticsearch?
R: Para a maioria das aplicações, a pesquisa de texto completo do PostgreSQL é mais que suficiente — ela lida com milhões de documentos com classificação, destaque e lematização. Use o Elasticsearch apenas para pesquisas facetadas avançadas em grande escala ou em muitas fontes de dados. Não adicione a infraestrutura do Elasticsearch prematuramente.

P: Como faço para lidar com erros de digitação na pesquisa?
R: Use a extensão pg_trgm para correspondência de similaridade de trigramas, que tolera erros de digitação e ortografia. Combine-o com a pesquisa de texto completo – use tsvector para a pesquisa principal e similaridade de trigrama como substituto ou para preenchimento automático/sugestões.

P: Por que minha pesquisa de texto completo está lenta?
R: Você provavelmente está calculando to_tsvector em todas as consultas sem índice. Adicione uma coluna tsvector gerada armazenada com um índice GIN. Isso pré-calcula a representação pesquisável e torna as pesquisas rápidas mesmo em tabelas grandes.

P: plainto_tsquery vs to_tsquery vs websearch_to_tsquery?
A: to_tsquery requer sintaxe de operador (&, |) — bom para consultas programáticas. plainto_tsquery lida com a entrada simples do usuário (trata as palavras como AND) — seguro para caixas de pesquisa do usuário. websearch_to_tsquery suporta sintaxe semelhante à do Google (aspas, OR, -) — melhor para pesquisas voltadas ao usuário.

P: Posso pesquisar em vários idiomas?
R: Sim — o PostgreSQL possui configurações de pesquisa de texto para vários idiomas (inglês, espanhol, francês, etc.) que lidam com lematização e palavras irrelevantes por idioma. Especifique o idioma em to_tsvector/to_tsquery. Armazene uma coluna de idioma se o seu conteúdo for multilíngue.

Conclusão

A pesquisa de texto completo integrada do PostgreSQL atende às necessidades de pesquisa da maioria dos aplicativos sem a complexidade de um mecanismo de pesquisa separado. Use umcoluna tsvector gerada armazenada com um índice GIN para desempenho, ts_rank para classificação de relevância, ts_headline para destaque e pg_trgm para tolerância a erros de digitação. Dê maior peso aos campos importantes (como títulos) com setweight e use plainto_tsquery ou websearch_to_tsquery para entrada segura do usuário. Reserve o Elasticsearch para pesquisa facetada avançada ou em grande escala. Para blogs, comércio eletrônico, sites de conteúdo e a maioria dos aplicativos SaaS, a pesquisa PostgreSQL é mais simples, não precisa de infraestrutura extra, permanece consistente com seus dados e tem um desempenho excelente — uma ótima opção padrão antes de recorrer a uma infraestrutura de pesquisa dedicada.

MD Rafikul Islam

Written by

MD Rafikul Islam is a software developer and the editor of TechPulse. He writes about developer tooling, hardware, and the practical decisions that come up in day-to-day engineering work — which laptop to buy, which framework to commit to, why a build broke at 2am. He tests the tools he writes about and says plainly when something is not worth the money. Corrections and corrections requests are welcome at rony.yf25@gmail.com.

✍️ Leave a Comment

Your email address will not be published. Required fields are marked *

🌐 Read in:🇬🇧 English🇩🇪 Deutsch🇧🇷 Português🇸🇦 العربية🇮🇳 हिन्दी🇧🇩 বাংলা