GroupBy MongoDB中的多个列

时间:2017-01-24 03:14:21

标签: mongodb mongodb-query aggregation-framework

我有一个shifts集合,如下所示

{
    "_id" : ObjectId("5885a1108c2fc432d649647d"),
    "from" : ISODate("2017-01-24T06:21:00.000Z"), //can be weekday, sat, sun
    "to" : ISODate("2017-01-24T08:21:00.000Z"),
    "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
    "hourlyRate" : 32 //this wil vary based on **from** field
}

{
    "_id" : ObjectId("5885a1108c2fc432d649647e"),
    "from" : ISODate("2017-01-25T06:21:00.000Z"),
    "to" : ISODate("2017-01-25T08:21:00.000Z"),
    "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
    "hourlyRate" : 32
}

{
    "_id" : ObjectId("5885a1108c2fc432d649647f"),
    "from" : ISODate("2017-01-26T06:21:00.000Z"),
    "to" : ISODate("2017-01-26T08:21:00.000Z"),
    "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
    "hourlyRate" : 32
}

我希望能够生成像这样的输出

作业A(可以使用jobId获得)

  • 平日收入 - $ 50(2 * $ 25)
  • 周六收入 - $ 90(3 * $ 30)
  • 周日收入 - 100美元(2 * 50美元)总收入= 240美元

工作B

  • 平日收入..............

总收入=工作A +工作B ..... = $ X

工作日或周六或周日可以从from字段派生。对于工作日,需要对周一至周五的所有日期进行分组。 可以通过从from中减去to来获得小时数。

所以,我认为需要完成以下工作,但我无法在MongoDB中实现

  • jobId分组,从from(也包括所有工作日组)中逐日分组,并通过减去fromto获得提取的小时数。
  • 最后,获取上述所有金额的总和以获得总收入
  • 获取所有工作收入的最终总和。

我发现了一些类似的问题,但我无法将其应用到我的情况中 mongodb group values by multiple fields
Mongodb Aggregation Framework | Group over multiple values?
How to group by multiple columns and multiple values in mongodb

使用案例:
当用户访问名为“收入”的页面时,我需要向他显示每个作业和总收入的上周收入(然后他可以更改日期范围)。所以,我打算将节目分成每个工作,进一步分为工作日,周六和周日收入以及 THAT 工作的总收入和工作时间。最终总收入是所有个人工作收入的总和。

P.S我正在使用MongoDB 3.4

2 个答案:

答案 0 :(得分:1)

按照以下聚合查询:

