Чи підтримує PostgreSQL "акценти нечутливі" зіставлення?


98

У Microsoft SQL Server можна вказати посилання "нечутливий до акцентів" (для бази даних, таблиці або стовпця), що означає, що це можливо для запиту, наприклад

SELECT * FROM users WHERE name LIKE 'João'

щоб знайти рядок з Joaoназвою.

Я знаю, що можна знімати наголоси з рядків у PostgreSQL за допомогою функції contccent_string contrib, але мені цікаво, чи підтримує PostgreSQL ці "нечутливі до акценту" порівняння, щоб SELECTвищезгадане спрацювало.


Дивіться цю відповідь для створення словника FTS з невідповідним: stackoverflow.com/a/50595181/124486
Еван Керролл

Ви хочете пошукати чутливість до регістру чи нечутливі регістри?
Еван Керролл

Відповіді:


204

Використовуйте для цього модуль непристойного характеру , який абсолютно відрізняється від того, з чим ви посилаєтесь.

unaccent - це текстовий словник пошуку, який видаляє наголоси (діакритичні знаки) з лексеми.

Встановіть один раз у базі даних за допомогою:

CREATE EXTENSION unaccent;

Якщо ви отримаєте помилку, наприклад:

ERROR: could not open extension control file
"/usr/share/postgresql/<version>/extension/unaccent.control": No such file or directory

Встановіть пакунок contrib на свій сервер бази даних, як зазначено в цій відповіді:

Крім усього іншого, він надає функцію, unaccent()яку ви можете використовувати зі своїм прикладом (де, LIKEздається, не потрібно).

SELECT *
FROM   users
WHERE  unaccent(name) = unaccent('João');

Покажчик

Щоб використовувати індекс для такого типу запиту, створіть індекс на виразі . Однак Postgres приймає лише IMMUTABLEфункції для індексів. Якщо функція може повернути інший результат за один і той же вхід, індекс може мовчки зламатися.

unaccent()тільки STABLEніIMMUTABLE

На жаль, unaccent()є тільки STABLE, ні IMMUTABLE. Відповідно до цієї теми на pgsql-bugs , це пов'язано з трьома причинами:

  1. Це залежить від поведінки словника.
  2. Немає жодного провідного зв’язку з цим словником.
  3. Тому це також залежить від струму search_path, який може легко змінитися.

Деякі підручники в Інтернеті вказують просто змінити волатильність функції IMMUTABLE. Цей метод грубої сили може порушитись за певних умов.

Інші пропонують просту IMMUTABLEфункцію обгортки (як я це робив у минулому).

Триває дискусія щодо того, чи робити варіант із двома параметрами, IMMUTABLE який чітко оголошує використаний словник. Прочитайте тут або тут .

Іншою альтернативою став би цей модуль з незмінною unaccent()функцією Musicbrainz , наданий на Github. Я не перевіряв його сам. Я думаю, що я придумав кращу ідею :

Найкраще зараз

Цей підхід є більш ефективним, оскільки інші рішення, що плавають навколо, і безпечніший .
Створіть функцію IMMUTABLEобгортки SQL, виконуючи форму з двома параметрами за допомогою жорсткої провідної схеми та функції словника.

Оскільки введення незмінювальної функції призведе до відключення функції вбудовування, базуйте її також на копії заявленої C-функції (підробленої) IMMUTABLE. Його тільки мета полягає в тому, щоб використовувати в функції SQL обгортку. Не призначений для використання самостійно.

Витонченість потрібна, оскільки немає можливості затягувати словник у декларації функції С. (Буде потрібно зламати сам код C.) Функція обгортки SQL робить це і дозволяє як функції вбудовування, так і індекси вираження.

CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
  RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';

CREATE OR REPLACE FUNCTION public.f_unaccent(text)
  RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1)
$func$;

Відкиньте PARALLEL SAFEобидві функції для Postgres 9.5 або новішої версії.

publicбудучи схемою, де ви встановили розширення ( publicза замовчуванням).

Явна декларація типу ( regdictionary) захищає від гіпотетичних атак із перевантаженими варіантами функції зловмисними користувачами.

Раніше я виступав за функцію обгортки, що базується на STABLEфункції, що unaccent()постачається з модулем без уваги. Це відключена функція вбудовування . Ця версія виконує в десять разів швидше, ніж проста функція обгортки, яку я мав тут раніше.
І це вже було вдвічі швидше, ніж перша версія, яка додала SET search_path = public, pg_tempфункцію - поки я не виявив, що словник теж може бути схемним. Все-таки (Postgres 12) не надто очевидно з документації.

Якщо вам не вистачає необхідних привілеїв для створення функцій C, ви повертаєтесь до другої найкращої реалізації: IMMUTABLEОбгортка функції навколо STABLE unaccent()функції, що надається модулем:

CREATE OR REPLACE FUNCTION public.f_unaccent(text)
  RETURNS text AS
$func$
SELECT public.unaccent('public.unaccent', $1)  -- schema-qualify function and dictionary
$func$  LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT;

Нарешті, індекс вираження для швидких запитів :

CREATE INDEX users_unaccent_name_idx ON users(public.f_unaccent(name));

Не забудьте відтворити індекси, що включають цю функцію після будь-якої зміни функції чи словника, як-от на місці оновлення основного випуску, яке не відтворило б індекси. Останні основні релізи мали оновлення для unaccentмодуля.

