arraysmongodbmongoosemongodb-querymongoose-schema

Querying for deeply nested subdocuments in Mongoose


I've been having trouble trying to query for a deeply nested subdocument while using Mongoose. My DB structure is like this:

{
    "_id": "662aa6ccae109745e30dc664",
    "username": "username",
    "email": "email@email.com",
    "lists": [
        {
            "_id": "662aa6dbae109745e30dc66a"
            "name": "list",
            "content": [
                {
                    "_id": "662aa6eeae109745e30dc670"
                    "name": "product",
                    "quantity": 30,
                    "brands": [],
                }
            ],
        },
    ],
}

All I want is to query for the product _id ("_id": "662aa6eeae109745e30dc670" for example) and get the list and user _id("_id": "662aa6dbae109745e30dc66a" and "_id": "662aa6ccae109745e30dc664" respectively, on this example).

I've tried queryin for:

const user = await findOne({ 'lists.content._id': '662aa6eeae109745e30dc670' })

But this returns the whole user object. How can I query so it's return only the product object (since I can use the 'child.parent()' method to get the listId)


Solution

  • Use an aggregation pipeline so you can $unwind each array lists & then lists.content. Then match on the criteria for lists.content._id and project the fields you want.

    db.collection.aggregate([
      { $unwind: "$lists" },
      { $unwind: "$lists.content" },
      {
        $match: {
          "lists.content._id": "662aa6eeae109745e30dc670"
        }
      },
      {
        $project: {
          // _id will be included anyway
          list_id: "$lists._id",
          content_id: "$lists.content._id"
        }
      },
      // limit to just one doc if that's a requirement; NOT recommended
      { $limit: 1 }
    ])
    

    Result:

    [
      {
        "_id": "662aa6ccae109745e30dc664",
        "content_id": "662aa6eeae109745e30dc670",
        "list_id": "662aa6dbae109745e30dc66a"
      }
    ]
    

    Mongo Playground


    If you want the _id fields as they were nested in the original doc, use this $project stage instead:

      {
        $project: {
          "lists._id": 1,
          "lists.content._id": 1
        }
      }
    

    Result:

    [
      {
        "_id": "662aa6ccae109745e30dc664",
        "lists": {
          "_id": "662aa6dbae109745e30dc66a",
          "content": {
            "_id": "662aa6eeae109745e30dc670"
          }
        }
      }
    ]