Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

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;

Ответ 2

Пол рассказал вам о 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-lHdfjab123lhdfj, сохранить его в дополнительном столбце и найти в нем поисковые термины, которые были преобразованы одинаково.

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

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