O recurso de busca de texto completo do PostgreSQL responde a uma pergunta enganosamente simples: este documento contém estas palavras, em algum sentido linguístico significativo, e quão bem ele corresponde em comparação com outros documentos?
Nos bastidores, essa pergunta é respondida por um pipeline de busca de texto que analisa, classifica e normaliza palavras antes que qualquer correspondência ou ranqueamento ocorra, o que diferencia a busca de texto completo de uma simples comparação de strings.
Esta página constrói o modelo conceitual por trás desse pipeline: como o texto bruto se torna um tsvector, por que a correspondência usa lemas em vez de substrings e como o ranqueamento transforma uma correspondência booleana em um conjunto de resultados ordenado.
A busca de texto completo converte o texto do documento e o texto da consulta em unidades linguísticas normalizadas chamadas lemas, depois corresponde e classifica os documentos com base nessas unidades, em vez de caracteres brutos.
Por que Importa: Os usuários esperam que "correndo", "corre" e "correu" correspondam a uma busca por "correr", e a correspondência de string simples não pode fazer isso sem uma etapa explícita de normalização linguística.
Quando Usar: Recorra a este modelo mental quando uma consulta de busca não encontrar documentos que você espera que ela encontre, ou quando a ordem de ranqueamento parecer errada e você precisar entender o porquê.
Limitações / Compensações: A mesma normalização que torna a busca tolerante a formas de palavras também a torna cega a substrings exatas, portanto, a busca de texto completo não pode substituir LIKE para correspondência de prefixo ou fragmentos de código.
Tópicos Relacionados: Mecânica de tsvector/tsquery, detalhes internos do índice GIN, busca híbrida palavra-chave-mais-vetor, FTS do Postgres versus Elasticsearch.
A busca de texto completo começa com um parser, um componente que divide o texto bruto em um fluxo de tokens classificados, em vez de uma lista plana de palavras.
O parser não apenas divide por espaços em branco; ele reconhece categorias como palavras, números, endereços de e-mail, URLs e compostos hifenizados, e etiqueta cada token com sua categoria.
Cada categoria de token é então roteada através de um dicionário, uma etapa de normalização plugável que pode reduzir uma palavra à sua raiz (stemming), descartá-la como palavra irrelevante (stop word) ou dobrá-la para uma grafia canônica.
A saída dessa etapa de dicionário é um lema (lexeme), a unidade normalizada que a busca de texto completo realmente indexa e corresponde. É por isso que "databases" e "database" se normalizam para o mesmo lema sob a configuração em inglês.
Um tsvector é simplesmente a coleção ordenada e deduplicada de lemas produzida para um pedaço de texto, carregando opcionalmente rótulos de peso e informações de posição para cada lema.
Um tsquery passa pelo mesmo pipeline de análise e normalização, o que explica exatamente por que uma string bruta construída manualmente pode produzir uma consulta que nunca corresponde a nada significativo.
A correspondência em si, expressa com o operador @@, é um teste booleano se a expressão de lemas da consulta é satisfeita pelos lemas presentes no tsvector do documento.
Como a correspondência ocorre em lemas normalizados em vez de texto bruto, a busca de texto completo é fundamentalmente uma operação linguística, não uma operação de comparação de caracteres.
O pipeline de análise e normalização é executado em uma ordem estrita toda vez que um tsvector ou tsquery é construído, e entender essa ordem explica a maioria das perguntas do tipo "por que isso não correspondeu".
Primeiro, o parser tokeniza e classifica o texto bruto em tokens tipados; segundo, cada tipo de token é mapeado para uma lista ordenada de dicionários através de uma configuração de busca de texto como english ou simple; terceiro, o primeiro dicionário nessa lista que reconhece o token o normaliza, o descarta como palavra irrelevante ou o passa para o próximo dicionário na cadeia.
É por isso que trocar configurações muda os resultados sem alterar uma única linha de dados: simple pula o stemming inteiramente e é adequado para códigos de produto, enquanto english faz stemming agressivamente e se adequa à prosa.
Posições também importam, porque um tsvector retém onde cada lema ocorreu no texto original, o que possibilita operadores de frase como <-> no momento da consulta.
Pesos se sobrepõem às posições: um lema pode ser rotulado A, B, C ou D, tipicamente para marcar que veio de um título versus um corpo versus metadados, e as funções de ranqueamento leem esses rótulos para influenciar as pontuações.
-- As mesmas palavras, duas configurações, dois conjuntos de lemas diferentesSELECT to_tsvector('simple', 'Running the databases');-- 'databases':3 'running':1 'the':2SELECT to_tsvector('english', 'Running the databases');-- 'databas':3 'run':1
O ranqueamento é onde a busca de texto completo passa de um sim/não booleano para um conjunto de resultados ordenado, e o PostgreSQL oferece duas funções de ranqueamento baseadas em diferentes teorias de relevância.
ts_rank é essencialmente uma pontuação de frequência de termo ponderada: conta quantos lemas da consulta corresponderam, considera seus pesos de posição e produz uma pontuação que trata as correspondências em qualquer lugar do documento de forma aproximadamente igual.
ts_rank_cd, a variante de densidade de cobertura, recompensa adicionalmente os documentos onde os lemas correspondentes aparecem próximos uns dos outros, com a teoria de que correspondências agrupadas indicam uma passagem mais focada e relevante.
Nenhuma das funções considera a normalização do comprimento do documento ou a raridade de termos em todo o corpus como o algoritmo BM25 de um motor de recuperação de informação completo faz, o que é uma compensação deliberada de simplicidade em troca de rodar inteiramente dentro do SQL.
O operador de correspondência @@ sozinho forçaria uma varredura sequencial em todos os tsvector das linhas, então a busca de texto completo depende de um índice GIN para tornar a correspondência rápida em escala, e o truque conceitual é o mesmo que todo motor de busca de índice invertido usa.
Um índice GIN inverte a relação entre documentos e lemas: em vez de armazenar "este documento contém estes lemas", ele armazena "este lema aparece nestes documentos", o que transforma uma consulta de correspondência em uma consulta rápida em vez de uma varredura por linha.
Essa inversão é precisamente o que distingue a busca de texto completo de LIKE '%term%' ou uma expressão regular, ambas as quais devem inspecionar os bytes reais de cada linha candidata porque nenhuma delas possui uma estrutura pré-construída conectando termos a linhas.
LIKE e regex são exatos em substring e sensíveis a maiúsculas/minúsculas/acentos por padrão, enquanto a busca de texto completo é linguisticamente normalizada e ciente da posição, então as duas técnicas resolvem problemas genuinamente diferentes e são frequentemente combinadas em vez de escolhidas exclusivamente.
Motores de busca externos como Elasticsearch são construídos sobre o mesmo conceito de índice invertido e frequência de termo, mas adicionam um ecossistema de analisadores muito mais rico, particionamento distribuído e modelos de relevância mais sofisticados, ao custo de executar e operar um segundo sistema fora do banco de dados transacional.
O ponto de decisão prático geralmente não é "qual técnica é mais inteligente", mas sim "o teto de relevância e escala de um índice invertido dentro do banco de dados atende às necessidades do produto sem pagar por um segundo cluster".
Abordagem
Força
Fraqueza
Melhor Ajuste
Busca de texto completo (tsvector/GIN)
Normalização linguística, consistência transacional, sem sistema adicional
Menor ajuste de relevância do que motores de busca dedicados
Busca de prosa dentro de um aplicativo existente baseado em Postgres
LIKE / regex
Correspondência exata de substring ou padrão, sem surpresas de normalização
Sem stemming, sem ranqueamento, baixo desempenho sem índices especializados
Códigos, IDs, consultas de fragmentos exatos
Motor de busca externo (Elasticsearch/OpenSearch)
Analisadores ricos, escala distribuída, ajuste avançado de relevância
Cluster separado para operar, atraso de consistência de escrita dupla ou CDC
Grandes corpus com infraestrutura de busca dedicada
Híbrido (FTS + embeddings)
Combina precisão lexical com recuperação semântica
Dois sistemas de pontuação para reconciliar e ajustar juntos
Busca que deve capturar sinônimos e paráfrases também
"Busca de texto completo é apenas LIKE com um índice" - é um pipeline linguístico construído sobre lemas analisados e normalizados, enquanto LIKE é uma comparação de substring bruta sem qualquer normalização.
"Um tsvector armazena o texto original" - ele armazena um conjunto deduplicado e ordenado de lemas normalizados, mais pesos e posições opcionais, e as palavras originais não são recuperáveis dele em geral.
"Um ts_rank mais alto sempre significa uma correspondência melhor para o usuário" - ts_rank reflete a frequência de termos e rótulos de peso, não a relevância semântica, então um documento pode ter um ranqueamento alto enquanto perde a intenção real do usuário.
"ts_rank e ts_rank_cd sempre concordarão na ordenação" - eles codificam diferentes teorias de relevância, frequência de termos versus agrupamento de correspondências, então eles podem e produzem ordenações diferentes para a mesma consulta.
"Stemming funciona da mesma forma para todas as línguas" - cada configuração de busca de texto tem sua própria cadeia de dicionários, então o stemming, as palavras irrelevantes e até mesmo os limites de tokenização diferem por configuração.
Um token é um pedaço bruto e classificado de texto produzido pelo parser, enquanto um lema é o que resta após um dicionário normalizar, fazer stemming ou descartar esse token.
Por que buscar por "correr" também corresponde a "correndo" e "correu"?
O dicionário na configuração de busca de texto faz stemming de todas as três formas da palavra para o mesmo lema normalizado antes que qualquer coisa seja armazenada ou correspondida.
Por que a busca de texto completo não encontra uma substring como "gres" dentro de "postgres"?
A correspondência opera em lemas inteiros normalizados, não em substrings de caracteres, então fragmentos de palavras parciais exigem uma ferramenta diferente como pg_trgm ou LIKE.
O que determina se uma palavra se torna uma palavra irrelevante (stop word)?
A cadeia de dicionários para a configuração de busca de texto ativa decide, e configurações comuns como english descartam palavras de alta frequência como "the" e "and" por design.
Como a busca de texto completo sabe onde um lema estava no texto original?
O tsvector armazena inteiros de posição ao lado de cada lema, que é o que permite que operadores de frase e distância verifiquem a adjacência no momento da consulta.
Qual é a diferença prática entre ts_rank e ts_rank_cd?
ts_rank pontua com base na frequência de termos e rótulos de peso, enquanto ts_rank_cd recompensa adicionalmente documentos onde os lemas correspondentes aparecem agrupados próximos uns dos outros.
Por que a consulta precisa passar por to_tsquery em vez de uma string simples?
Um tsquery deve passar pelo mesmo pipeline de análise e normalização que o texto do documento, ou as palavras brutas da consulta nunca corresponderão aos lemas normalizados do documento.
Como a busca de texto completo é diferente de uma busca por expressão regular?
Uma expressão regular corresponde a padrões literais de caracteres sem consciência linguística, enquanto a busca de texto completo corresponde a lemas normalizados que carregam significado, independentemente da forma exata da palavra.
Por que as pessoas comparam a busca de texto completo do Postgres com o Elasticsearch?
Ambos são construídos sobre o mesmo conceito de índice invertido que conecta termos aos documentos que os contêm, mas o Elasticsearch adiciona analisadores mais ricos, escala distribuída e modelos de relevância mais avançados como um sistema separado.
A busca de texto completo entende significado ou sinônimos por conta própria?
Não, ela entende formas de palavras normalizadas através de stemming e dicionários, não significado conceitual, razão pela qual equipes a combinam com embeddings de vetor para recuperação semântica.
Por que um índice GIN torna a busca de texto completo rápida?
Ele inverte a relação de armazenamento de documento-para-lemas para lema-para-documentos, transformando uma correspondência em uma consulta direta em vez de uma varredura em todas as linhas.
Duas configurações diferentes podem produzir resultados de busca diferentes para o mesmo texto?
Sim, porque cada configuração tem suas próprias regras de parser e cadeia de dicionários, então simple e english podem normalizar a mesma entrada em conjuntos de lemas completamente diferentes.