Below is an example of a document in a User collection below.
{
"_id" : 1,
"username" : bob,
"pause" : true,
"pause_date" : ISODate("2021-07-16T07:13:48.680Z"),
"learnt_item" : [
{
"memorized" : false,
"character" : "一",
"next_review" : ISODate("2021-07-20T11:02:44.979Z")
},
{
"memorized" : false,
"character" : "二",
"next_review" : ISODate("2021-07-20T11:02:44.979Z")
},
...
]
}
I need to update all the nested document in "learnt_item" if the "memorized" field is false.
The updates are:
I was able to achieve 1 & 2 using findOneAndUpdate with arrayFilters and also tested no.3 by updating the "next_review" field with a current date to make sure it is updating correctly.
User.findOneAndUpdate({"_id": req.user._id},
{$set:{"learnt_item.$[elem].next_review": DateTime.local(),"pause_date": null, "pause": value }},
{new:true, arrayFilters: [{"elem.memorized": false}]}).exec((err, doc) =>{if (err){res.send(err)} else {res.send(doc)}});
I was thinking of using the $add aggregation operator to increase the date base
"learnt_item.$[elem].next_review": {$add: ["$learnt_item.$[elem].next_review","$pause_date"]}
However, according to the documentation, arrayFilters is not available for updates that use an aggregation pipeline.
Is there another way more efficient way that I can update the ISOdate?
If you are running MongoDB 4.2 or later you can use a pipeline as the second parameter for the update function, this way you can use the operator $map with $cond to find the entries where the property memorized is equal to false and then add 4 days in milliseconds to the next_review date:
db.collection.update({
"_id": 1
},
[
{
$set: {
"pause_date": null,
"pause": false,
"learnt_item": {
$map: {
input: "$learnt_item",
as: "item",
in: {
$cond: [
{
$eq: [
"$$item.memorized",
false
]
},
{
memorized: "$$item.memorized",
character: "$$item.character",
next_review: {
$add: [
"$$item.next_review",
345600000
]
}
},
"$$item"
]
}
}
},
}
}
],
{
new: true,
});
You can check a running example here: https://mongoplayground.net/p/oHh1JWiP8vs