Postgresql Левенштейн и заранее составленный персонаж против комбинированного персонажа - PullRequest
2 голосов
/ 20 июня 2019

У меня есть строки, содержащие два похожих символа.Оба выглядят как маленькие 'a с огонеком:

ą

ą

(Примечание: в зависимости от рендера онииногда отображаются одинаково, иногда немного по-другому)

Однако они отличаются:

Характеристики 1-го символа:

В PostgreSQL:

select ascii('ą');
ascii 
-------
261

Кодировка UTF-8 в шестнадцатеричном формате: \xC4\x85

, поэтому это предварительно составленный символ (https://en.wikipedia.org/wiki/Precomposed_character)

Характеристики 2-го символа:

В PostgreSQL:

select ascii('ą');
ascii 
-------
97

(аналогично символу 'a')

Это сильно указывает на то, что отображаемый символобъединены из двух символов. И это действительно:

Кодировка UTF-8 в шестнадцатеричном формате: \x61\xCC\xA8

Так что это комбинация

a \x61\

и комбинационный символ (https://en.wikipedia.org/wiki/Combining_character), отдельный огонек:

̨ \xCC\xA8

Я хочу использовать PostgreSQL Левенштейн функционалn, чтобы определить сходство слов, и поэтому я хочу, чтобы оба символа рассматривались как одинаковые (как это, разумеется, подразумевают люди, которые пишут имя отличительного объекта с 1-м или 2-м символом).

Я предполагал, что могу использовать unaccent , чтобы всегда избавляться от огонека, но это не работает во 2-м случае:

1-й символ: ожидаемый результат:

select levenshtein('ą', 'x');
levenshtein 
-------------
       1

1-й символ: ожидаемый результат:

select levenshtein(unaccent('ą'), 'x');
levenshtein 
-------------
       1

2-й символ: ожидаемый результат:

select levenshtein('ą', 'x');
levenshtein 
-------------
       2

2-й символ: неожиданный результат:

select levenshtein(unaccent('ą'), 'x');
levenshtein 
-------------
       2

Итак,когда я сравниваю оба символа с levenshtein и unaccent , результат равен 1:

select levenshtein(unaccent('ą'), unaccent('ą'));
levenshtein 
-------------
       1

вместо 0.

Как я могу "«избавиться от огонек» во 2-м случае?

(Как) можно использовать коды строк UTF-8 для получения достигнутого результата?

Редактировать :Как предложил @ s-man, добавление комбинирующего символа к unaccent.rules решит эту конкретную проблему.Но чтобы решить проблему с предварительно составленным символом и комбинированным символом с unaccent , мне нужно было бы явно добавить / изменить каждый отсутствующий / "неправильно сконфигурированный" комбинированный символ, чтобы/ в конфиге.

Ответы [ 3 ]

2 голосов
/ 20 июня 2019

Удаление акцентов даст вам расстояние Левенштейна 0, но оно также даст вам расстояние 0 между ą и a, что не звучит идеально.

Лучшее решение было бы нормализует строки Юникода, т.е. преобразует объединяющую последовательность символов E'a\u0328' в предварительно составленный символ E'\u0105' перед их сравнением.

К сожалению, у 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;

А затем:

test=# select levenshtein(unicode_normalize(E'a\u0328'), unicode_normalize(E'\u0105'));
 levenshtein
-------------
           0

Это также решает проблему в вашем предыдущем вопросе , где объединяющий символ способствовал расстоянию Левенштейна:

test=# select levenshtein(unicode_normalize(E'a\u0328'), 'x');
 levenshtein
-------------
           1
2 голосов
/ 20 июня 2019

Вы должны изменить свою конфигурацию и добавить недостающие символы вручную в файле конфигурации, как описано в https://postgresql.org/docs/current/unaccent.html

1 голос
/ 20 июня 2019

Примечание. Это решение основано на предложении @ S-Man явно добавить отсутствующие символы в файл unaccent.rules.

Примечание. Предварительным условием этого ответа является то, что соответствующие предварительно составленные символы (https://en.wikipedia.org/wiki/Precomposed_character) уже сопоставлены в файле unaccent.rules. Если нет, их также необходимо добавить.

Существуют символы, состоящие из нескольких символов:

  • «базовый» символ (например, гласные, как a, согласные, как l)
  • a объединяющий символ (https://en.wikipedia.org/wiki/Combining_character), обычно один диакритический знак, например, острый (´) или точечный (·)

Цель состоит в том, чтобы отобразить «многосимвольный» символ на содержащий «базовый» символ.

(при условии, что соответствующие предварительно составленные символы сопоставлены с «базовым» символом, как в оригинальном файле unaccent.rules)

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

Вместо этого диакритические знаки должны отображаться на [ничего]. Этого можно достичь, оставив второй столбец в файле unaccent.rules (https://postgresql.org/docs/current/unaccent.html) пустым.

Это список диакритических знаков для латинского алфавита, полученный из https://en.wikipedia.org/wiki/Diacritic: ' ˝ ` ̏ ˘ ̑ ¸ ¨ · ̡ ̢ ̉ ̛ ˉ ˛ ˚ ˳

Добавьте к этому огонек вопроса, который отсутствует: ̨

Теперь (после перезапуска PostgreSQL, конечно), unaccent отображает символы «нескольких символов» на «основной» символ, как это происходит с предварительно скомпонованными символами.

Примечание. Приведенный выше список может быть неполным, но, по крайней мере, должен решить значительную часть проблемы «предварительно составленный символ или комбинированный символ».

...