Dobrým přístupem by bylo rozdělit agregát na několik kroků s cílem vypočítat agregáty pro každou skupinu, tj. roční, měsíční a týdenní agregáty.
Udělal jsem chabý pokus o generování uvedeného potrubí, ale nejsem si jistý, zda je to to, co hledáte, ale mohl by vám poskytnout nějaké vodítka k řešení, ještě lépe optimálnímu. Možná by někdo jiný mohl odpovědět lépe.
Zvažte následující netestováno potrubí:
db.statements.aggregate([
{
"$group": {
"_id": {
"name": "$name",
"year": { "$year": "$date" },
"month": { "$month": "$date" },
"week": { "$week": "$date" }
},
"total": { "$sum": "$amount" }
}
},
{
"$group": {
"_id": {
"name": "$_id.name",
"year": "$_id.year"
},
"YearlySpends": { "$push": "$total" },
"totalYearlyAmount": { "$sum": "$total" },
"data": { "$push": "$$ROOT" }
}
},
{ "$unwind": "$data" },
{
"$group": {
"_id": {
"name": "$_id.name",
"month": "$data._id.month"
},
"YearlySpends": { "$first": "$YearlySpends" },
"totalYearlyAmount": { "$first": "$totalYearlyAmount" },
"MonthlySpends": { "$push": "$data.total" },
"totalMonthlyAmount": { "$sum": "$data.total" },
"data": { "$push": "$data" }
}
},
{ "$unwind": "$data" },
{
"$group": {
"_id": {
"name": "$_id.name",
"week": "$data._id.week"
},
"YearlySpends": { "$first": "$YearlySpends" },
"totalYearlyAmount": { "$first": "$totalYearlyAmount" },
"MonthlySpends": { "$first": "$MonthlySpends" },
"totalMonthlyAmount": { "$first": "$totalMonthlyAmount" },
"WeeklySpends": { "$push": "$data.total" },
"totalWeeklyAmount": { "$sum": "$data.total" },
"data": { "$push": "$data" }
}
},
{ "$unwind": "$data" },
{
"$group": {
"_id": "$data._id",
"YearlySpends": { "$first": "$YearlySpends" },
"totalYearlyAmount": { "$first": "$totalYearlyAmount" },
"MonthlySpends": { "$first": "$MonthlySpends" },
"totalMonthlyAmount": { "$first": "$totalMonthlyAmount" },
"WeeklySpends": { "$first": "$WeeklySpends" },
"totalWeeklyAmount": { "$first": "$totalWeeklyAmount" }
}
}
])
Ukázkový výstup
/* 1 */
{
"_id" : {
"name" : "Tesco",
"year" : 2017,
"month" : 3,
"week" : 11
},
"YearlySpends" : [
-3.3
],
"totalYearlyAmount" : -3.3,
"MonthlySpends" : [
-3.3
],
"totalMonthlyAmount" : -3.3,
"WeeklySpends" : [
-3.3
],
"totalWeeklyAmount" : -3.3
}
/* 2 */
{
"_id" : {
"name" : "RINGGO",
"year" : 2017,
"month" : 4,
"week" : 17
},
"YearlySpends" : [
-3.3,
-26.3,
-33.3
],
"totalYearlyAmount" : -62.9,
"MonthlySpends" : [
-33.3
],
"totalMonthlyAmount" : -33.3,
"WeeklySpends" : [
-33.3
],
"totalWeeklyAmount" : -33.3
}
/* 3 */
{
"_id" : {
"name" : "RINGGO",
"year" : 2017,
"month" : 3,
"week" : 12
},
"YearlySpends" : [
-3.3,
-26.3,
-33.3
],
"totalYearlyAmount" : -62.9,
"MonthlySpends" : [
-3.3,
-26.3
],
"totalMonthlyAmount" : -29.6,
"WeeklySpends" : [
-3.3
],
"totalWeeklyAmount" : -3.3
}
/* 4 */
{
"_id" : {
"name" : "RINGGO",
"year" : 2017,
"month" : 3,
"week" : 11
},
"YearlySpends" : [
-3.3,
-26.3,
-33.3
],
"totalYearlyAmount" : -62.9,
"MonthlySpends" : [
-3.3,
-26.3
],
"totalMonthlyAmount" : -29.6,
"WeeklySpends" : [
-26.3
],
"totalWeeklyAmount" : -26.3
}
/* 5 */
{
"_id" : {
"name" : "Sky",
"year" : 2017,
"month" : 3,
"week" : 9
},
"YearlySpends" : [
-63.3
],
"totalYearlyAmount" : -63.3,
"MonthlySpends" : [
-63.3
],
"totalMonthlyAmount" : -63.3,
"WeeklySpends" : [
-63.3
],
"totalWeeklyAmount" : -63.3
}
/* 6 */
{
"_id" : {
"name" : "Amazon",
"year" : 2017,
"month" : 3,
"week" : 12
},
"YearlySpends" : [
-61.3
],
"totalYearlyAmount" : -61.3,
"MonthlySpends" : [
-61.3
],
"totalMonthlyAmount" : -61.3,
"WeeklySpends" : [
-61.3
],
"totalWeeklyAmount" : -61.3
}
AKTUALIZACE
Pokud chcete zahrnout filtry do agregované operace, pak bych vám doporučil použít $match
dotaz jako první fázi potrubí. Pokud však existuje počáteční $match
krok, pak by se předchozí kroky mírně změnily, protože budete agregovat filtrované výsledky, což se velmi liší od původně agregace všech dokumentů jako celku a následného použití filtru na výsledky.
Pokud chcete použít nejprve filtr, pak agregát cestě, zvažte spuštění agregované operace, která používá $match
jako první krok, který filtruje dokumenty podle dodavatele, pak předchozí $redact
krok kanálu k dalšímu filtrování dokumentů v měsíční části pole data a zbytek by pak byl $group
fáze:
Statements.aggregate([
{ "$match": { "name": req.params.vendor } },
{
"$redact": {
"$cond": [
{ "$eq": [{ "$month": "$date" }, parseInt(req.params.month) ]},
"$$KEEP",
"$$PRUNE"
]
}
},
.....
/*
add the remaining pipeline steps after
*/
], function(err, data){
if (err) throw err;
console.log(data);
})
Pokud chcete použít filtr nejprve skupiny route, pak by byl filtr za posledním kanálem, který poskytuje seskupený výsledek, ale aplikoval by se na různá pole, protože dokumenty v této části proudu by se lišily od původního schématu.
Tato trasa není výkonná, protože začínáte agregační operaci se všemi dokumenty v kolekci a poté filtrujete:
Statements.aggregate([
.....
/*
place the initial pipeline steps from
the original query above here
*/
.....
{
"$match": {
"_id.name": req.params.vendor,
"_id.month": parseInt(req.params.month)
}
}
], function(err, data){
if (err) throw err;
console.log(data);
})
Pro více parametrů filtru data je kód $redact
operátor by byl
{
"$redact": {
"$cond": [
{
"$and": [
{ "$eq": [{ "$year": "$date" }, parseInt(req.params.year) ]},
{ "$eq": [{ "$month": "$date" }, parseInt(req.params.month) ]},
{ "$eq": [{ "$week": "$date" }, parseInt(req.params.week) ]}
]
},
"$$KEEP",
"$$PRUNE"
]
}
}