2

I need to filter an array of nested objects and only return those which have active.email set to true.

So far I have an aggregation:

db.subscriptions.aggregate([
  {
    $match: { user: ObjectId('5f9a4f3070bd08b002498d43') },
  },
  {
    $project: {
      'subscriptions.user': 1,
      'subscriptions.active.email': 1,
    },
  })

Which returns a single user's subscriptions doc:

{
    "_id" : ObjectId("5f9a4f3170bd08b002498d44"),
    "subscriptions" : [ 
        {
            "active" : {
                "email" : true
            },
            "user" : ObjectId("5f9a4e5071713dc6120df47f")
        }, 
        {
            "active" : {
                "email" : true
            },
            "user" : ObjectId("5f9b7f2dc16811a281113ba1")
        }, 
        {
            "active" : {
                "email" : false
            },
            "user" : ObjectId("5f9b7e8ac16811a281113b9f")
        }
    ]
}

If I try to use filter on it:

db.subscriptions.aggregate([
  {
    $match: { user: ObjectId('5f9a4f3070bd08b002498d43') },
  },
  {
    $project: {
      'subscriptions.user': 1,
      'subscriptions.active.email': 1,
    },
  },
  {
    $filter: {
      input: '$subscriptions',
      as: 'subs',
      cond: { '$$subs.active.email': true },
    },
  },

it gives me this error: "Unrecognized pipeline stage name: '$filter'",

This is my desired output:


"subscriptions" : [ 
        {
            "active" : {
                "email" : true
            },
            "user" : ObjectId("5f9a4e5071713dc6120df47f")
        }, 
        {
            "active" : {
                "email" : true
            },
            "user" : ObjectId("5f9b7f2dc16811a281113ba1")
        }, 
    ]

Whats the correct way to use filter in this? I originally tried to use $elemMatch in the query, but since it's nested, this cannot be done. Also, if there's another method, I'm all ears.

1 Answer 1

4

You need to unwind the "subscriptions" array after you $match'ed the main _id, after that you need to $match again on the active mail. Use $project to create a nicer output. Make sure you only query on one main _id or else this will get messy with multiple items of potential different users.

db.subscriptions.aggregate([
  {
    $match: { _id: ObjectId('5f9a4f3170bd08b002498d44') },
  },
  {
    $unwind: {
      path: '$subscriptions',
    },
  },
  {
    $match: {
      'subscriptions.active.email': true,
    },
  },
  {
    $project: {
      activeUserId: '$subscriptions.user',
      _id: 0,
    },
  },
])
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I was just figuring this out as you sent it. I know filter was the wrong choice for this, but I'm still confused about where and when $filter is used. There doesn't seem to be very many questions out there for it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.