动态查询构建器堆栈太深,无法进行 Arel 查询 Rails

Dynamic query builder stack too deep for Arel query Rails

我正在寻找一种在 Rails 中构建大型动态查询并将更新应用于给定 ActiveRecord 关系的方法:

例如:

my_big_set = Set.new(big_array)
to_update_relationship = my_big_set.reduce(none) do |query, values_to_check|
                            where('condition = ? and condition_two = ?', values_to_check[:first], values_to_check[:two]).or(query)
                         end
    
to_update_relationship.update_all(field_to_update: true)

to_update_relationship不太大的时候效果很好。但是,如果它变大,那么 reduce 输出会在构建 Arel 查询时触发 SystemStackError: stack level too deep 错误。

有没有聪明的方法来解决这个问题? (除了拆分输入或增加 ruby 系统堆栈大小)。

谢谢

PS: 使用 rails 6 和 ruby 2.7

您可以使用聚合函数(例如,postgresql STRING_AGG 或 mysql GROUP_CONCAT)来收集 [condition, condition_two] 对,然后检查这些对 IN你的要求条件。

假设您使用 postgresql,并且条件类似于 [{first: 1, two: 2}, {}, ...]

pair_conditions = conditions.map {|pair| pair.values.join(",")} # ["1,2", ...]
Solution.group(:id, :condition, :condition_two)
.having("STRING_AGG(condition::TEXT || ',' || condition_two::TEXT, '') 
         IN (?)", pair_conditions)

上述查询将 group (condition, condition_two) 并将它们连接到格式为“condition,condition_two”的字符串,以便它们能够与预定义条件进行比较关于 having 条款。

我最后的解决办法是:

my_big_set = Set.new(big_array)
subquery = '(condition = ? and condition_two = ?)'
query = Array.new(my_big_set, subquery).join(' OR ')
query_values = my_big_set.map do |values_to_check|
   [values_to_check[:first], values_to_check[:two]]
end.flatten

where(query, *query_values).update_all(field_to_update: true)

这样,我们构造:

  1. SQL 查询
  2. 要传递给 where()
  3. 的值
  4. 我们仍然使用活动记录 where() 以防止注入等...

这解决了限制!