如何使用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
相关产品推荐
相关产品推荐

