Noções Básicas de Busca de Texto Completo
O PostgreSQL oferece busca de texto completo nativa com tsvector, tsquery e índices GIN. Para muitos produtos, ele substitui um cluster Elasticsearch separado quando o volume de dados, as necessidades de classificação e a equipe operacional favorecem um único banco de dados.
Receita
-- Tabela mínima de documentos pesquisáveis
CREATE TABLE articles (
id bigserial PRIMARY KEY,
title text NOT NULL,
body text NOT NULL,
published timestamptz DEFAULT now()
);
-- Coluna tsvector gerada (PostgreSQL 12+)
ALTER TABLE articles
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(title, '')), 'A') ||
setweight(to_tsvector('english', coalesce(body, '')), 'B')
) STORED;
CREATE INDEX articles_search_idx ON articles USING gin (search_vector);
-- Busca
SELECT id, title, ts_rank(search_vector, query) AS rank
FROM articles, plainto_tsquery('english', 'postgresql replication') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;Quando usar isso: Busca de texto completo sobre dados relacionais, tamanho de corpus moderado (milhões de linhas) e a equipe deseja consistência transacional sem escrita dupla para um mecanismo de busca.
Exemplo de Trabalho
INSERT INTO articles (title, body) VALUES
('Streaming Replication', 'Envio de WAL físico para standbys para HA.'),
('Logical Replication', 'Alterações em nível de linha para atualizações e fan-out.'),
('Full-Text Search', 'Classificação tsvector integrada com índices GIN.');
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, title,
ts_rank_cd(search_vector, query) AS rank,
ts_headline('english', body, query,
'MaxFragments=2, MaxWords=20, MinWords=8') AS snippet
FROM articles,
websearch_to_tsquery('english', 'replication OR "full text"') query
WHERE search_vector @@ query
ORDER BY rank DESC;O que isso demonstra:
- Campos com peso (
Atítulo,Bcorpo) influenciam a classificação websearch_to_tsqueryaceita sintaxe de busca amigável ao usuáriots_headlineconstrói trechos de resultados em SQLEXPLAINconfirma o uso do índice GIN em@@
Mergulho Profundo
FTS Integrado vs Busca Externa
| Fator | FTS do Postgres | Elasticsearch/OpenSearch |
|---|---|---|
| Ops | Um cluster que você já executa | Cluster dedicado, ajuste de JVM, atualizações |
| Consistência | Mesma transação que os dados da linha | Escrita dupla ou atraso de CDC |
| Classificação | ts_rank, pesos personalizados | Analisadores ricos, plugins de aprendizado para classificação |
| Teto de escala | Milhões a bilhões baixos com ajuste | Bilhões de documentos, agregações pesadas |
| Facetas | GROUP BY SQL em colunas | Agregações nativas |
Padrão ADR: Comece com o FTS do Postgres quando o corpus couber em uma única instância, as consultas forem por palavra-chave + levemente imprecisas e a equipe não puder gerenciar o Elasticsearch.
Opte pelo OpenSearch quando: busca sub-100ms em grande escala, analisadores complexos por idioma, fragmentação pesada de autocompletar ou equipe SRE de busca dedicada.
Objetos Principais
tsvector- Bolsa de léxemas normalizada com pesos/posições opcionais.tsquery- Expressão de busca analisada com operadores (&,|,!,<->frase).- Operador de correspondência
@@- Teste booleano de correspondência; o índice GIN o acelera. - Dicionários - Stemming, palavras irrelevantes, sinônimos (veja o artigo tsvector).
Configuração de Idioma
SELECT cfgname FROM pg_ts_config;
-- comum: english, simple, french
SELECT to_tsvector('simple', 'Running runs RUN');
-- simple: sem stemming, bom para SKUs e códigos de produtosUse simple para identificadores; use english (ou específico do local) para prosa.
Armadilhas
- Sem índice em
to_tsvector()no WHERE - Expressão não armazenada significa varredura sequencial. Correção: Coluna gerada armazenada outsvectormantido por trigger. - Consultas OR explodem os resultados -
websearch_to_tsquerycom muitos termos OR. Correção: Exigir limite mínimo de classificação ou padrões pesados em AND. - Caixa e diacríticos - FTS normaliza a caixa; acentos dependem do dicionário. Correção: Extensão
unaccentou colação ICU para busca sensível ao local. - Busca de subcadeia - FTS corresponde a léxemas, não
LIKE '%foo%'. Correção:pg_trgmpara subcadeia; combinar em consultas híbridas. - Vetores desatualizados após carga em massa - Colunas geradas são atualizadas automaticamente; as baseadas em trigger podem não ser. Correção: Verificar o caminho de manutenção após ETL.
- Inveja do Elasticsearch - Recriar índice invertido para cada ajuste de esquema. Correção: Iterar a classificação em SQL antes de adotar um segundo sistema.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Elasticsearch/OpenSearch | Escala enorme, relevância avançada | Equipe pequena, forte acoplamento transacional |
Apenas pg_trgm | Prefixo de SKU, subcadeia de e-mail | Prosa natural com stemming |
| SaaS Externo (Algolia) | Lançamento rápido, relevância hospedada | Blocos de residência de dados de terceiros |
| Híbrido com pgvector | Semântico + palavra-chave (ver artigo híbrido) | Site puramente de palavras-chave sem embeddings |
FAQs
O FTS do Postgres é bom o suficiente para busca em SaaS?
Frequentemente sim, até milhões de documentos com GIN e pesos adequados. Faça benchmark do p95 antes de adotar o Elasticsearch.plainto_tsquery vs websearch_to_tsquery?
`plainto_tsquery` junta termos com AND. `websearch_to_tsquery` suporta aspas e termos de menos como caixas de busca na web.Preciso de Elasticsearch para autocompletar?
Nem sempre. `pg_trgm` em consultas de prefixo ou tabelas de prefixo materializadas funcionam para tráfego moderado.Quão grandes podem ser os índices GIN?
Aproximadamente comparáveis à contagem de tokens do corpus. Monitore com `pg_relation_size`.Conteúdo multilíngue?
Use a configuração `simple` mais uma coluna de idioma, ou colunas `tsvector` por idioma.Realce na API?
`ts_headline` em SQL ou retorne posições através da análise de `tsvector` na aplicação.Impacto do atraso de replicação?
FTS lê índices locais na réplica; sem atraso especial além da replicação de streaming normal.Limites do FTS Postgres gerenciado?
Mesmo motor; verifique a lista de permissões de extensão para `unaccent`. Nenhum irmão gerenciado no estilo Elasticsearch no RDS.Segurança?
FTS herda políticas RLS da tabela base. Nenhuma camada separada de ACL de busca.Notas de atualização PG 18?
FTS principal estável; observe o comportamento de `websearch_to_tsquery` nas notas de lançamento para ajustes do analisador.Relacionados
- tsvector & tsquery - dicionários e classificação
- Índices GIN para FTS - construção e manutenção de índices
- Busca Híbrida - combine com pgvector
- GIN e GiST - visão geral do método de acesso
- Melhores Práticas de Busca de Texto Completo - checklist operacional
Versões da pilha: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PostGIS 3.5+, pgbouncer 1.x e Patroni 3.x.