12.6. Словари#

12.6. Словари

12.6. Словари #

Все словари используются для исключения слов, которые не должны учитываться при поиске (стоп-слова), а также для нормализации слов, чтобы различные производные формы одного и того же слова совпадали. Успешно нормализованное слово называется лексемой. Помимо улучшения качества поиска, нормализация и удаление стоп-слов уменьшают размер представления документа в виде tsvector, что улучшает производительность. Нормализация не всегда имеет лингвистическое значение и обычно зависит от семантики приложения.

Некоторые примеры нормализации:

  • Лингвистические — словари Ispell пытаются привести входные слова к нормализованной форме; словари стеммера удаляют окончания слова

  • Местоположения URL могут быть канонизированы, чтобы сделать эквивалентные URL-адреса совпадающими:

    • http://www.pgsql.ru/db/mw/index.html

    • http://www.pgsql.ru/db/mw/

    • http://www.pgsql.ru/db/../db/mw/index.html

  • Имена цветов могут быть заменены их шестнадцатеричными значениями, например, red, green, blue, magenta -> FF0000, 00FF00, 0000FF, FF00FF

  • Если индексируются числа, можно удалить некоторое количество десятичных знаков, чтобы уменьшить диапазон возможных чисел, так что, например, 3.14159265359, 3.1415926, 3.14 будут одинаковыми после нормализации, если после десятичной точки оставить только две цифры.

Словарь - это программа, которая принимает компонент в качестве входных данных и возвращает:

  • массив лексем, если входной компонент известен словарю (обратите внимание, что один компонент может порождать более одной лексемы)

  • один отдельный лексем с установленным флагом TSL_FILTER, чтобы заменить исходный компонент новым компонентом, который будет передан последующим словарям (словарь, выполняющий это, называется фильтрующим словарем)

  • пустой массив, если словарь знает компонент, но это стоп-слово

  • NULL если словарь не распознает входной компонент

Tantor BE предоставляет предопределенные словари для многих языков. Также есть несколько предопределенных шаблонов, которые можно использовать для создания новых словарей с настраиваемыми параметрами. Каждый предопределенный шаблон словаря описан ниже. Если ни один из существующих шаблонов не подходит, можно создать новые; см. раздел contrib/ в дистрибутиве Tantor BE для примеров.

Конфигурация полнотекстового поиска связывает парсер с набором словарей для обработки выходных компонентов парсера. Для каждого типа компонента, который может вернуть парсер, конфигурацией указывается отдельный список словарей. Когда парсер находит компонент этого типа, каждый словарь в списке проверяется по очереди, пока какой-то словарь не распознает его как известное слово. Если компонент идентифицируется как стоп-слово или если ни один словарь не распознает компонент, он будет отброшен и не будет проиндексирован или использован для поиска. Обычно первый словарь, который возвращает не-NULL результат, определяет результат, и остальные словари не проверяются; но фильтрующий словарь может заменить заданное слово на измененное слово, которое затем передается последующим словарям.

Общее правило для настройки списка словарей заключается в том, чтобы сначала разместить самый узкий, наиболее специфический словарь, затем более общие словари, заканчивая очень общим словарем, таким как стеммер Snowball или simple, который распознает все. Например, для поиска, связанного с астрономией (astro_en конфигурация), можно привязать тип компонента asciiword (ASCII-слово) к синонимическому словарю астрономических терминов, общему английскому словарю и английскому стеммеру Snowball:

ALTER TEXT SEARCH CONFIGURATION astro_en
    ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem;

Словарь фильтрации может быть размещен в любом месте списка, кроме конца, где он был бы бесполезен. Словари фильтрации полезны для частичной нормализации слов, чтобы упростить задачу последующих словарей. Например, словарь фильтрации может использоваться для удаления ударений с ударных букв, как это делает модуль unaccent.

12.6.1. Стоп-слова #

