You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB实现对象数组顺序扣减数值并更新属性

MongoDB顺序扣减数组元素值的解决方案

给定如下结构的MongoDB文档:

{ 
  "cantidad": 23,
  "historial": {
    "entradas": [
             {"cantidad": 5}, 
             {"cantidad":10}, 
             {"cantidad":8}
           ],
    "salidas": []
          } 
}

需要实现:指定一个扣减数值,按顺序从historial.entradas数组的元素中扣减——优先扣第一个元素,若元素值不够扣就置0,剩余扣减额转去扣下一个,直到扣减额用完;同时更新顶层cantidad为原值减去扣减额。

比如扣减7时,文档会变成这样:

{ 
  "cantidad": 16,
  "historial": {
    "entradas": [
             {"cantidad": 0}, 
             {"cantidad": 8}, 
             {"cantidad":8}
           ],
    "salidas": []
          } 
}

扣减17时,entradas数组会变为:

"entradas": [
             {"cantidad": 0}, 
             {"cantidad": 0}, 
             {"cantidad":6}
           ]

解决方案

用MongoDB 4.2及以上版本支持的聚合管道式更新就能实现这个逻辑,核心是用$reduce遍历数组并跟踪剩余扣减额的状态。

假设要扣减的数值是decrement(比如7或17),执行以下更新操作:

// 定义要扣减的数值
const decrement = 7;

db.vale.updateOne(
  // 替换成你的文档匹配条件,比如根据_id匹配
  {},
  [
    {
      $set: {
        // 更新顶层cantidad:直接减去扣减额
        "cantidad": { $subtract: ["$cantidad", decrement] },
        // 用$reduce处理entradas数组,生成包含结果数组和剩余扣减额的对象
        "historial.entradas": {
          $reduce: {
            input: "$historial.entradas",
            initialValue: { remaining: decrement, newArray: [] },
            in: {
              $let: {
                vars: {
                  // 当前元素扣减后的剩余值:如果剩余扣减额比当前元素大,就置0,否则用当前值减剩余扣减额
                  currentVal: { $max: [0, { $subtract: ["$$this.cantidad", "$$value.remaining"] }] },
                  // 更新后的剩余扣减额:如果当前元素值比剩余扣减额大,剩余额置0,否则用剩余额减当前元素值
                  newRemaining: { $max: [0, { $subtract: ["$$value.remaining", "$$this.cantidad"] }] }
                },
                in: {
                  remaining: "$$newRemaining",
                  newArray: { $concatArrays: ["$$value.newArray", [{ "cantidad": "$$currentVal" }]] }
                }
              }
            }
          }
        }
      }
    },
    // 提取处理后的数组,覆盖原entradas
    {
      $set: {
        "historial.entradas": "$historial.entradas.newArray"
      }
    }
  ]
);

逻辑说明

  1. 顶层cantidad更新:用$subtract直接计算原值减去扣减额,一步到位。
  2. 数组处理:
    • $reduce会遍历entradas数组,初始化一个状态对象,包含remaining(还需要扣减的额度)和newArray(保存处理后的元素)。
    • 对每个元素,先算扣减后的值:如果剩余扣减额比当前元素大,元素置0;否则用当前元素值减去剩余扣减额。
    • 再更新剩余扣减额:如果当前元素值够扣,剩余额置0;否则剩余额减去当前元素值。
    • 把处理后的元素加到newArray里,更新状态对象。
  3. 最后把newArray提取出来,替换原来的entradas数组。

注意事项

  • 必须使用MongoDB 4.2及以上版本,因为聚合管道式更新是这个版本新增的功能。
  • 要保证扣减额decrement不超过entradas数组所有元素的总和(也就是顶层的cantidad),否则最后一个元素会出现负数——你已经明确会保证这一点,所以没问题。

内容的提问来源于stack exchange,提问作者Adrian Gonzalez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 10:55:13