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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
43
0
34 417
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

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

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

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

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

где prefix - это переменная с искомой строкой.

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

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

Подстановочный знак% должен быть в конце, если вы ищете что-то, что начинается с префикса. MyModel.find (: all,: conditions => ["поле LIKE?", "# {Prefix}%"])

Owen 30.10.2008 23:05

Есть ли способ сделать это, а также защитить от инъекции символов %?

chakrit 18.11.2016 12:02
Ответ принят как подходящий

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

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

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")

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

Произошел интересный феномен. Вы сказали, что вам «нравится приведенный выше ответ», но вы имели в виду ответ, который больше не является тем, что находится прямо над вашим ответом. Возможно, в будущем будет разумнее ссылаться на ответ автора, а не на порядок.

Larry 10.08.2011 20:26

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

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

Ларри, я удалил P.S. где вы просили людей проголосовать за ваш ответ, потому что он ваш первый (кстати, на вопрос трехлетней давности). Если им это нравится, они проголосуют за него, независимо от того, сколько вы уже дали.

Michael Kohl 05.08.2011 21:10

мне кажется, что в эту можно было бы внедрить SQL. Скажем, prefix пришел из чего-то вроде формы поиска, я мог бы ввести something' OR 1 -- в эту форму. Затем будут перечислены ВСЕ записи. Это также указано в руководствах: guides.rubyonrails.org/…

amenthes 14.05.2014 13:25

Сейчас 2012 год, я подумал, что добавлю это обновление в ответ narsk.

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

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

Обратите внимание, что я удалил первый % из решения narsk, поскольку OP запрашивает совпадение «start_with», а не «contains».

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

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

… etc

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

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

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

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

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

Это отображает:

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

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

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

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

# 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)

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

Peter DeWeese 14.05.2018 18:19

Другие вопросы по теме