Стоп-слова - это слова, которые очень часто встречаются в почти каждом документе и не имеют дискриминационной ценности. Поэтому их можно игнорировать в контексте полнотекстового поиска. Например, в каждом английском тексте содержатся слова, такие как a и the, поэтому их хранение в индексе бесполезно. Однако стоп-слова влияют на позиции в tsvector, что, в свою очередь, влияет на ранжирование:

SELECT to_tsvector('english', 'in the list of stop words');
        to_tsvector
----------------------------
 'list':3 'stop':5 'word':6

Отсутствующие позиции 1, 2, 4 обусловлены стоп-словами. Ранги, рассчитанные для документов с и без стоп-слов, существенно отличаются:

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

Все зависит от конкретного словаря, как он обрабатывает стоп-слова. Например, словари ispell сначала нормализуют слова, а затем проверяют список стоп-слов, в то время как стеммеры Snowball сначала проверяют список стоп-слов. Причина различного поведения заключается в попытке уменьшить шум.

12.6.2. Простой Словарь #

Шаблон simple словаря работает путем преобразования входного компонента в нижний регистр и проверки его по файлу стоп-слов. Если компонент найден в файле, то возвращается пустой массив, что приводит к отбрасыванию компонента. Если же компонент не найден, то возвращается нормализованная лексема в нижнем регистре. В качестве альтернативы, словарь может быть настроен на отображение нераспознанных слов, не являющихся стоп-словами, что позволяет передавать их следующему словарю в списке.

Вот пример определения словаря, использующего шаблон simple:

CREATE TEXT SEARCH DICTIONARY public.simple_dict (
    TEMPLATE = pg_catalog.simple,
    STOPWORDS = english
);

Здесь english - это базовое имя файла стоп-слов. Полное имя файла будет $SHAREDIR/tsearch_data/english.stop, где $SHAREDIR означает общий каталог данных установки Tantor BE, часто /usr/local/share/postgresql (используйте pg_config --sharedir, чтобы определить его, если вы не уверены). Формат файла просто представляет собой список слов, по одному на строку. Пустые строки и конечные пробелы игнорируются, и прописные буквы преобразуются в строчные, но никакой другой обработки содержимого файла не выполняется.

Теперь можно протестировать наш словарь:

SELECT ts_lexize('public.simple_dict', 'YeS');
 ts_lexize
-----------
 {yes}

SELECT ts_lexize('public.simple_dict', 'The');
 ts_lexize
-----------
 {}

Мы также можем выбрать возвращать NULL, вместо слова в нижнем регистре, если оно не найдено в файле стоп-слов. Это поведение выбирается путем установки параметра Accept словаря в значение false. Продолжая пример:

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
-----------
 {}

С сохранением настроек по умолчанию Accept = true имеет смысл размещать только simple словарь в конце списка словарей, поскольку он никогда не передаст компонент следующему словарю. Напротив, Accept = false имеет смысл только при наличии хотя бы одного следующего словаря.

Предостережение

Большинство типов словарей полагается на файлы конфигурации, такие как файлы стоп-слов. Эти файлы должны быть сохранены в кодировке UTF-8. Они будут преобразованы в фактическую кодировку базы данных, если она отличается, при их чтении на сервере.

Предостережение

Обычно сессия базы данных считывает файл конфигурации словаря только один раз, когда он впервые используется в сессии. Если вы измените файл конфигурации и хотите, чтобы существующие сессии получили новое содержимое, выполните команду ALTER TEXT SEARCH DICTIONARY на словаре. Это может быть фиктивное обновление, которое фактически не изменяет значения параметров.

12.6.3. Словарь синонимов #

Этот шаблон словаря используется для создания словарей, заменяющих слово синонимом. Фразы не поддерживаются (для этого используйте шаблон тезауруса (Раздел 12.6.4). Синонимический словарь может использоваться для преодоления лингвистических проблем, например, чтобы предотвратить сокращение слова Paris до pari в английском словаре стеммера. Достаточно иметь строку Paris paris в синонимическом словаре и поместить ее перед словарем english_stem. Например:

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}