db.shifts.aggregate([{ 
    //this get the day of week and converts them into sunday, saturday
    $project: {
        jobId:1,
        hourlyRate:1, 
        dayOfWeek: { $dayOfWeek: "$from" }, 
        workedHours: {$divide:[{ $subtract: ["$to", "$from"] }, 3600000]}, 
        saturday:{$floor: {$divide:[{ $dayOfWeek: "$from" }, 7]}},
        sunday:{$floor: {$divide:[{$abs:{$subtract:[{ $dayOfWeek: "$from" }, 7]}}, 6]}},
    }
}, {
    //based on the values of sunday and saturday gets the value of weekday
    $project: {
        jobId:1,
        workedHours:1,
        hourlyRate:1,
        saturday:1,
        sunday: 1,
        weekday:{$abs: {$add:["$sunday","$saturday", -1]}},
    } 
}, {
    //here calculates the earnings for each job
    $group:{
        _id:"$jobId",
        sundayEarnings:{$sum: {$multiply:["$sunday", "$hourlyRate", "$workedHours"]}},
        saturdayEarnings:{$sum: {$multiply:["$saturday", "$hourlyRate", "$workedHours"]}},
        weekdayEarnings:{$sum: {$multiply:["$weekday", "$hourlyRate", "$workedHours"]}},
        totalEarnings: {$sum:{$multiply:["$hourlyRate", "$workedHours"]}},
        totalWorkedHours: {$sum: "$workedHours"}
    }
}, {
    //and finally calculates the total jobs earnings
    $group:{
        _id:null,
        jobs:{$push:{
            jobId: "$_id",
            sundayEarnings: "$sundayEarnings",
            saturdayEarnings: "$saturdayEarnings",
            weekdayEarnings: "$weekdayEarnings",
            totalEarnings: "$totalEarnings",
            totalWorkedHours: "$totalWorkedHours"
        }},
        totalJobsEarning: {$sum: "$totalEarnings"}
    }
}])
  1. 第一个$project聚合通过进行多次算术计算,根据saturday值为sundaydayOfWeek提供 0或1
  2. 第二个$project聚合根据weekdaysaturday值计算sunday的值。
  3. 第一个$group计算每项工作中每天的收入。
  4. 最后,第二个$group聚合计算所有工作的收入总和。
  5. <强>测试

    这是我的意见:

    {
        "_id" : ObjectId("5885a1108c2fc432d649647d"),
        "from" : ISODate("2017-01-24T06:21:00Z"),
        "to" : ISODate("2017-01-24T08:21:00Z"),
        "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
        "hourlyRate" : 32
    }
    {
        "_id" : ObjectId("5885a1108c2fc432d649647e"),
        "from" : ISODate("2017-01-25T06:21:00Z"),
        "to" : ISODate("2017-01-25T08:21:00Z"),
        "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
        "hourlyRate" : 32
    }
    {
        "_id" : ObjectId("5885a1108c2fc432d649647f"),
        "from" : ISODate("2017-01-26T06:21:00Z"),
        "to" : ISODate("2017-01-26T08:21:00Z"),
        "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
        "hourlyRate" : 32
    }
    {
        "_id" : ObjectId("58870cfd59dfb6b0c4eadd72"),
        "from" : ISODate("2017-01-28T06:21:00Z"),
        "to" : ISODate("2017-01-28T08:21:00Z"),
        "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
        "hourlyRate" : 32
    }
    {
        "_id" : ObjectId("58870dc659dfb6b0c4eadd73"),
        "from" : ISODate("2017-01-29T06:21:00Z"),
        "to" : ISODate("2017-01-29T08:21:00Z"),
        "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
        "hourlyRate" : 32
    }
    

    上述聚合查询提供以下输出:

    {
        "_id" : null,
        "jobs" : [
            {
                "jobId" : ObjectId("586d7d6acfc7e05669d6e2c8"),
                "sundayEarnings" : 64,
                "saturdayEarnings" : 64,
                "weekdayEarnings" : 192,
                "totalEarnings" : 320,
                "totalWorkedHours" : 10 
            }
        ],
        "totalJobsEarning" : 320
    }
    

    jobs数组中只有一个作业,因为shift集合的文档引用了相同的jobId。您可以使用不同的jobId来尝试此操作,它会为您提供总收入的不同工作。

答案 1 :(得分:0)

使用 $switch 运算符构建案例陈述的表达式来评估收入,您可以运行以下管道来获得所需的结果

var hoursWorked = { 
    "$divide": [
        { "$subtract": ["$to", "$from"] }, 
        3600000
    ]
};

db.collection('shifts').aggregate([
    {
        "$match": {
            "jobId": { "$in": [jobA_id, jobB_id] },
            "from": { "$gte": new Date() }
        }
    },
    {
        "$group": {
            "_id": null,
            "totalEarnings": {
                "$sum": {
                    "$switch": {
                        "branches": [
                            {
                                "case": { 
                                    "$not": { 
                                        "$in": [
                                            { "$dayOfWeek": "$from" }, 
                                            [1, 7] 
                                        ] 
                                    } 
                                }, 
                                "then": { "$multiply": [hoursWorked, 20] }
                            },
                            { 
                                "case": { 
                                    "$eq": [
                                        { "$dayOfWeek": "$from" }, 
                                        7
                                    ] 
                                }, 
                                "then": { "$multiply": [hoursWorked, 25] }
                            },
                            { 
                                "case": { 
                                    "$eq": [
                                        { "$dayOfWeek": "$from" }, 
                                        1 
                                    ] 
                                }, 
                                "then": { "$multiply": [hoursWorked, 30] } 
                            }
                        ]
                        "default": 0
                    }   
                } 
            }              
        }
    }       
], function(err, results) {
    if (err) throw err;
    console.log(JSON.stringify(results, null, 4));
    console.log(results[0].totalEarnings); 
});