Reputation: 901
I am trying to convert a mapReduce into an aggregation.
I'm stuck because I have multiple calls to emit() in the map() function. I don't see how I can do it with $group.
Here's the mapReduce:
function map() {
const dateHour = this.createdAt.toISOString().substr(0, 13);
const value = {
orders: 1,
amount: this.amount
};
emit({
date: dateHour,
type: 'global',
granularity: 'hour'
}, value);
if (this.companyId) {
emit({
date: dateHour,
type: 'company',
companyId: this.companyId,
granularity: 'hour'
}, value);
}
if (this.employeeId) {
emit({
date: dateHour,
type: 'employee',
employeeId: this.employeeId,
granularity: 'hour'
}, value);
}
emit({
date: dateHour,
type: 'kitchen',
kitchenId: this.kitchenId,
granularity: 'hour'
}, value);
}
function reduce(key, values) {
return values.reduce((accumulator, value) => ({
orders: accumulator.orders + value.orders,
amount: accumulator.amount + value.amount,
}), {
orders: 0,
amount: 0
});
}
Sample input:
{
"_id" : ObjectId("586f5fc7f3f3bf001178e2bf"),
"deliverySlotStart" : ISODate("2017-01-06T11:00:00.000+0000"),
"amount" : 11.0
"kitchenId" : ObjectId("5858154c3aa80f1120c78c08"),
"createdAt" : ISODate("2017-01-06T09:13:43.354+0000"),
}
{
"_id" : ObjectId("586f657ef3f3bf001178e2c0"),
"deliverySlotStart" : ISODate("2017-01-06T11:00:00.000+0000"),
"amount" : 11.0
"kitchenId" : ObjectId("5858154c3aa80f1120c78c08"),
"createdAt" : ISODate("2017-01-06T09:38:06.174+0000"),
"employeeId" : ObjectId("58948c82f1efa800115a484e"),
"companyId" : ObjectId("5891d7dd50b5e76b7733f27b")
}
Output:
{
"_id" : {
"date" : "2017-01-06T09",
"type" : "company",
"companyId" : ObjectId("5891d7dd50b5e76b7733f27b"),
"granularity" : "hour"
},
"value" : {
"orders" : 1.0,
"amount" : 11.0
}
}
{
"_id" : {
"date" : "2017-01-06T09",
"type" : "employee",
"employeeId" : ObjectId("58948c82f1efa800115a484e"),
"granularity" : "hour"
},
"value" : {
"orders" : 1.0,
"amount" : 11.0
}
}
{
"_id" : {
"date" : "2017-01-06T09",
"type" : "global",
"granularity" : "hour"
},
"value" : {
"orders" : 2.0,
"amount" : 22.0
}
}
{
"_id" : {
"date" : "2017-01-06T09",
"type" : "kitchen",
"kitchenId" : ObjectId("5858154c3aa80f1120c78c08"),
"granularity" : "hour"
},
"value" : {
"orders" : 2.0,
"amount" : 22.0
}
}
Upvotes: 2
Views: 294
Reputation: 42342
There are two ways to do what you are trying to do in aggregation in 3.4. One approach is to use the $facet
stage which allows you to split the pipeline into multiple parallel stages, the other way by creating an array field and then unwinding it, so that you can group by various combination of types and values.
Using $facet
:
db.coll.aggregate([
{$addFields:{dh:{$dateToString:{format:"%Y-%m-%dT%H",date:"$createdAt"}}}},
{$facet:{
global:[
{$group:{_id:{dateHour:"$dh", type: "global"},
orders:{$sum:1},
amount:{$sum:"$amount"}}} ],
company:[
{$group:{_id:{dateHour:"$dh", type: "company"},
orders:{$sum:{$cond:[{$eq:[{$ifNull:["$companyId",null]},null]},0,1]}},
amount:{$sum:{$cond:[{$eq:[{$ifNull:["$companyId",null]},null]},0,"$amount"]}} }} ],
employee:[
{$group:{_id:{dateHour:"$dh", type: "employee"},
orders:{$sum:{$cond:[{$eq:[{$ifNull:["$employeeId",null]},null]},0,1]}},
amount:{$sum:{$cond:[{$eq:[{$ifNull:["$employeeId",null]},null]},0,"$amount"]}} }} ],
kitchen:[
{$group:{_id:{dateHour:"$dh", type: "kitchen"},
orders:{$sum:{$cond:[{$eq:[{$ifNull:["$kitchenId",null]},null]},0,1]}},
amount:{$sum:{$cond:[{$eq:[{$ifNull:["$kitchenId",null]},null]},0,"$amount"]}} }} ]
}}
])
Generating array:
db.coll.aggregate([
{$addFields:{
a:{$let:{
vars: {dh:{$dateToString:{format:"%Y-%m-%dT%H",date:"$createdAt"}}},
in: {
dateHour:"$$dh",
t:[
{type:"global"},
{$cond:[{$eq:[{$ifNull:["$companyId",null]},null]}, "", {type:"company", companyId:"$companyId"}]},
{$cond:[{$eq:[{$ifNull:["$employeeId",null]},null]}, "", {type:"employee", employeeId:"$employeeId"}]},
{$cond:[{$eq:[{$ifNull:["$kitchenId",null]},null]}, "", {type:"kitchen", kitchenId:"$kitchenId"}]}
]
}}}}},
{$unwind:"$a.t"},
{$match:{"a.t":{$ne:""}}},
{$group:{
_id:{date:"$a.dateHour", type:"$a.t.type", companyId:"$a.t.companyId", kitchenId:"$a.t.kitchenId", employeeId:"$a.t.employeeId", granularity:"hour"},
orders:{$sum:1},
amount:{$sum:"$amount"}}},
{$sort:{"_id.type":1}}
])
Upvotes: 3