I am building an API endpoint, where a user can update nested entries of data in a nested array. The user can update one, many or all entries in that nested data, so I can’t hardcode the paths. In abstract, it looks something like this.
Data format
{
"id": 4211,
"foo": "some data",
"bar": [
{
"id": 1
"a": 123,
"b": {
"b1": "abc",
"b2": "def"
}
}
]
}
Example updates
The update will be provided as a dotified version of an object.
update1 = {
'b.b1': "vvv"
}
update2 = {
'a': 1337,
'b.b2': "zup"
}
Update function
async function updateNestedDataBar(baseId, id, update) {
return await UserData.findOneAndUpdate(
{ id: baseId, 'bar.id': id }, // To make sure that the subdoc exists
{ $set: { 'bar.$': update },
{ new: true }
);
}
Problem
When I update the data with the shown implementation, then it overwrites the whole bar
object with the provided id. For example, update1
from above would result in this:
{
"id": 4211,
"foo": "some data",
"bar": [
{
"b": {
"b1": "vvv",
}
}
]
}
What I’ve tried so far
According to this answer to a similar question, I have tried to rewrite the code like this. Still it overrides the whole nested object.
UserData.findOneAndUpdate(
{ id: baseId, 'bar.id': id }, // To make sure that the subdoc exists
{ $set: { 'bar.$[outer]': update },
{
arrayFilters: [{ 'outer.id': id }],
new: true
}
);
I have also tried to do it this way, by getting the whole document, modifiying it and then save()
it. But I can’t get it to work and I also don’t want to fetch the whole document first. Plus I want to keep the benefit of the atomic operation with findOneAndUpdate
.
Additionally, I have tried something crazy. Doesn’t work.
UserData.findOneAndUpdate(
{ id: baseId, 'bar.id': id }, // To make sure that the subdoc exists
`bar.$.${update}`,
{ new: true }
);
Question
- How can I achieve the desired result?
- Is there a good documentation on the
UpdateQuery
type in mongoose? I can’t find anything in the offical docs that describes my issue.
2
Answers
In reference to collab-with-tushar-raj's answer, here a quick general purpose solution for anyone trying to solve a similar problem.
I would slightly modify your function to below:-
This
updateNestedDataBar
function iterates through the keys of theupdate
object, constructs theupdateQuery
dynamically, and then uses this query in thefindOneAndUpdate
method to update the specified nested fields within thebar
array’s object.Now, you can use the
updateNestedDataBar
function with various update objects, such asupdate1
andupdate2
you provided