Единственным параметром, требуемым шаблоном synonym, является SYNONYMS, который является базовым именем его конфигурационного файла — my_synonyms в приведенном выше примере. Полное имя файла будет $SHAREDIR/tsearch_data/my_synonyms.syn (где $SHAREDIR означает общий каталог данных установки Tantor BE). Формат файла состоит из одной строки на каждое слово, которое должно быть заменено, с последующим синонимом, разделенными пробелом. Пустые строки и конечные пробелы игнорируются.

Шаблон synonym также имеет необязательный параметр CaseSensitive, который по умолчанию установлен в false. Когда CaseSensitive равно false, слова в файле синонимов приводятся к нижнему регистру, а также вводимые компоненты. Когда он равен true, слова и компоненты не приводятся к нижнему регистру, но сравниваются как есть.

Звездочка (*) может быть помещена в конце синонима в файле конфигурации. Это указывает на то, что синоним является префиксом. Звездочка игнорируется при использовании записи в to_tsvector(), но когда она используется в to_tsquery(), результатом будет элемент запроса с маркером совпадения префикса (см. Раздел 12.3.2). Например, предположим, что у нас есть следующие записи в $SHAREDIR/tsearch_data/synonym_sample.syn:

postgres        pgsql
postgresql      pgsql
postgre pgsql
gogle   googl
indices index*

Затем мы получим следующие результаты:

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)

12.6.4. Тезаурус Словарь #

Словарь тезауруса (иногда сокращенно называемый TZ) - это сборник слов, который содержит информацию о взаимосвязях слов и фраз, таких как более общие термины (BT), более узкие термины (NT), предпочтительные термины, непредпочтительные термины, связанные термины и т. д.

В основном, тезаурусный словарь заменяет все непредпочтительные термины одним предпочтительным термином и, по желанию, сохраняет исходные термины для индексации. Текущая реализация тезаурусного словаря в Tantor BE является расширением синонимического словаря с добавленной поддержкой фраз. Для тезаурусного словаря требуется файл конфигурации следующего формата:

# this is a comment
sample word(s) : indexed word(s)
more sample word(s) : more indexed word(s)
...

где символ двоеточия (:) действует как разделитель между фразой и ее заменой.

Словарь тезауруса использует подсловарь (который указывается в конфигурации словаря) для нормализации входного текста перед проверкой на соответствие фраз. Возможно выбрать только один подсловарь. Если подсловарь не распознает слово, будет выдана ошибка. В этом случае вы должны удалить использование этого слова или обучить подсловарь его распознаванию. Вы можете поставить звездочку (*) в начале индексированного слова, чтобы пропустить его обработку подсловарем, но все примеры слов должны быть известны подсловарю.

Словарь тезауруса выбирает самое длинное совпадение, если есть несколько фраз, соответствующих вводу, и разрешает противоречия, используя последнее определение.

Определенные стоп-слова, распознаваемые подсловарем, нельзя указать; вместо этого используйте ? для обозначения места, где может появиться любое стоп-слово. Например, предположим, что a и the являются стоп-словами согласно подсловарю:

? one ? two : swsw

соответствует a one the two и the one a two; оба будут заменены на swsw.

Так как тезаурусный словарь имеет возможность распознавать фразы, он должен запоминать свое состояние и взаимодействовать с парсером. Тезаурусный словарь использует эти присваивания, чтобы проверить, должен ли он обрабатывать следующее слово или прекратить накопление. Тезаурусный словарь должен быть настроен тщательно. Например, если тезаурусный словарь назначен для обработки только компонента asciiword, то определение тезаурусного словаря вида one 7 не будет работать, так как тип компонента uint не назначен для тезаурусного словаря.

Предостережение

