Alexandre Behling
Alexandre Behling

Reputation: 27

Error pushing objects into another object in mongodb

req.body.courses has multiples id's of courses that I want to add to a specific categorie, the problem is that when my code runs it save a course more that one time, sometimes four or five times, depending on the number of loops it does.

The function:

router.post('/categories/:cat_id/', function (req, res) {
    Categorie.findById(req.params.cat_id, function(err, categorie){
    if(err){
      console.log(err);
    } else {
      var courses = req.body.courses;
      courses.forEach(function (course){
          Course.findOne({  _id:  course }, function(err, foundCourse) {
              if(err){
                  console.log(err);
              } else {
                  categorie.courses.push(foundCourse._id);
                  categorie.save();
              }
          });
      });
    }
  });
  return res.redirect('/dash');
});

The CategorieSchema:

var categorieSchema = mongoose.Schema({
   name: String,
   courses: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Course"
        }    
    ]
});

Here is an example of trying to add 4 courses to the categorie:

{ "_id" : ObjectId("5a871964a6b4820ecf7abaa7"), "courses" : [ ObjectId("5a870a7374486e0b0d69f710"), ObjectId("5a870a7a74486e0b0d69f711"), ObjectId("5a870a6974486e0b0d69f70f"),   
 ObjectId("5a870a7374486e0b0d69f710"), ObjectId("5a870a7a74486e0b0d69f711"), ObjectId("5a870a6974486e0b0d69f70f"), 
 ObjectId("5a870a7374486e0b0d69f710"), ObjectId("5a870a7a74486e0b0d69f711"), ObjectId("5a870a6974486e0b0d69f70f") ], "name" : "test2", "__v" : 3 }

Upvotes: 0

Views: 60

Answers (2)

chridam
chridam

Reputation: 103425

An alternative would involve a first query returning the courses using $in operator with Course.find() and then update the courses array in the Categorie model with Categorie.findByIdAndUpdate():

router.post('/categories/:cat_id/', function (req, res) {
    Course.find({ '_id': { '$in': req.body.courses }}).exec((err, courses) => {
        Categorie.findByIdAndUpdate(
            req.params.cat_id, 
            { '$addToSet': { 'courses': courses } },
            { 'new': true },
            (err, categorie) => {
                if (err){
                    console.log(err);
                } else {
                    return res.redirect('/dash');
                }
            }
        });    
    });
});

Upvotes: 0

Rahul Sharma
Rahul Sharma

Reputation: 10111

Node.js Is async, It does not wait for the loop to execute completely and each time you are adding _id in existing array because of that adds 2-3 times.

Try this once I have not tested this.

const findOne = (course) => {
    return new Promise((resolve, reject) => {
        Course.findOne({
            _id: course
        }, (err, foundCourse) => {
            if (err)
                return reject(err);
            return resolve(foundCourse._id);
        });
    });
}

router.post('/categories/:cat_id/', function (req, res) {
    Categorie.findById(req.params.cat_id, function (err, categorie) {
        if (err) {
            console.log(err);
            res.status(400).json(err);
        } else {
            var courses = req.body.courses;

            Promise.all(courses.map((course) => {
                return findOne(course);
            })).then((data) => {
                // check if course id already there skip
                data = data.filter((course) => {
                    return !categorie.courses.includes(course);
                });
                categorie.courses = categorie.courses.concat(data);
                categorie.save();
                return res.redirect('/dash');
            }).catch((err) => {
                console.log(err);
                res.status(400).json(err);
            });
        }
    });
});

Upvotes: 1

Related Questions