先按类别分组,然后按周或月分组



我的文档是这样的:

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

我试图unwinddata数组,然后再次分组,但这导致了一个完全混乱。

希望你有一些好主意/解决方案来让它工作。

编辑:附加问题:

我已经把一个索引在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 ] }
])

相关内容

  • 没有找到相关文章

最新更新