Los diccionarios se utilizan para eliminar palabras que no deben considerarse en una
búsqueda (palabras vacías o stop words), y para normalizar
palabras de modo que coincidan diferentes formas derivadas de la misma palabra. Una palabra
normalizada con éxito se denomina lexema. Además de mejorar la calidad de la
búsqueda, la normalización y la eliminación de palabras vacías reducen el tamaño de la representación
tsvector de un documento, mejorando así el rendimiento. La normalización no siempre tiene un
significado lingüístico y suele depender de la semántica de la aplicación.
Algunos ejemplos de normalización:
Lingüística — Los diccionarios Ispell intentan reducir las palabras de entrada a una forma normalizada; los diccionarios de derivación (stemmer) eliminan las terminaciones de las palabras
Las ubicaciones URL se pueden canonizar para que coincidan URLs equivalentes:
http://www.pgsql.ru/db/mw/index.html
http://www.pgsql.ru/db/mw/
http://www.pgsql.ru/db/../db/mw/index.html
Los nombres de colores pueden reemplazarse por sus valores hexadecimales, por ejemplo,
red, green, blue, magenta -> FF0000, 00FF00, 0000FF, FF00FF
Si indexamos números, podemos eliminar algunos dígitos fraccionarios para reducir el rango de números posibles, así por ejemplo 3.14159265359, 3.1415926, 3.14 serán el mismo después de la normalización si solo se conservan dos dígitos después del punto decimal.
Un diccionario es un programa que acepta un token como entrada y devuelve:
un array de lexemas si el token de entrada es conocido por el diccionario (ten en cuenta que un token puede producir más de un lexema)
un solo lexema con la bandera TSL_FILTER establecida, para reemplazar
el token original por uno nuevo que se pasará a los diccionarios subsiguientes
(un diccionario que hace esto se llama diccionario de filtrado)
un array vacío si el diccionario conoce el token, pero es una palabra vacía
NULL si el diccionario no reconoce el token de entrada
PostgreSQL proporciona diccionarios predefinidos para
muchos idiomas. También hay varias plantillas predefinidas que se pueden
utilizar para crear nuevos diccionarios con parámetros personalizados. Cada plantilla
de diccionario predefinida se describe a continuación. Si ninguna plantilla existente
es adecuada, es posible crear otras nuevas; consulta el área
contrib/ de la distribución de PostgreSQL
para ver ejemplos.
Una configuración de búsqueda de texto vincula un analizador junto con un conjunto de
diccionarios para procesar los tokens de salida del analizador. Para cada tipo de token
que el analizador puede devolver, la configuración especifica una lista separada de diccionarios.
Cuando el analizador encuentra un token de ese tipo, se consulta cada diccionario de la lista
por turno, hasta que algún diccionario lo reconozca como una palabra conocida. Si se identifica
como una palabra vacía, o si ningún diccionario reconoce el token, se descartará y no se
indexará ni se buscará.
Normalmente, el primer diccionario que devuelve una salida que no es NULL
determina el resultado, y los diccionarios restantes no se consultan; pero un diccionario
de filtrado puede reemplazar la palabra dada con una palabra modificada, la cual se pasa luego
a los diccionarios subsiguientes.
La regla general para configurar una lista de diccionarios
es colocar primero el diccionario más estrecho y específico, luego los diccionarios más
generales, terminando con un diccionario muy general, como un derivador (stemmer)
Snowball o simple, el cual reconoce todo.
Por ejemplo, para una búsqueda específica de astronomía
(configuración astro_en) se podría vincular el tipo de token
asciiword (palabra ASCII) a un diccionario de sinónimos de términos
astronómicos, un diccionario general de inglés y un derivador (stemmer) de inglés
Snowball:
ALTER TEXT SEARCH CONFIGURATION astro_en
ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem;
Se puede colocar un diccionario de filtrado en cualquier parte de la lista, excepto al final, donde sería inútil. Los diccionarios de filtrado son útiles para normalizar parcialmente las palabras y simplificar la tarea de los diccionarios posteriores. Por ejemplo, se podría utilizar un diccionario de filtrado para eliminar los acentos de las letras acentuadas, tal como lo hace el módulo unaccent.
Las palabras vacías son palabras muy comunes, que aparecen en casi todos los
documentos y no tienen valor discriminatorio. Por lo tanto, se pueden ignorar
en el contexto de la búsqueda de texto completo. Por ejemplo, todo texto en inglés
contiene palabras como a y the, por lo que es
inútil almacenarlas en un índice. Sin embargo, las palabras vacías sí afectan las
posiciones en tsvector, lo que a su vez afecta la clasificación (ranking):
SELECT to_tsvector('english', 'in the list of stop words');
to_tsvector
----------------------------
'list':3 'stop':5 'word':6
Las posiciones faltantes 1, 2, 4 se deben a las palabras vacías. Las clasificaciones calculadas para documentos con y sin palabras vacías son bastante diferentes:
SELECT ts_rank_cd (to_tsvector('english', 'in the list of stop words'), to_tsquery('list & stop'));
ts_rank_cd
------------
0.05
SELECT ts_rank_cd (to_tsvector('english', 'list stop words'), to_tsquery('list & stop'));
ts_rank_cd
------------
0.1
Depende de cada diccionario específico cómo trata las palabras vacías. Por ejemplo,
los diccionarios ispell primero normalizan las palabras y luego
buscan en la lista de palabras vacías, mientras que los derivadores (stemmers) Snowball
primero verifican la lista de palabras vacías. La razón de este comportamiento diferente
es el intento de reducir el ruido.
La plantilla de diccionario simple funciona convirtiendo el
token de entrada a minúsculas y verificándolo contra un archivo de palabras vacías.
Si se encuentra en el archivo, se devuelve un array vacío, lo que hace que el token
sea descartado. Si no, la forma en minúsculas de la palabra se devuelve como el lexema
normalizado. Alternativamente, el diccionario se puede configurar para reportar las palabras
que no son vacías como no reconocidas, permitiendo que se pasen al siguiente diccionario de la lista.
Aquí tienes un ejemplo de una definición de diccionario utilizando la plantilla simple:
CREATE TEXT SEARCH DICTIONARY public.simple_dict (
TEMPLATE = pg_catalog.simple,
STOPWORDS = english
);
Aquí, english es el nombre base de un archivo de palabras vacías.
El nombre completo del archivo será
$SHAREDIR/tsearch_data/english.stop,
donde $SHAREDIR significa el directorio de datos compartidos de la
instalación de PostgreSQL, que a menudo es
/usr/local/share/postgresql (utiliza el comando pg_config
--sharedir para determinarlo si no estás seguro).
El formato del archivo es simplemente una lista de palabras, una por línea. Las líneas en blanco y los
espacios finales se ignoran, y las mayúsculas se convierten a minúsculas, pero no se realiza ningún
otro procesamiento en el contenido del archivo.
Ahora podemos probar nuestro diccionario:
SELECT ts_lexize('public.simple_dict', 'YeS');
ts_lexize
-----------
{yes}
SELECT ts_lexize('public.simple_dict', 'The');
ts_lexize
-----------
{}
También podemos optar por devolver NULL, en lugar de la palabra en minúsculas,
si no se encuentra en el archivo de palabras vacías. Este comportamiento se selecciona
estableciendo el parámetro Accept del diccionario en false.
Continuando con el ejemplo:
ALTER TEXT SEARCH DICTIONARY public.simple_dict ( Accept = false );
SELECT ts_lexize('public.simple_dict', 'YeS');
ts_lexize
-----------
SELECT ts_lexize('public.simple_dict', 'The');
ts_lexize
-----------
{}
Con la configuración predeterminada de Accept = true,
solo es útil colocar un diccionario simple al final de una lista de
diccionarios, ya que nunca pasará ningún token a un diccionario posterior. Por el contrario,
Accept = false solo es útil cuando hay al menos
un diccionario posterior.
La mayoría de los tipos de diccionarios dependen de archivos de configuración, como archivos de palabras vacías. Estos archivos deben guardarse en codificación UTF-8. Se traducirán a la codificación real de la base de datos, si esta es diferente, cuando se lean en el servidor.
Normalmente, una sesión de base de datos leerá un archivo de configuración de diccionario solo una vez,
cuando se utiliza por primera vez dentro de la sesión. Si modificas un archivo de configuración
y deseas forzar a las sesiones existentes a que tomen el nuevo contenido, ejecuta un comando
ALTER TEXT SEARCH DICTIONARY sobre el diccionario. Esto puede ser una
actualización ficticia (“dummy”) que en realidad no cambie ningún valor de parámetro.
Esta plantilla de diccionario se utiliza para crear diccionarios que reemplazan una
palabra por un sinónimo. Las frases no están admitidas (utiliza la plantilla de
thesaurus (Section 12.6.4) para eso). Se puede utilizar un
diccionario de sinónimos para superar problemas lingüísticos, por ejemplo, para evitar
que un diccionario derivador (stemmer) de inglés reduzca la palabra “Paris” a
“pari”. Es suficiente tener una línea Paris paris en el
diccionario de sinónimos y colocarlo antes del diccionario english_stem.
Por ejemplo:
SELECT * FROM ts_debug('english', 'Paris');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+-------+----------------+--------------+---------
asciiword | Word, all ASCII | Paris | {english_stem} | english_stem | {pari}
CREATE TEXT SEARCH DICTIONARY my_synonym (
TEMPLATE = synonym,
SYNONYMS = my_synonyms
);
ALTER TEXT SEARCH CONFIGURATION english
ALTER MAPPING FOR asciiword
WITH my_synonym, english_stem;
SELECT * FROM ts_debug('english', 'Paris');
alias | description | token | dictionaries | dictionary | lexemes
-----------+-----------------+-------+---------------------------+------------+---------
asciiword | Word, all ASCII | Paris | {my_synonym,english_stem} | my_synonym | {paris}
El único parámetro requerido por la plantilla synonym es
SYNONYMS, que es el nombre base de su archivo de configuración
— my_synonyms en el ejemplo anterior.
El nombre completo del archivo será
$SHAREDIR/tsearch_data/my_synonyms.syn
(donde $SHAREDIR significa el directorio de datos compartidos
de la instalación de PostgreSQL).
El formato del archivo es solo una línea por palabra a sustituir, con la palabra
seguida por su sinónimo, separados por espacios en blanco. Las líneas en blanco
y los espacios finales se ignoran.
La plantilla synonym también tiene un parámetro opcional
CaseSensitive, cuyo valor predeterminado es false.
Cuando CaseSensitive es false, las palabras en el
archivo de sinónimos se convierten a minúsculas, al igual que los tokens de entrada.
Cuando es true, las palabras y los tokens no se convierten a minúsculas,
sino que se comparan tal cual.
Se puede colocar un asterisco (*) al final de un sinónimo en el archivo de
configuración. Esto indica que el sinónimo es un prefijo. El asterisco se ignora cuando
la entrada se utiliza en to_tsvector(), pero cuando se utiliza en
to_tsquery(), el resultado será un elemento de consulta con la marca de
coincidencia de prefijo (ver la Section 12.3.2).
Por ejemplo, supongamos que tenemos estas entradas en
$SHAREDIR/tsearch_data/synonym_sample.syn:
postgres pgsql postgresql pgsql postgre pgsql gogle googl indices index*
Entonces obtendremos estos resultados:
mydb=# CREATE TEXT SEARCH DICTIONARY syn (template=synonym, synonyms='synonym_sample');
mydb=# SELECT ts_lexize('syn', 'indices');
ts_lexize
-----------
{index}
(1 row)
mydb=# CREATE TEXT SEARCH CONFIGURATION tst (copy=simple);
mydb=# ALTER TEXT SEARCH CONFIGURATION tst ALTER MAPPING FOR asciiword WITH syn;
mydb=# SELECT to_tsvector('tst', 'indices');
to_tsvector
-------------
'index':1
(1 row)
mydb=# SELECT to_tsquery('tst', 'indices');
to_tsquery
------------
'index':*
(1 row)
mydb=# SELECT 'indexes are very useful'::tsvector;
tsvector
---------------------------------
'are' 'indexes' 'useful' 'very'
(1 row)
mydb=# SELECT 'indexes are very useful'::tsvector @@ to_tsquery('tst', 'indices');
?column?
----------
t
(1 row)
Un diccionario de tesauro (a veces abreviado como TZ) es una colección de palabras que incluye información sobre las relaciones entre palabras y frases, es decir, términos más amplios (BT), términos más específicos (NT), términos preferidos, términos no preferidos, términos relacionados, etc.
Básicamente, un diccionario de tesauro reemplaza todos los términos no preferidos por un término preferido y, opcionalmente, conserva también los términos originales para la indexación. La implementación actual de PostgreSQL del diccionario de tesauro es una extensión del diccionario de sinónimos con soporte de frases añadido. Un diccionario de tesauro requiere un archivo de configuración con el siguiente formato:
# este es un comentario sample word(s) : indexed word(s) more sample word(s) : more indexed word(s) ...
donde el símbolo de dos puntos (:) actúa como delimitador entre una
frase y su reemplazo.
Un diccionario de tesauro utiliza un subdiccionario (que se
especifica en la configuración del diccionario) para normalizar el texto de entrada
antes de verificar las coincidencias de frases. Solo es posible seleccionar un
subdiccionario. Se reportará un error si el subdiccionario no reconoce una palabra.
En ese caso, debes eliminar el uso de la palabra o enseñarle al subdiccionario a reconocerla.
Puedes colocar un asterisco (*) al comienzo de una palabra indexada para
evitar aplicar el subdiccionario a ella, pero todas las palabras de ejemplo
deben ser conocidas por el subdiccionario.
El diccionario de tesauro elige la coincidencia más larga si hay varias frases que coinciden con la entrada, y los empates se resuelven utilizando la última definición.
No se pueden especificar palabras vacías específicas reconocidas por el subdiccionario;
en su lugar, utiliza ? para marcar la ubicación donde puede aparecer cualquier
palabra vacía. Por ejemplo, asumiendo que a y the son palabras
vacías según el subdiccionario:
? one ? two : swsw
coincide con a one the two y con the one a two;
ambas serían reemplazadas por swsw.
Dado que un diccionario de tesauro tiene la capacidad de reconocer frases, debe recordar su estado e
interactuar con el analizador. Un diccionario de tesauro utiliza estas asignaciones para comprobar si debe
manejar la siguiente palabra o detener la acumulación. El diccionario de tesauro debe configurarse con cuidado.
Por ejemplo, si el diccionario de tesauro está asignado para manejar solo el token asciiword,
entonces una definición de diccionario de tesauro como one 7 no funcionará ya que el tipo de
token uint no está asignado al diccionario de tesauro.
Los tesauros se utilizan durante la indexación, por lo que cualquier cambio en los parámetros del diccionario de tesauro requiere reindexar. Para la mayoría de los demás tipos de diccionario, los cambios pequeños como añadir o eliminar palabras vacías no obligan a reindexar.
Para definir un nuevo diccionario de tesauro, utiliza la plantilla thesaurus.
Por ejemplo:
CREATE TEXT SEARCH DICTIONARY thesaurus_simple (
TEMPLATE = thesaurus,
DictFile = mythesaurus,
Dictionary = pg_catalog.english_stem
);
Aquí:
thesaurus_simple es el nombre del nuevo diccionario.
mythesaurus es el nombre base del archivo de configuración del tesauro.
(Su nombre completo será $SHAREDIR/tsearch_data/mythesaurus.ths,
donde $SHAREDIR significa el directorio de datos compartidos de la instalación).
pg_catalog.english_stem es el subdiccionario (aquí, un derivador inglés Snowball)
que se utilizará para la normalización del tesauro. Ten en cuenta que el subdiccionario tendrá su
propia configuración (por ejemplo, palabras vacías), la cual no se muestra aquí.
Ahora es posible vincular el diccionario de tesauro thesaurus_simple a los tipos
de tokens deseados en una configuración, por ejemplo:
ALTER TEXT SEARCH CONFIGURATION russian
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
WITH thesaurus_simple;
Consideremos un tesauro astronómico simple thesaurus_astro,
que contiene algunas combinaciones de palabras astronómicas:
supernovae stars : sn crab nebulae : crab
A continuación creamos un diccionario y vinculamos algunos tipos de tokens a un tesauro astronómico y a un derivador inglés (stemmer):
CREATE TEXT SEARCH DICTIONARY thesaurus_astro (
TEMPLATE = thesaurus,
DictFile = thesaurus_astro,
Dictionary = english_stem
);
ALTER TEXT SEARCH CONFIGURATION russian
ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
WITH thesaurus_astro, english_stem;
Ahora podemos ver cómo funciona.
ts_lexize no es muy útil para probar un tesauro, porque trata su entrada como
un solo token. En su lugar, podemos utilizar plainto_tsquery y
to_tsvector, que dividirán sus cadenas de entrada en múltiples tokens:
SELECT plainto_tsquery('supernova star');
plainto_tsquery
-----------------
'sn'
SELECT to_tsvector('supernova star');
to_tsvector
-------------
'sn':1
En principio, se puede utilizar to_tsquery si se entrecomilla el argumento:
SELECT to_tsquery('''supernova star''');
to_tsquery
------------
'sn'
Observa que supernova star coincide con supernovae
stars en thesaurus_astro porque especificamos el derivador
english_stem en la definición del tesauro. El derivador eliminó la
e y la s.
Para indexar la frase original así como el sustituto, simplemente inclúyela en la parte derecha de la definición:
supernovae stars : sn supernovae stars
SELECT plainto_tsquery('supernova star');
plainto_tsquery
-----------------------------
'sn' & 'supernova' & 'star'
La plantilla de diccionario Ispell admite
diccionarios morfológicos, que pueden normalizar muchas
formas lingüísticas diferentes de una palabra en el mismo lexema. Por ejemplo,
un diccionario Ispell de inglés puede hacer coincidir todas las declinaciones
y conjugaciones del término de búsqueda bank, por ejemplo,
banking, banked, banks,
banks' y bank's.
La distribución estándar de PostgreSQL no incluye ningún archivo de configuración de Ispell. Los diccionarios para una gran cantidad de idiomas están disponibles en Ispell. Además, se admiten algunos formatos de archivos de diccionarios más modernos: MySpell (OO < 2.0.1) y Hunspell (OO >= 2.0.2). Hay disponible una gran lista de diccionarios en la Wiki de OpenOffice.
Para crear un diccionario Ispell, realiza los siguientes pasos:
Descarga los archivos de configuración del diccionario. Los archivos de extensión de
OpenOffice tienen la extensión .oxt. Es necesario extraer
los archivos .aff y .dic, y cambiar sus extensiones a
.affix y .dict. Para algunos archivos de diccionario, también
es necesario convertir los caracteres a la codificación UTF-8 con comandos (por ejemplo, para un diccionario
de idioma noruego):
iconv -f ISO_8859-1 -t UTF-8 -o nn_no.affix nn_NO.aff iconv -f ISO_8859-1 -t UTF-8 -o nn_no.dict nn_NO.dic
Copia los archivos en el directorio $SHAREDIR/tsearch_data.
Carga los archivos en PostgreSQL con el siguiente comando:
CREATE TEXT SEARCH DICTIONARY english_hunspell (
TEMPLATE = ispell,
DictFile = en_us,
AffFile = en_us,
Stopwords = english);
Aquí, DictFile, AffFile y StopWords especifican los
nombres base de los archivos de diccionario, afijos y palabras vacías. El archivo de palabras vacías tiene el
mismo formato explicado anteriormente para el tipo de diccionario simple. El formato de los
otros archivos no se especifica aquí, pero está disponible en los sitios web mencionados anteriormente.
Los diccionarios Ispell suelen reconocer un conjunto limitado de palabras, por lo que deben ir seguidos de otro diccionario más amplio; por ejemplo, un diccionario Snowball, que reconoce todo.
El archivo .affix de Ispell tiene la siguiente estructura:
prefixes
flag *A:
. > RE # As in enter > reenter
suffixes
flag T:
E > ST # As in late > latest
[^AEIOU]Y > -Y,IEST # As in dirty > dirtiest
[AEIOU]Y > EST # As in gray > grayest
[^EY] > EST # As in small > smallest
Y el archivo .dict tiene la siguiente estructura:
lapse/ADGRS lard/DGRS large/PRTY lark/MRS
El formato del archivo .dict es:
basic_form/affix_class_name
En el archivo .affix, cada bandera de afijo se describe en el
siguiente formato:
condition > [-stripping_letters,] adding_affix
Aquí, la condición tiene un formato similar al de las expresiones regulares.
Puede utilizar agrupaciones [...] y [^...].
Por ejemplo, [AEIOU]Y significa que la última letra de la palabra
es "y" y la penúltima letra es "a",
"e", "i", "o" o "u".
[^EY] significa que la última letra no es ni "e"
ni "y".
Los diccionarios Ispell admiten la división de palabras compuestas;
una característica muy útil.
Ten en cuenta que el archivo de afijos debe especificar una bandera especial utilizando la
sentencia compoundwords controlled que marca las palabras del diccionario
que pueden participar en la formación de palabras compuestas:
compoundwords controlled z
Aquí tienes algunos ejemplos para el idioma noruego:
SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent');
{over,buljong,terning,pakk,mester,assistent}
SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk');
{sjokoladefabrikk,sjokolade,fabrikk}
El formato de MySpell es un subconjunto de Hunspell.
El archivo .affix de Hunspell tiene la siguiente
estructura:
PFX A Y 1 PFX A 0 re . SFX T N 4 SFX T 0 st e SFX T y iest [^aeiou]y SFX T 0 est [aeiou]y SFX T 0 est [^ey]
La primera línea de una clase de afijo es la cabecera. Los campos de las reglas de afijo se enumeran después de la cabecera:
Nombre del parámetro (PFX o SFX)
Bandera (nombre de la clase de afijo)
Eliminación de caracteres del principio (en el prefijo) o del final (en el sufijo) de la palabra
Adición del afijo
Condición que tiene un formato similar al formato de las expresiones regulares.
El archivo .dict se parece al archivo .dict de
Ispell:
larder/M lardy/RT large/RSPMYT largehearted
MySpell no admite palabras compuestas. Hunspell tiene un soporte sofisticado para palabras compuestas. En la actualidad, PostgreSQL implementa únicamente las operaciones básicas de palabras compuestas de Hunspell.
La plantilla de diccionario Snowball se basa en un proyecto
de Martin Porter, inventor del popular algoritmo de derivación de Porter
para el idioma inglés. Snowball ahora proporciona algoritmos de derivación para
muchos idiomas (consulta el sitio de Snowball
para obtener más información). Cada algoritmo entiende cómo reducir formas variantes comunes
de palabras a una ortografía base, o raíz (stem), dentro de su idioma. Un diccionario Snowball
requiere un parámetro de idioma (language) para identificar qué derivador usar,
y opcionalmente puede especificar un nombre de archivo de palabras vacías (stopword)
que proporciona una lista de palabras a eliminar.
(Las listas de palabras vacías estándar de PostgreSQL también son
proporcionadas por el proyecto Snowball).
Por ejemplo, existe una definición integrada equivalente a:
CREATE TEXT SEARCH DICTIONARY english_stem (
TEMPLATE = snowball,
Language = english,
StopWords = english
);
El formato del archivo de palabras vacías es el mismo que ya se ha explicado.
Un diccionario Snowball reconoce todo, sea o no capaz de simplificar la palabra, por lo que debe colocarse al final de la lista de diccionarios. Es inútil tenerlo antes de cualquier otro diccionario porque un token nunca pasará a través de él al siguiente diccionario.