As i writed in the previous comment, i found a solution.
And now i post it with some description, maybe it will help someone.
Big thanks to @Prasad_Saya, who made a solution to a similar problem in here: Prasad_Saya’s post
The only difference is that, this solution works only with two level. And i needed a three level solution.
I used a same $reduce method to produce this object: { "items":[...], "updated":true } but after i transformed the object to the original array with a $let and not with a new aggregation step.
The process is:
- First i search the “A” object with a query filter and upsert it if not exists. (This is the easy part)
- After i found the “A” object, i reduce the “b” array inside it. The reduce produce this object
{ "items":[...], "updated":true }with the updated array and"updated":true, or if the id not found then the unmodified array and"updated":false. - After this, i transform the object with a $let. If updated==true then simply write the array or else add a new element to the array.
- And i made the same steps with the “c” array inside the “B” object.
And the full update pipeline is:
db.collection.update({
"a_id": A_ID
},
[
{
$set: {
count: {
$add: [
{
$ifNull: [
"$count",
0
]
},
1
]
},
b: {
$let: {
vars: { "data":
{
$reduce: {
input: {
$ifNull: [
"$b",
[]
]
},
initialValue: {
"items": [],
"updated": false
},
in: {
$cond: [
{
$eq: [
"$$this.b_id",
B_ID
]
},
{
"items": {
$concatArrays: [
"$$value.items",
[
{
"b_id": "$$this.b_id",
"count": {
$add: [
"$$this.count",
1
]
},
"c": {
$let: {
vars: { "data":
{
$reduce: {
input: {
$ifNull: [
"$$this.c",
[]
]
},
initialValue: {
"items": [],
"updated": false
},
in: {
$cond: [
{
$eq: [
"$$this.c_id",
C_ID
]
},
{
"items": {
$concatArrays: [
"$$value.items",
[
{
"c_id": "$$this.c_id",
"count": {
$add: [
"$$this.count",
1
]
}
}
]
]
},
"updated": true
},
{
"items": {
$concatArrays: [
"$$value.items",
[
"$$this"
]
]
},
"updated": "$$value.updated"
}
]
}
}
}
},
in: {
$cond: [
{
$eq: [
"$$data.updated",
false
]
},
{
$concatArrays: [
"$$data.items",
[
{
"c_id": C_ID,
"count": 1
}
]
]
},
{
$concatArrays: [
"$$data.items",
[]
]
}
]
}
}
}
}
]
]
},
"updated": true
},
{
"items": {
$concatArrays: [
"$$value.items",
[
"$$this"
]
]
},
"updated": "$$value.updated"
}
]
}
}
}
},
in: {
$cond: [
{
$eq: [
"$$data.updated",
false
]
},
{
$concatArrays: [
"$$data.items",
[
{
"b_id": B_ID,
"count": 1,
"c": [
{
"c_id": C_ID,
"count": 1,
}
]
}
]
]
},
{
$concatArrays: [
"$$data.items",
[]
]
}
]
}
},
}
}
}
],
{
"upsert": true
})