简体   繁体   中英

MongoDB aggregation filtering of a nested object

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.

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,
    },
  },
])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM