简介

在MongoDB中,聚合(aggregate)主要用于进行处理数据,比如统计求和,求平均数等,并返回计算后的数据结果,这给我们带来了很多便捷之处,因为可以在读取数据的同时进行数据处理。

场景介绍

在日常开发环境中经常涉及到数据报表,需要根据年月日对数据进行统计,下面我将简单介绍如何根据日期(yyyy-MM-dd)对营业额进行分组,并计算其当天的收入。

数据结构

Order表数据如下:

{
    "_id" : ObjectId("578c7cf8c2b8941e375a1178"),
    "price" : 10,
    "shop" : ObjectId("57751e70d35e816989656eb3"),
    "created_at" : ISODate("2016-07-01T06:53:44.148Z"),

},
{
    "_id" : ObjectId("578c7cf8c2b8941e375a2345"),
    "price" : 20,
    "shop" : ObjectId("57751e70d35e816989656eb3"),
    "created_at" : ISODate("2016-07-01T07:50:24.242Z"),

},
{
    "_id" : ObjectId("5778d62630cc9e92a723c370"),
    "price" : 5,
    "shop" : ObjectId("57751e70d35e816989656eb3"),
    "created_at" : ISODate("2016-07-02T09:08:54.656Z"),
}

可以看出上面数据有两条2016-07-01以及一条2016-07-02,现在让我们来聚合出我们想要的结果。

代码实现

var shop = req.shop; //店铺数据
//聚合
Order.aggregate([
    {
        $match: {
        "shop": shop._id //获取shop字段为shop._id,如同find
        }
     },
     {
        $project : {
            day : {$substr: [{"$add":["$created_at", 28800000]}, 0, 10] },//时区数据校准,8小时换算成毫秒数为8*60*60*1000=288000后分割成YYYY-MM-DD日期格式便于分组
            "price": 1 //设置原有price字段可用,用于计算总价
        },
     },
     {
        $group: {
            _id:"$day", //将_id设置为day数据
            totalPrice:{$sum: "$price"}, //统计price
     }
     },
     {
        $sort: {_id: 1}//根据date排序
     }
]).exec(function (err, turnover){//返回结果
    console.log(turnover);
});

代码解读

  1. $match用于匹配满足条件的文档,如同find函数。

  2. $project用于指示字段是否输出以及字段输出控制。

  3. $substr$add,一个是分割操作,另一个相加操作。不难发现我们的原始数据created_at字段是具体到秒,因此如果想根据日期进行分割的话,那么需要将created_at分割成我们想要的日期格式,这其中需要特别注意的是mongoodb存储的数据是按照世界时存储的,因此进行分割操作时候需要对时间进行时区校正,因此需使用$add加上时区差8小时(毫秒数)才能得到正确的数据,最后一步便是利用$group进行分组了。

    细心的小伙伴可以会发现aggregate自带日期操作$year,$month,$dayOfMonth用于获取年,月,日,会想着通过这三个参数来拼装成yyyy-MM-dd日期格式,可惜,fidding之前也是这么操作的,只是最后发现appregate并解析不了,故在此使用了$substr分割方法。

  4. $group分组以及统计,其中_id对应值便是我们所需分组的字段数据,totalPrice则是用$sum对同组数据的字段price进行求和,并将结果存放于totalPrice中。

  5. $sum字段求和。

  6. $sort排序。

结果返回

上面聚合结果turnover的值如下

[
    {_id: "2016-07-01", totalPrice: 30},
    {_id: "2016-07-02", totalPrice: 5}
]

正如我们所需要的,我们得到了按日期分组而成的当天收入总价。

happy coding!