Mongo / Mongoose – 按date汇总

我有一个mongo / mongoose模式,当查询重新生成文件如

{ "_id" : ObjectId("5907a5850b459d4fdcdf49ac"), "amount" : -33.3, "name" : "RINGGO", "method" : "VIS", "date" : ISODate("2017-04-26T23:00:00Z"), "importDate" : ISODate("2017-05-01T21:15:49.581Z"), "category" : "Not Set", "__v" : 0 } { "_id" : ObjectId("5907a5850b459d4fdcdf49ba"), "amount" : -61.3, "name" : "Amazon", "method" : "VIS", "date" : ISODate("2017-03-23T00:00:00Z"), "importDate" : ISODate("2017-05-01T21:15:49.592Z"), "category" : "Not Set", "__v" : 0 } { "_id" : ObjectId("5907a5850b459d4fdcdf49ce"), "amount" : -3.3, "name" : "Tesco", "method" : "VIS", "date" : ISODate("2017-03-15T00:00:00Z"), "importDate" : ISODate("2017-05-01T21:15:49.601Z"), "category" : "Not Set", "__v" : 0 } { "_id" : ObjectId("5907a5850b459d4fdcdf49cc"), "amount" : -26.3, "name" : "RINGGO", "method" : "VIS", "date" : ISODate("2017-03-16T00:00:00Z"), "importDate" : ISODate("2017-05-01T21:15:49.600Z"), "category" : "Not Set", "__v" : 0 } { "_id" : ObjectId("5907a5850b459d4fdcdf49f7"), "amount" : -63.3, "name" : "Sky", "method" : "VIS", "date" : ISODate("2017-03-02T00:00:00Z"), "importDate" : ISODate("2017-05-01T21:15:49.617Z"), "category" : "Not Set", "__v" : 0 } { "_id" : ObjectId("5907a5850b459d4fdcdf49be"), "amount" : -3.3, "name" : "RINGGO", "method" : "VIS", "date" : ISODate("2017-03-22T00:00:00Z"), "importDate" : ISODate("2017-05-01T21:15:49.593Z"), "category" : "Not Set", "__v" : 0 } 

我想写一个查询,提供每个供应商( "name" : "Amazon" )的年度,月度和周花费,例如供应商RINGGO:

  • 2017年有三项花费33.3 + 26.3 + 3.3,所以每年花费总额为59.9
  • 在2017-03两个月有两笔支出,总额为26.3 + 3.3,所以每月总额为26.6
  • 每个花费在不同的星期,所以每周总数将是(例如)wk12 26.3,wk13 3.3,wk 15 33.3

我可以写一个查询如

 db.statements.aggregate( [ { $group : { _id : "$name", amount: { $push: "$amount" } } } ] ) 

这将按供应商名称汇总所有花费( amount ),但是我不确定如何按照上述说明按年,月,周如何分解。

编辑回应评论我不完全确定的结果可能有的形状,但理想情况下,它将是如下所示:

我需要年,月,周等,这样的查询可以由url驱动(如domain.com/vendorname/2017 / domain.com/vendorname/2017/3 domain.com/vendorname/2017domain.com/vendorname/2017/3 / domain.com/vendorname/2017/3/12 domain.com/vendorname/2017domain.com/vendorname/2017/3 / domain.com/vendorname/2017/3/12

我也想每个年度/月/周的个人花费和总花费,因为我想打印这些页面。

 { "_id" : { "year" : 2017, "month" : 3, "week" : 12 }, "name": "RINGGO", //vendor name "YearlySpends":[ 33.3, 26.3, 3.3] "totalYearlylyAmount" : [ 59.9] "MonthlySpends":[ 26.3, 3.3] "totalMonthlyAmount" : [ 26.6] "WeeklylySpends":[ 3.3] "totalWeeklylyAmount" : [3.3] } 

一个好的办法是将总pipe道分成几个步骤,目的是计算每个小组的总量,即每年,每月和每周的总量。

我已经做了一个微弱的尝试,以产生上述pipe道,但不知道如果这是你以后,但可以给你一些导致解决scheme,更好,但最好的一个。 也许别人可以给出更好的答案。

考虑以下未经testing的pipe道:

 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" } } } ]) 

示例输出

 /* 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 } 

UPDATE

如果你想在集合操作中包含filter,那么我build议你使用$match查询作为第一个stream水线阶段。 但是,如果有初始$match步骤,则前面的步骤会稍微改变,因为您将汇总筛选结果,这与将所有文档整体汇总为最初的整体结果非常不同,然后对结果应用筛选。


如果要采用filter-first-then-aggregate路由,请考虑运行一个使用$match的集合操作,作为按供应商过滤文档的第一步,然后是前一个$redactpipe道步骤,以进一步过滤当月的文档部分date字段,然后剩下的将是$group阶段:

 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); }) 

如果要采用组先过滤的filter路由,那么filter将在最后一个给出分组结果的pipe道之后应用到不同的字段上,如同文档一样,stream的一部分将与原始模式不同。

此路由不是高性能的,因为您正在开始集合中所有文档的聚合操作,然后进行过滤:

 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); }) 

对于多个datefilter参数, $redact操作符将会是

 { "$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" ] } }