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



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

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 =  

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

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

编辑:

该解决方案需要使用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 节点才能传递给最终查询方法。

我正在使用以下内容,直到 rails 5 出来(在 rails 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))

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

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

基于@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

附言以前的代码 @bsd 无法在困难的情况下正常工作:User.where.or(User.where(rating: 3), User.where(startups: { progress: 100, rating: nil })

旧代码的结果是错误的:

选择"用户"

.*从"用户"位置(("启动")中。评级" = 3 或"初创公司"。进度" = 100) 或"启动"。评级"为空)

更改的代码生成正确:

从"用户

"位置("启动")中选择"用户"。评级" = 3 或"初创公司"。进度" = 100 和"初创公司"。评级"为空)

相关内容

  • 没有找到相关文章

最新更新