Reputation: 48
I have a collection like this
[
{
"_id" : {
"id" : 1
},
"group" : [
{
"id" : 1,
"users" : [
{
"id" : 3000,
"active" : true,
},
{
"id" : 3001,
"active" : false,
}
]
},
]
},
{
"_id" : {
"id" : 2
},
"group" : [
{
"id" : 2,
"users" : [
{
"id" : 4000,
"active" : true,
},
{
"id" : 4001,
"active" : false,
}
]
},
]
}
]
I would like to project all collection, but just the users with active equals to true. Like this:
[
{
"_id" : {
"id" : 1
},
"group" : [
{
"id" : 1,
"users" : [
{
"id" : 3000,
"active" : true,
},
]
},
]
},
{
"_id" : {
"id" : 2
},
"group" : [
{
"id" : 2,
"users" : [
{
"id" : 4000,
"active" : true,
},
]
},
]
}
]
I tried to use this to bring the result, but doesn't work.
{
$project: {
'_id': 1,
'groups': {
$filter: {
input: '$group',
as: 'g',
cond: {
$and: [
{ '$eq': ['$$g.users.active', true] }
],
},
},
}
}
}
If i just use { '$eq': ['$$g.id', 1] }
, it works but when i try to use the third level, this doesn't work.
Is there some way to access the third level and make a filter with the values? I searched in mongodb documentation but i didn't find anything.
Does anyone have any solution?
Upvotes: 2
Views: 113
Reputation: 3529
Adding another way to achieve the target O/P.
The idea here is first $unwind
the outer array field group
and so to feed group.users
at $filter
to chunk out active users. Then $group
back again using the _id
and modifying the structure of group
using $push
db.collection.aggregate([
{
$unwind: "$group"
},
{
$addFields: {
filteredUsers: {
$filter: {
input: "$group.users",
as: "u",
cond: {
$eq: ["$$u.active", true]
}
}
}
}
},
{
$group: {
_id: "$_id",
group: {
$push: {
id: "$group.id",
users: "$filteredUsers"
}
}
}
}
]);
Upvotes: 1
Reputation: 7588
I believe this pipeline will produce the desired output:
db.foo.aggregate([
{$addFields: {group: {$map: { // "overwrite" group with addFields
input: "$group",
as: "z",
in: {
id: "$$z.id", // just copy the inbound _id
users: {$filter: {
input: "$$z.users",
as: "zz",
cond: {$eq: [ "$$zz.active", true] }
}}
}
}}
}}
]);
Upvotes: 3
Reputation: 2093
You have to use $map
for every nested level. In addition, you need $mergeObjects
to extract all the properties belonging to group
that are not filtered.
[{
$project: {
'_id': 1,
'group': {
$map: {
'input': '$group',
'as': 'g',
'in': {
$mergeObjects: [
'$$g',
{
'users': {
$filter: {
"input": "$$g.users",
"as": "u",
"cond": {
$and: [{'$eq': ['$$u.active', true]}]
}
}
}
}
]
}
}
}
}
}]
The proposed aggregation returns everything you need and applies the requested filter. Thanks to $mergeObjects
I was able to extract not only the group.users
array, but also everything else that's inside the group
array (group._id
).
Upvotes: 3