how to insert into deeply nested element API nodejs mongoose

Viewed 63

I have 4 nested documents as follow:

//Nested sub document subControl
const SubControlSchema = new Schema({
   subControlNo: {
     type: String
   },
  updated: Date,
  created: {
    type: Date,
    default: Date.now
  }
});

//Nested sub document control
const ControlSubSchema = new Schema({

  mainControl: {
    type: String
  },
  subControls: [SubControlSchema],
  controlDescription: {
    type: String,
    trim: true
  },
  updated: Date,
  created: {
    type: Date,
    default: Date.now
  }
});

//Nested sub document domain
const DomainSubSchema = new Schema({
   _id: {
     type: Schema.ObjectId,
     auto: true
   },
   domainNo: {
     type: String,
    trim: true
   },
   domainName: {
    type: String,
    trim: true
   },
  domainDescription: {
    type: String,
    trim: true
  },
  controls: [ControlSubSchema],
  updated: Date,
  created: {
    type: Date,
    default: Date.now
  }
});
// framework Schema
const FrameworkSchema = new Schema({
  name: {
    type: String,
    trim: true
  },
  description: {
    type: String,
    trim: true
  },
  regulator: {
    type: Schema.Types.ObjectId,
    ref: 'Regulator',
    default: null
  },
  client: {
    type: Schema.Types.ObjectId,
    ref: 'Client',
    default: null
  },
  domains: [DomainSubSchema],
  updated: Date,
  created: {
    type: Date,
    default: Date.now
  }
});

module.exports = Mongoose.model('Framework', FrameworkSchema);

I'm trying to post a control under the domain which is inside the framework, here's what I have been trying to do:

//Add new control under a specific domain and framework
router.post('/add/:frameworkId/:domainId', auth, async (req, res) => {
  try {
    const control = req.body.controls; //take the request from the body
    const query = { _id: req.params.frameworkId, _id: req.params.domainId };//pushing into the framework model by taking the ID from URL

    await Framework.updateOne(query, { $push: { domains: control } }).exec(); //push the query into the framework model

    res.status(200).json({
      success: true,
      controls: control
    });
  } catch (error) {
    res.status(400).json({
    // error: 'Your request could not be processed. Please try again.'
    error
    });
  }
});

Data posted in postman:

Link: http://localhost:3000/api/framework/add/6233277f411377367f8ad1c0/6233277f411377367f8ad1c1
{
    "controls":
{
      "mainControl": "1-5",
      "subControls": [{
      "subControlNo": "1-4-1"
    },
    {
    "subControlNo": "1-4-2"
    }],
    "controlDescription": "controlDescriptionTest"
    }
}

Response:

{
    "success": true,
    "controls": {
        "mainControl": "1-5",
        "subControls": [
            {
                "subControlNo": "1-4-1"
            },
            {
                "subControlNo": "1-4-2"
            }
        ],
        "controlDescription": "controlDescriptionTest"
    }
}

Problem: I'm not getting any new data in mongodb , any idea if I'm approaching this the correct way? I'm guessing the data is posted correctly and It's a problem with saving it to the database

Picture of my schema: I want to be able to add elements under the controls: enter image description here

1 Answers

First if you want your code to insert and not update you should use insertOne and not updateOne, regarding an "update" operation I can see 2 potential "issues" here:

  1. req.params.frameworkId and req.params.domainId come as string type. And I assume the _id field is type ObjectId and not string. To fix this you just need to cast it to the proper type, like so:
import { ObjectId } from 'mongodb';
...
{ _id: new ObjectId(req.params.frameworkId) }
  1. Both parameters are "querying" the same field (_id), unless this is intentional somehow if these values are different it will never find a document to match, this should be changed.

Lastly if you want to update an existing object if exists, and if not insert then you should use updateOne with the upsert option:

await Framework.updateOne(query, { $push: { domains: control } }, { upsert: true }).exec(); 
Related