Общее решение Ruby для SQLite3 "LIKE" или PostgreSQL "ИЛИКЕ"?

Я использую SQLite3 для разработки и PostgreSQL для развертывания. Однако я столкнулся со следующей проблемой:

Мой простой поиск с помощью SQLite3:

def self.search(search)
    if search
      find(:all, :conditions => ["style LIKE ? OR construction LIKE ?", "%#{search}%", "%#{search}%"])
    else
      find(:all)
    end
end

Однако это не работает для PostgreSQL, и мне нужно заменить LIKE на ILIKE, чтобы решить проблему:

def self.search(search)
    if search
      find(:all, :conditions => ["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"])
    else
      find(:all)
    end
end

Есть ли способ "Ruby" для этих поисков в любой базе данных?

РЕДАКТИРОВАТЬ - на основе ваших ответов я не верю, что найду для вас общее решение Ruby.

Я последовал за Учебным пособием Ruby on Rails: Изучите Rails по примеру - Michael Hartl, где окончательный Gemfile показывает обе базы данных... ну, неутешительно...

Ответ 1

Здесь находится корень проблемы:

Я использую SQLite3 для разработки и PostgreSQL для развертывания.

Это плохая идея. Вы будете продолжать сталкиваться с несовместимостями - или хуже: не осознавать их до тех пор, пока не будет нанесен ущерб.
Используйте ту же СУБД (PostgreSQL) для разработки и производства и избавьте себя от бессмысленной проблемы.


Пока вы застряли в неудачной настройке, существует простое исправление:

lower(style) LIKE lower(?)

Работает на обеих платформах.

  • Вы можете оставить правую lower(), если вы предоставите шаблон поиска в нижнем регистре.

  • В стандартном SQLite lower(X) только сбрасываются буквы ASCII. Более того, я цитирую главу Основные функции в руководстве SQLite:

    Нижняя (X) функция возвращает копию строки X со всеми Символы ASCII преобразуются в нижний регистр. По умолчанию встроенный нижний() функция работает только для символов ASCII. Чтобы конвертировать события в не-ASCII-символы, загрузить расширение ICU.

    Акцент на мой.

  • PostgreSQL lower(X) работает с UTF-8 из коробки.


В качестве приветствуемого побочного эффекта вы можете ускорить этот запрос в PostgreSQL с индексом в выражении lower(style), что будет быстрее, чем использование ILIKE и базового индекса на style.

Кроме того, поскольку PostgreSQL 9.1 вы можете использовать индекс GIN или GIST с расширением pg_trgm для ускорения все теги LIKE и ILIKE - триграммы нечувствительны к регистру. Подробные инструкции и ссылки в этом связанном ответе:

Ответ 2

Я думаю, что Arel - лучший способ решить эту проблему. Он используется Rails для активной записи и независим от базы данных. Ваш код будет работать одинаково в sqlite3 или postgres, который, по-видимому, соответствует вашей ситуации. Использование метода совпадений автоматически переключится на ilike в среде postgres. Пример:

users=User.arel_table
User.where(users[:style].matches("%#{search}%").or(users[:construction].matches("%#{search}%")))

Вы можете получить дополнительную информацию от github: https://github.com/rails/arel/

Ответ 3

Нет, нет рубинового пути для поиска базы данных - Ruby on Rails (в частности ActiveRecord) содержит вспомогательные методы для выполнение CRUD-операций на RDB, поддерживаемых ActiveRecord, но нет лучшего способа поиска с использованием LIKE, а затем приведенных вами примеров.

Раздел документа Rails, связанный с этим обсуждением, будет ActiveRecord:: FinderMethods.

В качестве побочного примечания вместо find(:all) вы можете просто сделать all.

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

Person.exists?(['name LIKE ?', "%#{query}%"])

Использование вышеописанного метода совершенно безопасно.

Причина, по которой утверждения, подобные приведенным ниже, небезопасны, состоит в том, что строка where передается непосредственно в ваш запрос базы данных без какой-либо дезинфекции, что оставляет вашу базу данных открытой для эксплуатации (т.е. простой апостроф в params[:first_name] может испортить весь ваш запрос и оставьте уязвимость вашей базы данных, в частности, SQL-инъекцией). В приведенном выше примере ActiveRecord может дезактивировать параметры, которые вы передаете в запрос.

Client.where("first_name LIKE '%#{params[:first_name]}%'")

Ответ 4

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

where(["style ILIKE ? OR construction ILIKE ?", "%#{search}%", "%#{search}%"])

К сожалению, как вы, вероятно, поняли, этот альтернативный синтаксис столкнется с одной и той же проблемой. Это всего лишь одно из неприятностей, с которыми вы столкнетесь, имея существенно отличающуюся среду разработки от вашей производственной среды - есть и другие довольно существенные различия между SQLite и PostgresSQL. Я бы рекомендовал просто установить Postgres на вашу машину разработки и использовать ее. Это упростит процесс разработки и сделает ваш код намного более чистым.

Ответ 5

Хотя это не очень хорошая практика для использования различных баз данных в производстве и разработке, по-прежнему является хорошим примером использования squeel gem: https://github.com/ernie/squeel/

С его помощью вы можете писать свои запросы с помощью DSL, что легко и, возможно, намного чище и читабельно, чем raw sql, и этот камень будет обрабатывать его перевод на SQL, специфичный для используемой RDBMS.

У Райана Бэйтса есть хорошее видео: http://railscasts.com/episodes/354-squeel

Ответ 6

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

Я написал немного lib, которые позволяют использовать функцию для каждой базы данных:

class AdapterSpecific

  class << self
    def like_case_insensitive
      case ActiveRecord::Base.connection.adapter_name
      when 'PostgreSQL'
        'ILIKE'
      else
        'LIKE'
      end
    end    

    def random
      #something
    end
  end

Чтобы использовать его в своей модели:

def self.search(search)
    if search
      find(:all, :conditions => ["style #{AdapterSpecific.like_case_insensitive} :query OR construction #{AdapterSpecific.like_case_insensitive} :query", {:query => "%#{search}%"}])
    else
      find(:all)
    end
end

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

Вы также можете использовать полнотекстовый поиск Postgres, который сделает ваш текстовый поиск более эффективным, см. texticle для базового или pg_search, если вам нужна дополнительная настройка.

Ответ 7

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

Более длинный ответ: я бы рекомендовал использовать Postgres в разработке (ditching sqlite3), а затем иметь полнотекстовый индекс во всех ваших поисковых полях style, construction через тип Postgres tsvector.

Полнотекстовый поиск в Postgres при использовании индекса очень быстрый.