pg_trgm — поддержка схожести текста с использованием триграмм#

Примечание

Эта страница переведена при помощи нейросети GigaChat.

Модуль pg_trgm предоставляет функции и операторы для определения схожести буквенно-цифрового текста на основе сопоставления триграмм, а также классы операторов индексации, которые поддерживают быстрый поиск похожих строк.

Этот модуль считается «надежным», то есть его могут устанавливать обычные пользователи, обладающие привилегией CREATE на текущей базе данных.

Концепции триграмм (или триграфа)#

Триграмма - это группа из трех последовательных символов, взятых из строки. Можно измерить сходство двух строк, подсчитав количество триграмм, которые есть в обеих. Эта простая идея оказывается очень эффективной для измерения сходства слов во многих естественных языках.

Примечание

pg_trgm игнорирует неразборчивые символы (неалфавитные) при извлечении триграмм из строки. Каждое слово считается имеющим два пробела перед ним и один пробел после него при определении набора триграмм, содержащихся в строке. Например, набор триграмм в строке «cat» - это « c», « ca», «cat» и «at «. Набор триграмм в строке «foo|bar» - это « f», « fo», «foo», «oo «, « b», « ba», «bar» и «ar «.

Функции и операторы#

Функции, предоставляемые модулем, показаны в таблице «Функции pg_trgm», а операторы - в таблице «Операторы pg_trgm».

Функции pg_trgm:

Функция

Описание

similarity ( text, text ) → real

Возвращает число, которое указывает на степень сходства двух аргументов. Диапазон результата варьируется от нуля (что указывает на полную несхожесть двух строк) до единицы (что указывает на идентичность двух строк)

show_trgm ( text ) → text[]

Возвращает массив всех триграмм в данной строке (на практике это редко бывает полезно, за исключением отладки)

word_similarity ( text, text ) → real

Возвращает число, которое указывает на наибольшее сходство между набором триграмм в первой строке и любым непрерывным участком упорядоченного набора триграмм во второй строке. Подробности см. в приведенном ниже объяснении

strict_word_similarity ( text, text ) → real

То же самое, что и word_similarity, но принудительно устанавливает границы протяженности для совпадения границ слов. Поскольку нет межсловных триграмм, эта функция фактически возвращает наибольшую степень подобия между первой строкой и любой непрерывной последовательностью слов из второй строки

show_limit () → real

Возвращает текущий порог схожести, используемый оператором %. Значение устанавливает минимальную степень схожести между двумя словами, чтобы они считались достаточно похожими, чтобы быть опечатками друг друга, например (устарело, вместо этого используйте SHOW pg_trgm.similarity_threshold)

set_limit ( real ) → real

Устанавливает текущий порог подобия, который используется оператором %. Порог должен быть между 0 и 1 (по умолчанию 0,3). Возвращает то же значение, которое было передано (устарело, вместо этого используйте SET pg_trgm.similarity_threshold)

Рассмотрим пример:

# SELECT word_similarity('word', 'two words');
 word_similarity
-----------------
             0.8
(1 row)

В первой строке набор триграмм – это {" w"," wo","wor","ord","rd "}. Во второй строке упорядоченный набор триграмм – {" t"," tw","two","wo "," w"," wo","wor","ord","rds","ds "}. Наиболее похожий объем упорядоченного набора триграмм во второй строке – это {" w"," wo","wor","ord"}, а степень сходства составляет 0.8.

Эта функция возвращает значение, которое можно приблизительно понять как наибольшую степень сходства между первой строкой и любой подстрокой второй строки. Однако эта функция не добавляет заполнение к границам объема. Таким образом, количество дополнительных символов, присутствующих во второй строке, не учитывается, за исключением несоответствия границ слов.

В то же время, strict_word_similarity выбирает диапазон слов во второй строке. В приведенном выше примере strict_word_similarity выбрал бы диапазон одного слова 'words', набор триграмм которого {" w"," wo","wor","ord","rds","ds "}.

