i've this document schema in my 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',
}
]
}
]
}
and i want to move the entire object with _id content1
of object with _id lesson1
in position 3 of the same array OR move that object in position 2 of object with id lesson2
having only the id of content to move, the origin lesson id where the content is and and destination lesson id.
The scenario is:
I have a originLessonId and a targetLessonId (could be the same id), a contentId that i will move from origin lesson to the lesson target and an index that decides the position where i will move the content object.
For example:
i have this random move request { originLessonId: 'lesson1', targetLessonId: 'lesson1', contentId: 'content1', index: 2 }
So: i need to move content1 object from lesson1 to lesson1 (the same lesson object in this case) from position where it is to position 2.
The result will like:
{
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',
}
]
}
]
}
Or { originLessonId: 'lesson1', targetLessonId: 'lesson2', contentId: 'content1', index: 1 }
.
In this case: i need to move content1 object from lesson1 to lesson2 (different lesson object in this case) from position where it is to position 1 of other lesson object.
the result is like:
{
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',
}
]
}
]
}
The request values of query could be different every time.. so needs a query that could manage different cases (like these two).. I tried many ways but i cannot make it with these conditions..
Thank you!
One option is to use update with pipeline in two stages:
$set
the item
that needs to be removed and remove it from the lessons
.$reduce
and $range
to insert it to the right place.db.collection.updateOne(
{}, // _id: docId
[
{$set: {
item: {$reduce: {
input: "$lessons",
initialValue: {},
in: {$mergeObjects: [
"$$value",
{$first: {$filter: {
input: "$$this.contents",
as: "content",
cond: {$eq: ["$$content._id", contentId]}
}}}
]}
}},
lessons: {$map: {
input: "$lessons",
as: "lesson",
in: {$mergeObjects: [
"$$lesson",
{contents: {$cond: [
{$eq: ["$$lesson._id", originLessonId]},
{$filter: {
input: "$$lesson.contents",
cond: {$ne: ["$$this._id", contentId]}
}},
"$$lesson.contents"
]}}
]}
}}
}},
{$set: {
item: '$$REMOVE',
lessons: {$map: {
input: "$lessons",
as: "lesson",
in: {$mergeObjects: [
"$$lesson",
{contents: {$cond: [
{$eq: ["$$lesson._id", targetLessonId]},
{$reduce: {
input: {$range: [
0,
{$add: [{$size: "$$lesson.contents"}, 1]}
]},
initialValue: [],
in: {$concatArrays: [
"$$value",
{$cond: [
{$eq: ["$$this", index]},
["$item"],
[{$arrayElemAt: ["$$lesson.contents", "$$this"]}]
]}
]}
}},
"$$lesson.contents"
]}}
]}
}}
}}
])
See how it works on the playground example
The double nested array makes it a bit clumsy. Consider keeping each lesson
as a document if possible.