Wednesday 11 November 2020

MongoDB - Update all entries in nested array only if they exist

I have a multilevel nested document (its dynamic and some levels can be missing but maximum 3 levels). I want to update all the children and subchildren routes if any. The scenario is same as in any Windows explorer, where all subfolders' route need to change when a parent folder route is changed. For eg. In the below example, If I am at route=="l1/l2a" and it's name needs to be edited to "l2c", then I will update it's route as route=="l1/l2c and I will update all childrens' route to say "l1/l2c/l3a".

     {
    "name":"l1",
    "route": "l1",
    "children":
        [
            {
            "name": "l2a",
            "route": "l1/l2a",
            "children": 
                [
                    {
                    "name": "l3a",
                    "route": "l1/l2a/l3a"
                 }]
            },
            {
            "name": "l2b",
            "route": "l1/l2b",
            "children": 
                [
                    {
                    "name": "l3b",
                    "route": "l1/l2b/l3b"
                 }]
            }
      ]
     }

Currently I am able to go to a point and I am able to change its name and ONLY its route in the following manner:

router.put('/navlist',(req,res,next)=>{
newname=req.body.newName //suppose l2c
oldname=req.body.name //suppose l2a
route=req.body.route // existing route is l1/l2a
id=req.body._id


newroute=route.replace(oldname,newname); // l1/l2a has to be changed to l1/l2c
let segments = route.split('/');  
let query = { route: segments[0]};
let update, options = {};

let updatePath = "";
options.arrayFilters = [];
for(let i = 0; i < segments.length  -1; i++){
    updatePath += `children.$[child${i}].`;
    options.arrayFilters.push({ [`child${i}.route`]: segments.slice(0, i + 2).join('/') });
} //this is basically for the nested children

updateName=updatePath+'name'
updateRoute=updatePath+'route';

update = { $setOnInsert: { [updateName]:newDisplayName,[updateRoute]:newroute } };      
NavItems.updateOne(query,update, options)
 })

The problem is I am not able to edit the routes of it's children if any. Although I tried using the $[] operator. Suppose Path is the exact path to a point from where you need to update it's children.

updateChild = updatePath+'.children.$[].route'
updateChild2 = updatePath+'.children.$[].children.$[].route'
//update = { $set: { [updateChild]:'abc',[updateChild2]:'abc' } };

Its important that levels are customizable and thus I don't know whether there is "l3A" or not. Like there can be "l3A" but there may not be "l3B". But my code simply requires every correct path else it gives an error

code 500 MongoError: The path 'children.1.children' must exist in the document in order to apply array updates.

So the question is how can I apply changes using $set to a path that actually exists and how can I edit the existing route part. If the path exists, it's well and good and if the path does not exist, I am getting the ERROR.



from MongoDB - Update all entries in nested array only if they exist

No comments:

Post a Comment