12.4. Дополнительные возможности#
12.4. Дополнительные возможности #
Этот раздел описывает дополнительные функции и операторы, которые полезны в связи с текстовым поиском.
12.4.1. Работа с документами #
Раздел 12.3.1 показал, как сырые текстовые документы могут быть преобразованы в значения типа tsvector
.
Tantor BE также предоставляет функции и операторы, которые могут использоваться для манипуляции документами, которые уже находятся в форме tsvector
.
-
tsvector
||tsvector
Оператор конкатенации
tsvector
возвращает вектор, который объединяет лексемы и информацию о позиции двух векторов, заданных в качестве аргументов. Позиции и метки веса сохраняются при конкатенации. Позиции, появляющиеся в правом векторе, смещаются наибольшей позицией, упомянутой в левом векторе, так что результат почти эквивалентен результату выполнения функцииto_tsvector
для конкатенации двух исходных строк документа. (Эквивалентность не является точной, потому что любые стоп-слова, удаленные из конца аргумента слева, не повлияют на результат, в то время как они бы повлияли на позиции лексем в аргументе справа, если бы использовалась текстовая конкатенация).Один из преимуществ использования конкатенации в векторной форме, а не конкатенации текста перед применением функции
to_tsvector
, заключается в том, что вы можете использовать разные конфигурации для разбора разных разделов документа. Кроме того, поскольку функцияsetweight
помечает все лексемы данного вектора одинаковым образом, необходимо разобрать текст и выполнитьsetweight
перед конкатенацией, если нужно пометить разные части документа разными весами.-
setweight(
vector
tsvector
,weight
"char"
) returnstsvector
setweight
возвращает копию входного вектора, в котором каждая позиция помечена заданным весомweight
, либоA
,B
,C
, илиD
. (D
является значением по умолчанию для новых векторов и поэтому не отображается при выводе). Эти метки сохраняются при конкатенации векторов, что позволяет разным словам из разных частей документа иметь разные веса при использовании функций ранжирования.Обратите внимание, что метки веса применяются к позициям, а не к лексемам. Если входной вектор был очищен от позиций, то функция
setweight
ничего не делает.-
length(
vector
tsvector
) returnsinteger
Возвращает количество лексем, хранящихся в векторе.
-
strip(
vector
tsvector
) returnstsvector
Возвращает вектор, который содержит те же лексемы, что и заданный вектор, но не содержит информации о позиции или весе. Результат обычно намного меньше, чем у необработанного вектора, но он также менее полезен. Ранжирование по релевантности не работает так хорошо на обработанных векторах, как на необработанных. Кроме того, оператор
<->
(СЛЕДУЕТ ЗА)tsquery
никогда не будет соответствовать обработанному вводу, так как он не может определить расстояние между вхождениями лексемы.
Список всех функций, связанных с типом данных tsvector
, доступен в Таблица 9.43.
12.4.2. Изменение запросов #
Раздел 12.3.2 показал, как сырые текстовые запросы могут быть преобразованы в значения типа tsquery
.
Tantor BE также предоставляет функции и операторы, которые могут использоваться для манипуляции запросами, которые уже находятся в форме 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(
query1
tsquery
,query2
tsquery
[,distance
integer
]) returnstsquery
Возвращает запрос, который ищет совпадение с первым заданным запросом, за которым следует совпадение со вторым заданным запросом на расстоянии ровно
distance
лексем, используя оператор<
типаN
>tsquery
. Например:SELECT tsquery_phrase(to_tsquery('fat'), to_tsquery('cat'), 10); tsquery_phrase ------------------ 'fat' <10> 'cat'
-
numnode(
query
tsquery
) 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(
query
tsquery
) 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 (
query
tsquery
,target
tsquery
,substitute
tsquery
) returnstsquery
Эта форма функции
ts_rewrite
просто применяет одно правило перезаписи:target
заменяется наsubstitute
везде, где оно появляется вquery
. Например:SELECT ts_rewrite('a & b'::tsquery, 'a'::tsquery, 'c'::tsquery); ts_rewrite ------------ 'b' & 'c'
-
ts_rewrite (
query
tsquery
,select
text
) 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(sqlquery
text
, [weights
text
, ] OUTword
text
, OUTndoc
integer
, OUTnentry
integer
) returnssetof record
sqlquery
- это текстовое значение, содержащее SQL-запрос, который должен возвращать один столбец типа tsvector
.
ts_stat
выполняет запрос и возвращает статистику о каждом отдельном лексеме (слове), содержащемся в данных типа tsvector
.
Возвращаемые столбцы:
word
text
— значение лексемыndoc
integer
— количество документов (tsvector
s), в которых встречается данное словоnentry
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;