动态查询构建器堆栈对于 Arel 查询 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. 我们仍然使用活动记录 where() 以防止注入等...

这修正了限制!