title: mongoose save multi level nested objects
body: <p>I try to implement my first mongoose based REST API.</p>
  
  <p>I tried now for days but cannot get this up and running. I would like to save the survey with an array of controls and for each control an array of controlProperties.</p>
  
  <p>In different scenarios I got it to save survey with controls array but without controlProperties and sometime with not even controls array.</p>
  
  <p>Can someone please help me understand my error?</p>
  
  <p>Thanks a lot.</p>
  
  <p>The structure is as follows:</p>
  
  <p>Survey
      -- Array of control
          -- Array of controlProperty</p>
  
  <p>My schema files are: </p>
  
  <p>survey.js</p>
  
  <pre><code>const mongoose = require('mongoose');
  const Control = require('./control');
  
  const surveySchema = mongoose.Schema({
      _id: mongoose.Schema.Types.ObjectId,
      name: {
          type: String,
          required: true,
          min: 4,
          max: 255
      },
      description: {
          type: String,
          required: false,
          max: 1000
      },
     closeDate: {
         type: Date,
         required: false
     },
     controls: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Control' }]
  });
  
  module.exports = mongoose.model('Survey', surveySchema);
  </code></pre>
  
  <p>control.js</p>
  
  <pre><code>const mongoose = require('mongoose');
  const Survey = require('./survey');
  
  const controlSchema = new mongoose.Schema({
      _id: mongoose.Schema.Types.ObjectId,
      survey: {type: mongoose.Schema.Types.ObjectId, ref: 'Survey'},
      controlType: {
          type: String,
          required: true
      },
      name: {
          type: String, 
          required: true
      },
      isInput: {
          type: Boolean,
          required: true
  
      },
      order: {
          type: Number,
          required: true
      },
      controlProperties: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ControlProperty' }]
  });
  
  
  module.exports = mongoose.model('Control', controlSchema);
  </code></pre>
  
  <p>controlProperty.js</p>
  
  <pre><code>const mongoose = require('mongoose');
  const Control = require('./control');
  
  mongoose.Schema.Types.String.checkRequired(v =&gt; v != null);
  
  const controlPropertySchema = new mongoose.Schema({
      _id: mongoose.Schema.Types.ObjectId,
      control: { type: mongoose.Schema.Types.ObjectId, ref: 'Control' },
      propertyName: {
          type: String,
          required: true
      },
      propertyValue: {
          type: String,
          required: true
      },
      order: {
          type: Number,
          required: true
      }
  
  })
  
  module.exports = mongoose.model('ControlProperty', controlPropertySchema);
  </code></pre>
  
  <p>My node.js code to receive the post data is this one:</p>
  
  <p>/routes/survey.js</p>
  
  <pre><code>router.post("/", (req, res, next) =&gt; {
  
  
          Survey.find({ _id: req.body._id })
              .exec()
              .then(result =&gt; {
                  if (result.length &gt;= 1) {
                      return res.status(409).json({
                          message: "Survey exists"
                      });
                  } else {
  
                      const survey = new Survey({
                          _id: new mongoose.Types.ObjectId(),
                          name: req.body.name,
                          description: req.body.description,
                          closeDate: req.body.closeDate,
                          order: req.body.order
                      });
  
  
                      let controlData = req.body.controls;
                      let arControls = [];
  
                      if(controlData != null) {
  
                          for (var i = 0, clen = controlData.length; i &lt; clen; i++) {
                              let c = controlData[i];
                              let control = new Control({
                                  _id: new mongoose.Types.ObjectId(),
                                  controlType: c.controlType,
                                  name: c.name,
                                  isInput: c.isInput,
                                  order: c.order
                              })
  
                              let controlPropertyData = c.controlProperties;
                              let arControlProperty = [];
  
                              if(controlPropertyData != null) {
  
                                  for (var j = 0, cplen = controlPropertyData.length; j &lt; cplen; j++) {
                                      let cp = controlPropertyData[j];
                                      let controlProperty = new ControlProperty({
                                          _id: new mongoose.Types.ObjectId(),
                                          propertyName: cp.propertyName,
                                          propertyValue: cp.propertyValue, 
                                          order: cp.order                                
                                      })
  
                                      arControlProperty.push(controlProperty);
                                  }
  
                                  ControlProperty.insertMany(arControlProperty, forceServerObjectId=true,function (err,data) {
                                      if(err!=null){
                                          return console.log(err);
                                      }
                                      console.log(" " + j + " controlProperties for control " + i +  " saved");
  
                                      control.controlProperties = data;
                                      console.log(data);
                                  });
  
  
                              } 
                              arControls.push(control);
  
  
                          }
  
                          Control.insertMany(arControls, forceServerObjectId=true,function (err,data) {
                              if(err!=null){
                                  return console.log(err);
                              }
                              survey.controls = data;
  
                              console.log("controls saved");
                              console.log(data);
                          });
                      }
  
                      survey
                          .save()
                          .then(result =&gt; {
                              console.log("survey saved");
                              res.status(201).json(survey);
                          })
                          .catch(err =&gt; {
                              console.log(err);
                              res.status(500).json({
                                  error: err
                              });
                          });
                  }
  
          });
      });
  </code></pre>
  
  <p>sample post data:</p>
  
  <pre><code>{   
      "name": "TestSurvey",
      "description": "This is a test survey",
      "controls":  [
          {       
              "controlType": "Label",
              "name": "Label1",
              "isInput": false,
              "order": 1,
              "controlProperties": [
                  {
                      "propertyName": "FontSize", 
                      "propertyValue": "Large",
                      "order": 1
                  },
                  {
                      "propertyName": "BackgroundColor", 
                      "propertyValue": "Darkgreen",
                      "order": 2
                  },
                  {
                      "propertyName": "FontAttributes", 
                      "propertyValue": "Bold",
                      "order": 3
                  },
                  {
                      "propertyName": "HorizontalOptions", 
                      "propertyValue": "Fill",
                      "order": 4
                  },
                  {
                      "propertyName": "HorizontalTextAlignment", 
                      "propertyValue": "Center",
                      "order": 5
                  },
                  {
                      "propertyName": "TextColor", 
                      "propertyValue": "White",
                      "order": 6
                  },
                  {
                      "propertyName": "Text", 
                      "propertyValue": "Paris Work-Life Balance",
                      "order": 7
                  }
              ]
          },
          {       
              "controlType": "Label",
              "name": "Label2",
              "isInput": false,
              "order": 2,
              "controlProperties": [
                  {
                      "propertyName": "FontSize", 
                      "propertyValue": "Medium",
                      "order": 1
                  },
                  {
                      "propertyName": "Margin", 
                      "propertyValue": "20,0,20,0",
                      "order": 2
                  },
                  {
                      "propertyName": "FontAttributes", 
                      "propertyValue": "Bold",
                      "order": 3
                  },
                  {
                      "propertyName": "HorizontalOptions", 
                      "propertyValue": "StartAndExpand",
                      "order": 4
                  },
                  {
                      "propertyName": "HorizontalTextAlignment", 
                      "propertyValue": "Center",
                      "order": 5
                  },
                  {
                      "propertyName": "Text", 
                      "propertyValue": "Dear [[FirstName]], \nwas your workload on the case 12345 - 67(Company) compliant to the BCG Work Life Balance Ground Rules over the past week ?",
                      "order": 6
                  }
              ]
          },
          {
              "controlType": "PWLBControl",
              "name": "PWLB1",
              "isInput": true,
              "order": 3,
              "controlProperties": [
                  {
                          "propertyName": "Margin", 
                          "propertyValue": "20,0,20,0",
                          "order": 1
                  }
              ]
          },      
          {
              "controlType": "Button",
              "name": "button1",
              "isInput": false,
              "order": 4,
              "controlProperties": [
                  {
                      "propertyName": "Text", 
                      "propertyValue": "Submit",
                      "order": 1
                  },
                          {
                      "propertyName": "HorizontalOptions", 
                      "propertyValue": "StartAndExpand",
                      "order": 2
                  },
                  {
                      "propertyName": "IsSubmitButton",
                      "propertyValue": true,
                      "order": 3
                  }
              ]
          },
          {
              "controlType": "Image",
              "name": "image1",
              "isInput": false,
              "order": 5,
              "controlProperties": [
                  {
                      "propertyName": "Source",
                      "propertyValue": "",
                      "order": 1
                  },
                  {
                      "propertyName": "VerticalOptions",
                      "propertyValue": "End",
                      "order": 2
                  }
              ]
          }
      ]
  }
  </code></pre>