【问题标题】:Rails ActiveRecord - Search on Multiple AttributesRails ActiveRecord - 搜索多个属性
【发布时间】: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 注入?

非常感谢!

【问题讨论】:

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


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 2014-11-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-05
        • 1970-01-01
        • 2015-08-27
        • 2011-06-15
        相关资源
        最近更新 更多