6

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

我想找到这样的对象:

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 对......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 转义?

4

5 回答 5

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)

更新

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

Stuff.where('project_id = ? AND sent_to_api = 1', @project_id).
      where('first_name = :search OR last_name = :search', search: @search)
于 2013-09-10T15:41:23.220 回答
4

Rails 5 语法是

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

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

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

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

Stuff.where(["first_name = :name or last_name = :name", name: @search])
于 2016-05-09T12:10:49.623 回答
1

您有搜索字段数组,例如

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..

于 2015-05-22T09:10:19.753 回答
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)
于 2013-09-10T15:39:23.487 回答
-1

因为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 )
于 2015-12-02T20:23:58.697 回答