MongoDB嵌套数组执行$addToSet时upsert未创建父文档如何解决
问题原因
你当前的写法有两个核心问题导致upsert不生效:
- 当
_id=someId的父文档不存在时,查询条件中匹配chapters.chapter的规则在upsert场景下无法和$占位符兼容,MongoDB无法解析出chapters数组的创建结构 - 当父文档存在但
chapters数组中没有匹配chapterId的元素时,$占位符找不到对应数组索引,更新不会生效,也不会自动新增子项
解决方案
使用MongoDB 4.2及以上版本支持的聚合管道更新,可以在单次操作中覆盖所有场景,不需要额外的查询判断:
const mongoose = require('mongoose'); const someId = SOME_ID; const chapterId = "611478ac34dde61f28dbe4d8"; const newQuestions = [5, 10, 32, 6]; await model.findOneAndUpdate( { _id: someId }, [ { $set: { chapters: { $let: { vars: { targetIndex: { $indexOfArray: ["$chapters.chapter", mongoose.Types.ObjectId(chapterId)] } }, in: { $cond: [ // 场景1:chapters数组中已存在对应chapter { $ne: ["$$targetIndex", -1] }, { $map: { input: "$chapters", in: { $cond: [ { $eq: ["$$this.chapter", mongoose.Types.ObjectId(chapterId)] }, // 合并questions,$setUnion实现和$addToSet一致的去重效果 { $mergeObjects: [ "$$this", { questions: { $setUnion: ["$$this.questions", newQuestions] } } ] }, "$$this" ] } } }, // 场景2:chapters数组中不存在对应chapter,追加新元素 { $concatArrays: [ { $ifNull: ["$chapters", []] }, [{ chapter: mongoose.Types.ObjectId(chapterId), questions: newQuestions }] ] } ] } } } } } ], { upsert: true, new: true // 可选配置,返回更新后的文档 } ).lean().exec();
逻辑说明
- 父文档不存在时:upsert会自动创建
_id=someId的文档,同时初始化chapters数组为包含对应chapter和新questions的结构 - 父文档存在但无对应chapter:自动往
chapters数组追加新的子项 - 父文档存在且有对应chapter:通过
$setUnion合并新旧questions,自动去重,和原来$addToSet的效果完全一致 - 注意要将字符串格式的
chapterId转为Mongoose的ObjectId类型,避免类型不匹配导致的判断失效
内容的提问来源于stack exchange,提问作者Chique
相关产品推荐
相关产品推荐

