简体   繁体   中英

MongoDB - Mongoose: How Do I Update Simultaneously Two Separate Embedded Documents With Mongoose?

So I am trying to update the field status in my Report document and in my Station.reports sub-document which is an array of objects, in one single API call. The issue is that I am able to update the Report document, but not the station document when making the API call. After the call, the console.log(station.reports); returns the expected subdocument which is : [{"_id":"588fed278b50cd180bd6cc15","date":"2017-01-31T01:48:57.487Z","status":"Archived"}] But this is not saved in the corresponding Station document in my DB. Please I need help here. Thanks.

Station Document:

{
    "_id": "588a777d4e26720e7afa7e1e",
    "phone": "(007) – 007 – 7007",
    "name": "name1",
    "email": "name1@email.com",
    "reports": [
      {
        "status": "Submitted",
        "date": "2014-01-31T01:48:57.487Z",
        "_id": "588fed278b50cd180bd6cc15"
      }
    ]
}  

Report Document

{
    "_id": "588fed278b50cd180bd6cc15",
    "description": "Description of the report",
    "time": "05:48 PM",
    "date": "2017-01-31T01:48:57.487Z",
    "status": "Archived",
    "location" : "123 Main Street"
    "station" : "588a777d4e26720e7afa7e1e"
}  

API Call

router.put('/reports/:id/updateStatus', function (req, res) {

    Report.findById(req.params.id, function(err,report){
        // if there is an error retrieving, send the error.
        // nothing after res.send(err) will execute
        if (err)
          return res.send(err);

        //  Update the Report object 
        report.status = req.body.status;

        // Update the Corresponding station.reports subdocument
        Station.findOne({_id:report.station}, function (err, data) {
            if(err) return console.log(err);

            data.reports.forEach(function(rpt){
                if (rpt._id == req.params.id){

                    rpt.status = req.body.status
                    data.save(function (err, station) {
                        if (err)
                            return res.send(err); 

                        console.log(station.reports);
                    })
                }
            })
        })

        report.save(function (err, report) {
            if (err)
                return res.send(err); 

            res.json(report);
        })
    });
})

You are doing mistake while updating the station object. Use findOneAndUpdate to find the matching Station document, and then change the status of the matched reports item(matched using reports._id).

Try this:

Station.findOneAndUpdate({
        _id:report.station,"reports._id":req.params.id
    },{
        $set : {reports.$.status : req.body.status}
    },function(err){
    if(err)
        return res.send(err);
});

report._id will find the array element whose _id is req.params.id and report.$.status will update only the matching element of the array.

For more information on positional $(update) operator , Read mongoDB positional Documentation .

Also, i would suggest to save the report object in the callback of update . As nodejs is asynchronous , it will not wait for the update to finish, if you are saving report outside of the callback. And, you might get Cant set the headers after they are sent error . Thus, its recommended to do it in the callback.

Thus your final API code would look like:

router.put('/reports/:id/updateStatus', function (req, res) {

    Report.findById(req.params.id, function(err,report){
        // if there is an error retrieving, send the error.
        // nothing after res.send(err) will execute
        if (err)
          return res.send(err);

        //  Update the Report object 
        report.status = req.body.status;

        // Update the Corresponding station.reports subdocument
        Station.findOneAndUpdate({
                "_id":report.station,"reports._id":req.params.id
            },{
                $set : {"reports.$.status" : req.body.status}
            },function(err, result){
                if(err)
                    return res.send(err);
                console.log(result);
                report.save(function (err, report) {
                    if (err)
                        return res.send(err); 

                    res.json(report);
                });
        });
    });
})

UPDATE Alternate Method

Another way can be, You can proceed in the original way, but don't save the data inside the forEach, instead save the data sheet forEach finishes.

Station.findOne({_id:report.station}, function (err, data) {
    if(err) return console.log(err);

    data.reports.forEach(function(rpt){
        if (rpt._id == req.params.id){

            rpt.status = req.body.status
        }
    });
    data.save(function (err, station) {
        if (err)
            return res.send(err); 

        console.log(station.reports);
        report.save(function (err, report) {
            if (err)
                return res.send(err);
            res.json(report);
        });
    })
})

Hope this helps!

After multiple attempts, and with the help of Ravi, I was able to figure out a solution that worked for me pretty well. The only thing that changed was my API call. The rest of the code was unchanged.
Hope this helps someone having similar needs.

API CALL

router.put('/reports/:id/updateStatus', function (req, res) {

    Report.findById(req.params.id, function(err,report){
        // if there is an error retrieving, send the error.
        // nothing after res.send(err) will execute
        if (err)
          return res.send(err);

        //  Update the Report object 
        report.status = req.body.status;

        // Update the Corresponding station.reports subdocument
        Station.findOne({_id:report.station}, function (err, info) {
            if(err) return console.log(err);

            info.reports.forEach(function(rpt){
                if (rpt._id == req.params.id){

                    Station.update({_id:info._id, "reports._id":rpt._id },
                        {
                            $set:{"reports.$.status": req.body.status }
                        },function (err, results) {
                            if(err) return console.log("This Station couldn't be updated " + err);
                            console.log(results)
                        }
                    )
                }
            })
            report.save(function (err, report) {
                if (err)
                    return res.send(err); 

                res.json({report:report, station:info});
            });
        })
    });
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM