8.11. Tipos de búsqueda de texto #

8.11.1. tsvector
8.11.2. tsquery

PostgreSQL proporciona dos tipos de datos que están diseñados para soportar la búsqueda de texto completo, que es la actividad de buscar a través de una colección de documentos en lenguaje natural para localizar aquellos que mejor coincidan con una consulta. El tipo tsvector representa un documento en una forma optimizada para la búsqueda de texto; el tipo tsquery representa de manera similar una consulta de texto. La Chapter 12 proporciona una explicación detallada de esta facilidad, y la Section 9.13 resume las funciones y operadores relacionados.

8.11.1. tsvector #

Un valor tsvector es una lista ordenada de lexemas distintos, que son palabras que han sido normalizadas para fusionar diferentes variantes de la misma palabra (consulta la Chapter 12 para más detalles). La ordenación y la eliminación de duplicados se realizan automáticamente durante la entrada, como se muestra en este ejemplo:

SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector;
                      tsvector
----------------------------------------------------
 'a' 'and' 'ate' 'cat' 'fat' 'mat' 'on' 'rat' 'sat'

Para representar lexemas que contienen espacios en blanco o puntuación, rodéalos con comillas:

SELECT $$the lexeme '    ' contains spaces$$::tsvector;
                 tsvector
-------------------------------------------
 '    ' 'contains' 'lexeme' 'spaces' 'the'

(Utilizamos literales de cadena delimitados por dólares en este ejemplo y en el siguiente para evitar la confusión de tener que duplicar las comillas dentro de los literales). Las comillas incrustadas y las barras invertidas deben duplicarse:

SELECT $$the lexeme 'Joe''s' contains a quote$$::tsvector;
                    tsvector
------------------------------------------------
 'Joe''s' 'a' 'contains' 'lexeme' 'quote' 'the'

Opcionalmente, se pueden adjuntar posiciones enteras a los lexemas:

SELECT 'a:1 fat:2 cat:3 sat:4 on:5 a:6 mat:7 and:8 ate:9 a:10 fat:11 rat:12'::tsvector;
                                  tsvector
--------------------------------------------------------------------------------
 'a':1,6,10 'and':8 'ate':9 'cat':3 'fat':2,11 'mat':7 'on':5 'rat':12 'sat':4

Una posición normalmente indica la ubicación de la palabra de origen en el documento. La información posicional se puede utilizar para la clasificación por proximidad. Los valores de posición pueden variar de 1 a 16383; los números mayores se establecen silenciosamente en 16383. Las posiciones duplicadas para el mismo lexema se descartan.

Los lexemas que tienen posiciones pueden etiquetarse además con un peso, que puede ser A, B, C o D. D es el valor predeterminado y, por lo tanto, no se muestra en la salida:

SELECT 'a:1A fat:2B,4C cat:5D'::tsvector;
          tsvector
----------------------------
 'a':1A 'cat':5 'fat':2B,4C

Los pesos se utilizan normalmente para reflejar la estructura del documento, por ejemplo marcando las palabras del título de manera diferente a las palabras del cuerpo. Las funciones de clasificación de búsqueda de texto pueden asignar diferentes prioridades a los diferentes marcadores de peso.

Es importante entender que el propio tipo tsvector no realiza ninguna normalización de palabras; asume que las palabras que se le dan están normalizadas apropiadamente para la aplicación. Por ejemplo,

SELECT 'The Fat Rats'::tsvector;
      tsvector
--------------------
 'Fat' 'Rats' 'The'

Para la mayoría de las aplicaciones de búsqueda de texto en inglés, las palabras anteriores se considerarían no normalizadas, pero a tsvector no le importa. El texto del documento original normalmente debe pasarse a través de to_tsvector para normalizar las palabras de manera apropiada para la búsqueda:

SELECT to_tsvector('english', 'The Fat Rats');
   to_tsvector
-----------------
 'fat':2 'rat':3

Nuevamente, consulta la Chapter 12 para obtener más detalles.

8.11.2. tsquery #

Un valor tsquery almacena los lexemas que se van a buscar, y los puede combinar utilizando los operadores booleanos & (AND), | (OR), y ! (NOT), así como el operador de búsqueda de frases <-> (SEGUIDO POR). También hay una variante <N> del operador SEGUIDO POR, donde N es una constante entera que especifica la distancia entre los dos lexemas que se están buscando. <-> es equivalente a <1>.

Se pueden usar paréntesis para forzar la agrupación de estos operadores. En ausencia de paréntesis, ! (NOT) se asocia con mayor fuerza, luego <-> (SEGUIDO POR), después & (AND), siendo | (OR) el que se asocia con menor fuerza.

Aquí tienes algunos ejemplos:

SELECT 'fat & rat'::tsquery;
    tsquery
---------------
 'fat' & 'rat'

SELECT 'fat & (rat | cat)'::tsquery;
          tsquery
---------------------------
 'fat' & ( 'rat' | 'cat' )

SELECT 'fat & rat & ! cat'::tsquery;
         tsquery
------------------------
 'fat' & 'rat' & !'cat'

Opcionalmente, los lexemas en un tsquery se pueden etiquetar con una o más letras de peso, lo que los restringe a coincidir solo con lexemas de tsvector con uno de esos pesos:

SELECT 'fat:ab & cat'::tsquery;
    tsquery
------------------
 'fat':AB & 'cat'

Además, los lexemas en un tsquery se pueden etiquetar con * para especificar la coincidencia de prefijos:

SELECT 'super:*'::tsquery;
  tsquery
-----------
 'super':*

Esta consulta coincidirá con cualquier palabra en un tsvector que comience con super.

Las reglas de entrecomillado para los lexemas son las mismas descritas anteriormente para los lexemas en tsvector; y, al igual que con tsvector, cualquier normalización requerida de las palabras debe realizarse antes de convertirlas al tipo tsquery. La función to_tsquery es conveniente para realizar dicha normalización:

SELECT to_tsquery('Fat:ab & Cats');
    to_tsquery
------------------
 'fat':AB & 'cat'

Ten en cuenta que to_tsquery procesará los prefijos de la misma manera que otras palabras, lo que significa que esta comparación devuelve verdadero:

SELECT to_tsvector( 'postgraduate' ) @@ to_tsquery( 'postgres:*' );
 ?column?
----------
 t

porque postgres se reduce (stemmed) a postgr:

SELECT to_tsvector( 'postgraduate' ), to_tsquery( 'postgres:*' );
  to_tsvector  | to_tsquery
---------------+------------
 'postgradu':1 | 'postgr':*

lo cual coincidirá con la forma reducida de postgraduate.