Документация по PostgreSQL 9.4.1 | |||
---|---|---|---|
Пред. | Уровень выше | Глава 12. Полнотекстовый поиск | След. |
12.4. Дополнительные возможности
В этом разделе описываются дополнительные функции и операторы, которые могут быть полезны при поиске текста.
12.4.1. Обработка документов
В Подразделе 12.3.1 показывалось, как обычные текстовые документы можно преобразовать в значения tsvector. PostgreSQL предлагает также набор функций и операторов для обработки документов, уже представленных в формате tsvector.
- tsvector || tsvector
Оператор конкатенации значений tsvector возвращает вектор, объединяющий лексемы и позиционную информацию двух векторов, переданных ему в аргументах. В полученном результате сохраняются позиции и метки весов. При этом позиции в векторе справа сдвигаются на максимальное значение позиции в векторе слева, что почти равносильно применению
to_tsvector
к результату конкатенации двух исходных строк документов. (Почти, потому что стоп-слова, исключаемые в конце левого аргумента, при конкатенации исходных строк влияют на позиции лексем в правой части, а при конкатенации tsvector — нет.)Преимущество же конкатенации документов в векторной форме по сравнению с конкатенацией текста до вызова
to_tsvector
заключается в том, что так можно разбирать разные части документа, применяя разные конфигурации. И так как функцияsetweight
помечает все лексемы данного вектора одинаково, разбирать текст и выполнятьsetweight
нужно до объединения разных частей документа с подразумеваемым разным весом.- setweight(вектор tsvector, вес "char") returns tsvector
setweight
возвращает копию входного вектора, помечая в ней каждую позицию заданным весом, меткой A, B, C или D. (Метка D по умолчанию назначается всем векторам, так что при выводе она опускается.) Эти метки сохраняются при конкатенации векторов, что позволяет придавать разные веса словам из разных частей документа, и, как следствие, ранжировать их по-разному.Заметьте, что веса назначаются позициям, а не лексемам. Если входной вектор очищен от позиционной информации,
setweight
не делает ничего.- length(вектор tsvector) returns integer
Возвращает число лексем, сохранённых в векторе.
- strip(вектор tsvector) returns tsvector
Возвращает вектор с теми же лексемами, что и в данном, но без информации о позиции и весе. Хотя возвращённый вектор малополезен для ранжирования по релевантности, он обычно имеет гораздо меньший размер, чем полный вектор.
12.4.2. Обработка запросов
В Подразделе 12.3.2 показывалось, как обычные текстовые запросы можно преобразовывать в значения tsquery. PostgreSQL предлагает также набор функций и операторов для обработки запросов, уже представленных в формате tsquery.
- tsquery && tsquery
Возвращает логическое произведение (AND) двух данных запросов.
- tsquery || tsquery
Возвращает логическое объединение (OR) двух данных запросов.
- !! tsquery
Возвращает логическое отрицание (NOT) данного запроса.
- numnode(запрос tsquery) returns integer
Возвращает число узлов (лексем и операторов) в значении tsquery. Эта функция помогает определить, имеет ли смысл запрос (тогда её результат > 0) или он содержит только стоп-слова (тогда она возвращает 0). Примеры:
SELECT numnode(plainto_tsquery('the any')); ЗАМЕЧАНИЕ: запрос поиска текста игнорируется, так как содержит только стоп-слова или не содержит лексем numnode --------- 0 SELECT numnode('foo & bar'::tsquery); numnode --------- 3
- querytree(запрос tsquery) returns text
Возвращает часть tsquery, которую можно использовать для поиска по индексу. Эта функция помогает выявить неиндексируемые запросы, например, такие, которые содержат только стоп-слова или условия отрицания. Например:
SELECT querytree(to_tsquery('!defined')); querytree -----------
12.4.2.1. Перезапись запросов
Семейство запросов ts_rewrite
ищет в данном tsquery вхождения целевого подзапроса и заменяет каждое вхождение указанной подстановкой. По сути эта операция похожа на замену подстроки в строке, только рассчитана на работу с tsquery. Сочетание целевого подзапроса с подстановкой можно считать правилом перезаписи запроса. Набор таких правил перезаписи может быть очень полезен при поиске. Например, вы можете улучшить результаты, добавив синонимы (например, big apple, nyc и gotham для new york) или сузить область поиска, чтобы нацелить пользователя на некоторую область. Это в некотором смысле пересекается с функциональностью тезаурусов (Подраздел 12.6.4). Однако, при таком подходе вы можете изменять правила перезаписи «на лету», тогда как при обновлении тезауруса необходима переиндексация.
- ts_rewrite (запрос tsquery, цель tsquery, замена tsquery) returns tsquery
Эта форма
ts_rewrite
просто применяет одно правило перезаписи: цель заменяется подстановкой везде, где она находится в запросе. Например:SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery); ts_rewrite ------------ 'b' & 'c'
- ts_rewrite (запрос tsquery, выборка text) returns tsquery
Эта форма
ts_rewrite
принимает начальный запрос и SQL-команду select, которая задаётся текстовой строкой. Команда select должна выдавать две колонки типа tsquery. Для каждой строки результата select вхождения первой колонки (цели) заменяются значениями второй колонки (подстановкой) в тексте запроса. Например:CREATE TABLE aliases (t tsquery PRIMARY KEY, s tsquery); INSERT INTO aliases VALUES('a', 'c'); SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases'); ts_rewrite ------------ 'b' & 'c'
Заметьте, что когда таким способом применяются несколько правил перезаписи, порядок их применения может иметь значение, поэтому в исходном запросе следует добавить ORDER BY по какому-либо ключу.
Давайте рассмотрим практический пример на тему астрономии. Мы развернём запрос supernovae, используя правила перезаписи в таблице:
CREATE TABLE aliases (t tsquery primary key, s tsquery); INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn')); SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); ts_rewrite --------------------------------- 'crab' & ( 'supernova' | 'sn' )
Мы можем скорректировать правила перезаписи, просто изменив таблицу:
UPDATE aliases SET s = to_tsquery('supernovae|sn & !nebulae') WHERE t = to_tsquery('supernovae'); SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases'); ts_rewrite --------------------------------------------- 'crab' & ( 'supernova' | 'sn' & !'nebula' )
Перезапись может быть медленной, когда задано много правил перезаписи, так как соответствия будут проверяться для каждого правила. Чтобы отфильтровать явно неподходящие правила, можно использовать проверки включения для типа tsquery. В следующем примере выбираются только те правила, которые могут соответствовать исходному запросу:
SELECT ts_rewrite('a & b'::tsquery, 'SELECT t,s FROM aliases WHERE ''a & b''::tsquery @> t'); ts_rewrite ------------ 'b' & 'c'
12.4.3. Триггеры для автоматического обновления
Когда представление документа в формате tsvector хранится в отдельной колонке, необходимо создать триггер, который будет обновлять её содержимое при изменении колонок, из которых составляется исходный документ. Для этого можно использовать две встроенные триггерные функции или написать свои собственные.
tsvector_update_trigger(колонка_tsvector, имя_конфигурации, колонка_текста [, ...]) tsvector_update_trigger_column(колонка_tsvector, колонка_конфигурации, колонка_текста [, ...])
Эти триггерные функции автоматически вычисляют значение для колонки tsvector из одной или нескольких текстовых колонок с параметрами, указанными в команде CREATE TRIGGER. Пример их использования:
CREATE TABLE messages ( title text, body text, tsv tsvector ); CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(tsv, 'pg_catalog.english', title, body); INSERT INTO messages VALUES('title here', 'the body text is here'); SELECT * FROM messages; title | body | tsv ------------+-----------------------+---------------------------- title here | the body text is here | 'bodi':4 'text':5 'titl':1 SELECT title, body FROM messages WHERE tsv @@ to_tsquery('title & body'); title | body ------------+----------------------- title here | the body text is here
С таким триггером любое изменение в полях title или body будет автоматически отражаться в содержимом tsv, так что приложению не придётся заниматься этим.
Первым аргументом этих функций должно быть имя колонки tsvector, содержимое которой будет обновляться. Ещё один аргумент — конфигурация текстового поиска, которая будет использоваться для преобразования. Для tsvector_update_trigger
имя конфигурации передаётся просто как второй аргумент триггера. Это имя должно быть определено полностью, чтобы поведение триггера не менялось при изменениях в пути поиска (search_path). Для tsvector_update_trigger_column
во втором аргументе триггера передаётся имя другой колонки таблицы, которая должна иметь тип regconfig. Это позволяет использовать разные конфигурации для разных строк. В оставшихся аргументах передаются имена текстовых колонок (типа text, varchar, или char). Их содержимое будет включено в документ в заданном порядке. При этом значения NULL будут пропущены (а другие колонки будут индексироваться).
Ограничение этих встроенных триггеров заключается в том, что они обрабатывают все колонки одинаково. Чтобы колонки обрабатывались по-разному, например для текста заголовка задавался не тот же вес, что для тела документа, потребуется разработать свой триггер. К примеру, так это можно сделать на языке PL/pgSQL:
CREATE FUNCTION messages_trigger() RETURNS trigger AS $$ begin new.tsv := setweight(to_tsvector('pg_catalog.english', coalesce(new.title,'')), 'A') || setweight(to_tsvector('pg_catalog.english', coalesce(new.body,'')), 'D'); return new; end $$ LANGUAGE plpgsql; CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE PROCEDURE messages_trigger();
Помните, что, создавая значения tsvector в триггерах, важно явно указывать имя конфигурации, чтобы содержимое колонки не зависело от изменений default_text_search_config. В противном случае могут возникнуть проблемы, например результаты поиска изменятся после выгрузки и восстановления данных.
12.4.4. Сбор статистики по документу
Функция ts_stat
может быть полезна для проверки конфигурации и нахождения возможных стоп-слов.
ts_stat(sql_запрос text, [веса text,]
OUT слово text, OUT число_док integer,
OUT число_вхожд integer) returns setof record
Здесь sql_запрос — текстовая строка, содержащая SQL-запрос, который должен возвращать одну колонку tsvector. Функция ts_stat
выполняет запрос и возвращает статистику по каждой отдельной лексеме (слову), содержащейся в данных tsvector. Её результат представляется в колонках
слово text — значение лексемы
число_док integer — число документов (значений tsvector), в которых встретилось слово
число_вхожд integer — общее число вхождений слова
Если передаётся параметр weights, то подсчитываются только вхождения с указанными в нём весами.
Например, найти десять наиболее часто используемых слов в коллекции документов можно так:
SELECT * FROM ts_stat('SELECT vector FROM apod') ORDER BY nentry DESC, ndoc DESC, word LIMIT 10;
Следующий запрос возвращает тоже десять слов, но при выборе их учитываются только вхождения с весами A или B:
SELECT * FROM ts_stat('SELECT vector FROM apod', 'ab') ORDER BY nentry DESC, ndoc DESC, word LIMIT 10;
Пред. | Начало | След. |
Управление текстовым поиском | Уровень выше | Анализаторы |