12.4. Дополнительные возможности#
12.4. Дополнительные возможности #
Этот раздел описывает дополнительные функции и операторы, которые полезны в связи с текстовым поиском.
12.4.1. Работа с документами #
Раздел 12.3.1 показал, как сырые текстовые документы могут быть преобразованы в значения типа tsvector.
Tantor SE-1C также предоставляет функции и операторы, которые могут использоваться для манипуляции документами, которые уже находятся в форме tsvector.
-
tsvector||tsvector Оператор конкатенации
tsvectorвозвращает вектор, который объединяет лексемы и информацию о позиции двух векторов, заданных в качестве аргументов. Позиции и метки веса сохраняются при конкатенации. Позиции, появляющиеся в правом векторе, смещаются наибольшей позицией, упомянутой в левом векторе, так что результат почти эквивалентен результату выполнения функцииto_tsvectorдля конкатенации двух исходных строк документа. (Эквивалентность не является точной, потому что любые стоп-слова, удаленные из конца аргумента слева, не повлияют на результат, в то время как они бы повлияли на позиции лексем в аргументе справа, если бы использовалась текстовая конкатенация).Один из преимуществ использования конкатенации в векторной форме, а не конкатенации текста перед применением функции
to_tsvector, заключается в том, что вы можете использовать разные конфигурации для разбора разных разделов документа. Кроме того, поскольку функцияsetweightпомечает все лексемы данного вектора одинаковым образом, необходимо разобрать текст и выполнитьsetweightперед конкатенацией, если нужно пометить разные части документа разными весами.-
setweight(vectortsvector,weight"char") returnstsvector setweightвозвращает копию входного вектора, в котором каждая позиция помечена заданным весомweight, либоA,B,C, илиD. (Dявляется значением по умолчанию для новых векторов и поэтому не отображается при выводе). Эти метки сохраняются при конкатенации векторов, что позволяет разным словам из разных частей документа иметь разные веса при использовании функций ранжирования.Обратите внимание, что метки веса применяются к позициям, а не к лексемам. Если входной вектор был очищен от позиций, то функция
setweightничего не делает.-
length(vectortsvector) returnsinteger Возвращает количество лексем, хранящихся в векторе.
-
strip(vectortsvector) returnstsvector Возвращает вектор, который содержит те же лексемы, что и заданный вектор, но не содержит информации о позиции или весе. Результат обычно намного меньше, чем у необработанного вектора, но он также менее полезен. Ранжирование по релевантности не работает так хорошо на обработанных векторах, как на необработанных. Кроме того, оператор
<->(СЛЕДУЕТ ЗА)tsqueryникогда не будет соответствовать обработанному вводу, так как он не может определить расстояние между вхождениями лексемы.
Список всех функций, связанных с типом данных tsvector, доступен в Таблица 9.43.
12.4.2. Изменение запросов #
Раздел 12.3.2 показал, как сырые текстовые запросы могут быть преобразованы в значения типа tsquery.
Tantor SE-1C также предоставляет функции и операторы, которые могут использоваться для манипуляции запросами, которые уже находятся в форме tsquery.
-
tsquery&&tsquery Возвращает результат логической операции AND для двух заданных запросов.
-
tsquery||tsquery Возвращает OR-комбинацию двух заданных запросов.
-
!!tsquery Возвращает отрицание (NOT) заданного запроса.
-
tsquery<->tsquery Возвращает запрос, который ищет совпадение с первым заданным запросом, сразу за которым следует совпадение со вторым заданным запросом, используя оператор
<->(FOLLOWED BY) типаtsquery. Например:SELECT to_tsquery('fat') <-> to_tsquery('cat | rat'); ?column? ---------------------------- 'fat' <-> ( 'cat' | 'rat' )-
tsquery_phrase(query1tsquery,query2tsquery[,distanceinteger]) returnstsquery Возвращает запрос, который ищет совпадение с первым заданным запросом, за которым следует совпадение со вторым заданным запросом на расстоянии ровно
distanceлексем, используя оператор<типаN>tsquery. Например:SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10); tsquery_phrase ------------------ 'fat' <10> 'cat'-
numnode(querytsquery) returnsinteger Возвращает количество узлов (лексемы плюс операторы) в
tsquery. Эта функция полезна для определения, является лиqueryзначимым (возвращает > 0) или содержит только стоп-слова (возвращает 0). Примеры:SELECT numnode(plainto_tsquery('the any')); NOTICE: query contains only stopword(s) or doesn't contain lexeme(s), ignored numnode --------- 0 SELECT numnode('foo & bar'::tsquery); numnode --------- 3-
querytree(querytsquery) returnstext Возвращает часть
tsquery, которая может быть использована для поиска в индексе. Эта функция полезна для обнаружения неподдающихся индексации запросов, например, содержащих только стоп-слова или только отрицательные термины. Например:SELECT querytree(to_tsquery('defined')); querytree ----------- 'defin' SELECT querytree(to_tsquery('!defined')); querytree ----------- T
12.4.2.1. Переписывание запросов #
Семейство функций ts_rewrite выполняет поиск заданного подзапроса
в tsquery и заменяет каждое его вхождение на
заменяющий подзапрос. В сущности, эта операция является
версией замены подстроки, специфичной для tsquery.
Комбинацию цели и замены можно рассматривать как
правило переписывания запроса. Совокупность
таких правил переписывания может быть мощным инструментом поиска.
Например, вы можете расширить поиск с помощью синонимов
(например, new york, big apple, nyc,
gotham) или сузить поиск, чтобы направить пользователя на актуальную
тему. Есть некоторое перекрытие функциональности между этой функцией
и тезаурусными словарями (Раздел 12.6.4).
Однако, вы можете изменять набор правил переписывания на лету, без
переиндексации, в то время как обновление тезауруса требует переиндексации,
чтобы быть эффективным.
-
ts_rewrite (querytsquery,targettsquery,substitutetsquery) returnstsquery Эта форма функции
ts_rewriteпросто применяет одно правило перезаписи:targetзаменяется наsubstituteвезде, где оно появляется вquery. Например:SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery); ts_rewrite ------------ 'b' & 'c'-
ts_rewrite (querytsquery,selecttext) returnstsquery Эта форма функции
ts_rewriteпринимает начальныйqueryи команду SQLselect, которая задается в виде текстовой строки.selectдолжен возвращать два столбца типаtsquery. Для каждой строки результатаselectзначения первого столбца (цель) заменяются значениями второго столбца (замена) внутри текущего значенияquery. Например: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. Триггеры для автоматических обновлений #
Примечание
Метод, описанный в данном разделе, устарел и заменен использованием хранимых генерируемых столбцов, как описано в Раздел 12.2.2.
При использовании отдельного столбца для хранения представления tsvector ваших документов необходимо создать триггер для обновления столбца tsvector при изменении столбцов с содержимым документа. Для этого доступны две встроенные триггерной функции, или вы можете написать свою собственную.
tsvector_update_trigger(tsvector_column_name,config_name,text_column_name[, ... ]) tsvector_update_trigger_column(tsvector_column_name,config_column_name,text_column_name[, ... ])
Эти триггерной функции автоматически вычисляют столбец 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 FUNCTION
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 FUNCTION messages_trigger();
Учтите, что важно явно указывать имя конфигурации при создании значений tsvector внутри триггеров, чтобы содержимое столбца не подвергалось изменениям default_text_search_config. Невыполнение этого может привести к проблемам, таким как изменение результатов поиска после выполнения операций резервного копирования и восстановления.
12.4.4. Сбор статистики документа #
Функция ts_stat полезна для проверки вашей конфигурации и поиска кандидатов на стоп-слова.
ts_stat(sqlquerytext, [weightstext, ] OUTwordtext, OUTndocinteger, OUTnentryinteger) returnssetof record
sqlquery - это текстовое значение, содержащее SQL-запрос, который должен возвращать один столбец типа tsvector.
ts_stat выполняет запрос и возвращает статистику о каждом отдельном лексеме (слове), содержащемся в данных типа tsvector.
Возвращаемые столбцы:
wordtext— значение лексемыndocinteger— количество документов (tsvectors), в которых встречается данное словоnentryinteger— общее количество вхождений слова
Если веса 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;