You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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
        }
      });
    };
  }
};

问题原因

  1. Event查询字段错误:
    MongoDB文档默认主键字段是_id,但代码中用Event.findOne({ id: tempEventInfoForEdit._id })进行查询,导致匹配到错误的文档(日志中两个ID不一致就是直接证明)。
  2. findOneAndUpdate参数格式错误:
    调用Event.findOneAndUpdate时,第一个参数直接传入了ID字符串,而非标准的查询对象{ _id: tempEventInfoForEdit._id },MongoDB解析异常后,加上第一步查询到错误的文档,最终出现更新错误条目或创建新条目的情况。
  3. 逻辑顺序不合理:
    判断selectedEvent是否存在的逻辑放在了权限校验之后,若查询不到目标文档,访问selectedEvent.hostId会直接抛出异常,且错误逻辑无法被触发。

修复方案

  1. 修正Event查询条件:
    将查询语句改为匹配正确的主键字段:
    const selectedEvent = await Event.findOne({ _id: tempEventInfoForEdit._id });
    
  2. 修正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 } // 返回更新后文档,禁止自动创建新文档
    );
    
  3. 调整逻辑顺序:
    在权限校验前先判断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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 03:54:51