Luzan Baral
Luzan Baral

Reputation: 3698

Update nested object after matching id of main document and nested object

I have a following structure of mongo document

{
    "registrationDate": "2020-06-09T18:15:00.000Z",
    "_id": "5ee6dd003a97c93c6436349c",
    "ofSite": "5ee19fe085d70733243c3309",
    "patientID": "NGH-123",
    "neocareID": "",
    "babyName": "Joey",
    "motherName": "Phoebe",
    "dateOfBirth": "2020-06-08T18:15:00.000Z",
    "babySex": "Female",
    "birthType": "Single",
    "patientType": "Referred",
    "mobileNumber": "9802824412",
    "mobileOf": "Family Member",
    "linkedFCHV": "Racheal",
    "FCHVmobile": "9876225420",
    "address": "Holloywood",
    "examinations": [
        {
            "ODExam": {
                "plusODExam": "No",
                "zoneODExam": "I",
                "ropODExam": "No",
                "ropODStage": 1,
                "categoryOD": "None",
                "apROPOD": "Yes"
            },
            "OSExam": {
                "plusOSExam": "No",
                "zoneOSExam": "I",
                "ropOSExam": "Yes",
                "ropOSStage": 1,
                "categoryOS": "None",
                "apROPOS": "Yes"
            },
            "FindingFollowUp": {
                "ropFindings": "All Good so far",
                "followUpPlan": "Follow up in next month",
                "nameOfExaminer": "Luzan",
                "followUpDate": "2020-07-07T18:15:00.000Z"
            },
            "_id": "5ee6dd793a97c93c6436349e",
            "examinationDate": "06/12/2020",
            "currentPMA": 5,
            "daysOfLife": 42,
            "currentWeight": 4500,
            "visitType": "First"
        },
        {
            "ODExam": {
                "plusODExam": "No",
                "zoneODExam": "I",
                "ropODExam": "No",
                "ropODStage": 1,
                "categoryOD": "None",
                "apROPOD": "Yes"
            },
            "OSExam": {
                "plusOSExam": "No",
                "zoneOSExam": "I",
                "ropOSExam": "Yes",
                "ropOSStage": 1,
                "categoryOS": "None",
                "apROPOS": "Yes"
            },
            "FindingFollowUp": {
                "ropFindings": "All Good so far",
                "followUpPlan": "Follow up in next month",
                "nameOfExaminer": "Luzan",
                "followUpDate": "2020-07-07T18:15:00.000Z"
            },
            "_id": "5ee6dd913a97c93c643634a0",
            "examinationDate": "06/12/2020",
            "currentPMA": 5,
            "currentWeight": 4500,
            "visitType": "First"
        },
        {
            "ODExam": {
                "plusODExam": "No",
                "zoneODExam": "I",
                "ropODExam": "No",
                "ropODStage": 1,
                "categoryOD": "None",
                "apROPOD": "Yes"
            },
            "OSExam": {
                "plusOSExam": "No",
                "zoneOSExam": "I",
                "ropOSExam": "Yes",
                "ropOSStage": 1,
                "categoryOS": "None",
                "apROPOS": "Yes"
            },
            "FindingFollowUp": {
                "ropFindings": "All Good so far",
                "followUpPlan": "Follow up in next month",
                "nameOfExaminer": "Luzan",
                "followUpDate": "2020-07-07T18:15:00.000Z"
            },
            "_id": "5ee6ddf13a97c93c643634a2",
            "examinationDate": "06/12/2020",
            "currentPMA": 5,
            "currentWeight": 4500,
            "visitType": "First"
        },
        {
            "ODExam": {
                "plusODExam": "No",
                "zoneODExam": "I",
                "ropODExam": "No",
                "ropODStage": 1,
                "categoryOD": "None",
                "apROPOD": "Yes"
            },
            "OSExam": {
                "plusOSExam": "No",
                "zoneOSExam": "I",
                "ropOSExam": "Yes",
                "ropOSStage": 1,
                "categoryOS": "None",
                "apROPOS": "Yes"
            },
            "FindingFollowUp": {
                "ropFindings": "All Good so far",
                "followUpPlan": "Follow up in next month",
                "nameOfExaminer": "Luzan",
                "followUpDate": "2020-07-07T18:15:00.000Z"
            },
            "_id": "5ee6de093a97c93c643634a4",
            "examinationDate": "06/12/2020",
            "currentPMA": 5,
            "currentWeight": 4500,
            "visitType": "First"
        }
    ],
    "__v": 4
}

Let me tell you what I am trying to do here. This is my route

adminRoutes.post(
  '/examination/:patientID/:examID',
  userController.ensureAuthenticated,
  examController.findOneAndUpdate
)

I am passing _id of patient as :patientID (document) and _id of nested examinations object as :examID, my objective is to update the examination of patient by matching examination id. This is my controller.

/**
 * POST /admin/examination/:patientID/:examID
 **/
