Para implementar a procura de texto completo, deve haver função
para criar o tsvector a partir do documento, e
para criar o tsquery a partir da consulta do usuário.
Além disso, também é necessário retornar os resultados em uma ordem
útil, portanto é necessária uma função para comparar os documentos
baseado na sua relevância para a consulta.
Também é importante conseguir exibir os resultados de forma apropriada.
O PostgreSQL fornece suporte para todas
estas funções.
O PostgreSQL disponibiliza a função
to_tsvector para converter o documento para
o tipo de dados tsvector.
to_tsvector([configregconfig, ]documenttext) returnstsvector
A função to_tsvector converte o documento
textual em tokens, reduz os tokens
a lexemas, e retorna o tipo de dados tsvector,
que lista os lexemas junto com suas posições no documento.
O documento é processado segundo a configuração de procura de
texto especificada, ou segundo a configuração padrão,
se nenhuma configuração for especificada.
A seguir está um exemplo simples:
SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats');
to_tsvector
-----------------------------------------------------
'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4
-- Exemplo do tradutor abaixo
SELECT to_tsvector('portuguese', 'um gato gordo sentou no tapete - comeu ratos gordos');
to_tsvector
-------------------------------------------------------
'com':7 'gat':2 'gord':3,9 'rat':8 'sent':4 'tapet':6
No exemplo em inglês acima, vemos que o tsvector
resultante não contém as palavras a,
on ou it, a palavra
rats tornou-se rat,
e o sinal de pontuação - foi ignorado.
(no exemplo em português também ocorreram
situações semelhantes, 'um' e 'no' não aparecem no resultado,
mas suas posições foram contadas, '-' não aparece no resultado nem
sua posição foi contada, enquanto as demais palavras foram
convertidas nos seus lexemas)
A função to_tsvector chama internamente o
analisador que divide o texto do documento em tokens,
e atribui um tipo a cada token.
Para cada token é consultada uma lista de dicionários
(veja a Seção 12.6), onde a lista
pode variar dependendo do tipo do token.
O primeiro dicionário que reconhecer o
token gera um ou mais lexemas
normalizados para representar o token.
Por exemplo, em inglês rats se tornou
rat, porque um dos dicionários reconheceu que a
palavra rats é o plural de rat.
Algumas palavras são reconhecidas como sendo
palavras de parada
(stop words)
(veja a Seção 12.6.1),
o que faz com que sejam ignoradas, porque ocorrem com muita
frequência para serem úteis na procura.
No exemplo em inglês estas palavras são a,
on e it,
e no exemplo em português 'um' e 'no'.
Se nenhum dicionário da lista reconhecer o token,
ele também será ignorado.
Nesse exemplo isto aconteceu com o sinal de pontuação
-,
porque, de fato, não há dicionários atribuídos ao seu tipo de
token (Símbolos de espaço),
significando que os tokens de espaço nunca são
indexados.
As escolhas de analisador, dicionários, e quais tipos de
tokens indexar, são determinadas pela configuração
de procura de texto selecionada
(veja a Seção 12.7).
É possível haver muitas configurações diferentes no mesmo banco de
dados, e estão disponíveis configurações predefinidas para vários
idiomas.
No exemplo em inglês foi usada a configuração padrão
english para o idioma inglês,
e 'portuguese' para o idioma português.
A função setweight pode ser usada para rotular
as entradas de tsvector com um determinado
rótulo de peso,
(weight),
onde o rótulo de peso é uma das letras A,
B, C ou D.
Normalmente esta função é usada para marcar entradas provenientes
de partes diferentes do documento, como título versus corpo.
Depois estas informações podem ser usadas para pontuar
os resultados da procura.
Como to_tsvector(NULL)
retorna NULL, é recomendado usar a função
coalesce sempre que um campo puder ser nulo.
Abaixo está o método recomendado para criar tsvector
a partir de um documento estruturado:
UPDATE tt SET ti =
setweight(to_tsvector(coalesce(title,'')), 'A') || -- título
setweight(to_tsvector(coalesce(keyword,'')), 'B') || -- palavra-chave
setweight(to_tsvector(coalesce(abstract,'')), 'C') || -- resumo
setweight(to_tsvector(coalesce(body,'')), 'D'); -- corpo do documento
Aqui é usada a função setweight para acrescentar
um rótulo de peso ao tsvector retornado, conforme a
origem de cada lexema, e, em seguida, os valores com seus pesos são
concatenados usando operador de concatenação ||
de tsvector.
(A Seção 12.4.1 fornece detalhes
sobre estas operações.)
O PostgreSQL disponibiliza as funções
to_tsquery, plainto_tsquery,
phraseto_tsquery e
websearch_to_tsquery
para converter uma consulta para o tipo de dados tsquery.
A função to_tsquery oferece acesso a mais
recursos do que plainto_tsquery ou
phraseto_tsquery, mas é mais exigente quanto
a entrada.
A função websearch_to_tsquery é uma versão
simplificada da função to_tsquery com uma
sintaxe alternativa, semelhante à usada pelos motores de busca na web.
to_tsquery([configregconfig, ]querytexttext) returnstsquery
A função to_tsquery cria um valor
tsquery a partir do querytext,
que deve consistir em tokens únicos separados pelos
operadores tsquery &
(AND), | (OR),
! (NOT) e
<-> (FOLLOWED BY),
possivelmente agrupados usando parênteses.
Em outras palavras, a entrada para to_tsquery
já deve seguir as regras gerais para entrada de tsquery,
conforme descrito na Seção 8.11.2.
A diferença, é que enquanto a entrada básica de tsquery
recebe os tokens diretamente, a função
to_tsquery normaliza cada token
em um lexema usando a configuração especificada, ou a configuração
padrão, e descarta todos os tokens que são
palavras de parada segundo a configuração. Por exemplo:
SELECT to_tsquery('english', 'The & Fat & Rats');
to_tsquery --------------- 'fat' & 'rat'
-- Exemplo do tradutor abaixo
SELECT to_tsquery('portuguese', 'Os & Ratos & Gordos');
to_tsquery ---------------- 'rat' & 'gord'
Como na entrada básica de tsquery, podem ser
anexados rótulos de peso a cada lexema, para restringi-los de forma
que correspondam apenas aos lexemas de tsvector com
estes rótulos de peso. Por exemplo:
SELECT to_tsquery('english', 'Fat | Rats:AB');
to_tsquery
------------------
'fat' | 'rat':AB
-- Exemplo do tradutor abaixo
SELECT to_tsquery('portuguese', 'Gordos | Ratos:AB');
to_tsquery
-------------------
'gord' | 'rat':AB
Além disso, pode ser anexado um * a um lexema
para especificar a correspondência de prefixo:
SELECT to_tsquery('english', 'supern:*A & star:A*B');
to_tsquery
--------------------------
'supern':*A & 'star':*AB
-- Exemplo do tradutor abaixo
SELECT to_tsquery('portuguese', 'estrela:A*B & supernova:*A');
to_tsquery
------------------------------
'estrel':*AB & 'supernov':*A
SELECT to_tsquery('portuguese', 'estrela:A & supernova:*AB')
@@ setweight(to_tsvector('portuguese', 'Uma supernovidade da Estrela'), 'A');
?column? ---------- t
Estes lexemas vão corresponder a qualquer palavra em um
tsvector que comece com a cadeia de caracteres fornecida.
A função to_tsquery também aceita frases
entre apóstrofos.
É útil principalmente quando a configuração inclui um
thesaurus (dicionário de
sinônimos) que pode acionar tais frases.
No exemplo abaixo, o thesaurus
contém a regra supernovae stars : sn:
SELECT to_tsquery('''supernovae stars'' & !crab');
to_tsquery --------------- 'sn' & !'crab'
Sem os apóstrofos, a função to_tsquery
gera um erro de sintaxe para tokens que não são
separados pelo operador AND, OR,
ou FOLLOWED BY.
plainto_tsquery([configregconfig, ]querytexttext) returnstsquery
A função plainto_tsquery transforma o texto não
formatado querytext em um valor
tsquery.
O texto é analisado e normalizado como para a função
to_tsvector, então o operador
& (AND) do
tsquery é inserido entre as palavras sobreviventes.
Exemplo:
SELECT plainto_tsquery('english', 'The Fat Rats');
plainto_tsquery ----------------- 'fat' & 'rat'
-- Exemplo do tradutor abaixo
SELECT plainto_tsquery('portuguese', 'Os Ratos Gordos');
plainto_tsquery ----------------- 'rat' & 'gord'
Note que a função plainto_tsquery não
reconhece operadores tsquery, rótulos de peso ou
rótulos de correspondência de prefixo em sua entrada:
SELECT plainto_tsquery('english', 'The Fat & Rats:C');
plainto_tsquery --------------------- 'fat' & 'rat' & 'c'
-- Exemplo do tradutor abaixo
SELECT plainto_tsquery('portuguese', 'Os Ratos:C Gordos');
plainto_tsquery ---------------------- 'rat' & 'c' & 'gord'
Nos exemplos acima, os sinais gráficos da entrada
(:) são desprezados.
phraseto_tsquery([configregconfig, ]querytexttext) returnstsquery
A função phraseto_tsquery se comporta de maneira
muito parecida com a função plainto_tsquery,
exceto por inserir o operador <->
(FOLLOWED BY) entre as palavras sobreviventes, em
vez do operador & (AND).
Além disso, as palavras de parada não são simplesmente descartadas,
sendo contabilizadas pela inserção de operadores
<, em vez de
operadores N><->.
Esta função é útil na procura por sequências exatas de lexemas,
porque os operadores FOLLOWED BY verificam a
ordem dos lexemas, e não apenas a presença de todos os lexemas.
Exemplo:
SELECT phraseto_tsquery('english', 'The Fat Rats');
phraseto_tsquery
------------------
'fat' <-> 'rat'
Assim como a função plainto_tsquery, a função
phraseto_tsquery não irá reconhecer operadores
tsquery, rótulos de peso ou rótulos de correspondência
de prefixo em sua entrada:
SELECT phraseto_tsquery('english', 'The Fat & Rats:C');
phraseto_tsquery
-------------------------
'fat' <-> 'rat' <-> 'c'
-- Exemplo do tradutor abaixo
SELECT phraseto_tsquery('portuguese', 'Os Ratos:C Gordos');
phraseto_tsquery
--------------------------
'rat' <-> 'c' <-> 'gord'
websearch_to_tsquery([configregconfig, ]querytexttext) returnstsquery
A função websearch_to_tsquery cria um valor
tsquery a partir de querytext,
usando uma sintaxe alternativa onde o texto simples não formatado
é uma consulta válida.
Diferentemente de plainto_tsquery e
phraseto_tsquery, esta função reconhece
certos operadores.
Além disso, esta função nunca gera erros de sintaxe,
possibilitando o uso de entrada bruta fornecida pelo usuário para
procura.
Há suporte para:
unquoted text: o texto não delimitado é
convertido em termos separados pelo operador
&, como se tivesse sido processado pela
função plainto_tsquery.
"quoted text": o texto delimitado é convertido
em termos separados pelo operador <->,
como se tivesse sido processado pela função
phraseto_tsquery.
OR: a palavra “or” é convertida
no operador | (OR).
-: o hífen é convertido no operador
! (NOT).
As demais pontuações são ignoradas.
Então, assim como plainto_tsquery e
phraseto_tsquery, a função
websearch_to_tsquery não reconhece operadores
do tsquery, rótulos de peso, ou rótulos de
correspondência de prefixo, em sua entrada.
Exemplos:
SELECT websearch_to_tsquery('english', 'The fat rats');
websearch_to_tsquery
----------------------
'fat' & 'rat'
SELECT websearch_to_tsquery('english', '"supernovae stars" -crab');
websearch_to_tsquery
----------------------------------
'supernova' <-> 'star' & !'crab'
SELECT websearch_to_tsquery('english', '"sad cat" or "fat rat"');
websearch_to_tsquery
-----------------------------------
'sad' <-> 'cat' | 'fat' <-> 'rat'
SELECT websearch_to_tsquery('english', 'signal -"segmentation fault"');
websearch_to_tsquery
---------------------------------------
'signal' & !( 'segment' <-> 'fault' )
SELECT websearch_to_tsquery('english', '""" )( dummy \\ query <->');
websearch_to_tsquery
----------------------
'dummi' <-> 'queri'
Exemplos em português do tradutor:
SELECT websearch_to_tsquery('portuguese', 'Os Ratos Gordos');
websearch_to_tsquery
----------------------
'rat' & 'gord'
SELECT websearch_to_tsquery('portuguese', '"estrelas supernovas" -Kepler');
websearch_to_tsquery
-----------------------------------
'estrel' <-> 'supernov' & !'kepl'
-- 'or' precisa ser mantido em inglês
SELECT websearch_to_tsquery('portuguese', '"gato triste" or "rato gordo"');
websearch_to_tsquery
--------------------------------------
'gat' <-> 'trist' | 'rat' <-> 'gord'
-- 'ou' é palavra de parada, não tem a função de 'or'
SELECT websearch_to_tsquery('portuguese', '"gato triste" ou "rato gordo"');
websearch_to_tsquery
--------------------------------------
'gat' <-> 'trist' & 'rat' <-> 'gord'
SELECT websearch_to_tsquery('portuguese', 'sinal -"falha de segmentação"');
websearch_to_tsquery
-------------------------------------
'sinal' & !( 'falh' <2> 'segment' )
SELECT websearch_to_tsquery('portuguese', '""" )( consulta \\ fictícia <->');
websearch_to_tsquery
------------------------
'consult' <-> 'fictíc'
A pontuação (classificação) tenta medir a relevância dos documentos para uma determinada consulta, de modo que, quando ocorrem muitas correspondências, os documentos mais relevantes possam ser exibidos primeiro. O PostgreSQL fornece duas funções de pontuação predefinidas, que consideram informações léxicas, de proximidade e estruturais; ou seja, estas funções consideram com que frequência os termos da consulta aparecem no documento, quão próximos os termos estão um do outro no documento, e quão importante é a parte do documento onde eles ocorrem. Entretanto, o conceito de relevância é vago e muito específico da aplicação. Aplicações diferentes podem exigir informações adicionais para a pontuação, como, por exemplo, a hora da modificação do documento. As funções de classificação nativas são apenas exemplos. Você pode escrever suas próprias funções de pontuação, e/ou combinar seus resultados com fatores adicionais para atender suas necessidades específicas.
As duas funções de pontuação disponíveis atualmente são:
ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
Pontua vetores com base na frequência de seus lexemas correspondentes.
ts_rank_cd([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
Esta função calcula a pontuação de
densidade de cobertura para o vetor
e consulta fornecidos, conforme publicado no artigo
Relevance ranking for one to three term queries, de
Charles L.A. Clark, Gordon V. Cormac e Elizabeth A. Tudhope,
no periódico “Information Processing and Management”,
de março de 2000.
A densidade de cobertura é semelhante à pontuação da função
ts_rank, exceto por ser considerada a
proximidade dos lexemas correspondentes.
Esta função requer a informação de posição do lexema para
realizar seu cálculo.
Portanto, ignora qualquer lexema “sem posição”
em tsvector.
Se não existirem lexemas com posição na entrada, o resultado
será zero.
(Veja a Seção 12.4.1
para obter mais informações sobre a função
strip, e as informações de posição em
tsvector.)
Para estas duas funções, o argumento opcional
weights (pesos)
oferece a possibilidade de dar mais peso, ou menos peso, a certas
palavras, dependendo de como estão rotuladas.
As matrizes de peso especificam o peso de cada categoria de palavra,
na ordem:
{D-weight, C-weight, B-weight, A-weight}
Se o argumento weights
não for fornecido, então serão usados os valores padrão:
{0.1, 0.2, 0.4, 1.0}
Normalmente, os pesos são usados para marcar palavras de áreas específicas do documento, como o título ou um resumo inicial, para poderem ser tratadas com mais importância, ou menos importância, que as palavras no corpo do documento.
Como um documento mais longo tem uma chance maior de conter um termo
da consulta, é razoável considerar o tamanho do documento.
Por exemplo, um documento de cem palavras com cinco ocorrências de
uma palavra de procura é provavelmente mais relevante do que um
documento de mil palavras com cinco ocorrências.
As duas funções de pontuação contêm o argumento opcional
normalization, um número inteiro
que especifica “se e como” o tamanho do documento
deve afetar sua pontuação.
Este número inteiro controla vários comportamentos e, por isto,
é uma máscara de bits:
é possível especificar um ou mais comportamentos usando
| (por exemplo, 2|4).
0 (o padrão) ignora o comprimento do documento
1 divide a pontuação por 1 + o logaritmo do comprimento do documento
2 divide a pontuação pelo comprimento do documento
4 divide a pontuação pela média harmônica da distância entre as palavras
(implementado apenas pela função ts_rank_cd)
8 divide a pontuação pelo número de palavras únicas no documento
16 divide a pontuação por 1 + o logaritmo do número de palavras únicas no documento
32 divide a pontuação por ela mesma + 1
Se for especificado mais de um bit sinalizador, as transformações serão aplicadas na ordem listada acima.
É importante observar que as funções de pontuação não usam nenhuma
informação global, por isto é impossível produzir uma normalização
equilibrada para 1% ou 100%, como às vezes se deseja.
A opção de normalização 32 (rank/(rank+1))
pode ser aplicada para manter todas as pontuações no intervalo de
zero a um, mas é claro ser apenas uma mudança cosmética;
não vai afetar a ordem dos resultados da procura.
A seguir está um exemplo que seleciona apenas as dez correspondências mais bem pontuadas:
SELECT title, ts_rank_cd(textsearch, query) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE query @@ textsearch
ORDER BY rank DESC
LIMIT 10;
title | rank
-----------------------------------------------+----------
Neutrinos in the Sun | 3.1
The Sudbury Neutrino Detector | 2.4
A MACHO View of Galactic Dark Matter | 2.01317
Hot Gas and Dark Matter | 1.91171
The Virgo Cluster: Hot Plasma and Dark Matter | 1.90953
Rafting for Solar Neutrinos | 1.9
NGC 4650A: Strange Galaxy and Dark Matter | 1.85774
Hot Gas and Dark Matter | 1.6123
Ice Fishing for Cosmic Neutrinos | 1.6
Weak Lensing Distorts the Universe | 0.818218
Este é o mesmo exemplo usando pontuação normalizada:
SELECT title, ts_rank_cd(textsearch, query, 32 /* rank/(rank+1) */ ) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE query @@ textsearch
ORDER BY rank DESC
LIMIT 10;
title | rank
-----------------------------------------------+-------------------
Neutrinos in the Sun | 0.756097569485493
The Sudbury Neutrino Detector | 0.705882361190954
A MACHO View of Galactic Dark Matter | 0.668123210574724
Hot Gas and Dark Matter | 0.65655958650282
The Virgo Cluster: Hot Plasma and Dark Matter | 0.656301290640973
Rafting for Solar Neutrinos | 0.655172410958162
NGC 4650A: Strange Galaxy and Dark Matter | 0.650072921219637
Hot Gas and Dark Matter | 0.617195790024749
Ice Fishing for Cosmic Neutrinos | 0.615384618911517
Weak Lensing Distorts the Universe | 0.450010798361481
A pontuação pode ter um custo alto, porque requer a consulta do
tsvector de cada documento que corresponde,
que pode ser limitado por E/S, portanto lento.
Infelizmente é quase impossível evitar, porque as consultas práticas
geralmente resultam em um grande número de correspondências.
Para apresentar os resultados da procura, o ideal é mostrar uma
parte de cada documento, e como ele se relaciona com a consulta.
Normalmente, os motores de busca mostram fragmentos do documento
com os termos da procura destacados.
O PostgreSQL disponibiliza a função
ts_headline que implementa esta funcionalidade.
ts_headline([configregconfig, ]documenttext,querytsquery[,optionstext]) returnstext
A função ts_headline aceita um documento junto
com uma consulta, e retorna um excerto do documento onde os termos
da consulta são destacados.
Especificamente, a função irá usar a consulta para selecionar
fragmentos de texto relevantes e, em seguida, irá destacará todas
as palavras que aparecem na consulta, mesmo que as posições destas
palavras não correspondam às restrições da consulta.
A configuração a ser usada para analisar o documento pode ser
especificada pelo argumento config;
se config for omitido, será usada
a configuração default_text_search_config.
Se a cadeia de caracteres options
for especificada, deverá consistir em uma lista separada por
vírgulas de um ou mais pares
option=value.
As opções disponíveis são:
MaxWords, MinWords (inteiros):
estes números determinam os cabeçalhos mais longos e mais curtos
a serem produzidos. Os valores padrão são 35 e 15.
ShortWord (inteiro): as palavras desse tamanho,
ou menores, serão descartadas no início e no final do cabeçalho,
a menos que sejam termos da consulta.
O valor padrão, igual a 3, elimina os artigos comuns em inglês.
HighlightAll (booleano): se for
true, todo o documento será usado como
cabeçalho, ignorando os três parâmetros anteriores.
O padrão é false.
MaxFragments (inteiro): número máximo de
fragmentos de texto a serem mostrados.
O valor padrão zero seleciona um método de geração de cabeçalho
não baseado em fragmentos.
Um valor maior que zero seleciona a geração de cabeçalhos baseada
em fragmentos (veja abaixo).
StartSel, StopSel
(cadeias de caracteres): as cadeias de caracteres com as quais
serão delimitadas as palavras da consulta que aparecem no documento,
para distingui-las das outras palavras extraídas.
Os valores padrão são “<b>” e
“</b>”, que podem ser
adequadas para a saída HTML (mas veja a advertência abaixo).
FragmentDelimiter (cadeia de caracteres):
Quando mais de um fragmento for exibido, os fragmentos serão
separados por esta cadeia de caracteres.
O padrão é “ ... ”.
Não há garantia de que o resultado da função
ts_headline seja seguro para inclusão direta
em páginas da web.
Quando HighlightAll é false
(o padrão), algumas etiquetas XML simples são removidas do documento,
mas isto não garante a remoção completa da marcação HTML.
Portanto, isto não oferece uma defesa eficaz contra ataques como
os de cross-site scripting (XSS)
(injeção de scripts maliciosos) ao trabalhar com entradas não
confiáveis.
Para se proteger contra estes ataques, toda a marcação HTML deve
ser removida do documento de entrada, ou deve ser usado
um sanitizador de HTML na saída.
Esses nomes de opções são reconhecidos sem haver distinção entre letras maiúsculas e minúsculas. Devem ser colocadas aspas nos valores cadeia de caracteres, se contiverem espaços ou vírgulas.
Na geração de cabeçalhos não baseada em fragmentos, a função
ts_headline localiza as correspondências para a
query fornecida,
e escolhe apenas uma para exibir, preferindo a correspondência que
tenha o maior número de palavras da consulta dentro do tamanho
permitido do cabeçalho.
Na geração de cabeçalhos baseada em fragmentos, a função
ts_headline localiza as correspondências da
consulta e divide cada correspondência em “fragmentos”
com não mais que MaxWords palavras cada,
preferindo fragmentos com mais palavras da consulta e, quando possível,
“esticando” os fragmentos para incluir palavras ao redor.
O modo baseado em fragmentos é, portanto, mais útil quando as
correspondências da consulta abrangem grandes seções do documento,
ou quando se deseja exibir várias correspondências.
Em qualquer um dos modos, se nenhuma correspondência da consulta
puder ser identificada, um único fragmento das primeiras
MinWords palavras do documento será exibido.
Por exemplo:
SELECT ts_headline('english',
'The most common type of search
is to find all documents containing given query terms
and return them in order of their similarity to the
query.',
to_tsquery('english', 'query & similarity'));
ts_headline
------------------------------------------------------------
containing given <b>query</b> terms +
and return them in order of their <b>similarity</b> to the+
<b>query</b>.
SELECT ts_headline('english',
'Search terms may occur
many times in a document,
requiring ranking of the search matches to decide which
occurrences to display in the result.',
to_tsquery('english', 'search & term'),
'MaxFragments=10, MaxWords=7, MinWords=3, StartSel=<<, StopSel=>>');
ts_headline
------------------------------------------------------------
<<Search>> <<terms>> may occur +
many times ... ranking of the <<search>> matches to decide
-- Exemplos do tradutor abaixo
SELECT ts_headline('portuguese', $$o tipo mais comum de procura
é encontrar todos os documentos que
contenham determinados termos da consulta,
e devolvê-los em ordem de semelhança com a consulta.$$,
to_tsquery('portuguese', 'consulta & ordem'));
ts_headline
--------------------------------------------------------------------
contenham determinados termos da <b>consulta</b>, +
e devolvê-los em <b>ordem</b> de semelhança com a <b>consulta</b>.
SELECT ts_headline('portuguese', $$Os termos de procura podem
ocorrer muitas vezes em um documento,
exigindo a pontuação das correspondências da procura,
para decidir quais ocorrências serão exibidas no resultado.$$,
to_tsquery('portuguese', 'procura & resultado'),
'MaxFragments=10, MaxWords=7, MinWords=3, StartSel=<<, StopSel=>>');
ts_headline
--------------------------------------------------------------------
termos de <<procura>> podem +
ocorrer muitas vezes ... correspondências da <<procura>>, +
para decidir quais ocorrências ... serão exibidas no <<resultado>>
A função ts_headline usa o documento original,
e não um resumo do tsvector, por isto pode ser lenta,
devendo ser usada com cuidado.