MongoDB中同数组/跨数组移动元素的实现方案求助
MongoDB 跨/同课时移动内容的通用查询语句
原始文档结构
{ lessons: [ { _id: 'lesson1', title: 'lesson1', contents: [ { _id: 'content1', title: 'content1', }, { _id: 'content2', title: 'content2', }, { _id: 'content3', title: 'content3', } ] }, { _id: 'lesson2', title: 'lesson2', contents: [ { _id: 'content1', title: 'content1', }, { _id: 'content2', title: 'content2', }, { _id: 'content3', title: 'content3', } ] } ] }
需求说明
已知originLessonId、targetLessonId(可与源ID相同)、contentId和目标位置index,需实现两种场景的内容移动:
- 同课时内调整位置:将指定课时下的内容移至该课时内的目标索引位置
请求参数示例:{ originLessonId: 'lesson1', targetLessonId: 'lesson1', contentId: 'content1', index: 2 }
预期结果:{ lessons: [ { _id: 'lesson1', title: 'lesson1', contents: [ { _id: 'content2', title: 'content2' }, { _id: 'content3', title: 'content3' }, { _id: 'content1', title: 'content1' } ] }, { _id: 'lesson2', title: 'lesson2', contents: [ { _id: 'content1', title: 'content1' }, { _id: 'content2', title: 'content2' }, { _id: 'content3', title: 'content3' } ] } ] } - 跨课时移动内容:将源课时下的内容移至目标课时的指定索引位置,同时从源课时中移除该内容
请求参数示例:{ originLessonId: 'lesson1', targetLessonId: 'lesson2', contentId: 'content1', index: 1 }
预期结果:{ lessons: [ { _id: 'lesson1', title: 'lesson1', contents: [ { _id: 'content2', title: 'content2' }, { _id: 'content3', title: 'content3' } ] }, { _id: 'lesson2', title: 'lesson2', contents: [ { _id: 'content1', title: 'content1' }, { _id: 'content1', title: 'content1' }, { _id: 'content2', title: 'content2' }, { _id: 'content3', title: 'content3' } ] } ] }
通用更新查询语句
使用updateOne结合聚合管道,可同时适配两种场景:
// 定义操作参数 const params = { originLessonId: 'lesson1', targetLessonId: 'lesson2', contentId: 'content1', index: 1 }; db.collection.updateOne( {}, // 可根据实际需求添加文档匹配条件 [ { $set: { lessons: { $map: { input: "$lessons", as: "lesson", in: { $cond: [ { $eq: ["$$lesson._id", params.originLessonId] }, // 处理源课时:移除指定content { _id: "$$lesson._id", title: "$$lesson.title", contents: { $filter: { input: "$$lesson.contents", as: "content", cond: { $ne: ["$$content._id", params.contentId] } } } }, { $cond: [ { $eq: ["$$lesson._id", params.targetLessonId] }, // 处理目标课时:插入content到指定位置 { _id: "$$lesson._id", title: "$$lesson.title", contents: { $let: { vars: { targetContent: { $first: { $filter: { input: "$lessons.contents", as: "c", cond: { $and: [ { $eq: ["$$c._id", params.contentId] }, { $eq: [{ $arrayElemAt: ["$lessons", { $indexOfArray: ["$lessons._id", params.originLessonId] }] }._id, params.originLessonId] } ] } } } } }, in: { $concatArrays: [ { $slice: ["$$lesson.contents", params.index] }, ["$$targetContent"], { $slice: ["$$lesson.contents", params.index, { $size: "$$lesson.contents" }] } ] } } } }, // 其他课时保持原样 "$$lesson" ] } ] } } } } } ] );
语句逻辑说明
- 遍历课时数组:用
$map对每个课时进行判断,区分源课时、目标课时和普通课时 - 源课时处理:通过
$filter移除指定contentId的内容 - 目标课时处理:
- 先从源课时中筛选出要移动的content对象
- 再用
$concatArrays和$slice将content插入到指定索引位置
- 自动适配同课时场景:当
originLessonId与targetLessonId相同时,源课时先移除内容,目标课时(同一课时)再插入该内容,实现位置调整
内容的提问来源于stack exchange,提问作者Kouga
相关产品推荐
相关产品推荐

