Search code examples
mongodbmongoosemongodb-querymongoose-schema

Insert a new object under embedded subdocument


This is my sample record:

enter image description here

Building id as highlighted in above image, has a subdocument gateways. I am able to find building id but not sure how to push / insert a new object gateway under gateways subdocument.

here is my body for new gateway object to be inserted from request.

{
    "gatewayName": "gatewayName",
    "gatewayKey": "sampleKey",
    "suite": "3300"
}

This is what I am trying currently. It returns the same document.
First I find account, then get building. then Find building based on buildingId provided. There I need to insert a new gateway object under Gateways subdocument

   const account = await Account.findOne({ _id: new ObjectId(req.query.id) });
      const allBuildings = account.buildings;
      const filteredBuildings = _.filter(allBuildings, { _id: ObjectId(req.query.buildingId) });
      const gateways = _.flatMap(filteredBuildings, b => b.gateways);

      gateways.push({
        "gatewayName": "TD Bank Tower22223",
        "gatewayKey": "sdasdasdasd",
                  "suite": "3300"
      }
      );
      const updatedAccount = await account.save();
      console.log(updatedAccount)
res.json(updatedAccount);

Solution

  • Yes, you can do the find and update in a single findOneAndUpdate using $elemMatch, $addToSet and the $ operators.

    First match on the document id and the building id within the array. You can then access the matched building using the $ operator and use $addToSet to insert a new array element.

    Example:

    const gateway = {
        "gatewayName": "gatewayName",
        "gatewayKey": "sampleKey",
        "suite": "3300"
    };
    
    const update = await Account.findOneAndUpdate(
        {
            _id: new ObjectId(req.query.id),
            buildings: {$elemMatch: {_id: {$eq: ObjectId(req.query.buildingId)}}}
        },
        {
            $addToSet: {"buildings.$.gateways": gateway}
        },
        {
            new: true
        }
    );