Mongoose更新数组中disabledBy对象时验证失败的问题求助
在Mongoose中定义了Purchase模型,其中包含payments对象数组,尝试为数组内的disabledBy对象设置数据时,Mongoose始终提示未获取到该对象的相关信息。
模型结构
payments: [{ createdAt: { type: String, required:true }, paymentNumber: { type: Number, default:0 }, previousBalance:{ type: Number, required: [true, 'El Saldo anterior es obligatorio'], default:0 }, paymentAmount:{ type: Number, required: [true, 'La Cantidad del Pago(Abono) es obligatorio'], default:0 }, outstandingBalance:{ type: Number, required: [true, 'El Saldo pendiente es obligatorio'], default:0 }, createdBy:{ uid : { type: String, required: true }, username:{ type: String, required: true } }, comments: { type: String, maxlength:300, }, status: { type: Boolean, default: true }, disabledBy:{ // 尝试更新的对象 uid:{ type: String, required:true }, username:{ type: String, required:true }, date:{ type: Date, default: Date.now }, comments: { type: String, maxlength:300, required: [true, 'El Comentario es obligatorio'] } } }],
Express 更新代码
const payment = await Purchase.updateOne( { _id: id, 'payments._id': body.paymentId }, { $set: { 'payments.$.status': false, 'payments.$.disabledBy': { uid: req.user._id, username: req.user.username, comments: body.comments } } },{ new: true });
问题现象与错误信息
更新payments的status属性可正常执行,但更新disabledBy时失败,Mongoose返回错误:
error Purchase validation failed: payments.0.disabledBy.comments: El Comentario es obligatorio, payments.0.disabledBy.username: Path
disabledBy.usernameis required., payments.0.disabledBy.uid: PathdisabledBy.uidis required.
尽管传入的disabledBy数据正确,但始终触发上述验证错误,请问如何正确设置该对象的数据?
原因分析
Mongoose默认会对整个文档做验证,而非仅更新部分。当更新数组元素时,它会检查数组内所有元素的验证规则,若原数组中存在未初始化disabledBy的元素(比如新增payment时未设置该字段),就会触发必填项验证失败。另外,嵌套对象的更新场景下,默认验证逻辑可能误判传入数据的合规性。
解决办法
1. 仅验证更新字段
在updateOne的选项中添加runValidators: true和context: 'query',让Mongoose只验证你要更新的字段,而非整个文档:
const payment = await Purchase.updateOne( { _id: id, 'payments._id': body.paymentId }, { $set: { 'payments.$.status': false, 'payments.$.disabledBy': { uid: req.user._id, username: req.user.username, comments: body.comments } } }, { new: true, runValidators: true, context: 'query' });
runValidators: true:启用更新操作的验证逻辑context: 'query':指定验证范围为查询中的更新字段
2. 把disabledBy设为可选字段
如果业务允许payment创建时不设置disabledBy,可修改模型定义,将disabledBy标记为可选:
disabledBy:{ type: { uid:{ type: String, required:true }, username:{ type: String, required:true }, date:{ type: Date, default: Date.now }, comments: { type: String, maxlength:300, required: [true, 'El Comentario es obligatorio'] } }, required: false // 设置为可选字段 }
这样只有当你主动设置disabledBy时,才会验证其内部字段的必填性,未设置时不会触发错误。
3. 逐个更新disabledBy的内部字段
避免直接替换整个disabledBy对象,而是单独更新每个内部字段,让验证逻辑更精准:
const payment = await Purchase.updateOne( { _id: id, 'payments._id': body.paymentId }, { $set: { 'payments.$.status': false, 'payments.$.disabledBy.uid': req.user._id, 'payments.$.disabledBy.username': req.user.username, 'payments.$.disabledBy.comments': body.comments // date字段会自动使用默认值,无需手动传入 } }, { new: true, runValidators: true });
该方式适用于原payment已存在disabledBy对象(或已初始化空对象)的场景,仅更新需要修改的字段,避免触发不必要的验证。
内容的提问来源于stack exchange,提问作者OscarDev

