Как создать простой нечеткий поиск только с помощью 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
сверху.
обзор методов сопоставления шаблонов: