'MongoDB push to nested array with findOneAndUpdate
I would like to know whether it is possible to implement the following problem within single call of findOneAndUdate() method.
Lets consider this collection entry:
[
{
"key": 1,
"logs": [
{
"app_logs": [
{
"stdout": "app_stdout_1",
"stderr": "app_stderr_1"
}
],
"syslog": "syslog_val1"
},
{
"app_logs": [
{
"stdout": "app_stdout_2",
"stderr": "app_stderr_2"
}
],
"syslog": "syslog_val2"
}
]
},
]
I need to push additional element into "app_logs" array but only within the last entry of "logs" array. Currently to bypass this issue, I fetch the document as it is, execute such push in the application and then use findOneAndReplace() method. While this works I would like it to be implemented withing single MongoDB call. I have tried various approaches like pipeline aggregations, update array filters etc. but could not get any of them to work. Also tried to find solution online, read the docs but still can't find any similar problem that was successfully solved.
Most common similar problem people tend to hit is updating specific element in nested array under condition which can be solved by using positional operator with something like:
findOneAndUpdate({
"key": 1,
"logs.syslog": "syslog_val1"
},
{
"$push": {
"logs.$.app_logs": {
"stdout": "app_stdout_new",
"stderr": "app_stderr_new"
}
}
})
In my case I want to execute such push not for the specific element of "logs" array but only for the last one. Since I already spent lot of time with this problem it leads me to the question whether it is even possible to implement or whether there are any limitations with current MongoDB version which would make such implementation impossible. Thanks for any help.
Solution 1:[1]
For your scenario, via update query with Aggregation Pipeline to achieve it.
$set- Get the last element oflogsaslastLog.$set2.1
$concatArrays- Merge arrays with all the elements oflogs(except the last document) and the result of 2.2.2.2
$mergeObjects- Merge objects withlastLogand the result of 2.3 aimed to override theapp_logsfield.2.3
$mergeArrays- Merge arrays withlastLog.app_logsand new document (as array).$unset- RemovelastLogfield.
db.collection.update({
"key": 1,
"logs.syslog": "syslog_val1"
},
[
{
$set: {
"lastLog": {
$arrayElemAt: [
"$logs",
-1
]
}
}
},
{
$set: {
"logs": {
$concatArrays: [
{
$slice: [
"$logs",
{
$add: [
{
$size: "$logs"
},
-1
]
}
]
},
[
{
$mergeObjects: [
"$lastLog",
{
"app_logs": {
$concatArrays: [
"$lastLog.app_logs",
[
{
"stdout": "app_stdout_new",
"stderr": "app_stderr_new"
}
]
]
}
}
]
}
]
]
}
}
},
{
$unset: "lastLogs"
}
])
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Yong Shun |
