Home > database >  Get a single sub-object from a nested object array with MongoDB
Get a single sub-object from a nested object array with MongoDB

Time:02-03

I have a node.js server using express and mongoose and I also have a Json structure of :

[
    {
        "_id": "63dbaf5daabee478202ae59f",
        "experimentId": "85a91abe-ef2f-416f-aa13-ec1bdf5d9766",
        "experimentData": [
            {
                "scanId": 1652890241,
                "scanData": [
                    {
                        "areaName": "A1",
                        "areaData": [],
                        "_id": "63dbaf94aabee478202ae5a5"
                    },
                    ...
                ],
                "_id": "63dbaf6caabee478202ae5a3"
            },
            ...
        ],
        "__v": 2
    },
   ...
]

How can I create a query to return a single object from the scanData array like

        {
          "areaName": "A1",
          "areaData": [],
          "_id": "63dbb006e322869df811eea4"
        }

The best I was able to do was:

// * Get all shapes in a well/area
// ! uses the experiment id, scan id and areaName
router.get("/:id/scan/:scanId/area/:areaName", async (req, res) => {
    try {
        const experiment = await Experiment.findOne({
            experimentId: req.params.id,
            experimentData: {
                $elemMatch: {
                    scanId: req.params.scanId,
                    scanData: {
                        $elemMatch: {
                            areaName: req.params.areaName
                        }
                    }
                }
            }
        }, {'experimentData.scanData.$': 1})
        console.log(experiment)
        if (!experiment || experiment.length === 0) res.status(404).json({})
        else {
            res.send(experiment.experimentData[0])
        }
    } catch (err) {
        res.status(500).json({ message: err.message })
    }
})

But that just returned the scanData array it would be great if I could go one level deeper and just get the object with the areaName. I also tried some solutions with $aggregate but was not able to get any data displayed it kept returning an empty array

CodePudding user response:

You can $match by your criteria layer-by-layer and $unwind to get the final scanData object in an aggregation pipeline. Use $replaceRoot to get only the scanData object.

db.collection.aggregate([
  {
    "$match": {
      "experimentId": "85a91abe-ef2f-416f-aa13-ec1bdf5d9766"
    }
  },
  {
    "$unwind": "$experimentData"
  },
  {
    "$match": {
      "experimentData.scanId": 1652890241
    }
  },
  {
    "$unwind": "$experimentData.scanData"
  },
  {
    "$match": {
      "experimentData.scanData.areaName": "A1"
    }
  },
  {
    "$replaceRoot": {
      "newRoot": "$experimentData.scanData"
    }
  }
])

Mongo Playground

  • Related