exports.findOneAndUpdate = function (req, res) {
  const query = { examinations: { $elemMatch: { _id: new ObjectId(req.params.examID) } } }
  const update = {
    $set: {
      examinationDate: req.body.examinationDate,
      daysOfLife: req.body.daysOfLife,
      currentPMA: req.body.currentPMA,
      currentWeight: req.body.currentWeight,
      visitType: req.body.visitType,
      ODExam: {
        plusODExam: req.body.plusODExam,
        zoneODExam: req.body.zoneODExam,
        ropODExam: req.body.ropODExam,
        ropODStage: req.body.ropODStage,
        categoryOD: req.body.categoryOD,
        apROPOD: req.body.apROPOD
      },
      OSExam: {
        plusOSExam: req.body.ropOSExam,
        zoneOSExam: req.body.zoneOSExam,
        ropOSExam: req.body.ropOSExam,
        ropOSStage: req.body.ropOSStage,
        categoryOS: req.body.categoryOS,
        apROPOS: req.body.apROPOS
      },
      FindingFollowUp: {
        ropFindings: req.body.ropFindings,
        followUpPlan: req.body.followUpPlan,
        nameOfExaminer: req.body.nameOfExaminer,
        followUpDate: req.body.followUpDate
      }
    }
  }
  const options = { arrayFilters: [{ 'examinations._id': new ObjectId(req.params.examID) }] }
  Patient.updateOne(query, update, options, function (err, result) {
    if (err) {
      console.log(err)
      res.send(err)
    } else {
      console.log(result)
      res.send(result)
    }
  })
}

I took reference from Selecting and updating a nested object by it's ObjectId in Mongoose.js. But I am getting an error.

MongoError: The array filter for identifier 'examinations' was not used in the update { $set: { daysOfLife: 45 } }

Am I doing it worng, what might be an effective way to update a single examination in this case. And is this a proper way to update only the data what has been changed? Thank you for your time reading this.

Update [Solved]. I fixed it with the help of comment from @thammada.ts . All thanks to him. Changed my update as:

const update = {
    $set: {
      'examinations.$[exam].examinationDate': req.body.examinationDate,
      'examinations.$[exam].daysOfLife': req.body.daysOfLife,
      'examinations.$[exam].currentPMA': req.body.currentPMA,
      'examinations.$[exam].currentWeight': req.body.currentWeight,
      'examinations.$[exam].visitType': req.body.visitType,
      'examinations.$[exam].ODExam': {
        plusODExam: req.body.plusODExam,
        zoneODExam: req.body.zoneODExam,
        ropODExam: req.body.ropODExam,
        ropODStage: req.body.ropODStage,
        categoryOD: req.body.categoryOD,
        apROPOD: req.body.apROPOD
      },
      'examinations.$[exam].OSExam': {
        plusOSExam: req.body.ropOSExam,
        zoneOSExam: req.body.zoneOSExam,
        ropOSExam: req.body.ropOSExam,
        ropOSStage: req.body.ropOSStage,
        categoryOS: req.body.categoryOS,
        apROPOS: req.body.apROPOS
      },
      'examinations.$[exam].FindingFollowUp': {
        ropFindings: req.body.ropFindings,
        followUpPlan: req.body.followUpPlan,
        nameOfExaminer: req.body.nameOfExaminer,
        followUpDate: req.body.followUpDate
      }
    }
  }

and used 'exam.id' on arrayFilters.

Upvotes: 2

Views: 87

Answers (1)

thammada.ts
thammada.ts

Reputation: 5245

You have to change your update and options as follows

/*
Keep in mind that this will replace the whole element,
if you have other fields not included in the update, they will be removed.
To update new fields without replacing the element,
you have to specify all the fields like this

{
  $set: {
...
    "examinations.$[exam].examinationDate": req.body.examinationDate,
    "examinations.$[exam].daysOfLife": req.body.daysOfLife,

...
    "examinations.$[exam].ODExam.plusODExam": req.body.plusODExam,
    "examinations.$[exam].ODExam.zoneODExam": req.body.zoneODExam
...
  }
}
*/
const update = {
  $set: {
    "examinations.$[exam]": {
      _id: new ObjectId(req.params.examID),
      examinationDate: req.body.examinationDate,
      daysOfLife: req.body.daysOfLife,
      currentPMA: req.body.currentPMA,
      currentWeight: req.body.currentWeight,
      visitType: req.body.visitType,
      ODExam: {
        plusODExam: req.body.plusODExam,
        zoneODExam: req.body.zoneODExam,
        ropODExam: req.body.ropODExam,
        ropODStage: req.body.ropODStage,
        categoryOD: req.body.categoryOD,
        apROPOD: req.body.apROPOD
      },
      OSExam: {
        plusOSExam: req.body.ropOSExam,
        zoneOSExam: req.body.zoneOSExam,
        ropOSExam: req.body.ropOSExam,
        ropOSStage: req.body.ropOSStage,
        categoryOS: req.body.categoryOS,
        apROPOS: req.body.apROPOS
      },
      FindingFollowUp: {
        ropFindings: req.body.ropFindings,
        followUpPlan: req.body.followUpPlan,
        nameOfExaminer: req.body.nameOfExaminer,
        followUpDate: req.body.followUpDate
      }
    }
  }
}
const options = {
  arrayFilters: [{ 'exam._id': new ObjectId(req.params.examID) }]
}

Tip: you don't really have to do $elemMatch in your query. You can do

const query = { "examinations._id": new ObjectId(req.params.examID) }

Or to make sure it's updating the correct patient

const query = {
  "_id": new ObjectId(req.params.patientID),
  "examinations._id": new ObjectId(req.params.examID)
}

Upvotes: 1

Related Questions