skip to Main Content

I am new to MongoDB and I’m struggling in extract a subdocument from a document.
In my case, I have the following schema:

    [
      {
        user_id: "u1",
        subscriptions: [
          {
            "tier": "Basic",
            "requests": [
              {
                "name": "aaaa"
              },
              {
                "name": "bbbb"
              }
            ]
          },
          {
            "tier": "Premium",
            "requests": [
              {
                "name": "cccc"
              },
              {
                "name": "dddd"
              }
            ]
          }
        ]
      },
      {
        user_id: "u2",
        subscriptions: [
          {
            "tier": "Premium",
            "requests": [
              {
                "name": "eeee"
              },
              {
                "name": "ffff"
              }
            ]
          }
        ]
      }
    ]

In my case I would like to get ONLY the list of requests for user 1.
This is what I would like to get :

[
  { "name": "aaaa" },
  { "name": "bbbb" },
  { "name": "cccc" },
  { "name": "dddd" }
]

How can I fix it ?

4

Answers


  1. Use $unwind and then $project

    db.collection.aggregate([
      {
        $match: {
          user_id: "u1"
        }
      },
      {
        $unwind: "$subscriptions"
      },
      {
        $unwind: "$subscriptions.requests"
      },
      {
        $project: {
          name: "$subscriptions.requests.name"
        }
      },
      {
        $unset: "_id"
      }
    ])
    

    mongoplayground

    Login or Signup to reply.
  2. A simple way will be:

    db.collection.aggregate([
      {
        $match: {
          user_id: "u1"
        }
      },
      {
        $unwind: "$subscriptions"
      },
      {
        $unwind: "$subscriptions.requests"
      },
      {
        $project: {
          "name": "$subscriptions.requests.name",
          _id: 0
        }
      }
    ])
    

    As you can see on the playground

    The $unwind separates the array elements into single documents. The $project allows to format the document

    Login or Signup to reply.
  3. With the aggregation framework, you can create a pipeline that first filters out documents in the collection with the given condition using the following
    a $match pipeline

    { $match: { user_id: 'u1' } }
    

    The next pipeline will need to display the desired output using $project pipeline stage and the expression to achieve this will use a $reduce array operator.

    This operator enables you to flatten the 2d array expression '$subscriptions.requests' which mongo resolves from this

    [
      [
         { "name": "aaaa" },
         { "name": "bbbb" }
      ],
      [
         { "name": "cccc" },
         { "name": "dddd" }
      ]
    ]
    

    by concatenating the inner arrays with [$concatArrays] operator 3 to just

    [
      { "name": "aaaa" },
      { "name": "bbbb" },
      { "name": "cccc" },
      { "name": "dddd" }
    ]
    

    without the need to introduce a couple of $unwind steps which can potentially be a huge performance cost when you have large arrays because it produces a copy of each document per array entry, which uses more memory (possible memory cap on aggregation pipelines of 10% total memory) and thus also takes time to produce/process and that’s not really necessary.

    The $reduce expression in this case becomes

    {
      $reduce: {
         input: '$subscriptions.requests',
         initialValue: [],
         in: { $concatArrays: ['$$value', '$$this'] }
      }
    } 
    

    Thus your overall aggregation operation follows:

    db.collection.aggregate([
      { $match: { user_id: 'u1' } },
      { $project: {
         _id: 0,
         requests: {
            $reduce: {
               input: '$subscriptions.requests',
               initialValue: [],
               in: {$concatArrays: ['$$value', '$$this']}
            }
         } 
      } }
    ])
    

    Mongo Playground

    Login or Signup to reply.
  4. db.collection.aggregate([
    {
        $unwind: {path:"$subscriptions"}
    },
    {
        $addFields :
        {
            "request": "$subscriptions.requests"
        }
    },
    {
        $unwind : "$request"
    },
    {
        $addFields : 
        {
            "name": "$request.name"
        }
    },
    {
        $project :
        {
            _id : 0,
            name: 1
        }
    }
    ])
    .toArray((err,doc)=>
    {
        if(!err)
        {
          console.log(doc)
        }
    })
    

    Result will be

    [
        {
            "name" : "aaaa"
        },  
        {
            "name" : "bbbb"
        },
        {
            "name" : "ddd"
        },
        {
            "name" : "eee"
        },
        {
            "name" : "fff"
        },
        {
            "name" : "ggg"
        }
    ]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search