Os exemplos da seção anterior mostraram a correspondência de texto completo usando constantes cadeias de caracteres simples. Esta seção mostra como procurar dados em tabela, opcionalmente usando índices.
É possível fazer uma procura de texto completo sem índice.
Uma consulta simples para mostrar o campo
title de cada linha contendo a palavra
friend no seu campo body é:
SELECT title
FROM pgweb
WHERE to_tsvector('english', body) @@ to_tsquery('english', 'friend');
Esta consulta também encontrará palavras relacionadas, como
friends e friendly,
porque são todas reduzidas ao mesmo lexema normalizado.
A consulta acima especifica que deve ser usada a configuração
english para analisar e normalizar as
cadeias de caracteres.
Como alternativa, podem ser omitidos os parâmetros de configuração:
SELECT title
FROM pgweb
WHERE to_tsvector(body) @@ to_tsquery('friend');
Esta consulta vai usar a configuração definida por default_text_search_config.
Um exemplo mais complexo é selecionar os dez documentos mais
recentes que contêm create e
table no campo title
ou no campo body:
SELECT title
FROM pgweb
WHERE to_tsvector(title || ' ' || body) @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;
Para maior clareza, foram omitidas as chamadas à função
coalesce, que seriam necessárias para encontrar
linhas contendo NULL em um dos dois campos.
Embora estas consultas funcionem sem um índice, a maioria das
aplicações vai achar esta abordagem muito lenta, exceto talvez
para procuras ad hoc ocasionais.
O uso prático da procura de texto completo geralmente requer a
criação de um índice.
Pode ser criado um índice GIN (Seção 12.9) para acelerar procuras de texto:
CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector('english', body));
Note que está sendo usada a versão de 2 argumentos da função
to_tsvector.
Em índices de expressão, somente podem ser usadas as funções de
procura de texto completo que especificam o nome da configuração
(Seção 11.7).
Isto acontece, porque o conteúdo do índice não deve ser afetado pelo
parâmetro default_text_search_config.
Se os índices forem afetados, o conteúdo do índice pode se tornar
inconsistente, porque diferentes entradas podem conter
tsvectors que foram criados com diferentes
configurações de procura de texto, e não haveria como adivinhar
qual é qual.
Seria impossível salvar e recuperar este índice corretamente.
Como foi usada a versão de dois argumentos da função
to_tsvector no índice acima, apenas uma
referência de consulta que usa a versão de dois argumentos de
to_tsvector com o mesmo nome de configuração
usará este índice. Ou seja,
WHERE to_tsvector('english', body) @@ 'a & b'
pode usar o índice, mas
WHERE to_tsvector(body) @@ 'a & b' não pode.
Isto garante que o índice será usado apenas com a mesma configuração
usada para criar as entradas do índice.
É possível configurar índices de expressão mais complexos, onde o nome da configuração é especificado por outra coluna como, por exemplo,
CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector(config_name, body));
onde config_name é uma coluna da tabela
pgweb.
Isto permite configurações mistas no mesmo índice, porque salva
qual configuração foi usada para cada entrada do índice.
Seria útil, por exemplo, se a coleção de documentos contivesse
documentos em diferentes idiomas.
Novamente, as consultas destinadas a usar um índice devem ser
escritas para corresponder ao índice, como, por exemplo,
WHERE to_tsvector(config_name, body) @@ 'a & b'.
Os índices podem até mesmo concatenar colunas:
CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector('english', title || ' ' || body));
Outra abordagem é criar uma coluna tsvector separada
para armazenar a saída da função to_tsvector.
Para manter esta coluna atualizada automaticamente com seus dados
de origem, deve ser usada uma coluna gerada armazenada.
Este exemplo é uma concatenação dos campos title
e body usando a função coalesce
para garantir que um campo ainda será indexado quando o outro for
NULL:
ALTER TABLE pgweb
ADD COLUMN textsearchable_index_col tsvector
GENERATED ALWAYS AS
(to_tsvector('english', coalesce(title, '') || ' '
|| coalesce(body, ''))) STORED;
Em seguida, é criado um índice GIN para acelerar a procura:
CREATE INDEX textsearch_idx ON pgweb USING GIN (textsearchable_index_col);
Agora estamos prontos para realizar uma procura de texto completo rápida:
SELECT title
FROM pgweb
WHERE textsearchable_index_col @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;
Uma vantagem da abordagem de coluna separada sobre um índice de
expressão, é não ser necessário especificar explicitamente a
configuração de procura de texto nas consultas para usar o
índice.
Conforme mostrado no exemplo acima, a consulta pode depender de
default_text_search_config.
Outra vantagem, é que as procuras serão mais rápidas, porque não será
necessário refazer as chamadas à função
to_tsvector para verificar correspondências de
índice.
(Isto é mais importante ao usar um índice GiST do que um índice GIN;
veja a Seção 12.9.)
Entretanto, a abordagem de índice de expressão é mais fácil de
configurar, e requer menos espaço em disco, porque a representação
tsvector não é fisicamente armazenada.