Rails 4:如何在查找方法的条件之间使用OR

eme*_*his 6 ruby sql-injection ruby-on-rails ruby-on-rails-4

我的问题类似于这个,但没有一个答案解决我的具体问题.

我想找到这样的东西:

conditions = {first_name: @search OR last_name: @search}
Stuff.where( conditions )
Run Code Online (Sandbox Code Playgroud)

显然,这种语法无效,但这是我能想到的最简单的方式来展示我想做的事情.我想在复杂/复合条件下使用更干净的哈希语法.

我知道你可以用这样的"纯字符串条件"手工写出来Stuff.where("first_name=#{@search} OR last_name=#{@search}") ......但这不是我想知道的.

更新看起来你可以对这样的数组执行OR : Stuff.where( some_column: ["option1", "option2"]). 这非常有用,但它并不能解决我的问题,因为我需要将OR应用于不同的key = value对... 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 )
Run Code Online (Sandbox Code Playgroud)

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

Pin*_*nyM 6

如何为您生成可怕的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)
Run Code Online (Sandbox Code Playgroud)

UPDATE

如果您由于某种原因不想添加方法,则可以非常安全地动态执行此操作而无需担心注入.在这种情况下(IMHO)最优雅的方式是将过滤器链接在一起:

Stuff.where('project_id = ? AND sent_to_api = 1', @project_id).
      where('first_name = :search OR last_name = :search', search: @search)
Run Code Online (Sandbox Code Playgroud)


MrT*_*rus 0

我只能想到三种方法来做到这一点:

  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)
Run Code Online (Sandbox Code Playgroud)