使用Node+Express更新MongoDB条目时匹配错误的问题排查
问题分析:MongoDB更新Event时错误创建新条目
问题描述
使用Node.js+Express+MongoDB开发应用,尝试根据请求体中tempEventInfoForEdit._id查找并更新数据库中的Event条目。当用户仅注册一个Event时编辑功能正常,但存在多个Event时,无法匹配正确目标条目,反而创建新条目:
- 编辑Event1修改场地为'Home'会生成Event3
- 编辑Event2修改场地为'Away'则会更新Event3
控制台日志显示:
tempEventInfoForEdit._id 64a3ef985ef705278a198396
selectedEvent._id new ObjectId("64a3eeb65ef705278a198346")
请求体内容:
req.body = tempEventInfoForEdit { _id: '64a3ef985ef705278a198396', host: 'elinelin', hostId: '63e4face7f8e01e7bff3ae09', venue: 'Hemma', county: 'Dalarnas county', eventDate: '2023-07-05T10:08:07.000Z', eventTime: '14:08', game: 'Frostpunk: The Board Game', openSpots: 1, totalSpots: 2, description: 'It will be fun', image: 'https://s3-us-west-1.amazonaws.com/5cc.images/games/uploaded/1592244158290', isFull: false, pendingPartyMembers: [], createdAt: '2023-07-04T10:08:24.744Z', __v: 0 }
相关代码:
import { User, Event } from '../../Models'; export const updateEvent = async (req, res) => { const {tempEventInfoForEdit} = req.body; console.log('tempEventInfoForEdit', tempEventInfoForEdit) if (tempEventInfoForEdit) { try { const selectedEvent = await Event.findOne({ id: tempEventInfoForEdit._id }); const user = await User.findOne({ accessToken: req.header("Authorization") }); const host = await User.findOne({ _id: selectedEvent.hostId }); if (user.username === host.username) { if (selectedEvent) { console.log('tempEventInfoForEdit._id', tempEventInfoForEdit._id) console.log('selectedEvent._id', selectedEvent._id) const eventUpdatedFromModel = await Event.findOneAndUpdate(tempEventInfoForEdit._id, { $set: { venue: tempEventInfoForEdit.venue, county: tempEventInfoForEdit.county, game: tempEventInfoForEdit.game, openSpots: tempEventInfoForEdit.openSpots, totalSpots: tempEventInfoForEdit.totalSpots, description: tempEventInfoForEdit.description, eventDate: tempEventInfoForEdit.eventDate, eventTime: tempEventInfoForEdit.eventTime, eventName: tempEventInfoForEdit.eventName, image: tempEventInfoForEdit.image } } ); if (eventUpdatedFromModel) { const updatedHostingEvents = await User.findOneAndUpdate({ _id: user._id, 'hostingEvents._id': selectedEvent._id }, { $set: { 'hostingEvents.$.venue': tempEventInfoForEdit.venue, 'hostingEvents.$.county': tempEventInfoForEdit.county, 'hostingEvents.$.game': tempEventInfoForEdit.game, 'hostingEvents.$.openSpots': tempEventInfoForEdit.openSpots, 'hostingEvents.$.totalSpots': tempEventInfoForEdit.totalSpots, 'hostingEvents.$.description': tempEventInfoForEdit.description, 'hostingEvents.$.eventDate': tempEventInfoForEdit.eventDate, 'hostingEvents.$.eventTime': tempEventInfoForEdit.eventTime, 'hostingEvents.$.eventName': tempEventInfoForEdit.eventName, 'hostingEvents.$.image': tempEventInfoForEdit.image }}, { new: true } ); if (updatedHostingEvents) { res.status(200).json({ success: true, response: { eventUpdatedFromModel: eventUpdatedFromModel, hostingEvents: updatedHostingEvents.hostingEvents, message: "The event has been updated" } }); } }; } else { res.status(400).json({ success: false, response: { message: "Event was not found" } }); }; } else { res.status(401).json({ success: false, response: { message: "Unauthorized attempt to edit event" } }); } } catch (err) { res.status(500).json({ success: false, response: { message: err.stack } }); }; } };
问题原因
- Event查询字段错误:
MongoDB文档默认主键字段是_id,但代码中用Event.findOne({ id: tempEventInfoForEdit._id })进行查询,导致匹配到错误的文档(日志中两个ID不一致就是直接证明)。 findOneAndUpdate参数格式错误:
调用Event.findOneAndUpdate时,第一个参数直接传入了ID字符串,而非标准的查询对象{ _id: tempEventInfoForEdit._id },MongoDB解析异常后,加上第一步查询到错误的文档,最终出现更新错误条目或创建新条目的情况。- 逻辑顺序不合理:
判断selectedEvent是否存在的逻辑放在了权限校验之后,若查询不到目标文档,访问selectedEvent.hostId会直接抛出异常,且错误逻辑无法被触发。
修复方案
- 修正Event查询条件:
将查询语句改为匹配正确的主键字段:const selectedEvent = await Event.findOne({ _id: tempEventInfoForEdit._id }); - 修正
findOneAndUpdate参数:
传入标准查询对象,并显式声明选项避免创建新文档:const eventUpdatedFromModel = await Event.findOneAndUpdate( { _id: tempEventInfoForEdit._id }, { $set: { venue: tempEventInfoForEdit.venue, county: tempEventInfoForEdit.county, game: tempEventInfoForEdit.game, openSpots: tempEventInfoForEdit.openSpots, totalSpots: tempEventInfoForEdit.totalSpots, description: tempEventInfoForEdit.description, eventDate: tempEventInfoForEdit.eventDate, eventTime: tempEventInfoForEdit.eventTime, eventName: tempEventInfoForEdit.eventName, image: tempEventInfoForEdit.image } }, { new: true, upsert: false } // 返回更新后文档,禁止自动创建新文档 ); - 调整逻辑顺序:
在权限校验前先判断selectedEvent是否存在,避免空指针异常:const selectedEvent = await Event.findOne({ _id: tempEventInfoForEdit._id }); if (!selectedEvent) { return res.status(400).json({ success: false, response: { message: "Event was not found" } }); } // 后续执行用户查询、权限校验等逻辑
内容的提问来源于stack exchange,提问作者Elin
相关产品推荐
相关产品推荐

