Примеры в предыдущем разделе иллюстрировали механизм полнотекстового поиска с использованием простых константных строк. В данном разделе рассматривается выполнение поиска по табличным данным, в том числе с использованием индексов.
Полнотекстовый поиск может быть выполнен без использования индексных структур. Простой запрос для вывода заголовок каждой строки, содержащей слово
friend в поле body имеет следующий вид:
SELECT title
FROM pgweb
WHERE to_tsvector('english', body) @@ to_tsquery('english', 'friend');
Данный запрос также вернет связанные слова, такие как друзья
и дружелюбный, так как все они сводятся к одной и той же нормализованной лексеме.
Приведенный выше запрос определяет, что english конфигурация
должна использоваться для синтаксического анализа и нормализации строк. Кроме того, параметры конфигурации можно опустить:
SELECT title
FROM pgweb
WHERE to_tsvector(body) @@ to_tsquery('friend');
В данном запросе будет использована конфигурация, заданная параметром default_text_search_config.
Более сложный пример — выборка десяти последних документов, содержащих создать и
таблица в заголовок или body:
SELECT title
FROM pgweb
WHERE to_tsvector(title || ' ' || body) @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;
Для упрощения были опущены coalesce вызовы функций,
необходимые для поиска строк, содержащих NULL
в одном из двух полей.
Хотя эти запросы могут выполняться и без индекса, для большинства приложений такой подход будет слишком медленным, за исключением случаев проведения разовых поисковых запросов. Практическое использование текстового поиска обычно требует создания индекса.
Существует возможность создания GIN индекса (Раздел 2.9.9) для ускорения текстового поиска:
CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector('english', body));
Обратите внимание, что используется версия функции с двумя аргументами to_tsvector is
used. Только функции текстового поиска, в которых явно указано имя конфигурации, могут быть использованы в индексах по выражениям (Раздел 2.8.7).
Это обусловлено тем, что содержимое индекса не должно зависеть от default_text_search_config. В противном случае содержимое индекса может стать противоречивым, так как различные записи могут содержать tsvectors, созданные с использованием различных конфигураций текстового поиска, и способ определить, какая конфигурация использовалась в конкретном случае, будет отсутствовать. Корректная выгрузка и восстановление такого индекса будут невозможны.
Поскольку версия функции с двумя аргументами to_tsvector был использован в приведенном выше индексе; только ссылка в запросе, использующая версию с двумя аргументами to_tsvector с тем же именем конфигурации будет использовать данный индекс. То есть, WHERE
to_tsvector('english', body) @@ 'a & b' может использовать данный индекс,
в то время как WHERE to_tsvector(body) @@ 'a & b' не может.
Это гарантирует, что индекс будет задействован только при использовании той же конфигурации,
с которой создавались элементы этого индекса.
Допускается создание более сложных индексов на основе выражений, в которых имя конфигурации извлекается из другого столбца, например:
CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector(config_name, body));
где config_name является столбцом pgweb
table. Это позволяет совмещать различные конфигурации в рамках одного индекса, сохраняя информацию о том, какая именно конфигурация применялась для каждой записи. Данный подход может быть полезен, например, если коллекция документов содержит тексты на разных языках. Следует учитывать, что
запросы, в которых планируется использование индекса, должны быть составлены аналогичным образом, например:
WHERE to_tsvector(config_name, body) @@ 'a & b'.
Индексы могут объединять значения нескольких столбцов:
CREATE INDEX pgweb_idx ON pgweb USING GIN (to_tsvector('english', title || ' ' || body));
Альтернативный подход заключается в создании отдельного tsvector столбца
для хранения выходных данных функции to_tsvector. Чтобы автоматически поддерживать актуальность этого столбца в соответствии с исходными данными, следует использовать хранимый генерируемый столбец. Данный пример демонстрирует
конкатенацию полей заголовок и body,
с применением функции coalesce для обеспечения индексации одного поля в тех случаях, когда другое содержит значение NULL:
ALTER TABLE pgweb
ADD COLUMN textsearchable_index_col tsvector
GENERATED ALWAYS AS (to_tsvector('english', coalesce(title, '') || ' ' || coalesce(body, ''))) STORED;
Затем создается GIN индекс для повышения производительности поиска:
CREATE INDEX textsearch_idx ON pgweb USING GIN (textsearchable_index_col);
Теперь можно выполнить быстрый полнотекстовый поиск:
SELECT title
FROM pgweb
WHERE textsearchable_index_col @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;
Одним из преимуществ подхода с использованием отдельного столбца перед функциональным индексом является отсутствие необходимости явно указывать конфигурацию текстового поиска в запросах для использования индекса. Как показано
в приведенном выше примере, запрос может зависеть от
default_text_search_config. Другим преимуществом является более высокая скорость поиска, так как не потребуется повторно выполнять
to_tsvector вызовы функций для проверки соответствий индекса. (Это более
важно при использовании индекса GiST, чем GIN; см. Раздел 2.9.9.) Однако подход с использованием функциональных индексов проще в настройке и требует меньше дискового пространства, так как
tsvector нормализованное представление не хранится явно.