使用findOneAndUpdate无法更新Mongoose鉴别器对象的问题
解决Mongoose鉴别器对象更新无效的问题
我懂你遇到的困扰了——用基础Event模型更新鉴别器扩展的sportEvent时,那些专属的嵌套字段完全没生效对吧?这其实是Mongoose鉴别器的一个常见坑:直接用基础模型执行findOneAndUpdate,框架不会自动识别并应用对应鉴别器的Schema规则,自然就没法处理那些扩展字段的更新了。
问题根源
你的Event是基础模型,sportEvent是它基于eventType字段扩展出的鉴别器。当你调用Event.findOneAndUpdate时,Mongoose只会加载基础的eventSchema来处理更新操作,完全忽略鉴别器里定义的sportEvent嵌套结构,导致这些字段的更新既不会被应用,连验证规则也不会触发。
解决方案1:先匹配鉴别器模型再更新
我们可以先获取目标文档的eventType,找到对应的鉴别器模型,再用这个模型执行更新:
router.put('/events/:eventId', async function(req, res, next){ const eventId = req.params.eventId; if(!objectId.isValid(eventId)){ return sendError(res, 400, "Invalid Event ID"); } try { // 先获取文档,确定它的事件类型 const targetEvent = await Event.findById(eventId); if(!targetEvent){ return sendError(res, 404, "Event not found"); } // 根据eventType获取对应的鉴别器模型,找不到就用基础模型兜底 const TargetEventModel = Event.discriminators[targetEvent.eventType] || Event; // 用鉴别器模型执行更新,确保扩展字段被正确处理 const updatedEvent = await TargetEventModel.findOneAndUpdate( {_id: eventId}, {$set: req.body}, {new: true, runValidators: true} ); sendResponse(res, "Event Successfully Updated", updatedEvent); } catch(err) { next(err); } });
解决方案2:用findById + save的方式更新
另一种更稳妥的方式是先取出文档,用set方法注入更新内容,再调用save——这种方式下Mongoose会自动根据文档的eventType匹配对应鉴别器的Schema,确保字段验证和更新逻辑都生效:
router.put('/events/:eventId', async function(req, res, next){ const eventId = req.params.eventId; if(!objectId.isValid(eventId)){ return sendError(res, 400, "Invalid Event ID"); } try { const event = await Event.findById(eventId); if(!event){ return sendError(res, 404, "Event not found"); } // 直接设置请求体内容,Mongoose会自动匹配鉴别器Schema处理扩展字段 event.set(req.body); const updatedEvent = await event.save(); sendResponse(res, "Event Successfully Updated", updatedEvent); } catch(err) { next(err); } });
额外提醒
- 确保
req.body中的eventType字段和原文档一致(如果需要修改事件类型,Mongoose也支持,但要额外处理模型切换的逻辑) - 两种方案都保留了
runValidators: true,确保鉴别器中定义的必填字段(比如sportName、firstTeam)在更新时依然会被验证 - 用异步/await替代回调函数,代码可读性和错误处理流程会更清晰
内容的提问来源于stack exchange,提问作者Yogendra Rawal
相关产品推荐
相关产品推荐

