Нечувствительный к Postgres акцент LIKE поиск в Rails 3.1 на Heroku - PullRequest
16 голосов
/ 11 февраля 2012

Как я могу изменить условие где / как в поисковом запросе в Rails:

find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])

, чтобы результаты соответствовали независимо от ударений?(например, метро = метро).Поскольку я использую utf8, я не могу использовать "to_ascii".Производство работает на Heroku.

Ответы [ 5 ]

28 голосов
/ 14 февраля 2012

Решение для бедного человека

Если вы можете создать функцию, вы можете использовать эту.Я составил список, начинающийся с здесь , и со временем добавил его.Это довольно полно.Вы можете даже удалить некоторые символы:

CREATE OR REPLACE FUNCTION lower_unaccent(text)
  RETURNS text AS
$func$
SELECT lower(translate($1
     , '¹²³áàâãäåāăąÀÁÂÃÄÅĀĂĄÆćčç©ĆČÇĐÐèéêёëēĕėęěÈÊËЁĒĔĖĘĚ€ğĞıìíîïìĩīĭÌÍÎÏЇÌĨĪĬłŁńňñŃŇÑòóôõöōŏőøÒÓÔÕÖŌŎŐØŒř®ŘšşșߊŞȘùúûüũūŭůÙÚÛÜŨŪŬŮýÿÝŸžżźŽŻŹ'
     , '123aaaaaaaaaaaaaaaaaaacccccccddeeeeeeeeeeeeeeeeeeeeggiiiiiiiiiiiiiiiiiillnnnnnnooooooooooooooooooorrrsssssssuuuuuuuuuuuuuuuuyyyyzzzzzz'
     ));
$func$ LANGUAGE sql IMMUTABLE;

Ваш запрос должен работать следующим образом:

find(:all, :conditions => ["lower_unaccent(name) LIKE ?", "%#{search.downcase}%"])

Для поиска с левой привязкой вы можете использовать индекс для функции для очень быстрые результаты:

CREATE INDEX tbl_name_lower_unaccent_idx
  ON fest (lower_unaccent(name) text_pattern_ops);

Для таких запросов, как:

SELECT * FROM tbl WHERE (lower_unaccent(name)) ~~ 'bob%'

Правильное решение

В PostgreSQL 9.1 + ,с необходимыми привилегиями вы можете просто:

CREATE EXTENSION unaccent;

, который предоставляет функцию unaccent(), делая то, что вам нужно (за исключением lower(), просто используйте это дополнительно, если необходимо),Прочтите руководство об этом расширении .
Также доступно для PostgreSQL 9.0 , но синтаксис CREATE EXTENSION впервые введен в 9.1.

Подробнее о unaccent и indexes:

14 голосов
/ 05 июля 2015

Для таких, как я, у которых возникли проблемы с добавлением расширения unaccent для PostgreSQL и настройкой его работы с приложением Rails, вот миграция, которую вам нужно создать:

class AddUnaccentExtension < ActiveRecord::Migration
  def up
    execute "create extension unaccent"
  end

  def down
    execute "drop extension unaccent"
  end
end

И, конечно же, после rake db:migrate вы сможете использовать функцию unaccent в своих запросах: unaccent(column) similar to ... или unaccent(lower(column)) ...

3 голосов
/ 25 декабря 2013

Прежде всего, вы устанавливаете postgresql-contrib.Затем вы подключаетесь к своей БД и выполняете:

CREATE EXTENSION unaccent;

, чтобы включить расширение для вашей БД.

В зависимости от вашего языка, вам может потребоваться создать новый файл правил (в моем случаеgreek.rules, расположенный в /usr/share/postgresql/9.1/tsearch_data), или просто добавьте к существующему unaccent.rules (довольно просто).

В случае, если вы создаете свой собственный файл .rules, вам нужно установить его по умолчанию:

ALTER TEXT SEARCH DICTIONARY unaccent (RULES='greek');

Это изменение является постоянным, поэтому вам не нужно его переделывать.

Следующим шагом будет добавление метода в модель для использования этой функции.

Одним простым решением было бы определение функции в модели.Например:

class Model < ActiveRecord::Base
    [...]
    def self.unaccent(column,value)
        a=self.where('unaccent(?) LIKE ?', column, "%value%")
        a
    end
    [...]
end

Затем я могу просто вызвать:

Model.unaccent("name","text")

Вызов той же команды без определения модели будет таким же простым, как:

Model.where('unaccent(name) LIKE ?', "%text%"

Примечание: приведенный выше пример был протестирован и работает на postgres9.1, Rails 4.0, Ruby 2.0.

ОБНОВЛЕНИЕ ИНФОРМАЦИИ
Исправлен потенциальный бэкдор SQLi благодаря обратной связи @Henrik N

2 голосов
/ 12 февраля 2012

Есть 2 вопроса, связанных с вашим поиском в StackExchange: https://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search

Но поскольку вы работаете в Heroku, я сомневаюсь, что это хорошее совпадение (если у вас нет выделенного плана базы данных).

Существует также это на SO: Удаление ударений / диакритических знаков из строки при сохранении других специальных символов .

Но это предполагает, что ваши данные хранятся без акцента.

Надеюсь, это укажет вам правильное направление.

0 голосов
/ 23 января 2016

Предположим, Foo - это модель, которую вы ищете, а name - столбец. Объединение Postgres translate и ActiveSupport транслитерируется . Вы можете сделать что-то вроде:

Foo.where(
  "translate(
    LOWER(name),
    'âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ',
    'aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu'
  )
  LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%"
)
...