Rails ActiveRecord - 搜索多个属性

Posted

技术标签:

【中文标题】Rails ActiveRecord - 搜索多个属性【英文标题】:Rails ActiveRecord - Search on Multiple Attributes 【发布时间】:2012-08-02 18:04:55 【问题描述】:

我正在实现一个简单的搜索功能,它应该检查用户名、姓氏和名字中的字符串。我在旧的 RailsCast 上看到过这个 ActiveRecord 方法:

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

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

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

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

非常感谢!

【问题讨论】:

【参考方案1】:

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

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

关于您对 SQL 注入的担忧 - 两种代码 sn-ps 都不受 SQL 注入的影响。只要您不直接将字符串嵌入到 WHERE 子句中,就可以了。易于注入的代码示例如下:

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

【讨论】:

如果控制器被更改为传递整个params 而不仅仅是params[:search] 并且在我们的查询中我们使用"%#params[:search]%" 而不是"%#search%"。这仍然不受 SQL 注入的影响吗? @Dennis 是的。只要你用?并在查询中传递额外的参数来替换它们就可以了。 谢谢@ErezRabih。那么使用哈希呢?例如。 where(name: params[:name]) 这个查询就像一个魅力! (使用活动记录 5) 拥有这三个参数真的很难看。最好这样命名Model.where("x ILIKE :query OR y ILIKE :query", query: "%#params[:query]%")【参考方案2】:

虽然选择的答案会起作用,但我注意到如果您尝试输入搜索“Raul Riera”,它会中断,因为这两种情况都会失败,因为 Raul Riera 既不是我的名字也不是我的姓氏。我的名字和姓氏......我通过这样做解决了它

Model.where("lower(first_name || ' ' || last_name) LIKE ?", "%#search.downcase%")

【讨论】:

我发现这个答案更有用。谢谢! 使用ILIKE而不是小写不是更好吗?让数据库处理它。 last_name 或 first_name 等于 nil 时不起作用,如果是,则需要将默认的空字符串值设置为名字和姓氏【参考方案3】:

最好的方法是:

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

【讨论】:

【参考方案4】:

使用 Arel,您可以避免手动编写 SQL,如下所示:

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

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

【讨论】:

你忘记了 .map 行的结束符 ,修改它【参考方案5】:

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

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

【讨论】:

以上是关于Rails ActiveRecord - 搜索多个属性的主要内容,如果未能解决你的问题,请参考以下文章

使用 Activerecord、Rails 和 Postgres 查找具有多个重复字段的行

Rails 4,ActiveRecord,查找当前用户未评论的所有帖子

Rails/Arel:选择所有记录作为 ActiveRecord::Relation

如何编写迁移以重命名 Rails 中的 ActiveRecord 模型及其表?

Rails,ActiveRecord,Squeel:检查数组中是不是存在值

Rails ActiveRecord关系无效外键错误