Suppose I have the following the document structure.
[
{
"_id": 1,
"depots": [
{
"_id": 1,
"isFavourite": true
},
{
"_id": 2,
"isFavourite": false
},
{
"_id": 3,
"isFavourite": true
},
{
"_id": 4,
"isFavourite": false
}
]
}
]
I want to write a single update query which filters for the document with _id: 1
and first sets every isFavourite
value to false
and then sets the second isFavourite
value (or any specified index) to true
.
The resulting document should look like this.
[
{
"_id": 1,
"depots": [
{
"_id": 1,
"isFavourite": false
},
{
"_id": 2,
"isFavourite": true
},
{
"_id": 3,
"isFavourite": false
},
{
"_id": 4,
"isFavourite": false
}
]
}
]
What I tried:
db.collection.update({
_id: 1
},
[
{
"$set": {
"depots.isFavourite": false
}
},
{
"$set": {
"depots.2.isFavourite": true
}
}
])
Yet strangely this does not work. See the linked playground for the result of this query.
CodePudding user response:
Using the index as a dot notation only works when the update is not a pipeline.
One option is to "rebuild" the array using $reduce
, which allow us to use the size of the currently built array to find the item with the requested index, and then $mergeObjects
it with the updated field and value:
db.collection.update(
{_id: 1},
[
{$set: {"depots.isFavourite": false}},
{$set: {
depots: {
$reduce: {
input: "$depots",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[
{$cond: [
{$eq: [{$size: "$$value"}, requestedIndex]},
{$mergeObjects: ["$$this", {isFavourite: true}]},
"$$this"
]
}
]
]
}
}
}
}
}
])
See how it works on the playground example
CodePudding user response:
What do you think about this:
db.collection.update({
_id: 1
},
{
"$set": {
"depots.$[y].isFavourite": false,
"depots.$[x].isFavourite": true
}
},
{
arrayFilters: [
{
"x._id": 2
},
{
"y._id": {
$ne: 2
}
}
],
"multi": true
})
Explained:
Set two arrayFilters x & y that match the two conditions ...