12.1. Введение#
12.1. Введение #
Система полнотекстового поиска (или просто полнотекстовый поиск) обеспечивает возможность идентификации документов на естественном языке, удовлетворяющих запросу и, при необходимости, их сортировки по степени соответствия запросу. Самый распространенный тип поиска - это поиск всех документов, содержащих заданные термины запроса и возвращение их в порядке similarity с запросом. Понятия query
и similarity
очень гибкие и зависят от конкретного приложения. Простейший поиск рассматривает query
как набор слов, а similarity
- как частоту встречаемости слов запроса в документе.
Текстовые операторы поиска существуют в базах данных уже много лет.
Tantor BE имеет
операторы ~
, ~*
, LIKE
и
ILIKE
для текстовых типов данных, но они не обладают
многими необходимыми свойствами, требуемыми современными информационными системами:
Нет лингвистической поддержки, даже для английского языка. Регулярные выражения не достаточны, так как они не могут легко обрабатывать производные слова, например,
satisfies
иsatisfy
. Вы можете пропустить документы, содержащиеsatisfies
, хотя, вероятно, вы хотели бы найти их при поискеsatisfy
. Возможно использоватьOR
для поиска нескольких производных форм, но это трудоемко и подвержено ошибкам (некоторые слова могут иметь несколько тысяч производных).Они не предоставляют упорядочивания (рейтинга) результатов поиска, что делает их неэффективными, когда найдено тысячи соответствующих документов.
Они обычно работают медленно, потому что не поддерживают индексы, поэтому для каждого поиска они должны обрабатывать все документы.
Полнотекстовый индекс позволяет предварительно обрабатывать документы и сохранять индекс для последующего быстрого поиска. Предварительная обработка включает в себя:
Разбор документов на компоненты. Это полезно для идентификации различных классов компонентов, например, чисел, слов, сложных слов, адресов электронной почты, чтобы их можно было обрабатывать по-разному. В принципе, классы компонентов зависят от конкретного приложения, но для большинства целей достаточно использовать предопределенный набор классов. Tantor BE использует парсер для выполнения этого шага. Предоставляется стандартный парсер, и можно создавать пользовательские парсеры для конкретных потребностей.
Преобразование компонентов в лексемы. Лексема - это строка, такая же, как компонент, но она была нормализована, чтобы разные формы одного и того же слова стали похожими. Например, нормализация почти всегда включает преобразование прописных букв в строчные и часто включает удаление суффиксов (таких как
s
илиes
в английском языке). Это позволяет поиску находить варианты одного и того же слова, не вводя все возможные варианты вручную. Кроме того, на этом этапе обычно удаляются стоп-слова, которые являются настолько распространенными, что бесполезны для поиска. (Короче говоря, компоненты - это необработанные фрагменты текста документа, а лексемы - это слова, которые считаются полезными для индексирования и поиска). Tantor BE использует словари для выполнения этого шага. Предоставляются различные стандартные словари, и можно создавать пользовательские словари для конкретных потребностей.Сохранение предварительно обработанных документов, оптимизированных для поиска. Например, каждый документ может быть представлен в виде отсортированного массива нормализованных лексем. Вместе с лексемами часто желательно хранить информацию о позиции, чтобы использовать для ранжирования по близости, так что документ, содержащий более плотную область слов запроса, получает более высокий ранг, чем документ с разбросанными словами запроса.
Словари позволяют тонко настраивать процесс нормализации компонентов. С помощью соответствующих словарей вы можете:
Определите стоп-слова, которые не должны быть проиндексированы.
Сопоставьте все синонимы с одним словом, используя Ispell.
Сопоставьте фразы с одним словом, используя тезаурус.
Сопоставьте различные варианты слова с канонической формой, используя словарь Ispell.
Соответствие различных вариаций слова к канонической форме с использованием правил стеммера Snowball.
Предоставляется тип данных tsvector
для хранения предварительно обработанных документов, а также тип tsquery
для представления обработанных запросов (Раздел 8.11). Для этих типов данных доступно множество функций и операторов (Раздел 9.13), наиболее важным из которых является оператор сопоставления @@
, который мы представляем в разделе Раздел 12.1.2. Полнотекстовые поиски могут быть ускорены с использованием индексов (Раздел 12.9).
12.1.1. Что такое документ? #
Документ является единицей поиска в системе полнотекстового поиска; например, статья в журнале или электронное письмо. Поисковый движок должен уметь анализировать документы и сохранять связи лексем (ключевых слов) с их родительским документом. Позже эти связи используются для поиска документов, содержащих запрашиваемые слова.
Для поиска внутри Tantor BE, документ обычно представляет собой текстовое поле в строке таблицы базы данных, или, возможно, комбинацию (конкатенацию) таких полей, возможно, хранящихся в нескольких таблицах или полученных динамически. Другими словами, документ может быть создан из разных частей для индексации и он может не быть храниться где-либо в целом. Например:
SELECT title || ' ' || author || ' ' || abstract || ' ' || body AS document FROM messages WHERE mid = 12; SELECT m.title || ' ' || m.author || ' ' || m.abstract || ' ' || d.body AS document FROM messages m, docs d WHERE m.mid = d.did AND m.mid = 12;
Примечание
Собственно, в этих примерах запросов следует использовать функцию coalesce
, чтобы предотвратить преобразование отдельного атрибута NULL
в NULL
результат для всего документа.
Еще одна возможность - хранить документы в виде простых текстовых файлов в файловой системе. В этом случае база данных может использоваться для хранения полнотекстового индекса и выполнения поисковых запросов, а для получения документа из файловой системы может использоваться некий уникальный идентификатор. Однако, для получения файлов извне базы данных требуются привилегии суперпользователя или поддержка специальных функций, поэтому это обычно менее удобно, чем хранение всех данных внутри Tantor BE. Кроме того, хранение всего внутри базы данных обеспечивает легкий доступ к метаданным документа для помощи в индексации и отображении.
Для целей текстового поиска каждый документ должен быть преобразован в предварительно обработанный формат tsvector
. Поиск и ранжирование выполняются полностью на основе представления документа в виде tsvector
- исходный текст нужно извлекать только тогда, когда документ был выбран для отображения пользователю. Поэтому мы часто говорим о tsvector
как о документе, но, конечно же, это только компактное представление полного документа.
12.1.2. Основное сопоставление текста #
Полнотекстовый поиск в Tantor BEоснован на операторе сравнения @@
, который возвращает true
, если tsvector
(документ) соответствует tsquery
(запросу). Неважно, какой тип данных указан первым.
SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery; ?column? ---------- t SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector; ?column? ---------- f
Как и предыдущий пример подразумевает, tsquery
- это не просто сырой текст, так же как и tsvector
. tsquery
содержит поисковые термины, которые должны быть уже нормализованными лексемами и могут объединять несколько терминов с помощью операторов AND, OR, NOT и FOLLOWED BY. (Подробности синтаксиса см. в Раздел 8.11.2). Существуют функции to_tsquery
, plainto_tsquery
и phraseto_tsquery
, которые помогают преобразовывать текст, написанный пользователем, в правильный tsquery
, в основном путем нормализации слов, появляющихся в тексте. Аналогично, to_tsvector
используется для разбора и нормализации строки документа. Таким образом, на практике поиск по тексту будет выглядеть примерно так:
SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat'); ?column? ---------- t
Обратите внимание, что это совпадение не будет успешным, если записано как
SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat'); ?column? ---------- f
поскольку здесь не будет нормализации слова rats
, элементы tsvector
представляют собой лексемы, которые предполагаются уже нормализованными, поэтому rats
не соответствует rat
.
Оператор @@
также поддерживает ввод типа text
,
позволяя пропустить явное преобразование текстовой строки в типы tsvector
или tsquery
в простых случаях. Доступны следующие варианты:
tsvector @@ tsquery tsquery @@ tsvector text @@ tsquery text @@ text
Первые два из них мы уже видели.
Форма text
@@
tsquery
эквивалентна to_tsvector(x) @@ y
.
Форма text
@@
text
эквивалентна to_tsvector(x) @@ plainto_tsquery(y)
.
Внутри tsquery
оператор &
(AND) указывает, что оба его аргумента должны присутствовать в документе для совпадения. Аналогично, оператор |
(OR) указывает, что должен присутствовать хотя бы один из его аргументов, в то время как оператор !
(NOT) указывает, что его аргумент должен не присутствовать для совпадения.
Например, запрос fat & ! rat
совпадает с документами, содержащими слово fat
, но не содержащими слово rat
.
Поиск фраз возможен с помощью оператора <->
(FOLLOWED BY) типа tsquery
, который сопоставляется только в том случае, если его аргументы имеют совпадения, которые являются смежными и в заданном порядке. Например:
SELECT to_tsvector('fatal error') @@ to_tsquery('fatal <-> error'); ?column? ---------- t SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal <-> error'); ?column? ---------- f
Существует более общая версия оператора FOLLOWED BY, имеющая форму <
,
где N
>N
- целое число, указывающее разницу между позициями совпадающих лексем. <1>
эквивалентно
<->
, в то время как <2>
позволяет появление ровно одной другой лексемы между совпадениями, и так далее.
Функция phraseto_tsquery
использует этот оператор для создания tsquery
, который может сопоставляться с многословной
фразой, когда некоторые из слов являются стоп-словами. Например:
SELECT phraseto_tsquery('cats ate rats'); phraseto_tsquery ------------------------------- 'cat' <-> 'ate' <-> 'rat' SELECT phraseto_tsquery('the cats ate the rats'); phraseto_tsquery ------------------------------- 'cat' <-> 'ate' <2> 'rat'
Особый случай, который иногда бывает полезным, заключается в том, что <0>
может использоваться для требования совпадения двух шаблонов с одним и тем же словом.
Скобки могут использоваться для управления вложенностью операторов tsquery
.
Без скобок, оператор |
имеет наименьший приоритет,
затем &
, затем <->
,
и !
имеет наивысший приоритет.
Стоит отметить, что операторы AND/OR/NOT имеют несколько иное значение, когда они находятся в аргументах оператора FOLLOWED BY, чем когда они находятся вне его, потому что внутри FOLLOWED BY точное положение совпадения имеет значение. Например, обычно !x
соответствует только документам, не содержащим x
в любом месте. Но !x <-> y
соответствует y
, если он не находится сразу после x
; наличие x
в другом месте документа не препятствует совпадению. Еще один пример: x & y
обычно требует, чтобы x
и y
появлялись где-то в документе, но (x & y) <-> z
требует, чтобы x
и y
совпадали в одном месте, непосредственно перед z
. Таким образом, этот запрос ведет себя иначе, чем x <-> z & y <-> z
, который будет соответствовать документу, содержащему две отдельные последовательности x z
и y z
. (Этот конкретный запрос бесполезен в текущей форме, так как x
и y
не могут совпадать в одном месте; но с более сложными ситуациями, такими как шаблоны с префиксным совпадением, запрос такой формы может быть полезен).
12.1.3. Настройки #
Вышеуказанные примеры являются простыми примерами текстового поиска. Как уже упоминалось ранее, функциональность полнотекстового поиска включает возможность выполнения множества других действий: пропуск индексации определенных слов (стоп-слов), обработка синонимов и использование сложного анализа, например, анализ на основе не только пробелов. Эта функциональность контролируется конфигурациями полнотекстового поиска. Tantor BE поставляется с предопределенными конфигурациями для многих языков, и вы можете легко создать свои собственные конфигурации. (Команда \dF
в psql показывает все доступные конфигурации).
Во время установки выбирается соответствующая конфигурация, и default_text_search_config устанавливается соответственно в файле postgresql.conf
. Если вы используете одну и ту же конфигурацию полнотекстового поиска для всего кластера, вы можете использовать значение в файле postgresql.conf
. Чтобы использовать разные конфигурации во всем кластере, но одну и ту же конфигурацию в каждой базе данных, используйте ALTER DATABASE ... SET
. В противном случае, вы можете установить default_text_search_config
в каждой сессии.
Каждая функция текстового поиска, которая зависит от конфигурации, имеет необязательный аргумент regconfig
, чтобы конфигурацию можно было указать явно. default_text_search_config
используется только в том случае, если этот аргумент не указан.
Для упрощения создания настраиваемых конфигураций текстового поиска, конфигурация составляется из более простых объектов базы данных. Средство текстового поиска Tantor BE предоставляет четыре типа объектов базы данных, связанных с конфигурацией:
Синтаксические анализаторы текстового поиска разбивают документы на компоненты и классифицируют каждый компонент (например, как слова или числа).
Словари текстового поиска преобразуют компоненты в нормализованную форму и отклоняют стоп-слова.
Шаблоны текстового поиска предоставляют функции, лежащие в основе словарей. (Словарь просто определяет шаблон и набор параметров для шаблона).
Конфигурации полнотекстового поиска выбирают парсер и набор словарей для нормализации компонентов, созданных парсером.
Парсеры и шаблоны текстового поиска создаются на основе низкоуровневых функций на языке C; поэтому для разработки новых требуется знание языка C, а для установки в базу данных — привилегии суперпользователя. (В дистрибутиве Tantor BE в каталоге contrib/
содержатся примеры дополнительных парсеров и шаблонов). Так как словари и конфигурации являются параметризованными и связывающими элементами некоторых базовых парсеров и шаблонов, для создания нового словаря или конфигурации не требуется особых привилегий. Примеры создания пользовательских словарей и конфигураций приведены позже в этой главе.