Как создать простой нечеткий поиск только с помощью Postgresql?

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

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

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

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

2 ответов


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;

Павел рассказал вам о 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 сверху.

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