The mongodb collection looks like the array below
[
{
"_id": 1,
"arr": [
{
"key": "abc",
"val_a": 2
}
]
},
{
"_id": 2
}
]
What I want to do is increment 1 to the element in arr
array with key
def
. If the element with key
def
is not in the array arr
, I want to add a new element like below to arr
{
"key": "def",
"val_a": 1
}
I tried
db.collection.update({
"_id": 1,
"arr.key": "def",
},
{
"$inc": {
"arr.$.val_a": 1
}
})
But it is not adding the new element. The result is like
[
{
"_id": 1,
"arr": [
{
"key": "abc",
"val_a": 2
}
]
},
{
"_id": 2
}
]
What I am expecting is something like this:
[
{
"_id": 1,
"arr": [
{
"key": "abc",
"val_a": 2
},
{
"key": "def",
"val_a": 1
}
]
},
{
"_id": 2
}
]
I want to do it in one operation as I am intending to do is a BulkWrite
operation with UpdateOneModel
.
CodePudding user response:
Query
- finds the
_id
- new-member you set any document to add
- if arr is not array make it an empty array, so concat to work later
- check if it exists
- if not exists, add to the end of the array
- else map to find it and increase its
val_a
*this is pipeline update, to do it with 1 query, alternative way is with 2 queries, one to check if it exists, and the second to $push
(if not exists) or to $set
with arrayFilters.
(you would had to send 2 queries and write 3 queries, and its not atomic safe)
update(
{"_id": {"$eq": 1}},
[{"$set": {"new-member": {"key": "abc", "val_a": 1}}},
{"$set": {"arr": {"$cond": [{"$isArray": ["$arr"]}, "$arr", []]}}},
{"$set":
{"not-exists":
{"$eq":
[{"$filter":
{"input": "$arr",
"cond": {"$eq": ["$$this.key", "$new-member.key"]}}},
[]]}}},
{"$set":
{"arr":
{"$cond":
["$not-exists", {"$concatArrays": ["$arr", ["$new-member"]]},
{"$map":
{"input": "$arr",
"in":
{"$cond":
[{"$eq": ["$$this.key", "$new-member.key"]},
{"$mergeObjects":
["$$this", {"val_a": {"$add": ["$$this.val_a", 1]}}]},
"$$this"]}}}]}}},
{"$unset": ["new-member", "not-exists"]}])