Details
-
Improvement
-
Resolution: Won't Do
-
Minor - P4
-
None
-
4.2.0
-
None
Description
I always dread having to $group after an $unwind. I find myself commonly performing $unwind so I can do operations on the children like $lookup.
A common simplified scenario might be getting the total inventory count for items in a basket:
db.basket.insertMany([
|
{
|
person: 'andrea', |
basket: [
|
{name: 'apple', qty: 2}, |
{name: 'banana', qty: 3}, |
{name: 'cucumber', qty: 1}, |
]
|
}, {
|
person: 'bob', |
basket: [
|
{name: 'banana', qty: 20} |
]
|
}, {
|
person: 'curtis', |
basket: []
|
}
|
]);
|
db.item.insertMany([
|
{name: 'apple', location: 'California', inventory: 1}, |
{name: 'banana', location: 'California', inventory: 2}, |
{name: 'cucumber', location: 'California', inventory: 3}, |
{name: 'apple', location: 'Colorado', inventory: 11}, |
{name: 'banana', location: 'Colorado', inventory: 12}, |
{name: 'cucumber', location: 'Colorado', inventory: 13} |
]);
|
Now, to get this, the aggregation is:
db.basket.aggregate([
|
{
|
$unwind: {
|
path: '$basket', |
preserveNullAndEmptyArrays: true |
}
|
},
|
{$lookup: {
|
from: 'item', |
let: {
|
's_name': '$basket.name' |
},
|
as: 'basket._item', |
pipeline: [
|
{$match: {$expr: {$eq: ['$name', '$$s_name']}}}, |
{$group: {
|
_id: null, |
total_inventory: {$sum: '$inventory'} |
}},
|
{$project: {
|
_id: 0 |
}}
|
]
|
}},
|
{$unwind: {
|
path: '$basket._item', |
preserveNullAndEmptyArrays: true |
}},
|
{
|
$group: {
|
_id: {
|
'_id': '$_id' |
},
|
person: {
|
$first: '$person' |
},
|
basket: {
|
$push: '$basket' |
}
|
}
|
}
|
]);
|
Now, the 1st $unwind and $lookup stages are fine. The 2nd $unwind stage is a pet peeve of mine (I wish there was just a $lookupOne operator, but that's beside the point).
So that leaves us with the $group stage. You can see the pattern for $grouping after an $unwind is just
pathOfThingUnwound: {$push: '$pathOfThingUnwound'} |
The problem, annoying thing, however, is having to do all of the $first operations. It gets quite cumbersome to keep track of all of the fields that are in the document up to that point. I imagine the stage `$addFields` was created from this same reasoning (using a $project and repeating everything all over again is tedious).
Is there a solution?