mongodb - 计算年、月、日的日期差异

标签 mongodb mongoose aggregation-framework

我有以下疑问:

db.getCollection('user').aggregate([
   {$unwind: "$education"},
   {$project: {
      duration: {"$divide":[{$subtract: ['$education.to', '$education.from'] }, 1000 * 60 * 60 * 24 * 365]}
   }},
   {$group: {
     _id: '$_id',
     "duration": {$sum: '$duration'}  
   }}]
])

以上查询结果为:

{
    "_id" : ObjectId("59fabb20d7905ef056f55ac1"),
    "duration" : 2.34794520547945
}

/* 2 */
{
    "_id" : ObjectId("59fab630203f02f035301fc3"),
    "duration" : 2.51232876712329
}

但我想要做的是以 year+ month + day 格式获取其持续时间,例如:2 y , 3 m, 20 d。 还有一点,如果类(class)正在进行中,to 字段为 null,而另一个字段 isGoingOn: true,所以这里我应该使用当前日期而不是计算持续时间to 字段。 并且用户有类(class)子文档数组

education: [
   {
      "courseName": "Java",
      "from" : ISODate("2010-12-08T00:00:00.000Z"),
      "to" : ISODate("2011-05-31T00:00:00.000Z"), 
      "isGoingOn": false
   },
   {
      "courseName": "PHP",
      "from" : ISODate("2013-12-08T00:00:00.000Z"),
      "to" : ISODate("2015-05-31T00:00:00.000Z"), 
      "isGoingOn": false
   },
   {
      "courseName": "Mysql",
      "from" : ISODate("2017-02-08T00:00:00.000Z"),
      "to" : null, 
      "isGoingOn": true
   }
]

另外一点是:该日期在一个子文档中可能与另一个子文档不连续。一个用户可能有一个类(class) 1 年,然后两年后,他/她开始了他/她的下一个类(class),为期 1 年零 3 个月(这意味着该用户总共有 2 年零 3 个月的类(class)时长) . 我想要的是获取educations数组中每个子文档的日期差异,并将它们相加。假设我的示例数据中 Java 类(class)时长是 6 个月 22 天,PHP 类(class)时长是 1 年 6 个月 22 天,最后一个是从 2017 年 2 月 8 日到现在,而且还在继续,所以我的教育时间是这些时间间隔的总和。

最佳答案

请尝试这种聚合得到日、月、年的日期差异,添加多个$addFields阶段计算并减少日期差异,月份范围没有下溢,这里假设是1个月= 30天

管道

db.edu.aggregate(
    [
        {
            $addFields : {
                trainingPeriod : {
                    $map : {
                        input : "$education",
                        as : "t",
                        in : {
                            year: {$subtract: [{$year : {$ifNull : ["$$t.to", new Date()]}}, {$year : "$$t.from"}]},
                            month: {$subtract: [{$month : {$ifNull : ["$$t.to", new Date()]}}, {$month : "$$t.from"}]},
                            dayOfMonth: {$subtract: [{$dayOfMonth : {$ifNull : ["$$t.to", new Date()]}}, {$dayOfMonth : "$$t.from"}]}
                        }
                    }
                }
            }
        },
        {
            $addFields : {
                trainingPeriod : {
                    $map : {
                        input : "$trainingPeriod",
                        as : "d",
                        in : {
                            year: "$$d.year",
                            month: {$cond : [{$lt : ["$$d.dayOfMonth", 0]}, {$subtract : ["$$d.month", 1]}, "$$d.month" ]},
                            day: {$cond : [{$lt : ["$$d.dayOfMonth", 0]}, {$add : [30, "$$d.dayOfMonth"]}, "$$d.dayOfMonth" ]}
                        }
                    }
                }
            }
        },
        {
            $addFields : {
                trainingPeriod : {
                    $map : {
                        input : "$trainingPeriod",
                        as : "d",
                        in : {
                            year: {$cond : [{$lt : ["$$d.month", 0]}, {$subtract : ["$$d.year", 1]}, "$$d.year" ]},
                            month: {$cond : [{$lt : ["$$d.month", 0]}, {$add : [12, "$$d.month"]}, "$$d.month" ]},
                            day: "$$d.day"
                        }
                    }
                }
            }
        },
        {
            $addFields : {
                total : {
                    $reduce : {
                        input : "$trainingPeriod",
                        initialValue : {year : 0, month : 0, day : 0},
                        in : {
                            year: {$add : ["$$this.year", "$$value.year"]},
                            month: {$add : ["$$this.month", "$$value.month"]},
                            day: {$add : ["$$this.day", "$$value.day"]}
                        }
                    }
                }
            }
        },
        {
            $addFields : {
                total : {
                    year : "$total.year",
                    month : {$add : ["$total.month", {$floor : {$divide : ["$total.day", 30]}}]},
                    day : {$mod : ["$total.day", 30]}
                }
            }
        },
        {
            $addFields : {
                total : {
                    year : {$add : ["$total.year", {$floor : {$divide : ["$total.month", 12]}}]},
                    month : {$mod : ["$total.month", 12]},
                    day : "$total.day"
                }
            }
        }
    ]
).pretty()

结果

{
    "_id" : ObjectId("5a895d4721cbd77dfe857f95"),
    "education" : [
        {
            "courseName" : "Java",
            "from" : ISODate("2010-12-08T00:00:00Z"),
            "to" : ISODate("2011-05-31T00:00:00Z"),
            "isGoingOn" : false
        },
        {
            "courseName" : "PHP",
            "from" : ISODate("2013-12-08T00:00:00Z"),
            "to" : ISODate("2015-05-31T00:00:00Z"),
            "isGoingOn" : false
        },
        {
            "courseName" : "Mysql",
            "from" : ISODate("2017-02-08T00:00:00Z"),
            "to" : null,
            "isGoingOn" : true
        }
    ],
    "trainingPeriod" : [
        {
            "year" : 0,
            "month" : 5,
            "day" : 23
        },
        {
            "year" : 1,
            "month" : 5,
            "day" : 23
        },
        {
            "year" : 1,
            "month" : 0,
            "day" : 10
        }
    ],
    "total" : {
        "year" : 2,
        "month" : 11,
        "day" : 26
    }
}
> 

关于mongodb - 计算年、月、日的日期差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47236343/

相关文章:

node.js - Postman 可以工作,但 Fetch 不能。我究竟做错了什么?

mongodb - 如何将本地Mongo数据库连接到docker

mongodb - 仅检索 MongoDB 集合中对象数组中的查询元素

MySQL 与 MongoDB 聚合性能对比

mongodb - 在聚合 $group 中使用 $map

mongodb - MongoDB 中的反向数组字段

javascript - 实例化导出类的属性时出错

node.js - forEach 完成后需要发送响应

javascript - createScript (vm.js :56:10) Node js when running gulp dev only on windows 处出现意外标记)

MongoDB updateMany,动态过滤字段