Reputation: 835
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.
Upvotes: 2
Views: 2995
Reputation: 340
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,
},
},
])
Upvotes: 4