Поиск ActiveRecord начинается с - PullRequest
40 голосов
/ 30 октября 2008

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

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

Ответы [ 8 ]

98 голосов
/ 30 октября 2008

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

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

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

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

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

В Rails 3.0+ это становится:

MyModel.where("field LIKE :prefix", prefix: "#{prefix}%")
11 голосов
/ 30 октября 2008

Я очень рекомендую плагин 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 достаточно ... как вы можете сказать.

8 голосов
/ 05 августа 2011

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

# 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, вы можете заменить реализацию отдельным индексатором в одном месте, и вы будете готовы к работе.

7 голосов
/ 31 октября 2014

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

# 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)
4 голосов
/ 22 мая 2009

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

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

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

User.with_name_like("Samson")

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

Или:

some_association.users.with_name_like("Samson")

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

2 голосов
/ 01 сентября 2012

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

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

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

Заметьте, я удалил первую % из решения narsk, так как ОП запрашивает совпадение «старты_с не содержит».

Теперь вы можете делать такие вещи, как

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

… etc

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

1 голос
/ 20 июня 2014

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

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

Это делает:

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

Это делает работу, поскольку 'prefiy' является первой строкой в ​​алфавитном порядке, не соответствующей префиксу 'prefix' .

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

1 голос
/ 14 февраля 2013

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

class User
  scope :name_starts_with, (lambda do |str|
                              {:conditions => ['lower(name) like ?', "#{str.downcase}%"]}
                            end )
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...