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

如何使用Mongoose在MongoDB中成功保存含Schema数组的用户对象?

问题描述

我平时从MongoDB查询数据后,能像修改普通对象一样修改再调用save()保存,但这种方式对Schema数组无效。我相关经验不多,希望得到易懂的解答。

相关Schema

const bracketClicksSchema = new mongoose.Schema({
  bracketID: String,
  duo1Click: Boolean,
  duo2Click: Boolean,
  duo3Click: Boolean,
  duo4Click: Boolean,
  duo5Click: Boolean,
  duo6Click: Boolean,
  duo7Click: Boolean
});

const userSchema = new mongoose.Schema({
  userId: String,
  brackets: [bracketClicksSchema]
});

用户模型

const User = mongoose.model('User', userSchema);

尝试的代码

我尝试创建新用户并填充brackets数组,控制台显示对象已正确修改,但重新查询数据库发现未实际保存:

app.post('/api/user', async (req, res) => {
  let user = await User.findOne({
    userId: req.body.id
  });
  
  if (user.brackets == null || user.brackets[0].bracketID != req.body.date1) {
    user = new User({
      userId: req.body.id,
      brackets: [
        {
          bracketID: req.body.date1,
          duo1Click: false,
          duo2Click: false,
          duo3Click: false,
          duo4Click: false,
          duo5Click: false,
          duo6Click: false,
          duo7Click: false
        },
        {
          bracketID: req.body.date2,
          duo1Click: false,
          duo2Click: false,
          duo3Click: false,
          duo4Click: false,
          duo5Click: false,
          duo6Click: false,
          duo7Click: false
        },
        {
          bracketID: req.body.date3,
          duo1Click: false,
          duo2Click: false,
          duo3Click: false,
          duo4Click: false,
          duo5Click: false,
          duo6Click: false,
          duo7Click: false
        },
      ]
    });
  }
  
  console.log("new user: " + user);
  try {
    await user.save();
    res.send(user);
  } catch (error) {
    console.log(error);
  }

  // Check if saved successfully
  user = await User.findOne({
    userId: req.body.id
  });
  console.log("finding user again: " + user);
});

控制台输出

new user: {
  userId: '1234',
  brackets: [
    {
      bracketID: '2023-02-05',
      duo1Click: false,
      duo2Click: false,
      duo3Click: false,
      duo4Click: false,
      duo5Click: false,
      duo6Click: false,
      duo7Click: false,
      _id: new ObjectId("63e01fa1ae236745bf8ce09c")
    },
    {
      bracketID: '2023-02-04',
      duo1Click: false,
      duo2Click: false,
      duo3Click: false,
      duo4Click: false,
      duo5Click: false,
      duo6Click: false,
      duo7Click: false,
      _id: new ObjectId("63e01fa1ae236745bf8ce09d")
    },
    {
      bracketID: '2023-02-03',
      duo1Click: false,
      duo2Click: false,
      duo3Click: false,
      duo4Click: false,
      duo5Click: false,
      duo6Click: false,
      duo7Click: false,
      _id: new ObjectId("63e01fa1ae236745bf8ce09e")
    }
  ],
  _id: new ObjectId("63e01fa1ae236745bf8ce09b")
}
finding user again: {
  _id: new ObjectId("63b9b3da9e19eabef4d4987b"),
  userId: '1234',
  brackets: null,
  __v: 0
}

请问如何成功将创建的新用户对象保存到MongoDB?


解决方案

问题根源

你的代码逻辑存在核心问题:当查询到已存在的用户时,你直接用new User()创建了一个全新的用户实例,但这个实例和数据库里的现有用户完全独立。调用save()时,Mongoose会尝试插入一条新数据,而非更新现有记录;同时因为你没有给userId加唯一约束,数据库里会存在两条同userId的记录,查询时返回的是旧数据,导致你误以为保存失败。

另外,直接替换数组时,Mongoose可能无法自动检测到字段变化,这也是数组更新不生效的常见原因。

正确处理方式

方式一:用findOneAndUpdate(推荐,一步到位)

使用Mongoose的findOneAndUpdate方法,一次性完成查询、更新(或创建)操作:

app.post('/api/user', async (req, res) => {
  const { id: userId, date1, date2, date3 } = req.body;
  
  // 定义要更新的brackets数组
  const newBrackets = [
    { bracketID: date1, duo1Click: false, duo2Click: false, duo3Click: false, duo4Click: false, duo5Click: false, duo6Click: false, duo7Click: false },
    { bracketID: date2, duo1Click: false, duo2Click: false, duo3Click: false, duo4Click: false, duo5Click: false, duo6Click: false, duo7Click: false },
    { bracketID: date3, duo1Click: false, duo2Click: false, duo3Click: false, duo4Click: false, duo5Click: false, duo6Click: false, duo7Click: false }
  ];

  try {
    // upsert: true 表示用户不存在则创建新用户;new: true 返回更新后的文档
    const user = await User.findOneAndUpdate(
      { userId },
      { $set: { brackets: newBrackets } },
      { new: true, upsert: true }
    );
    res.send(user);
    
    // 验证保存结果
    const savedUser = await User.findOne({ userId });
    console.log("finding user again: ", savedUser);
  } catch (error) {
    console.log(error);
    res.status(500).send(error);
  }
});

方式二:手动处理实例(适合理解原理)

如果坚持用实例操作,需要区分“用户不存在创建新实例”和“用户存在更新现有实例”的场景,同时手动标记数组字段已修改:

app.post('/api/user', async (req, res) => {
  const { id: userId, date1, date2, date3 } = req.body;
  
  let user = await User.findOne({ userId });
  
  const newBrackets = [
    { bracketID: date1, duo1Click: false, duo2Click: false, duo3Click: false, duo4Click: false, duo5Click: false, duo6Click: false, duo7Click: false },
    { bracketID: date2, duo1Click: false, duo2Click: false, duo3Click: false, duo4Click: false, duo5Click: false, duo6Click: false, duo7Click: false },
    { bracketID: date3, duo1Click: false, duo2Click: false, duo3Click: false, duo4Click: false, duo5Click: false, duo6Click: false, duo7Click: false }
  ];

  if (!user) {
    // 用户不存在,创建新用户
    user = new User({ userId, brackets: newBrackets });
  } else {
    // 用户存在,检查是否需要更新brackets
    if (!user.brackets || user.brackets[0]?.bracketID !== date1) {
      user.brackets = newBrackets;
      // 手动标记字段已修改,确保save()同步更新到数据库
      user.markModified('brackets');
    }
  }
  
  try {
    await user.save();
    res.send(user);
    
    const savedUser = await User.findOne({ userId });
    console.log("finding user again: ", savedUser);
  } catch (error) {
    console.log(error);
    res.status(500).send(error);
  }
});

额外建议

给userSchema的userId添加唯一约束,避免重复创建用户:

const userSchema = new mongoose.Schema({
  userId: { type: String, unique: true },
  brackets: [bracketClicksSchema]
});

这样当尝试创建重复userId的用户时,Mongoose会直接抛出错误,方便排查问题。

内容的提问来源于stack exchange,提问作者Thomas Hart

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:05:22