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.
📋 Table of Contents
- Por que usar o PostgreSQL para pesquisa?
- Noções básicas sobre tsvector e tsquery
- Pesquisa básica de texto completo
- Adicionando uma coluna tsvector pré-computada com índice
- Classificação dos resultados por relevância
- Destacando termos correspondentes
- Usando-o no Node.js
- Adicionando pesquisa difusa/tolerante a erros de digitação
- Quando usar o Elasticsearch
- Perguntas Frequentes
- Conclusão
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.
🔗 Share this article
✍️ Leave a Comment