Как создать простой нечеткий поиск только с Postgresql? - PullRequest
33 голосов
/ 11 октября 2011

У меня небольшая проблема с функциями поиска на моем сайте на основе RoR. У меня есть много продуктов с некоторыми кодами. Этот код может быть любой строкой, такой как «AB-123-lHdfj». Теперь я использую оператор ILIKE для поиска товаров:

Product.where("code ILIKE ?", "%" + params[:search] + "%")

Работает нормально, но не может найти продукт с кодами типа "AB123-lHdfj" или "AB123lHdfj".

Что мне для этого сделать? Может быть, в postgresql есть какая-то функция нормализации строк или какие-то другие методы, которые могут мне помочь? :)

Ответы [ 2 ]

47 голосов
/ 11 октября 2011

Postgres предоставляет модуль с несколькими функциями сравнения строк, такими как soundex и metaphone. Но вы захотите использовать функцию редактирования расстояния Левенштейн .

Example:

test=# SELECT levenshtein('GUMBO', 'GAMBOL');
 levenshtein
-------------
           2
(1 row)

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

Попробуйте этот пример запроса: (с вашими собственными именами объектов и данными, конечно)

SELECT * 
FROM some_table
WHERE levenshtein(code, 'AB123-lHdfj') <= 3
ORDER BY levenshtein(code, 'AB123-lHdfj')
LIMIT 10

Этот запрос говорит:

Дайте мне первые 10 результатов всех данных из some_table, где расстояние редактирования между значением кода и входом 'AB123-lHdfj' меньше 3. Вы получите все строки, где значение кода находится в пределах 3 символов. разница с 'AB123-lHdfj' ...

Примечание: если вы получаете сообщение об ошибке типа:

function levenshtein(character varying, unknown) does not exist

Установите расширение fuzzystrmatch, используя:

test=# CREATE EXTENSION fuzzystrmatch;
39 голосов
/ 13 октября 2011

Павел рассказал вам о levenshtein().Это очень полезный инструмент, но он также очень медленный с большими столами.Он должен рассчитать расстояние Левенштейна от условия поиска для каждой строки, это дорого.

Прежде всего, , если ваши требования просты, как показано в примере, вы все равно можете использовать LIKE.Просто замените любой - в вашем поисковом запросе на %, чтобы создать WHERE предложение

WHERE code LIKE "%AB%123%lHdfj%"

вместо

WHERE code LIKE "%AB-123-lHdfj%"

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

  • Существует, конечно, полнотекстовый поиск .Но это может быть излишним в вашем случае.

  • Более вероятным кандидатом является pg_trgm .Обратите внимание, что вы можете комбинировать это с LIKE в PostgreSQL 9.1.Смотрите это сообщение в блоге Depesz .
    Также очень интересно в этом контексте: функция similarity() или оператор % этого модуля.Подробнее:

  • Последнее, но не менее важное, вы можете реализовать решение, связанное вручнуюфункция нормализации строк для поиска.Например, вы можете преобразовать AB1-23-lHdfj -> ab123lhdfj, сохранить его в дополнительном столбце и выполнить поиск по поисковым запросам, которые были преобразованы таким же образом.

    Или используйте индекс для выражения вместо избыточного столбца.(Включенные функции должны быть IMMUTABLE.) И возможно объединить это с pg_tgrm сверху.

Обзор методов сопоставления с образцом:

...