【问题标题】:Rails 4 LIKE query - ActiveRecord adds quotesRails 4 LIKE 查询 - ActiveRecord 添加引号
【发布时间】:2013-10-06 23:51:48
【问题描述】:

我正在尝试像这样进行类似查询

def self.search(search, page = 1 )
  paginate :per_page => 5, :page => page,
    :conditions => ["name LIKE '%?%' OR postal_code like '%?%'", search, search],   order => 'name'
end

但是当它运行时,添加引号会导致 sql 语句像这样出现

SELECT COUNT(*)
FROM "schools" 
WHERE (name LIKE '%'havard'%' OR postal_code like '%'havard'%')):

所以你可以看到我的问题。 我正在使用我从未使用过的 Rails 4 和 Postgres 9,所以不确定它是 activerecord 还是 postgres。

我该如何设置,以便在最后的查询中有 '%my_search%' 之类的?

【问题讨论】:

    标签: sql ruby-on-rails ruby postgresql activerecord


    【解决方案1】:

    你的占位符被一个字符串替换,你没有正确处理它。

    替换

    "name LIKE '%?%' OR postal_code LIKE '%?%'", search, search
    

    "name LIKE ? OR postal_code LIKE ?", "%#{search}%", "%#{search}%"
    

    【讨论】:

    • 这不是易受 SQL 注入攻击吗?我的意思是,那些search 字符串是否经过消毒?
    • @jdscosta91 ? 将负责消毒
    • @house9 实例 %_search 中不会被清理,在这种方法下。
    • 当使用 '?'通过这种方式,在 rails 中它被转换为参数化查询。参数中的数据未经过清理(%),但不可能从查询中更改上下文并将其转换为已处理的 SQL 语句。
    【解决方案2】:

    试试

     def self.search(search, page = 1 )
        paginate :per_page => 5, :page => page,
          :conditions => ["name LIKE  ? OR postal_code like ?", "%#{search}%","%#{search}%"],   order => 'name'
      end
    

    有关更多信息,请参阅有关 AREL 条件的docs

    【讨论】:

      【解决方案3】:

      不要使用 Rails 2 中的 conditions 语法,而是使用 Rails 4 的 where 方法:

      def self.search(search, page = 1 )
        wildcard_search = "%#{search}%"
      
        where("name ILIKE :search OR postal_code LIKE :search", search: wildcard_search)
          .page(page)
          .per_page(5)
      end
      

      注意:上面使用参数语法而不是?占位符:它们都应该生成相同的 sql。

      def self.search(search, page = 1 )
        wildcard_search = "%#{search}%"
      
        where("name ILIKE ? OR postal_code LIKE ?", wildcard_search, wildcard_search)
          .page(page)
          .per_page(5)
      end
      

      注意:使用 ILIKE 作为名称 - LIKE 的 postgres 不区分大小写版本

      【讨论】:

      • 这对 Rails 5 仍然有效吗?因为如果我有 Movie.where("title ILIKE :s", s: search_string) 它会被 ActiveRecord (Rails 5.1.6) 翻译成 SELECT 1 AS one FROM "movies" WHERE (title ILIKE 'test') LIMIT $1 - 请注意 ILIKE 之后没有百分比符号)
      • @sekmo - 它应该工作,百分比将进入search_string 变量。我认为SELECT 1 AS one 输出仅在rails 控制台中,或者您正在使用limit(1)?仅供参考:Rails 5.1.6 存在安全问题,请改用 5.1.6.2 或 5.1.7
      【解决方案4】:

      虽然字符串插值会起作用,但由于您的问题指定了 rails 4,您可以为此使用 Arel 并保持您的应用程序数据库不可知。

      def self.search(query, page=1)
        query = "%#{query}%"
        name_match = arel_table[:name].matches(query)
        postal_match = arel_table[:postal_code].matches(query)
        where(name_match.or(postal_match)).page(page).per_page(5)
      end
      

      【讨论】:

      • 应该真的很容易创建一个函数来动态地使用属性列表来执行此操作
      • 未经测试,虽然它可能是这样的:scope search_attributes, ->(query, attributes){ arel_attributes = attributes.map{|a| arel_table[a]} arel_queries = arel_attributes.map{|a| a.matches(query)} return where(arel_queries.reduce{|res,q| res.or q}) }
      【解决方案5】:
      .find(:all, where: "value LIKE product_%", params: { limit: 20, page: 1 })
      

      【讨论】:

        【解决方案6】:

        ActiveRecord 足够聪明,知道? 引用的参数是一个字符串,因此它用单引号将其括起来。 可以正如一篇文章所建议的那样,使用 Ruby 字符串插值来用所需的 % 符号填充字符串。但是,这可能会使您面临 SQL 注入(这很糟糕)。我建议你使用 SQL CONCAT() 函数来准备这样的字符串:

        "name LIKE CONCAT('%',?,'%') OR postal_code LIKE CONCAT('%',?,'%')", search, search)

        【讨论】:

        • 我刚刚在一个应用程序中实现了这个,效果很好。谢谢约翰。
        • 关于注射,不,它没有,无论如何这没有区别。该字符串被插入到 sql 中,并且 rails 之前应该已经对其进行了验证(无论是否附加/前置 % 都无关紧要)。要么按预期工作,要么 Rails 存在影响这两种情况的主要错误。
        【解决方案7】:

        如果有人使用"key""value" 之类的列名,那么您仍然会看到与您的mysql 查询语法错误相同的错误。这应该可以解决:

        .where("`key` LIKE ?", "%#{key}%")
        

        【讨论】:

          猜你喜欢
          • 2015-09-04
          • 1970-01-01
          • 1970-01-01
          • 2018-08-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-23
          相关资源
          最近更新 更多