Тезаурусы используются во время индексации, поэтому любое изменение параметров словаря тезауруса требует повторной индексации. Для большинства других типов словарей небольшие изменения, такие как добавление или удаление стоп-слов, не приводят к принудительной повторной индексации.

12.6.4.1. Конфигурация тезауруса #

Для определения нового словаря тезауруса используйте шаблон thesaurus. Например:

CREATE TEXT SEARCH DICTIONARY thesaurus_simple (
    TEMPLATE = thesaurus,
    DictFile = mythesaurus,
    Dictionary = pg_catalog.english_stem
);

Здесь:

  • thesaurus_simple - это имя нового словаря.

  • mythesaurus - это базовое имя файла конфигурации тезауруса. (Полное имя файла будет $SHAREDIR/tsearch_data/mythesaurus.ths, где $SHAREDIR означает общий каталог установки данных).

  • pg_catalog.english_stem - это подсловарь (здесь используется английский стеммер Snowball) для использования нормализации тезауруса. Обратите внимание, что у подсловаря будет своя собственная конфигурация (например, стоп-слова), которая здесь не показана.

Теперь возможно привязать словарь тезауруса thesaurus_simple к желаемым типам компонентов в конфигурации, например:

ALTER TEXT SEARCH CONFIGURATION russian
    ALTER MAPPING FOR asciiword, asciihword, hword_asciipart
    WITH thesaurus_simple;

12.6.4.2. Пример тезауруса #

Рассмотрим простой астрономический тезаурус thesaurus_astro, который содержит некоторые астрономические словосочетания:

supernovae stars : sn
crab nebulae : crab

Ниже мы создаем словарь и связываем некоторые типы компонентов с астрономическим тезаурусом и английским стеммером:

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;

Теперь можно увидеть, как это работает. ts_lexize не очень полезна для тестирования тезауруса, потому что она рассматривает входные данные как один компонент. Вместо этого можно использовать plainto_tsquery и to_tsvector, которые разобьют входные строки на несколько компонентов:

SELECT plainto_tsquery('supernova star');
 plainto_tsquery
-----------------
 'sn'

SELECT to_tsvector('supernova star');
 to_tsvector
-------------
 'sn':1

В принципе, можно использовать to_tsquery, если заключить аргумент в кавычки:

SELECT to_tsquery('''supernova star''');
 to_tsquery
------------
 'sn'

Обратите внимание, что supernova star соответствует supernovae stars в thesaurus_astro, потому что мы указали стеммер english_stem в определении тезауруса. Стеммер удалил e и s.

Чтобы проиндексировать исходную фразу, а также ее замену, просто включите ее в правую часть определения:

supernovae stars : sn supernovae stars

SELECT plainto_tsquery('supernova star');
       plainto_tsquery
-----------------------------
 'sn' & 'supernova' & 'star'

12.6.5. Ispell Словарь #

Словарный шаблон Ispell поддерживает морфологические словари, которые могут нормализовать множество различных лингвистических форм слова в один и тот же лексем. Например, английский словарь Ispell может сопоставить все склонения и спряжения искомого термина bank, например, banking, banked, banks, banks' и bank's.

Стандартное распространение Tantor BE не включает в себя файлы конфигурации Ispell. Словари для большого числа языков доступны на сайте Ispell. Также поддерживаются некоторые более современные форматы файлов словарей — MySpell (OO < 2.0.1) и Hunspell (OO >= 2.0.2). Большой список словарей доступен на вики-странице OpenOffice.

Для создания словаря Ispell выполните следующие шаги:

  • Скачайте файлы конфигурации словаря. Файлы расширения OpenOffice имеют расширение .oxt. Необходимо извлечь файлы .aff и .dic, изменить расширения на .affix и .dict. Для некоторых словарей также требуется преобразование символов в кодировку UTF-8 с помощью команд (например, для словаря на норвежском языке).

    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
    

  • Скопируйте файлы в каталог $SHAREDIR/tsearch_data.

  • загрузите файлы в PostgreSQL с помощью следующей команды:

    CREATE TEXT SEARCH DICTIONARY english_hunspell (
        TEMPLATE = ispell,
        DictFile = en_us,
        AffFile = en_us,
        Stopwords = english);
    

