renard
renard

Reputation: 1468

mongoose - array of subdocument are not saved in parent document

I'm trying to push subdocuments into a parent document using push() method. After that, I want to save the parent document calling save() method on it. But the result is an empty array.

I've also tried to call parent.markModified('children') before saving the parent doc, but it makes no difference.

Below is my Schemas and the code that should save the subdocuments :

Schema :

const profileSessionSchema = mongoose.Schema({
    firstName: { type: String, trim:true},
...
   realisations : [realisationSchema],
 });
ProfileSession = mongoose.model('ProfileSession',profileSessionSchema);



const realisationSchema = mongoose.Schema({
   profileId :{ type: mongoose.Schema.Types.ObjectId, ref: 'ProfileSession'},
   ...
   type : { type: String, enum:conf.wallTypes, required:true, default:'classic'},
});
Realisation = mongoose.model('Realisation', realisationSchema);


ProfileSession.create(profileData,function(err,profile){
   for(j=0;j<realisations.length;j++){  // realisations array is not empty
     var r = Realisation(realisations[j])
     r.save(function(a,real){
        profile.realisations.push(real)
     }) 
   }

   profile.markModified('realisations')
   profile.save()
})

The profile document is indeed created in DB, but without the realisations subdocuments. I've found plenty of subjects about this, and it appears that the method markModified should solve the issue. But it does not in my case and I can't understand why...

Thank you for you help. Cheers

Upvotes: 3

Views: 1275

Answers (4)

shiraz27
shiraz27

Reputation: 2636

This is "A" correct/tested general structure of your code, If you're trying to create a document(child) within another document(parent).

My mistake was to order the save() method and that I'm not used to indenting Node code.

General guideline

  • /** 1. create parent object **/
  • /** 2. save parent **/
  • /** 3. create child object **/
  • /** 4. save child **/
  • /** 5. attach child object to parent **/
  • /** 6. save parent again with the child **/

Actual code

exports.addNewParentWithAChild = (req, res) => {
      /** 1. create parent object **/
      const parent = new Parent({
        parentName: req.body.parentName,
      });
      /** 2. save parent **/
      parent.save((err, u) => {
        if (err) {
          res.status(500).send({ message: err });
          return;
        }
        /** 3. create child object **/
        const child = new Parent({
          childName: req.body.childName,
        });
        /** 4. save child **/
        child.save((err, c) => {
          if (err) {
            res.status(500).send({ message: err });
            return;
          }
          /** 5. attach child object to parent **/
          p.children = [child]; //I created one child, so my array has one child object, (you can use map and push for multiple children adding at one)
          /** 6. save parent again with the child **/
          parent.save((err, p) => {
            if (err) {
              res.status(500).send({ message: err });
              return;
            } else {
              res
                .status(200)
                .send({ message: "success", lastParent: p, childAdded: c });
            }
          });
        });
      });
    };

Upvotes: 0

Vikash_Singh
Vikash_Singh

Reputation: 1896

callback won't wait its non-blocking so it will immediately execute profile.save before push anything into array, you should try to save it inside the callback or you should use async/await. I am posting both solution where we will save profile inside of callback and using async/await. I would prefer you using async/await. see both solution below :

within callback:

ProfileSession.create(profileData, function(err, profile) {
  for (j = 0; j < realisations.length; j++) { // realisations array is not empty
    var r = Realisation(realisations[j])
    r.save(function(a, real) {
      profile.realisations.push(real)
      profile.markModified('realisations')
      profile.save()
    })
  }


})

with async/await :

async function foo() {
  let newProfileData = await new ProfileSession(profileData)
  let profile = newProfileData.save();
  for (let j = 0; j < realisations.length; j++) { // realisations array is not empty
    let r = new Realisation(realisations[j])
    let real = await r.save();
    profile.realisations.push(real);
    profile.markModified('realisations')
    profile.save()
  }
}
foo()

Upvotes: 1

developing2020
developing2020

Reputation: 322

I think your for loop is iterating too quickly for r.save() to complete and then it's calling profile.save() before it actually saves. Maybe put some console.log inside of your r.save() and then above your profile.save() so you can see the order in which each get called

Upvotes: 2

Akrion
Akrion

Reputation: 18515

If you are actually trying to create new Realisation then you would need to use the keyword to create a new model from the schema:

ProfileSession.create(profileData,function(err,profile){
   for(j=0;j<realisations.length;j++){  // realisations array is not empty
     var r = new Realisation(realisations[j])  // <-- use new
     r.save(function(a,real){
        profile.realisations.push(real)
     }) 
   }

   profile.markModified('realisations')
   profile.save()
})

Upvotes: 0

Related Questions