Поиск ActiveRecord начинается с

Действительно простой вопрос - как мне выполнить поиск, чтобы найти все записи, где имя начинается с определенной строки в ActiveRecord. Я видел всевозможные биты по всему Интернету, где используются дословные предложения LIKE SQL, но из того, что я слышал, это не "правильный" способ сделать это.

Есть ли правильный путь Rails?

Ответ 1

Я очень рекомендую Searchlogic плагин.

Тогда это так же просто, как:

@search = Model.new_search(params[:search])
@search.condition.field_starts_with = "prefix"
@models = @search.all

Searchlogic достаточно умен, как ActiveRecord, для поиска имени поля в starts_with. Он также будет обрабатывать всю разбивку на страницы.

Этот метод поможет предотвратить SQL-инъекцию, а также будет агностикой базы данных. Searchlogic в конечном итоге обрабатывает поиск по-разному в зависимости от используемого вами адаптера базы данных. Вам также не нужно писать SQL!

Searchlogic имеет отличную документацию и прост в использовании (я новичок в Ruby и Rails). Когда я застрял, автор плагина даже ответил на прямую электронную почту в течение нескольких часов, помогая мне исправить мою проблему. Я не могу рекомендовать Searchlogic достаточно... как вы можете сказать.

Ответ 2

Если вы хотите выполнить поиск в базе данных, вам нужно использовать SQL.

И, конечно же, вам нужно будет выполнить поиск в базе данных, иначе вам нужно будет загрузить все объекты в Ruby (что не очень хорошо).

Итак, вам понадобится что-то вроде

MyModel.find(:all, :conditions => ["field LIKE ?", "#{prefix}%"])

где prefix - переменная со строкой, которую вы ищете.

В Rails 3.0+ это будет:

MyModel.where("field LIKE :prefix", prefix: "#{prefix}%")

Ответ 3

Отказ от ответственности: я новичок в Ruby и Rails, и я все еще пытаюсь научиться Ruby Way делать что-то, но я кодировал более половины своей жизни и профессионально в течение десятилетия. DRY - это концепция, с которой я очень хорошо знаком. Вот как я его реализовал. Это очень похоже на нарский ответ, который, я думаю, тоже хорош.

# name_searchable.rb
# mix this into your class using
#   extend NameSearchable
module NameSearchable
  def search_by_prefix (prefix)
    self.where("lower(name) LIKE '#{prefix.downcase}%'")
  end
end

И затем в вашей модели:

class User < ActiveRecord::Base
  extend NameSearchable
  ...
end

И затем, когда вы хотите использовать его:

User.search_by_prefix('John')   #or
User.search_by_prefix("#{name_str}")

Одно дело:

Традиционные реляционные базы данных не очень хорошо удовлетворяют эти запросы. Если вы ищете высокочувствительную реализацию, которая не будет убивать ваши базы данных под нагрузкой, вы, вероятно, должны использовать решение, специально предназначенное для этой цели. Популярные примеры включают Solr или Sphinx, а также много других. С этой реализацией, поскольку вы DRY, вы можете заменить реализацию отдельным индексом только в одном месте, и вы будете готовы качать.

Ответ 4

Очень похоже на приведенный выше ответ, но если вы используете ActiveRecord... 2.1 или выше, вы можете использовать именованную область, которая позволит вам использовать этот "finder" для записей, полученных через ассоциации:

class User
  named_scope :with_name_like, lambda {|str|
    :conditions => ['lower(name) like ?', %(%#{str.downcase}%)]
  }
end

Используется как:

User.with_name_like("Samson")

(возвращает всех пользователей в вашей базе данных с именем типа "Самсон".

Или:

some_association.users.with_name_like("Samson")

Пользователи отключены от этой ассоциации только с именем типа "Самсон".

Ответ 5

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

# initializers/active_record_initializers.rb
class ActiveRecord::Base
  # do not accept a column_name from the outside without sanitizing it
  # as this can be prone to sql injection
  def self.starts_with(column_name, prefix)
    where("lower(#{column_name}) like ?", "#{prefix.downcase}%")
  end
end

Назовите его следующим образом:

User.starts_with('name', 'ab').limit(1)

Ответ 6

Теперь, в 2012 году, я думал, что заброшу это обновление в ответ нарск.

named_scope стал просто scope некоторое время назад, поэтому пример становится

class User
  scope :name_starts_with, lambda {|str|
    :conditions => ['lower(name) like ?', "#{str.downcase}%"]
  }
end

Примечание. Я удалил первый % из решения narsk, так как OP запрашивает "start_with" не совпадение "содержит".

Теперь вы можете делать что-то вроде

User.name_starts_with("b").each do {|bs| puts bs.inspect}
bob = User.name_starts_with("bob").first

… etc

Обратите внимание, что области всегда возвращают коллекции, а не индивидуальные результаты. Это бросило меня, когда я впервые начал работать с AR, и мне все равно удается это время от времени забывать.

Ответ 7

Дэйв Саг, я думаю, первая часть вашего кода должна быть

class User
  scope :name_starts_with, (lambda do |str|
                              {:conditions => ['lower(name) like ?', "#{str.downcase}%"]}
                            end )
end

Ответ 8

Очень краткий синтаксис для того же самого может быть:

Model.where(field: ('prefix'...'prefiy'))

Это означает:

WHERE ( field >= 'prefix' AND field < 'prefiy')

Это задание как "prefiy" - это первая строка в алфавитном порядке, не соответствующая префиксу "префикс".

Я бы не использовал его в обычном режиме (вместо этого я бы пошел на squeel или ransack), но он может сэкономить ваш день, если по какой-то причине вам придется придерживаться хэш-синтаксиса для запросов...