Адаптуйте запити відповідно до індексу (таким чином планувальник запитів буде використовувати його):

SELECT * FROM users
WHERE  f_unaccent(name) = f_unaccent('João');

Вам не потрібна функція у правильному виразі. Там же ви можете також поставити рядки без акцентування, як 'Joao'безпосередньо.

Більш швидка функція не перетворюється на набагато швидші запити, використовуючи індекс вираження . Це працює на попередньо обчислених значеннях і вже дуже швидко. Але обслуговування індексів та запити, що не використовують перевагу індексу.

Безпека для клієнтських програм була затягнута з Postgres 10,3 / 9.6.8 і т.д. Ви потрібні для схеми-кваліфікуватися функціями і ім'я словника , як показано при використанні в будь-яких індексах. Побачити:

Лігатури

У Postgres 9.5 або старіших лігатур типу "Œ" або "ß" потрібно розширити вручну (якщо це потрібно), оскільки unaccent()завжди підміняє одну букву:

SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
E A e a S

Вам сподобається це оновлення без уваги в Postgres 9.6 :

Розгорніть contrib/unaccentстандартний unaccent.rulesфайл, щоб обробити всі діакритики, відомі Unicode, і правильно розгорніть лігатури (Томас Манро, Леонард Бенедетті)

Сміливий акцент мій. Тепер ми отримуємо:

SELECT unaccent('Œ Æ œ æ ß');

unaccent
----------
OE AE oe ae ss

Узгодження шаблону

Для LIKEабо ILIKEз довільними візерунками комбінуйте це з модулем pg_trgmу PostgreSQL 9.1 або пізнішої версії. Створіть триграмовий GIN (як правило, бажано) або індекс вираження GIST. Приклад для GIN:

CREATE INDEX users_unaccent_name_trgm_idx ON users
USING gin (f_unaccent(name) gin_trgm_ops);

Може використовуватися для запитів, таких як:

SELECT * FROM users
WHERE  f_unaccent(name) LIKE ('%' || f_unaccent('João') || '%');

Індекси GIN і GIST дорожче підтримувати, ніж звичайний btree:

Існують більш прості рішення для лише ліворізаних моделей. Докладніше про відповідність шаблону та ефективність:

pg_trgmтакож надає корисні оператори для "подібності" ( %) та "відстані" ( <->) .

Індекси триграму також підтримують прості регулярні вирази з ~співавт. і нечутливий рисунок регістру, що відповідає ILIKE:


Чи використовуються у вашому рішенні індекси чи мені потрібно створити індекс unaccent(name)?
Даніель Серодіо

@ErwinBrandstetter У psql 9.1.4 я отримую "функції в виразі індексу повинні бути позначені НЕПРАВНИМИ", оскільки непридатна функція STABLE, замість INMUTABLE. Що ви порадите?
e3matheus

1
@ e3matheus: відчуваючи вину за те, що не перевірив попереднє рішення, яке я запропонував, я дослідив та оновив свою відповідь новим і кращим рішенням (ІМХО) проблеми, ніж те, що зараз плаває.
Ервін Брандстеттер

Чи не utf8_general_ciвідповідність відповіді на подібні питання?
Мед

5
Ваші відповіді такі ж хороші, як і документація Postgres: феноменально!
електротип

6

Ні, PostgreSQL не підтримує порівняння в цьому сенсі

PostgreSQL не підтримує такі зібрання (наголос, нечутливий чи ні), оскільки жодне порівняння не може повернутись рівним, якщо речі не є двійковими рівними. Це тому, що внутрішньо він би вніс багато складностей для таких речей, як хеш-індекс. З цієї причини порівняння в їх найсуворішому розумінні впливають лише на впорядкованість, а не на рівність.

Обхідні шляхи

Повнотекстовий пошуковий словник, який не містить ліксеми.

Для FTS ви можете визначити власний словник за допомогою unaccent,

CREATE EXTENSION unaccent;

CREATE TEXT SEARCH CONFIGURATION mydict ( COPY = simple );
ALTER TEXT SEARCH CONFIGURATION mydict
  ALTER MAPPING FOR hword, hword_part, word
  WITH unaccent, simple;

Що потім можна індексувати за допомогою функціонального індексу,

-- Just some sample data...
CREATE TABLE myTable ( myCol )
  AS VALUES ('fóó bar baz'),('qux quz');

-- No index required, but feel free to create one
CREATE INDEX ON myTable
  USING GIST (to_tsvector('mydict', myCol));

Тепер ви можете запросити його дуже просто

SELECT *
FROM myTable
WHERE to_tsvector('mydict', myCol) @@ 'foo & bar'

    mycol    
-------------
 fóó bar baz
(1 row)

Дивитися також

Непристойний сам по собі.

unaccentМодуль також може бути використаний сам по собі , без FTS-інтеграції, для цього закінчував відповідь Ервін


2

Я впевнений, що PostgreSQL для порівняння покладається на базову операційну систему. Це дійсно підтримує створення нових параметрів сортування і налаштування сортування . Я не впевнений, скільки роботи може бути для вас. (Може бути досить багато.)


1
Наразі підтримка нового порівняння в основному обмежена обгортками та псевдонімами для локальних систем операційної системи. Це дуже просто. Немає підтримки функцій фільтрів, спеціальних компараторів або будь-якого з того, що вам знадобиться для справжніх спеціальних зіставлень.
Крейг Рінгер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.