Update nested array of object

const updateComplete = async (req, res) => {
  const { id, monthIndex, dayIndex } = req.body;

  try {
    const calendarData = await CalendarModel.findOne({ _id: id });
  
    const updateData = await CalendarModel.updateOne(
      {
        _id: id,
      },
      {
        $set: {
          "getFullYear.$[month].days.$[day].isComplete": true,
        },
      },
      {
        arrayFilters: [
          {
            "month._id": monthIndex,
          },
          {
            "day._id": dayIndex,
          },
        ],
      }
    );
    res.status(200).json({ _id: updateData._id });
  } catch (error) {
    res.status(404).json(error.message);
  }
};

I am trying to toggle between true to false. Here is my data structure

[
 {
   _id: "65643c670b0bfd17c27d3496",
   habitName: "PHP",
   getFullYear: [
     {
       month: "January",
       year: 2023,
       days: [
         {
           objId: "1f5a9609-35aa-490e-aaf3-bacee3f4345c",
           day: 1,
           isComplete: false,
           _id: "65643c670b0bfd17c27d3498",
         },
         {
           objId: "12785478-6c47-4303-97b3-733fad5e8987",
           day: 2,
           isComplete: false,
           _id: "65643c670b0bfd17c27d3499",
         },
       ]
     }
   ]
 }
]

I have no idea how to do that. Do you have any idea on that? Can you provide resources to read about to work with complex data like that.

Hello @Aung_Mon, Welcome to the MongoDB community forum,

You can use update with the aggregation pipeline starting from MongoDB v4.2, And you don’t need to do 2 queries,

We are checking conditions in an aggregation pipeline, if you are using Mongoose npm make sure to convert your monthIndex and dayIndex to objectId type from string.

If you can understand the logic here, we have used,

  • $map to iterate getFullYear array and days loop.
  • $mergeObjects to merge the existing object and updated properties.
  • $not to toggle the boolean value of isComplete property.
const updateComplete = async (req, res) => {
    const { id, monthIndex, dayIndex } = req.body;
    try {
        const updateData = await CalendarModel.updateOne(
            { _id: id },
            [{
                $set: {
                    getFullYear: {
                        $map: {
                            input: "$getFullYear",
                            in: {
                                $cond: [
                                    { $eq: ["$$this._id", monthIndex] },
                                    {
                                        $mergeObjects: [
                                            "$$this",
                                            {
                                                days: {
                                                    $map: {
                                                        input: "$$this.days",
                                                        in: {
                                                            $cond: [
                                                                { $eq: ["$$this._id", dayIndex] },
                                                                {
                                                                    $mergeObjects: [
                                                                        "$$this",
                                                                        { isComplete: { $not: "$$this.isComplete" } }
                                                                    ]
                                                                },
                                                                "$$this"
                                                            ]
                                                        }
                                                    }
                                                }
                                            }
                                        ]
                                    },
                                    "$$this"
                                ]
                            }
                        }
                    }
                }
            }]
        );
        res.status(200).json({ _id: id });
    } catch (error) {
        res.status(404).json(error.message);
    }
};

Playground

Warning: Test in the development/staging environment before executing directly in production.

2 Likes