Здесь, DictFile, AffFile и StopWords указываются базовые имена файлов словаря, аффиксов и стоп-слов. Файл стоп-слов имеет тот же формат, о котором говорилось выше для типа словаря simple. Формат других файлов не указан здесь, но доступен на указанных выше веб-сайтах.

Ispell словари обычно распознают ограниченный набор слов, поэтому они должны быть дополнены другим более широким словарем; например, словарем Snowball, который распознает все.

Файл .affix приложения Ispell имеет следующую структуру:

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

И файл .dict имеет следующую структуру:

lapse/ADGRS
lard/DGRS
large/PRTY
lark/MRS

Формат файла .dict следующий:

basic_form/affix_class_name

В файле .affix каждый флаг аффикса описывается в следующем формате:

condition > [-stripping_letters,] adding_affix

Здесь условие имеет формат, аналогичный формату регулярных выражений. Оно может использовать группировки [...] и [^...]. Например, [AEIOU]Y означает, что последняя буква слова это "y", а предпоследняя буква - "a", "e", "i", "o" или "u". [^EY] означает, что последняя буква не является ни "e", ни "y".

Поддержка словарей Ispell разделения сложных слов; полезная функция. Обратите внимание, что файл аффиксов должен указывать специальный флаг с использованием оператора compoundwords controlled, который помечает слова словаря, которые могут участвовать в образовании сложных слов:

compoundwords  controlled z

Вот несколько примеров для норвежского языка:

SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent');
   {over,buljong,terning,pakk,mester,assistent}
SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk');
   {sjokoladefabrikk,sjokolade,fabrikk}

MySpell формат является подмножеством Hunspell. Файл .affix Hunspell имеет следующую структуру:

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]

Первая строка класса аффикса является заголовком. Поля правил аффиксов перечисляются после заголовка:

  • имя параметра (PFX или SFX)

  • флаг (название класса аффикса)

  • удаление символов в начале (префиксе) или конце (суффиксе) слова

  • добавление приставки

  • условие, которое имеет формат, аналогичный формату регулярных выражений.

Файл .dict выглядит как файл .dict приложения Ispell:

larder/M
lardy/RT
large/RSPMYT
largehearted

Примечание

MySpell не поддерживает сложные слова. Hunspell имеет сложную поддержку сложных слов. В настоящее время Tantor BE реализует только основные операции со сложными словами Hunspell.

12.6.6. Snowball Словарь #

Шаблон словаря Snowball основан на проекте Мартина Портера (Martin Porter), изобретателя популярного алгоритма стемминга Портера для английского языка. Сейчас Snowball предоставляет алгоритмы стемминга для многих языков (см. сайт Snowball для получения дополнительной информации). Каждый алгоритм понимает, как привести общие варианты слов к базовой или корневой форме в пределах своего языка. Для работы со словарем Snowball требуется указать параметр language, чтобы определить, какой стеммер использовать, и при необходимости можно указать имя файла stopword, содержащего список слов для исключения. (Стандартные списки стоп-слов Tantor BE также предоставляются проектом Snowball). Например, встроено определение, эквивалентное

CREATE TEXT SEARCH DICTIONARY english_stem (
    TEMPLATE = snowball,
    Language = english,
    StopWords = english
);

Формат файла стоп-слов такой же, как уже объяснялось.

Словарь Snowball распознает все, независимо от того, может ли он упростить слово, поэтому его следует поместить в конец списка словарей. Бесполезно иметь его перед любым другим словарем, потому что компонент никогда не пройдет через него к следующему словарю.