WHERE子句中的OR运算符与Rails 4.2中的Arel

时间:2014-12-23 20:15:13

标签: ruby-on-rails arel

以下代码在Rails 4.1中构造了一个带有where运算符的有效OR子句

MyModel.where(
  MyModel.where(attribute1: 1, attribute2: 2).where_values.reduce(:or)
)

大致相当于SQL

select * from my_models where (attribute1 = 1 OR attribute2 = 2)

在Rails 4.2中,相同的代码生成一个SQL查询,其缺少值为绑定参数

select * from my_models where attribute1 =  OR attribute2 =  

...并且由于缺少绑定值的值而生成错误。

使用OR运算符生成有效查询的 Rails 4.2 中的等效代码是什么?

修改

该解决方案需要使用Arel::Nodes::Node派生对象,以便它本身可以通过AND和OR分组与其他条件组合。

rel = MyModel.where(attribute1: 1, attribute2: 2)
conditions = [rel.where_values.reduce(:or).to_sql, *rel.bind_values.map(&:last)]

MyModel.where(conditions)

conditions var必须是Arel::Nodes::Node的衍生物。上述解决方案适用于简单查询,但对于更复杂的查询,conditions必须是要传递给最终查询方法的Arel节点。

3 个答案:

答案 0 :(得分:5)

我使用下面的内容直到导轨5出来(在导轨5中AR支持.or):

ActiveRecord::QueryMethods::WhereChain.class_eval do
  def or(*scopes)
    scopes_where_values = []
    scopes_bind_values  = []
    scopes.each do |scope|
      case scope
      when ActiveRecord::Relation
        scopes_where_values += scope.where_values
        scopes_bind_values += scope.bind_values
      when Hash
        temp_scope = @scope.model.where(scope)
        scopes_where_values += temp_scope.where_values
        scopes_bind_values  += temp_scope.bind_values
      end
    end
    scopes_where_values = scopes_where_values.inject(:or)
    @scope.where_values += [scopes_where_values]
    @scope.bind_values  += scopes_bind_values
    @scope
  end
end

通过上述内容,您可以:

MyModel.where.or(attribute1: 1, attribute2: 2)
# or
MyModel.where.or(MyModel.where(some conditions), MyModel.where(some other conditions))

答案 1 :(得分:3)

更正确的解决方案基于@bsd答案,但允许输入上的任意范围

 ActiveRecord::QueryMethods::WhereChain.class_eval do
   def or(*scopes)
     scopes_where_values = []
     scopes_bind_values  = []
     scopes.each do |scope|
       case scope
       when ActiveRecord::Relation
         scopes_where_values << scope.where_values.reduce(:and)
         scopes_bind_values += scope.bind_values
       when Hash
         temp_scope = @scope.model.where(scope)
         scopes_where_values << temp_scope.where_values.reduce(:and)
         scopes_bind_values  += temp_scope.bind_values
       end
     end
     scopes_where_values = scopes_where_values.inject(:or)
     @scope.where_values += [scopes_where_values]
     @scope.bind_values  += scopes_bind_values
     @scope
   end
 end

P.S。 @bsd以前的代码在一些困难的情况下无法正常工作: User.where.or(User.where(评级:3),User.where(初创公司:{progress:100,rating:nil})

旧代码的结果错误:

  

SELECT“users”。* FROM“users”WHERE((“startups”。“rating”= 3 OR“startups”。“progress”= 100)或“startups”。“rating”IS NULL)

更改的代码生成正确:

  

SELECT“users”。* FROM“users”WHERE(“startups”。“rating”= 3 OR“startups”。“progress”= 100 AND“startups”。“rating”IS NULL)

答案 2 :(得分:2)

使用raw arel可能是更好的选择:

t = MyModel.arel_table
MyModel.where(
  t[:attribute1].eq(1).or(
    t[:attribute2].eq(2)
  )
)