1

Im trying to set a value of a nested array object Schema:

courses: [
    {
      days: [
        {
          courseDate: {
            type: String,
          },
          attendance: {
            type: Boolean,
          },
          reason: {
            type: String,
          },
        },
      ],
      courseId: {
        type: String,
      },
      name: {
        type: String,
      },
      startTime: {
        type: String,
      },
      endTime: {
        type: String,
      },
    },
  ],

Here are my attemapts:

await Student.findOneAndUpdate(
      { "courses.days._id": "6117b0c45345db20f0dc3336" },
      { $set: { "courses.$.days.$.attendance": true } },
      { new: true }
    );

    await Student.findOneAndUpdate(
      { "courses.days._id": req.params.dayId },
      { "courses.$[courseIndex].days.$[dayIndex].attendance": true },
      {
        arrayFilters: [
          {
            courseIndex: req.params.courseId,
          },
          {
            dayIndex: req.params.dayId,
          },
        ],
      }
    );

Here is the document:

 "courses" : [ 
        {
            "_id" : ObjectId("6117b0c45345db20f0dc3330"),
            "courseId" : "61155838b1fff211dd9a8765",
            "name" : "succ",
            "startTime" : "20:20",
            "endTime" : "23:20",
            "days" : [ 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3331"),
                    "courseDate" : "Wed Aug 04 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }, 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3332"),
                    "courseDate" : "Wed Aug 11 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }, 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3333"),
                    "courseDate" : "Wed Aug 18 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }, 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3334"),
                    "courseDate" : "Wed Aug 25 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }
            ]
        }, 
        {
            "_id" : ObjectId("6117b0c45345db20f0dc3335"),
            "courseId" : "61155838b1fff211dd9a8765",
            "name" : "test",
            "startTime" : "13:40",
            "endTime" : "15:40",
            "days" : [ 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3336"),
                    "courseDate" : "Thu Aug 05 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }, 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3337"),
                    "courseDate" : "Thu Aug 12 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }, 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3338"),
                    "courseDate" : "Thu Aug 19 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : true,
                    "reason" : ""
                }, 
                {
                    "_id" : ObjectId("6117b0c45345db20f0dc3339"),
                    "courseDate" : "Thu Aug 26 2021 00:00:00 GMT+0300 (Israel Daylight Time)",
                    "attendance" : false,
                    "reason" : ""
                }
            ]
        }
    ],

The first one is throwing an Error:

Too many positional (i.e. '$') elements found in path 'courses.$.days.$.attendance'

The second one doesn't work... I don't really understand where is the issue in the second approach.

It finds the right doc but it doesn't update anything.

It's important to note that the position of every array is dynamic so the second approach is good as well.

Gaurav Sharma
  • 573
  • 1
  • 4
  • 26

1 Answers1

0

The filtered positional operator $[] identifies the array elements that match the arrayFilters conditions for an update operation.

{ <update operator>: { "<array>.$[<identifier>]" : value } },
{ arrayFilters: [ { <identifier>: <condition> } ] }

In case identifier is an object, we can further query over that object in arrayFilters condition.

As arrayFilters are something related to mongodb, you need to cast id in mongoose with ObjectId

    await Student.findOneAndUpdate(
      { 
        "courses": { "$elemMatch": { "_id": ObjectId(req.params.courseId)} }, 
        "courses.days": { "$elemMatch": { "_id": ObjectId(req.params.dayId)} } 
      },
      { "courses.$[courseIndex].days.$[dayIndex].attendance": true },
      {
        arrayFilters: [
          {
            courseIndex._id: ObjectId(req.params.courseId),
          },
          {
            dayIndex._id: ObjectId(req.params.dayId),
          },
        ],
        new: true,
      }
    );

Edit:
You need to use new: true to get the updated value

Gaurav Sharma
  • 573
  • 1
  • 4
  • 26
  • Gaurav Sharma its still doesnt work.... Just making sure. req.params.dayId and req.params.courseId are the id of each element. They are not the indexs – David Axelrod Aug 15 '21 at 13:21
  • @DavidAxelrod Ohh.. I thought they are indexes of the array – Gaurav Sharma Aug 15 '21 at 13:27
  • Thank you so so much!!! i really appreciate it!!!! But could you please explain me why in the filter it written ._id ? and just courseIndex? – David Axelrod Aug 15 '21 at 13:39
  • @DavidAxelrod See my updated answer. `In case identifier is an object, we can further query over that object in arrayFilters condition.` – Gaurav Sharma Aug 15 '21 at 13:45
  • One last thing. I'm trying to log the new array and it stays the same. but in the actuall data base it is changed. Im asking it becuase im sending the wrong response to the cliend side – David Axelrod Aug 15 '21 at 14:17
  • @ Gaurav Sharma your method works. but now when im sending the response the old object is getting sent. not the updated. So I'm sending the wrong info to the client side. – David Axelrod Aug 15 '21 at 14:25
  • You need to use `new: true` to get the updated value. Also updated in answer. Do upvote if it helps. – Gaurav Sharma Aug 15 '21 at 14:30
  • 1
    Hi @Gaurav Sharma i would like to upvote :) but im getting this message: Thanks for the feedback! You need at least 15 reputation to cast a vote, but your feedback has been recorded. Can I help in another way ? – David Axelrod Aug 16 '21 at 10:29
  • 1
    Thanks @DavidAxelrod, We learn from each other. Just keep on contributing to the community. That's all I want :) – Gaurav Sharma Aug 16 '21 at 18:40