使用Mongoose更新数组内对象save返回正常但数据库未更新
问题原因
这是Mongoose默认的变更检测(脏检查)机制限制导致的:
- 你定义的
cards字段是无结构的Array类型,直接修改数组内对象的属性时,Mongoose无法自动识别到该字段发生了变更,因此save()调用时不会向数据库提交对应字段的更新 push、pop这类数组原型方法已经被Mongoose重写,调用时会自动标记数组为已变更状态,所以可以正常同步到数据库
修复方案
方案1:手动标记字段变更(改最少的代码适配现有逻辑)
修改完数组内的对象属性后,手动调用markModified方法标记cards字段已变更,再执行save()即可:
const { deckId, cardId } = req.body; const deck = await DeckModel.findById(deckId); const index = deck.cards.findIndex((card) => card.id == cardId); deck.cards[index].level++; // 新增以下行,手动标记字段变更 deck.markModified('cards'); await deck.save();
方案2:明确声明cards字段的嵌套结构
修改Schema定义,明确cards数组内的元素结构,Mongoose即可自动识别嵌套属性的变更,不需要额外调用markModified:
new Schema({ name: String, owner: { type: Schema.ObjectId, ref: "Users" }, // 替换原来的Array类型,明确嵌套字段 cards: [{ id: String, level: Number, // 补充其他card对应的字段定义 }], });
方案3:直接用原子更新操作符(性能最优)
不需要先查询文档再修改保存,直接通过原子更新操作符修改数据库文档,完全避开变更检测问题:
const { deckId, cardId } = req.body; await DeckModel.findByIdAndUpdate(deckId, // 给匹配到的card的level字段+1 { $inc: { 'cards.$[targetCard].level': 1 } }, // 数组过滤条件,匹配id等于cardId的card元素 { arrayFilters: [{ 'targetCard.id': cardId }] } );
内容的提问来源于stack exchange,提问作者MrCano369
相关产品推荐
相关产品推荐