# SELECT strict_word_similarity('word', 'two words'), similarity('word', 'words');
 strict_word_similarity | similarity
------------------------+------------
               0.571429 |   0.571429
(1 row)

Таким образом, функция strict_word_similarity полезна для поиска схожести целых слов, тогда как word_similarity больше подходит для поиска схожести частей слов.

Операторы pg_trgm:

Оператор

Описание

text % textboolean

Возвращает true, если его аргументы имеют сходство, которое больше текущего порога сходства, установленного параметром pg_trgm.similarity_threshold

text <% textboolean

Возвращает true, если сходство между триграммным набором в первом аргументе и непрерывной областью упорядоченного триграммного набора во втором аргументе больше текущего порога схожести слов, установленного параметром pg_trgm.word_similarity_threshold

text %> textboolean

Коммутатор оператора <%

text <<% textboolean

Возвращает true, если его второй аргумент имеет непрерывный диапазон упорядоченного триграммного набора, который соответствует границам слов, и его сходство с триграммным набором первого аргумента больше текущего строгого порога подобия слов, установленного параметром pg_trgm.strict_word_similarity_threshold

text %>> textboolean

Коммутатор оператора <<%

text <-> textreal

Возвращает «расстояние» между аргументами, то есть единицу минус значение similarity()

text <<-> textreal

Возвращает «расстояние» между аргументами, то есть единицу минус значение word_similarity()

text <->> textreal

Коммутатор оператора <<->

text <<<-> textreal

Возвращает «расстояние» между аргументами, то есть единицу минус значение strict_word_similarity()

text <->>> textreal

Коммутатор оператора <<<->

Параметры GUC#

pg_trgm.similarity_threshold (real)

Устанавливает текущий порог подобия, который используется оператором %. Порог должен быть между 0 и 1 (по умолчанию 0,3).

pg_trgm.word_similarity_threshold (real)

Устанавливает текущий порог подобия слов, который используется операторами <% и %>. Порог должен быть между 0 и 1 (по умолчанию 0,6).

pg_trgm.strict_word_similarity_threshold (real)

Устанавливает текущий строгий порог подобия слов, который используется операторами <<% и %>>. Порог должен быть между 0 и 1 (по умолчанию 0,5).

Поддержка индексов#

Модуль pg_trgm предоставляет классы операторов GiST и GIN, которые позволяют создать индекс по текстовому столбцу с целью очень быстрого поиска похожих элементов. Эти типы индексов поддерживают вышеописанные операторы сходства, а также дополнительно поддерживают поиск по индексам на основе триграмм для запросов LIKE, ILIKE, ~, ~* и =. В стандартной сборке pg_trgm регистр при определении схожести не учитывается. Операторы неравенства не поддерживаются. Обратите внимание, что эти индексы могут быть менее эффективными, чем обычные индексы B-дерева для оператора равенства.

Пример:

CREATE TABLE test_trgm (t text);
CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops);

или

CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);

gist_trgm_ops GiST opclass аппроксимирует набор триграмм как битовый сигнатурный файл. Его необязательный целочисленный параметр siglen определяет длину подписи в байтах. Длина по умолчанию составляет 12 байтов. Допустимые значения длины подписи находятся между 1 и 2024 байтами. Более длинные подписи приводят к более точному поиску (сканированию меньшей доли индекса и меньшего количества страниц кучи) за счет увеличения размера индекса.

Пример создания такого индекса с длиной подписи 32 байта:

CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops(siglen=32));

На этот момент будет индекс на столбце t, который можно использовать для поиска похожих элементов. Типичный запрос выглядит так:

SELECT t, similarity(t, 'word') AS sml
  FROM test_trgm
  WHERE t % 'word'
  ORDER BY sml DESC, t;

Это вернет все значения в столбце text, которые достаточно похожи на word, отсортированные от лучшего совпадения до худшего. Индекс будет использоваться для того, чтобы сделать эту операцию быстрой даже над очень большими наборами данных.

Вариант вышеуказанного запроса:

