Есть ли многобайтовый Postgresql Левенштейн?
Когда я использую функцию fuzzystrmatch Левенштейна с диакритическими символами, она возвращает неверный / многобайтовый невежественный результат:
select levenshtein('ą', 'x');
levenshtein
-------------
2
(Примечание: первый символ - это "а" с диакритическим знаком ниже, он не отображается должным образом после того, как я скопировал его здесь)
Документация по fuzzystrmatch ( https://www.postgresql.org/docs/9.1/fuzzystrmatch.html) предупреждает, что:
В настоящее время функции soundex, metaphone, dmetaphone и dmetaphone_alt плохо работают с многобайтовыми кодировками (такими как UTF-8).
Но так как она не называет функцию Левенштейна, мне было интересно, существует ли многобайтовая версия Левенштейна.
Я знаю, что я мог бы использовать функцию unaccent в качестве обходного пути, но я должен держать диакритические знаки.
1 ответ
Примечание: это решение было предложено @Nick Barnes в его ответе на связанный вопрос.
'A' с диакритическим знаком представляет собой последовательность символов, то есть комбинацию символов a и символа объединения, диакритический знак ̨: E'a\u0328'
Существует эквивалентный предварительно составленный символ ą: E'\u0105'
Решением было бы нормализовать строки Unicode, то есть преобразовать объединяющую последовательность символов в предварительно составленный символ перед их сравнением.
К сожалению, в Postgres, похоже, нет встроенной функции нормализации Unicode, но вы легко можете получить к ней доступ через расширения языка PL/Perl или PL/Python.
Например:
create extension plpythonu;
create or replace function unicode_normalize(str text) returns text as $$
import unicodedata
return unicodedata.normalize('NFC', str.decode('UTF-8'))
$$ language plpythonu;
Теперь, как последовательность символов E'a\u0328'
отображается на эквивалентный предварительно составленный символ E'\u0105'
используя unicode_normalize
, расстояние Левенштейна является правильным:
select levenshtein(unicode_normalize(E'a\u0328'), 'x');
levenshtein
-------------
1