I have this schema:
...
recommended:{
type:[{
movieId:String,
recommendedBy:[String]
}],
default:[],
},
name: {
type: String,
}
from the request I get movieId and id.
I want to add to recommended array a new object if there is no other item with the same movieId there already, and add theid to the recommendedBy nested array in both cases.
so far I have this:
const user = User.findByIdAndUpdate({_id:'123'},
{$AddToSet:{'recommended.$[movie].recommendedBy':id}},
{arrayFilters:[{'movie.movieId':movieId}]})
this will only set the recommendedBy of an already existing item with the same movieId,
but will not push a new object with the new movieId property.
(so it's basically $AddToSet for both arrays)
How could I achieve that? thanks!
CodePudding user response:
Maybe you need something simple like this that is doing upsert or update for the recommended array element:
db.collection.update({},
[
{
$set: {
recommended: {
"$reduce": {
"input": "$recommended",
"initialValue": // assign your edit data as init value
[
{
"movieId": "1",
"recommendedBy": "who recommended"
}
],
"in": {
"$cond": {
"if": {
$in: [
"$$this.movieId",
"$$value.movieId"
]
},
"then": "$$value",
"else": {
"$concatArrays": [
"$$value",
[
"$$this"
]
]
}
}
}
}
}
}
}
])
explained:
- No filter but you can add anything in the initial document filter to search only limited set of documents.
- In the update stage you use $reduce to check input array elements that you need to upsert or update the value in the affected array element.
( Confirmed to work in mongod 4.4 )
CodePudding user response:
I couldn't understand @R2D2's answer so I came up with this workaround which does unexpectedly well for me, since I also need to be able to remove the movieId and all the recommendedBy with it.
I changed my schema to this:
recommanded:{
type: Map,
of:[String],
default:{}
},
I'm removing/ adding with:
User.findByIdAndUpdate({_id:"123",{$addToSet:{[`recommended.${movieId}`]:id}})
And
User.findByIdAndUpdate({_id:"123",{$unset:{[`recommended.${movieId}`]:""}})
