Rails ActiveRecord - 在多个属性上搜索

时间:2012-08-05 13:42:18

标签: ruby-on-rails ruby-on-rails-3 search activerecord sql-injection

我正在实现一个简单的搜索功能,该功能应检查username,last_name和first_name中的字符串。我在旧的RailsCast上看过这个ActiveRecord方法:

http://railscasts.com/episodes/37-simple-search-form

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

但是如何设置它以便在name,last_name和first name中搜索关键字并在其中一个字段与该术语匹配时返回记录?

我也想知道RailsCast上的代码是否容易进行SQL注入?

非常感谢!

5 个答案:

答案 0 :(得分:22)

我假设您的模型名称是模型 - 只需在执行实际查询时将其替换为您的真实模型名称:

Model.where("name LIKE ? OR last_name LIKE ? OR first_name LIKE ?", "%#{search}%","%#{search}%","%#{search}%")

关于您对SQL注入的担忧 - 两个代码段都不受SQL注入的影响。只要你没有直接将字符串嵌入到WHERE子句中就可以了。易注入代码的一个例子是:

Model.where("name LIKE '#{params[:name]}'")

答案 1 :(得分:12)

虽然选择的答案会起作用,但我注意到如果你试图输入一个搜索“Raul Riera”就会中断,因为它会在两种情况下失败,因为Raul Riera不是我的名字或我的姓。我的名字和姓氏......我通过

解决了这个问题
Model.where("lower(first_name || ' ' || last_name) LIKE ?", "%#{search.downcase}%")

答案 2 :(得分:2)

使用Arel,您可以避免使用以下内容手动编写SQL:

Model.where(
  %i(name first_name last_name)
    .map { |field| Model.arel_table[field].matches("%#{query}%")}
    .inject(:or)
)

如果要匹配的字段列表是动态的,这将特别有用。

答案 3 :(得分:0)

最好的方法是:

Model.where("attr_a ILIKE :query OR attr_b ILIKE :query", query: "%#{query}%")

答案 4 :(得分:0)

在模型的所有字段中搜索的更通用的解决方案是这样的

def search_in_all_fields model, text
  model.where(
    model.column_names
      .map {|field| "#{field} like '%#{text}%'" }
      .join(" or ")
  )
end

或者更好地作为模型本身的作用域

class Model < ActiveRecord::Base
  scope :search_in_all_fields, ->(text){
    where(
      column_names
        .map {|field| "#{field} like '%#{text}%'" }
        .join(" or ")
    )
  }
end

您只需要这样称呼

Model.search_in_all_fields "test"

开始之前..,不,sql注入在这里可能无法工作,但效果更好,更短

class Model < ActiveRecord::Base
  scope :search_all_fields, ->(text){
    where("#{column_names.join(' || ')} like ?", "%#{text}%")
  }
end