fuzzystrmatch — определение схожести и расстояния между строками
Модуль fuzzystrmatch предоставляет несколько функций для определения схожести и расстояния между строками.
ВНИМАНИЕ!
В настоящее время функции soundex, metaphone, dmetaphone и dmetaphone_alt плохо работают с многобайтными кодировками (такими, как UTF-8). Используйте с такими данными функции daitch_mokotoff или levenshtein.
Этот модуль считается «доверенным», то есть его могут устанавливать обычные пользователи с правом CREATE в текущей базе данных.
Soundex
Система Soundex представляет собой метод сопоставления похожих по звучанию имен путем приведения их к одному коду. Изначально она использовалась для обработки данных переписи населения США в 1880, 1900 и 1910 г. Обратите внимание, что Soundex не очень полезна для неанглоязычных имен.
Модуль fuzzystrmatch предоставляет две функции для работы с кодами Soundex:
soundex(text) returns text
difference(text, text) returns int
Функция soundex преобразует строку в ее код Soundex. Функция difference преобразует две строки в их коды Soundex, а затем сообщает количество совпадающих позиций в этих кодах. Поскольку коды Soundex состоят из четырех символов, результат находится в диапазоне от нуля до четырех, где ноль обозначает полное несоответствие, а четыре — точное совпадение. (Таким образом, имя этой функции некорректно — лучшим именем для нее было бы similarity.)
Несколько примеров использования:
SELECT soundex('hello world!');
SELECT soundex('Anne'), soundex('Ann'), difference('Anne', 'Ann');
SELECT soundex('Anne'), soundex('Andrew'), difference('Anne', 'Andrew');
SELECT soundex('Anne'), soundex('Margaret'), difference('Anne', 'Margaret');
CREATE TABLE s (nm text);
INSERT INTO s VALUES ('john');
INSERT INTO s VALUES ('joan');
INSERT INTO s VALUES ('wobbly');
INSERT INTO s VALUES ('jack');
SELECT * FROM s WHERE soundex(nm) = soundex('john');
SELECT * FROM s WHERE difference(s.nm, 'john') > 2;
Daitch-Mokotoff Soundex
Как и исходная система Soundex, Daitch-Mokotoff Soundex сопоставляет похожие по звучанию имена, преобразуя их в один и тот же код. Однако Daitch-Mokotoff Soundex гораздо более полезна для неанглийских имен, чем оригинальная система. Основные улучшения по сравнению оригинальной системой:
-
Код основан на первых шести значимых буквах, а не на четырех.
-
Буква или комбинация букв сопоставляется с десятью возможными кодами, а не с семью.
-
Если две последовательные буквы образуют один звук, они кодируются одной цифрой.
-
Когда буква или комбинация букв могут иметь разные звуки, производится несколько кодов, чтобы охватить все возможные варианты.
Эта функция генерирует коды Daitch-Mokotoff Soundex codes для своих входных данных:
daitch_mokotoff(source text) returns text[]
Результат может содержать один или более кодов в зависимости от количества вероятных вариантов произношения, поэтому он представлен в виде массива.
Поскольку код Daitch-Mokotoff Soundex состоит всего из 6 цифр, предпочтительно, чтобы в source передавалось одно слово или имя.
Несколько примеров:
SELECT daitch_mokotoff('George');
daitch_mokotoff
-----------------
{595000}
SELECT daitch_mokotoff('John');
daitch_mokotoff
-----------------
{160000,460000}
SELECT daitch_mokotoff('Bierschbach');
daitch_mokotoff
-----------------------------------------------------------
{794575,794574,794750,794740,745750,745740,747500,747400}
SELECT daitch_mokotoff('Schwartzenegger');
daitch_mokotoff
-----------------
{479465}
Для сопоставления отдельных имен возвращаемые текстовые массивы могут сравниваться
напрямую с помощью оператора &&: любое пересечение можно считать соответствием.
Для большей эффективности можно воспользоваться индексом GIN, см. главу Индексы GIN
и этот пример:
CREATE TABLE s (nm text);
CREATE INDEX ix_s_dm ON s USING gin (daitch_mokotoff(nm)) WITH (fastupdate = off);
INSERT INTO s (nm) VALUES
('Schwartzenegger'),
('John'),
('James'),
('Steinman'),
('Steinmetz');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Swartzenegger');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jane');
SELECT * FROM s WHERE daitch_mokotoff(nm) && daitch_mokotoff('Jens');
Для индексации и сопоставления любого числа имен в любом порядке можно использовать функционал полнотекстового поиска. См. главу Полнотекстовый поиск и этот пример:
CREATE FUNCTION soundex_tsvector(v_name text) RETURNS tsvector
BEGIN ATOMIC
SELECT to_tsvector('simple',
string_agg(array_to_string(daitch_mokotoff(n), ' '), ' '))
FROM regexp_split_to_table(v_name, '\s+') AS n;
END;
CREATE FUNCTION soundex_tsquery(v_name text) RETURNS tsquery
BEGIN ATOMIC
SELECT string_agg('(' || array_to_string(daitch_mokotoff(n), '|') || ')', '&')::tsquery
FROM regexp_split_to_table(v_name, '\s+') AS n;
END;
CREATE TABLE s (nm text);
CREATE INDEX ix_s_txt ON s USING gin (soundex_tsvector(nm)) WITH (fastupdate = off);
INSERT INTO s (nm) VALUES
('John Doe'),
('Jane Roe'),
('Public John Q.'),
('George Best'),
('John Yamson');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('jane doe');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('john public');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('besst, giorgio');
SELECT * FROM s WHERE soundex_tsvector(nm) @@ soundex_tsquery('Jameson John');
Если желательно избежать пересчета кодов Soundex при перепроверке индекса, вместо индекса по выражению можно использовать индекс по отдельному столбцу. Для этого можно использовать хранимый генерируемых столбец; см. раздел Генерируемые столбцы.
Левенштейн
Эта функция вычисляет расстояние Левенштейна между двумя строками:
levenshtein(source text, target text, ins_cost int, del_cost int, sub_cost int) returns int
levenshtein(source text, target text) returns int
levenshtein_less_equal(source text, target text, ins_cost int, del_cost int, sub_cost int, max_d int) returns int
levenshtein_less_equal(source text, target text, max_d int) returns int
И в source, и в target может быть передана любая строка, отличная от NULL, не длиннее 255 символов. Параметры стоимости (ins_cost, del_cost, sub_cost) определяют затраты на добавление, удаление или замену символов соответственно. Эти параметры можно опустить, как во второй версии функции; в этом случае все они по умолчанию равны 1.
Функция levenshtein_less_equal является ускоренной версией функции Левенштейна, предназначенной для использования, когда интерес представляют только небольшие расстояния. Если фактическое расстояние меньше или равно max_d, то levenshtein_less_equal возвращает его точное значение; в противном случае она возвращает некоторое значение, превышающее max_d. Если значение max_d отрицательное, она работает так же, как функция levenshtein.
Примеры:
test=# SELECT levenshtein('GUMBO', 'GAMBOL');
levenshtein
-------------
2
(1 row)
test=# SELECT levenshtein('GUMBO', 'GAMBOL', 2, 1, 1);
levenshtein
-------------
3
(1 row)
test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 2);
levenshtein_less_equal
------------------------
3
(1 row)
test=# SELECT levenshtein_less_equal('extensive', 'exhaustive', 4);
levenshtein_less_equal
------------------------
4
(1 row)
Metaphone
Metaphone, как и Soundex, основывается на идее составления кода, представляющего входную строку. Две строки признаются похожими, если их коды совпадают.
Эта функция вычисляет код метафона входной строки:
metaphone(source text, max_output_length int) returns text
В source должна быть передана строка, отличная от NULL, не длиннее 255 символов. Параметр max_output_length устанавливает максимальную длину выходного кода метафона; если код оказывается длиннее, выходная строка обрезается до этой длины.
Пример:
test=# SELECT metaphone('GUMBO', 4);
metaphone
-----------
KM
(1 row)
Double Metaphone
Система Double Metaphone (двойной метафон) вычисляет две строки «похожего звучания» для заданной строки — «первичную» и «альтернативную». В большинстве случаев они совпадают, но для неанглоязычных имен в особенности они могут немного отличаться, в зависимости от произношения. Эти функции вычисляют первичный и альтернативный коды:
dmetaphone(source text) returns text
dmetaphone_alt(source text) returns text
Ограничение на длину входных строк отсутствует.
Пример:
test=# SELECT dmetaphone('gumbo');
dmetaphone
------------
KMP
(1 row)