SELECT t, t <-> 'word' AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

Это может быть довольно эффективно реализовано с помощью индексов GiST, но не с индексами GIN. Обычно он превосходит первую формулировку, когда требуется только небольшое количество ближайших совпадений.

Также можно использовать индекс для столбца t для похожести слов или строгой похожести слов. Типичные запросы:

SELECT t, word_similarity('word', t) AS sml
  FROM test_trgm
  WHERE 'word' <% t
  ORDER BY sml DESC, t;

и

SELECT t, strict_word_similarity('word', t) AS sml
  FROM test_trgm
  WHERE 'word' <<% t
  ORDER BY sml DESC, t;

Это вернет все значения в столбце текста, для которых существует непрерывный диапазон в соответствующем упорядоченном наборе триграмм, который достаточно похож на набор триграмм из word, отсортированный от лучшего совпадения до худшего. Индекс будет использоваться для того, чтобы сделать эту операцию быстрой даже над очень большими наборами данных.

Возможные варианты вышеуказанных запросов:

SELECT t, 'word' <<-> t AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

и

SELECT t, 'word' <<<-> t AS dist
  FROM test_trgm
  ORDER BY dist LIMIT 10;

Это может быть реализовано довольно эффективно с помощью индексов GiST, но не с индексами GIN.

Начиная с PostgreSQL 9.1, эти типы индексов также поддерживают поиск по индексам для LIKE и ILIKE, например,

SELECT * FROM test_trgm WHERE t LIKE '%foo%bar';

Поиск по индексу работает путем извлечения триграмм из строки поиска, а затем их поиска в индексе. Чем больше триграмм в строке поиска, тем эффективнее поиск по индексу. В отличие от поисков на основе B-дерева, строка поиска не обязательно должна быть привязана к левому краю.

Начиная с PostgreSQL 9.3, эти типы индексов также поддерживают поиск по индексам для соответствия регулярным выражениям (~ и ~* операторы), например,

SELECT * FROM test_trgm WHERE t ~ '(foo|bar)';

Поиск по индексу работает путем извлечения триграмм из регулярного выражения, а затем их поиска в индексе. Чем больше триграмм можно извлечь из регулярного выражения, тем эффективнее поиск по индексу. В отличие от поисков на основе B-дерева, строка поиска не обязательно должна быть привязана к левому краю.

Для обоих LIKE и регулярных выражений поиска имейте в виду, что шаблон без извлекаемых триграмм деградирует до полного сканирования индекса.

Выбор между индексированием GiST и GIN зависит от относительных характеристик производительности GiST и GIN, которые обсуждаются в другом месте.

Интеграция поиска по тексту#

Сопоставление триграмм является очень полезным инструментом при использовании в сочетании с полным текстовым индексом. В частности, он может помочь распознать слова с ошибками ввода, которые не будут напрямую совпадать с механизмом поиска полного текста.

Первый шаг - создать вспомогательную таблицу, содержащую все уникальные слова в документах:

CREATE TABLE words AS SELECT word FROM
        ts_stat('SELECT to_tsvector(''simple'', bodytext) FROM documents');

Где documents является таблицей, которая имеет поле текста bodytext, которое хотим искать. Причина использования конфигурации simple с функцией to_tsvector, вместо использования специфичной для языка конфигурации, заключается в том, что хотим получить список исходных (необработанных) слов.

Далее создайте триграммный индекс на столбце слов:

CREATE INDEX words_idx ON words USING GIN (word gin_trgm_ops);

Теперь запрос SELECT, аналогичный предыдущему примеру, может быть использован для предложения правописания для неправильно написанных слов в поисковых запросах пользователя. Полезным дополнительным тестом будет требование, чтобы выбранные слова также были аналогичной длины с неправильным словом.

Примечание

Поскольку таблица words была создана как отдельная статическая таблица, ее необходимо периодически регенерировать, чтобы она оставалась достаточно актуальной с коллекцией документов. Обычно нет необходимости поддерживать его в точности актуальным.