我的文档是这样的:
{
category: "1",
timestamp: ISODate("2016-07-16T00:00:00.000Z"),
amount: 0
},
{
category: "1",
timestamp: ISODate("2016-08-18T00:00:00.000Z"),
amount: 15
},
{
category: "1",
timestamp: ISODate("2016-08-01T00:00:00.000Z"),
amount: 5
},
{
category: "2",
timestamp: ISODate("2016-08-18T00:00:00.000Z"),
amount: 10
}
现在我想首先按类别分组(已经工作):
{ "$match" : { "timestamp" : { "$gt" : FROM , "$lt" : TO }}},
{ "$sort" : { "timestamp" : 1 }},
{ "$group" : {
"_id" : "$category",
"data" : { "$push" : { "timestamp" : "$timestamp" , "amount" : "$amount" }}
}}
和然后将这些对象分组在data
数组内。获取每周(或每月-取决于用户输入)的最大金额。
结果应该是这样的(按月分组):
{
_id: "1",
data: [
{
timestamp: "2016-07", // could also be an ISODate with
amount: 0 // first (or last) day of month
}, // if that makes things easier
{
timestamp: "2016-08",
amount: 15
}
]
},
{
_id: "2",
data: [
{
timestamp: "2016-08",
amount: 10
}
]
}
我试图unwind
的data
数组,然后再次分组,但这导致了一个完全混乱。
希望你有一些好主意/解决方案来让它工作。
编辑:附加问题:
我已经把一个索引在category
上,它对$match
很好。将索引放在timestamp
上进行排序(因为插入顺序可能与时间戳顺序不同)是否也有用,或者该索引在聚合中不会有任何影响?
我接受了Styvane的回答(再次感谢!)并简化了一下:
{$match: { timestamp: { $gt: FROM , $lt: TO }}},
{$group: {
_id: {
id: "$category",
timestamp: { $concat: [
{ $toLower: { $year:"$timestamp" } },
"-",
{ $toLower: { $month: "$timestamp" } }
] }
},
amount: { $max: "$amount" }
}},
{$sort: { "_id.timestamp": 1 } },
{$group: {
_id: "$_id.id",
data: { $push: { timestamp: "$_id.timestamp", amount: "$amount" } }
}}
我试图在第一个$group
之前$sort
,但这确实有时会产生意想不到的结果。虽然我只是把$sort
放在$group
阶段之间。这样,在timestamp
上有一个索引就不再重要了。
在$sort
阶段之后,您需要$group
按"category"然后$unwind
按"data"字段。
var group1 = { "$group": {
"_id": "$category",
"data": {
"$push": {
"timestamp": "$timestamp",
"amount": "$amount"
}
}
}};
var unwind = { "$unwind": "$data"};
从那里,您需要重新$group
您的文档,但这次您不仅需要考虑timestamp
字段,还需要考虑_id
字段,并且在$toLower
操作符的帮助下,您可以将年和月值转换为字符串,您可以使用$concat
操作符将其连接起来。
还返回$sum
组的和。
var group2 = { "$group": {
"_id": {
"id": "$_id",
"timestamp": {
"$concat": [
{ "$toLower": { "$year": "$data.timestamp" } },
"-",
{ "$toLower": { "$month": "$data.timestamp" } }
]
}},
"amount": { "$sum": "$data.amount" }
}}
最后一个阶段是另一个$group
阶段,在这个阶段中,您只需按之前的_id.id
值对文档进行分组,并使用$push
累加运算符返回数据数组。
var group3 = { "$group": {
"_id": "$_id.id",
"data": {
"$push": {
"timestamp": "$_id.timestamp",
"amount": "$amount"
}
}
}};
最终的管道看起来像这样:
db.collection.aggregate(
[
// $match and `$sort here
group1,
unwind,
group2,
group3
]
)
这个查询可以在即将到来的MongoDB版本中使用$facet
操作符进行改进。
db.collection.aggregate([
// $match and `$sort here
{ "$facet": { "data": [ group1, unwind, group2, group3 ] }
])