Esta sección describe funciones y operadores adicionales que son útiles en relación con la búsqueda de texto.
La Section 12.3.1 mostró cómo se pueden
convertir los documentos de texto sin procesar en valores de tsvector.
PostgreSQL también proporciona funciones y
operadores que se pueden utilizar para manipular documentos que ya están
en formato tsvector.
tsvector || tsvector
El operador de concatenación de tsvector
devuelve un vector que combina los lexemas y la información posicional
de los dos vectores proporcionados como argumentos. Las posiciones y las etiquetas de peso
se conservan durante la concatenación.
Las posiciones que aparecen en el vector de la derecha se desplazan según la posición
más alta mencionada en el vector de la izquierda, de modo que el resultado es
casi equivalente al resultado de aplicar to_tsvector
en la concatenación de las dos cadenas de documentos originales. (La
equivalencia no es exacta, porque las palabras vacías eliminadas del
final del argumento de la izquierda no afectarán al resultado, mientras que
habrían afectado a las posiciones de los lexemas en el
argumento de la derecha si se utilizara la concatenación de texto).
Una ventaja de utilizar la concatenación en formato de vector, en lugar de
concatenar el texto antes de aplicar to_tsvector, es que
se pueden utilizar diferentes configuraciones para analizar diferentes secciones
del documento. Además, dado que la función setweight
etiqueta todos los lexemas del vector dado de la misma manera, es necesario
analizar el texto y hacer setweight antes de concatenar
si se desea etiquetar diferentes partes del documento con diferentes
pesos.
setweight(vector tsvector, weight "char") returns tsvector
setweight devuelve una copia del vector de entrada en la que cada
posición ha sido etiqualizada con el weight dado, ya sea
A, B, C o
D. (D es el valor por omisión para los nuevos
vectores y, como tal, no se muestra en la salida). Estas etiquetas se
conservan cuando se concatenan los vectores, lo que permite que las palabras de diferentes
partes de un documento sean pesadas de manera diferente por las funciones de clasificación.
Ten en cuenta que las etiquetas de peso se aplican a las posiciones, no
a los lexemas. Si al vector de entrada se le han despojado las
posiciones, entonces setweight no hace nada.
length(vector tsvector) returns integer
Devuelve el número de lexemas almacenados en el vector.
strip(vector tsvector) returns tsvector
Devuelve un vector que enumera los mismos lexemas que el vector dado, pero
carece de cualquier información de posición o peso. El resultado suele ser mucho
más pequeño que un vector no despojado, pero también es menos útil.
La clasificación de relevancia no funciona tan bien en vectores despojados como
en los no despojados. Además,
el operador de tsquery <-> (FOLLOWED BY)
nunca coincidirá con una entrada despojada, ya que no puede determinar la
distancia entre las apariciones de los lexemas.
Una lista completa de las funciones relacionadas con tsvector está disponible
en la Table 9.43.
La Section 12.3.2 mostró cómo se pueden
convertir las consultas de texto sin procesar en valores de tsquery.
La herramienta de búsqueda de texto de PostgreSQL también proporciona funciones y
operadores que se pueden utilizar para manipular consultas que ya están
en formato tsquery.
tsquery && tsquery
Devuelve la combinación AND de las dos consultas dadas.
tsquery || tsquery
Devuelve la combinación OR de las dos consultas dadas.
!! tsquery
Devuelve la negación (NOT) de la consulta dada.
tsquery <-> tsquery
Devuelve una consulta que busca una coincidencia para la primera consulta dada
seguida inmediatamente por una coincidencia para la segunda consulta dada, utilizando
el operador de tsquery <-> (FOLLOWED BY). Por ejemplo:
SELECT to_tsquery('fat') <-> to_tsquery('cat | rat');
?column?
----------------------------
'fat' <-> ( 'cat' | 'rat' )
tsquery_phrase(query1 tsquery, query2 tsquery [, distance integer ]) returns tsquery
Devuelve una consulta que busca una coincidencia para la primera consulta dada
seguida por una coincidencia para la segunda consulta dada a una distancia de exactamente
distance lexemas, utilizando el operador de
tsquery <. Por ejemplo:
N>
SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10);
tsquery_phrase
------------------
'fat' <10> 'cat'
numnode(query tsquery) returns integer
Devuelve el número de nodos (lexemas más operadores) en un
tsquery. Esta función es útil
para determinar si la consulta (query) es significativa
(devuelve > 0), o si contiene únicamente palabras vacías (stop words) (devuelve 0).
Ejemplos:
SELECT numnode(plainto_tsquery('the any'));
NOTICE: query contains only stopword(s) or doesn't contain lexeme(s), ignored
numnode
---------
0
SELECT numnode('foo & bar'::tsquery);
numnode
---------
3
querytree(query tsquery) returns text
Devuelve la parte de un tsquery que se puede utilizar para
buscar en un índice. Esta función es útil para detectar
consultas no indexables, por ejemplo aquellas que contienen solo palabras vacías
o solo términos negados. Por ejemplo:
SELECT querytree(to_tsquery('defined'));
querytree
-----------
'defin'
SELECT querytree(to_tsquery('!defined'));
querytree
-----------
T
La familia de funciones ts_rewrite busca en un
tsquery dado las apariciones de una subconsulta objetivo
y reemplaza cada aparición con una subconsulta sustituta.
En esencia, esta operación es una versión específica para tsquery
del reemplazo de subcadenas.
Una combinación de objetivo y sustituto se puede considerar como una
regla de reescritura de consulta. Una colección de tales reglas
de reescritura puede ser una poderosa ayuda en la búsqueda.
Por ejemplo, puedes expandir la búsqueda utilizando sinónimos
(por ejemplo, new york, big apple, nyc,
gotham) o reducir la búsqueda para dirigir al usuario a algún tema candente.
Existe cierto solapamiento en la funcionalidad entre esta característica
y los diccionarios de sinónimos (thesaurus) (Section 12.6.4).
Sin embargo, puedes modificar un conjunto de reglas de reescritura sobre la marcha sin
volver a indexar, mientras que actualizar un diccionario de sinónimos requiere volver a indexar
para que sea efectivo.
ts_rewrite (query tsquery, target tsquery, substitute tsquery) returns tsquery
Esta forma de ts_rewrite simplemente aplica una sola
regla de reescritura: target
se reemplaza por substitute
dondequiera que aparezca en query.
Por ejemplo:
SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery);
ts_rewrite
------------
'b' & 'c'
ts_rewrite (query tsquery, select text) returns tsquery
Esta forma de ts_rewrite acepta una
query inicial y un comando SQL select,
el cual se proporciona como una cadena de texto. El select debe
producir dos columnas de tipo tsquery. Para cada fila del resultado del
select, las apariciones del valor de la primera columna
(el objetivo) se reemplazan por el valor de la segunda columna (el sustituto)
dentro del valor de query actual. Por ejemplo:
CREATE TABLE aliases (t tsquery PRIMARY KEY, s tsquery);
INSERT INTO aliases VALUES('a', 'c');
SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases');
ts_rewrite
------------
'b' & 'c'
Ten en cuenta que cuando se aplican múltiples reglas de reescritura de esta manera,
el orden de aplicación puede ser importante; por lo que en la práctica querrás
que la consulta de origen use ORDER BY con alguna clave de ordenamiento.
Consideremos un ejemplo astronómico de la vida real. Expandiremos la consulta
supernovae utilizando reglas de reescritura basadas en tablas:
CREATE TABLE aliases (t tsquery primary key, s tsquery);
INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn'));
SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases');
ts_rewrite
---------------------------------
'crab' & ( 'supernova' | 'sn' )
Podemos cambiar las reglas de reescritura simplemente actualizando la tabla:
UPDATE aliases
SET s = to_tsquery('supernovae|sn & !nebulae')
WHERE t = to_tsquery('supernovae');
SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases');
ts_rewrite
---------------------------------------------
'crab' & ( 'supernova' | 'sn' & !'nebula' )
La reescritura puede ser lenta cuando hay muchas reglas de reescritura, ya que
comprueba cada regla para buscar una posible coincidencia. Para filtrar las reglas
que obviamente no son candidatas, podemos utilizar los operadores de contención para el
tipo tsquery. En el siguiente ejemplo, seleccionamos únicamente aquellas
reglas que podrían coincidir con la consulta original:
SELECT ts_rewrite('a & b'::tsquery,
'SELECT t,s FROM aliases WHERE ''a & b''::tsquery @> t');
ts_rewrite
------------
'b' & 'c'
El método descrito en esta sección ha quedado obsoleto por el uso de columnas generadas almacenadas, como se describe en Section 12.2.2.
Cuando utilizas una columna separada para almacenar la representación tsvector
de tus documentos, es necesario crear un trigger para actualizar la columna
tsvector cuando cambian las columnas de contenido del documento.
Hay dos funciones de trigger integradas disponibles para esto, o puedes escribir
la tuya propia.
tsvector_update_trigger(tsvector_column_name,config_name,text_column_name[, ... ]) tsvector_update_trigger_column(tsvector_column_name,config_column_name,text_column_name[, ... ])
Estas funciones de trigger calculan automáticamente una columna tsvector
a partir de una o más columnas de texto, bajo el control de los parámetros especificados
en el comando CREATE TRIGGER. Un ejemplo de su uso es:
CREATE TABLE messages (
title text,
body text,
tsv tsvector
);
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON messages FOR EACH ROW EXECUTE FUNCTION
tsvector_update_trigger(tsv, 'pg_catalog.english', title, body);
INSERT INTO messages VALUES('title here', 'the body text is here');
SELECT * FROM messages;
title | body | tsv
------------+-----------------------+----------------------------
title here | the body text is here | 'bodi':4 'text':5 'titl':1
SELECT title, body FROM messages WHERE tsv @@ to_tsquery('title & body');
title | body
------------+-----------------------
title here | the body text is here
Una vez creado este trigger, cualquier cambio en title o
body se reflejará automáticamente en
tsv, sin que la aplicación tenga que preocuparse por ello.
El primer argumento del trigger debe ser el nombre de la columna tsvector
que se va a actualizar. El segundo argumento especifica la configuración de búsqueda de texto
que se utilizará para realizar la conversión. Para
tsvector_update_trigger, el nombre de la configuración simplemente
se proporciona como el segundo argumento del trigger. Debe estar calificado con el esquema, como
se muestra arriba, para que el comportamiento del trigger no cambie con los cambios
en search_path. Para
tsvector_update_trigger_column, el segundo argumento del trigger
es el nombre de otra columna de la tabla, que debe ser de tipo
regconfig. Esto permite realizar una selección de configuración por fila.
Los argumentos restantes son los nombres de las columnas de texto
(de tipo text, varchar o char). Estas
se incluirán en el documento en el orden proporcionado. Los valores NULL se
omitirán (pero las otras columnas se seguirán indexando).
Una limitación de estos triggers integrados es que tratan todas las columnas de entrada por igual. Para procesar las columnas de manera diferente — por ejemplo, para dar un peso diferente al título que al cuerpo — es necesario escribir un trigger personalizado. Aquí tienes un ejemplo utilizando PL/pgSQL como lenguaje del trigger:
CREATE FUNCTION messages_trigger() RETURNS trigger AS $$
begin
new.tsv :=
setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') ||
setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D');
return new;
end
$$ LANGUAGE plpgsql;
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE
ON messages FOR EACH ROW EXECUTE FUNCTION messages_trigger();
Ten en cuenta que es importante especificar el nombre de la configuración
explícitamente cuando creas valores de tsvector dentro de los triggers,
de modo que el contenido de la columna no se vea afectado por los cambios en
default_text_search_config. No hacer esto probablemente
provocará problemas como que los resultados de búsqueda cambien después de un volcado y restauración.
La función ts_stat es útil para verificar tu
configuración y para encontrar palabras candidatas a palabras vacías (stop-words).
ts_stat(sqlquerytext, [weightstext, ] OUTwordtext, OUTndocinteger, OUTnentryinteger) returnssetof record
sqlquery es un valor de texto que contiene una consulta SQL
que debe devolver una sola columna de tipo tsvector.
ts_stat ejecuta la consulta y devuelve estadísticas sobre
cada lexema (palabra) distinto contenido en los datos
tsvector. Las columnas devueltas son:
word text — el valor de un lexema
ndoc integer — número de documentos
(tsvectors) en los que ocurrió la palabra
nentry integer — número total de
apariciones de la palabra
Si se proporciona weights, solo se cuentan las apariciones
que tienen uno de esos pesos.
Por ejemplo, para encontrar las diez palabras más frecuentes en una colección de documentos:
SELECT * FROM ts_stat('SELECT vector FROM apod')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;
Lo mismo, pero contando solo las apariciones de palabras con peso A
o B:
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab')
ORDER BY nentry DESC, ndoc DESC, word
LIMIT 10;