问题描述
我正在寻找一种在 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)
那样,我们构造:
- SQL 查询
- 传递给
where()
的值 - 我们仍然使用活动记录
where()
以防止注入等...
这修正了限制!