MongoDB更新对象数组内嵌套属性时balance字段总为默认值如何解决
问题原因
- 首先你在更新语句中写的
balance:{currentBal}存在两处语法/逻辑错误:一是你错误传递了对象类型的值给定义为Number类型的balance字段,类型不匹配时Mongoose会自动应用默认值0;二是此处直接写的currentBal是Node.js当前运行环境的变量,而非MongoDB文档中存储的currentBal字段值,如果你没有在代码里提前定义该变量,此处值为undefined,同样会触发默认值逻辑。 - 其次MongoDB的单文档更新操作中,同一更新内的所有算子(比如你用到的
$inc和$push)都是基于更新前的原始文档状态执行,你无法在同一个普通更新操作中直接获取$inc执行后的最新currentBal值赋值给交易记录的balance字段。
解决方案
方案1:先查询后更新(适合并发量低的场景)
逻辑为先查询到目标用户文档,手动计算更新后的余额,再组装交易记录写入文档后保存,代码示例:
// 先查用户 const customer = await Customer.findOne({ accNo: id }); if (!customer) { return res.json({ message: "用户不存在" }); } // 计算新余额 const newBalance = customer.currentBal + Number(amount); // 更新余额 customer.currentBal = newBalance; // 组装交易记录push customer.transactions.push({ transactionType: "deposit", transactionDetails: { transferredFrom: "Self", transferredTo: "Self", balance: newBalance, amount: Number(amount), } }); // 保存文档 await customer.save(); console.log("Balance has been updated successfully"); res.redirect(`/customers/${id}`);
方案2:使用聚合管道更新(原子操作,适合高并发场景)
MongoDB 4.2及以上版本支持将聚合管道作为更新参数,可以在更新过程中引用字段的最新计算值,保证操作原子性,代码示例:
Customer.findOneAndUpdate( { accNo: id }, [ // 第一步:计算新的余额 { $set: { currentBal: { $add: ["$currentBal", Number(amount)] } } }, // 第二步:将新余额写入新的交易记录,push到数组 { $set: { transactions: { $concatArrays: [ "$transactions", [ { transactionType: "deposit", transactionDetails: { transferredFrom: "Self", transferredTo: "Self", balance: "$currentBal", amount: Number(amount) } } ] ] } } } ], (err, customer) => { if (err !== null && err.name === "ValidationError") { res.json({ message: err._message }); } else { console.log("Balance has been updated successfully"); res.redirect(`/customers/${id}`); } } );
内容的提问来源于stack exchange,提问作者yuvraj pawar
相关产品推荐
相关产品推荐

