为何order_items_info.pluck(:quantity_shipped).sum.abs会触发nil异常?
问题分析:sum方法触发inject(:+)的nil错误
原代码
order_items_info.pluck(:quantity_shipped).sum.abs
错误日志
Error reindexing order 123456 - NoMethodError - undefined method `+' for nil:NilClass
inject(:+) || 0 ^^^^^^
已测试场景
Loading production environment (Rails 6.1.7) irb(main):001:0> order_items_info = nil => nil irb(main):002:0> order_items_info.pluck(:quantity_shipped).sum.abs (irb):2:in `<main>': undefined method `pluck' for nil:NilClass (NoMethodError) irb(main):003:0> order_items_info = [] => [] irb(main):004:0> order_items_info.pluck(:quantity_shipped).sum.abs => 0 irb(main):005:0> order_items_info = [{quantity_shipped: nil}] => [{:quantity_shipped=>nil}] irb(main):006:0> order_items_info.pluck(:quantity_shipped).sum.abs => 0 irb(main):007:0> order_items_info = [nil] => [nil] irb(main):008:0> order_items_info.pluck(:quantity_shipped).sum.abs /home/deploy/zonmaster/shared/vendor/bundle/ruby/3.1.0/gems/activesupport-6.1.7/lib/active_support/core_ext/enumerable.rb:157:in `block in pluck': undefined method `[]' for nil:NilClass (NoMethodError) map { |element| element[key] } ^^^^^ irb(main):009:0> order_items_info = [{quantity_shipped: 'abd'}] => [{:quantity_shipped=>"abd"}] irb(main):010:0> order_items_info.pluck(:quantity_shipped).sum.abs (irb):10:in `<main>': undefined method `abs' for "abd":String (NoMethodError)
错误原因
这个错误的核心是数组中同时存在nil和数值类型,且nil是数组的第一个元素。
Rails的sum方法逻辑:
- 若数组为空,直接返回0;
- 若数组非空,调用
inject(:+)逐个累加元素; - 末尾的
|| 0是兜底逻辑,但如果inject(:+)在执行过程中就报错(比如第一个元素是nil,第二个是数值,执行nil + 数值),|| 0根本不会生效。
你之前的测试场景没有覆盖[nil, 数值]这种混合数组,比如当order_items_info = [{quantity_shipped: nil}, {quantity_shipped: 10}]时,pluck得到[nil,10],执行sum就会触发nil +10的错误,和日志中的报错完全一致。
另外还有极小概率是并发修改导致:在pluck获取数组后、sum执行前,数组元素被异步修改为nil开头的混合数组,但这种情况需要结合业务场景进一步排查。
修复方案
- 过滤nil值:先去掉数组中的nil再求和
order_items_info.pluck(:quantity_shipped).compact.sum.abs - 指定初始值:利用Rails
sum方法的参数设置初始值,避免nil参与累加order_items_info.pluck(:quantity_shipped).sum(0).abs - 直接求和转换:跳过pluck,直接遍历处理每个元素的nil值
order_items_info.sum { |item| item[:quantity_shipped].to_i }.abs
内容的提问来源于stack exchange,提问作者phil
相关产品推荐
相关产品推荐

