【问题标题】:Rails 4: how to use OR between conditions on find methodsRails 4:如何在查找方法的条件之间使用 OR
【发布时间】:2013-09-10 15:27:10
【问题描述】:

我的问题类似于this one,但那里的答案都没有解决我的具体问题。

我想找到这样的对象:

conditions = {first_name: @search OR last_name: @search}
Stuff.where( conditions )

显然,这种语法是无效的,但它是我能想到的最简单的方法来展示我想要做什么。我想在复杂/复合条件下使用更简洁的哈希语法。

我知道你可以用像这样的“纯字符串条件”Stuff.where("first_name=#{@search} OR last_name=#{@search}") ...但这不是我想知道的。

更新看起来您可以对这样的数组执行 OR:Stuff.where( some_column: ["option1", "option2"])。知道这一点非常有用,但它并不能解决我的问题,因为我需要 OR 来应用于不同的键=值对...key=value OR key=value 而不是key=value OR value

Update2 我不想使用 SQL 字符串的原因是因为我需要分几部分构建查询,但我不知道如何做到这一点,同时仍然逃避插入的变量。我还没有测试过,但我假设这不起作用:

conditions = ["(project_id=? AND sent_to_api=1)", @project_id]
conditions = conditions + ["first_name=? OR last_name=?", @search, @search]
Stuff.where( conditions )

希望这是有道理的。有没有办法用 SQL 字符串语法做我需要的,同时仍然保留 Rails 的内置 SQL 转义?

【问题讨论】:

  • 如果唯一的方法是将条件写成字符串,如Stuff.where(["first_name = ? OR last_name = ?", @search]),你想知道吗?
  • ActiveRecord OR query的可能重复
  • 这有很好的记录,所以谢谢,但没有。 (我知道我的稻草人示例不是最佳的)
  • 查看Squeel gem 以获得更简洁的 ActiveRecord 语法。
  • 更新的方法实际上并没有生成OR,它使用IN () ...通常被SQL引擎解释为OR,但我想指出这一点以便澄清.

标签: ruby-on-rails ruby ruby-on-rails-4 sql-injection


【解决方案1】:

如何为您生成可怕的 SQL 字符串的可重用方法,并且安全:

class ActiveRecord::Base
  def self.in_any(value, *column_names)
    raise 'At least one column must be supplied' unless column_names.present?
    sql_fragment = column_names.map{|f| "#{quote_column_name(f)} = :search"}.join(" OR ")
    where(sql_fragment, search: value)
  end
end

Stuff.in_any(@search, :first_name, :last_name)

更新

如果您出于某种原因不想添加方法,您可以在运行中非常安全地执行此操作,而不必担心注入。在这种情况下(恕我直言),最优雅的方法是将过滤器链接在一起:

Stuff.where('project_id = ? AND sent_to_api = 1', @project_id).
      where('first_name = :search OR last_name = :search', search: @search)

【讨论】:

  • 我不知道你可以在一个查询中链接多个 .where 。这开辟了一些可能性......你能想出一种方法来有条件地根据外部因素附加 .where 方法吗? Stuff.where(project_id: id).where( something ? ['first_name=?', @search] : nil )
  • @Emerson - 该代码应该可以工作,尽管您可能需要将 nil 替换为空数组(对此不确定)。更简单:where(['first_name=?', @search] if something).
  • 它似乎有效!但是我无法将@search 正确插入到 LIKE 中:...LIKE %?%", @search 产生 last_name LIKE %'foobar'% 请注意 % 中的单引号
  • @Emerson,对 - 而不是 LIKE %? 这样做:where('foo LIKE ?', "%#{@search}%")。这将导致 '%' 与搜索字符串一起转义。
  • @search 还会被 Rails 转义吗?
【解决方案2】:

Rails 5 语法是

Stuff.where(first_name: @search).or(Stuff.where(last_name: @search))

对于旧版本的 Rails,可以使用 sql 字符串

Stuff.where(["first_name = ? or last_name = ?", 'John', 'Smith'])

或者如果两个查询参数相同,您也可以使用哈希

Stuff.where(["first_name = :name or last_name = :name", name: @search])

【讨论】:

    【解决方案3】:

    你有类似的搜索字段数组

    search_fields = ['first_name', 'last_name', 'email']
    

    参数来自控制器

    params = {'first_name' => 'abc', 'email' => abc@gmail.com}
    
    search_field_params = params.slice(*search_fields)
    
    query = search_field_params.keys.map{|field| "#{field} = :#{field}" }.join(" OR ")
    
    query_params = search_field_params.symbolize_keys
    

    Stuff.where(user_id: user_id).where(query, query_params)

    SELECT * FROM stuffs WHERE user_id = 537 AND (first_name = 'abc' OR email = 'abc@gmail.com')

    更多条件加上 where..

    【讨论】:

      【解决方案4】:

      我只能想到三种方法:

      1. 编写 SQL 字符串 (.where('first_name = ? OR last_name = ?', 'foo', 'bar')。你说过你不想这样做。很公平。

      2. 使用 AREL。 fotanus 链接的问题包括:ActiveRecord OR query。您的评论表明您也不喜欢这个选项。

      3. 使用Squeel 或其他提供自己的 DSL 来创建查询的 gem。这应该让您将查询编写为:

        Stuff.where{(first_name = search) | (last_name = search)}

      更新: 如果您正在编写 SQL 字符串,那么这是一种通过几个步骤构建查询的方法。这感觉有点陈旧 - 可能有更优雅的方法,但它应该可以正常工作:

      conditions = []
      values = []
      
      conditions << 'first_name = ?'
      values << 'Bob'
      
      conditions << 'last_name = ?'
      values << 'Smith'
      
      #repeat as needed.
      
      Stuff.where(conditions.join(' OR '), values)
      

      【讨论】:

      • 我对 #1 持开放态度,但请参阅上面的 update2 了解我不想这样做的原因。
      • 它可能并不性感,但看起来它可以完成工作。
      • 尝试此操作时出现错误:“绑定变量的数量错误。”从我发现 where 方法中的第二个参数不能是数组。
      • @Bob - 你可以使用*values 来解决这个问题。
      【解决方案5】:

      对于rails 5,应该可以使用#or 方法写入条件:

      Stuff.where( first_name: @search ).or( Stuff.where( last_name: @search ) )
      

      但对于旧版本Arel 技术可用:

      condition = Stuff.arel_table[:first_name].eq( @search ).or( Stuff.arel_table[:last_name].eq( @search ) )
      Stuff.where( condition )
      

      【讨论】:

      • 第一个版本不适用于 Rails 5。Santhosh 的答案给出了正确的语法
      • 你的答案不起作用。更新语法后,我很乐意为它投票
      • @AugustoSamaméBarrientos 是哪一个?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-18
      • 2014-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-06
      相关资源
      最近更新 更多