Mongo聚合 - 将分割间隔值转换为受影响的月份

时间:2018-02-23 12:25:22

标签: node.js mongodb mongodb-query aggregation-framework

我在mongo中有一个棘手的数据聚合,我不知道如何直接在mongo中实现它,而不需要以后的数据处理。

以下是我馆藏文件的简化示例

[
    {
        "from" : ISODate("2017-01-15T00:00:00.000Z"),
        "to" : ISODate("2017-02-15T00:00:00.000Z"),
        "value" : 1000
    },
    {
        "from" : ISODate("2017-02-01T00:00:00.000Z"),
        "to" : ISODate("2017-02-28T00:00:00.000Z"),
        "value" : 2000
    },
    {
        "from" : ISODate("2017-02-20T00:00:00.000Z"),
        "to" : ISODate("2017-03-14T00:00:00.000Z"),
        "value" : 1000
    }
]

不,我想获得属于特定月份的每月价值总和。

[
{janurary: 500}, /* 1/2 of interval id 1 is January so take half the value */
{february: 2833}, /* 500 + 2000 + 333 */
{march: 666}, /* 2/3 of interval id 3 is March */
]

计算必须精确,所以我不能简单地说所有月份都有30天。但我能做的是从间隔的每个月的代码中提供此信息。因此,应该可以提供此查询信息january2017 = 31 days, february2017 = 28 days, march2017 = 31 days

我知道我可以在我的node.js代码中执行此操作,但该DB中可能有很多文档,所以我宁愿不将所有这些文件提取到服务器来执行计算。

1 个答案:

答案 0 :(得分:0)

Pah,我希望其他人提出更好的答案,但这是实现目标的一种方式:

db.collection.aggregate({
    $addFields: {
        dayFrom: { $dayOfMonth: "$from" },
        dayTo: { $dayOfMonth: "$to" },
        monthFrom: { $month: "$from" },
        monthTo: { $month: "$to" },
        numberOfDays: { $subtract: [ { $dayOfMonth: "$to" }, { $dayOfMonth: "$from" } ] },
        numberOfMonths: { $subtract: [ { $month: "$to" }, { $month: "$from" } ] },
    }
}, {
    $addFields: {
        numberOfDaysInFromMonth: { $dayOfMonth: { $subtract: [ { $dateFromParts : { year: { $year: "$from" }, month: { $add: [ "$monthFrom", 1 ] }, day: 1  } }, 1 ] } },
    }
}, {
    $addFields: {
        numberOfDaysAccountingForFromMonth: { $subtract: [ { $add: [ "$numberOfDaysInFromMonth", 1 ] }, "$dayFrom" ] },
        numberOfDaysAccountingForToMonth: { $subtract: [ "$dayTo", 1 ] }, // assuming the "to" day does not count anymore
    }
}, {
    $addFields: {
        totalNumberOfDays: { $add: [ "$numberOfDaysAccountingForFromMonth", "$numberOfDaysAccountingForToMonth" ] }
    }
}, {
    $addFields: {
        percentageAccountingForFromMonth: { $divide: [ "$numberOfDaysAccountingForFromMonth", "$totalNumberOfDays" ] },
        percentageAccountingForToMonth: { $divide: [ "$numberOfDaysAccountingForToMonth", "$totalNumberOfDays" ] },
    }
}, {
    $facet: {
        "from": [{
            $group: {
                _id: "$monthFrom",
                sum: { $sum: { $multiply: [ "$value", "$percentageAccountingForFromMonth" ] } }
            }
        }],
        "to": [{
            $group: {
                _id: "$monthTo",
                sum: { $sum: { $multiply: [ "$value", "$percentageAccountingForToMonth" ] } }
            }
        }]
    }
}, {
    $project: {
        total: { $concatArrays: [ "$from", "$to" ] }
    }
}, {
    $unwind: "$total"
}, {
    $group: {
        _id: "$total._id",
        sum: { $sum: "$total.sum" }
    }
})

一些评论:

  1. 您需要优化它以匹配您的精确定义 什么构成日期范围的一部分以及如何计算天数 (“是2018-01-30到2018-01-31 一个天还是两个天?”。

  2. 您可以使用$let和美化该查询 一些筑巢。我认为使用后续的$addFields阶段会更容易让野兽更容易理解。

  3. 该代码不支持触及两个月以上的fromto值(例如2018-01-01至2018-03-01)。