【问题标题】:Dynamic query builder stack too deep for Arel query Rails对于 Arel 查询 Rails,动态查询构建器堆栈太深
【发布时间】:2026-01-18 16:20:04
【问题描述】:

我正在寻找一种在 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

【问题讨论】:

    标签: ruby-on-rails ruby activerecord arel


    【解决方案1】:

    我的最终解决方案是:

    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() 以防止注入等...

    这修正了限制!

    【讨论】:

      【解决方案2】:

      您可以使用聚合函数(例如,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 子句中的预定义条件进行比较。

      【讨论】: