【问题标题】:Aggregate trunc date function?聚合截断日期函数?
【发布时间】:2018-06-29 07:31:57
【问题描述】:

我之前使用过一堆 SQL 数据库;像 Postgres 和 BigQuery 一样,它们具有日期截断功能(例如:date_truncTIMESTAMP_TRUNC )。

不知道mongodb有没有DATE_TRUNC函数?

我找到了$trunc 运算符,但它仅适用于数字。

我想要一个 DATE_TRUNC 函数将给定的日期(其他 SQL 数据库中的时间戳类型)截断到特定的边界,例如年初、月初、小时的开始,可能可以通过获取它的年、月、日、小时。

有人有一些解决方法吗?特别是对于 WEEK 的开始时刻和 ISOWEEK 的开始,有没有人有好的解决方法?

【问题讨论】:

    标签: mongodb date aggregation-framework truncate


    【解决方案1】:

    Mongo 5 开始,$dateTrunc 运算符已满足您的愿望。

    例如,将日期截断为年份:

    // { date: ISODate("2021-12-05T13:20:56Z") }
    // { date: ISODate("2019-04-27T05:00:32Z") }
    db.collection.aggregate([
      { $project: { year: { $dateTrunc: { date: "$date", unit: "year" } } } }
    ])
    // { year: ISODate("2021-01-01T00:00:00Z") }
    // { year: ISODate("2019-01-01T00:00:00Z") }
    

    您可以使用unit 参数在不同的单位级别(年、月、日、小时、...甚至季度)截断。对于不同单位倍数的给定单位(例如 3 年、6 个月……),使用 binSize 参数。

    您还可以指定星期的开始日期:

    // { date: ISODate("2021-12-05T13:20:56Z") } <= Sunday
    // { date: ISODate("2021-12-06T05:00:32Z") } <= Monday
    db.collection.aggregate([
      { $project: {
        week: { $dateTrunc: { date: "$date", unit: "week", startOfWeek: "monday" } }
      }}
    ])
    // { week: ISODate("2021-11-29T00:00:00Z") }
    // { week: ISODate("2021-12-06T00:00:00Z") }
    

    【讨论】:

      【解决方案2】:

      可以组合$dateToParts$dateFromParts

      小时分钟

      db.getCollection("data").aggregate([
        {"$addFields": {
           "dateVarFull": {"$dateToParts": {date: {"$toDate" : "2020-08-27T13:00:00Z"}}}
        }},
        {"$addFields": {
          "dateVarTrunc": { "$dateFromParts": {
            'year': "$dateVarFull.year",
            'month': "$dateVarFull.month",
            'day': "$dateVarFull.day"
          }}
        }}
      ])
      

      结果:

      {
          "dateVarFull" : {
              "year" : NumberInt(2020), 
              "month" : NumberInt(8), 
              "day" : NumberInt(27), 
              "hour" : NumberInt(13), 
              "minute" : NumberInt(0), 
              "second" : NumberInt(0), 
              "millisecond" : NumberInt(0)
          }, 
          "dateVarTrunc" : ISODate("2020-08-27T00:00:00.000+0000")
      }
      

      trunc 使用iso8601: true 参数:

      db.getCollection("data").aggregate([
        {"$addFields": {
          "dateVarFull": {
             "$dateToParts": {
               date: {"$toDate" : "2020-08-27T13:00:00Z"},
               iso8601: true
             }
          }
        }},
        {"$addFields": {
          "dateVarTrunc": { "$dateFromParts": {
            'isoWeekYear': "$dateVarFull.isoWeekYear",
            'isoWeek': "$dateVarFull.isoWeek",
            'isoDayOfWeek': 1
          }}
        }}
      ])
      

      结果:

      {
          "dateVarFull" : {
              "isoWeekYear" : NumberInt(2020), 
              "isoWeek" : NumberInt(35), 
              "isoDayOfWeek" : NumberInt(4), 
              "hour" : NumberInt(13), 
              "minute" : NumberInt(0), 
              "second" : NumberInt(0), 
              "millisecond" : NumberInt(0)
          }, 
          "dateVarTrunc" : ISODate("2020-08-24T00:00:00.000+0000")
      }
      

      【讨论】:

        【解决方案3】:

        可以使用 $dateFromParts 函数将日期截断为 iso 周:

        例如

        db.dd.aggregate(
          {
            $dateFromParts: {
              isoWeekYear: { $isoWeekYear: "$date" },
              isoWeek: { $isoWeek: "$date" }
            }
          }
        )
        

        对于Fri, 22 Jun 2018 20:46:50 UTC +00:00,它返回Fri, 18 Jun 2018 00:00:00 UTC +00:00

        要截断为小时、日、月等。使用$dateFromString$dateToString 更容易。以下示例将日期截断为小时:

        db.dd.aggregate(
          {
            $dateFromString: {
              dateString: { 
                $dateToString: {
                  format: '%Y-%m-%dT%H:00:00+00:00',
                  date: '$date'
                }
              }
            }
          }
        )
        

        【讨论】:

          【解决方案4】:

          可以通过对日期或时间戳字段进行算术来获得 ISO 周的开始,这里的开始是 Monday (1),结束是 Sunday (7)

          db.dd.aggregate(
              [
                  {
                      $addFields : { 
                          startOfWeek : 1, // Monday
                          currentDayOfWeek : {$dayOfWeek : "$date"}, 
                          daysToMinus : { $subtract : [{$dayOfWeek : "$date"} , 1] },
                          startOfThisWeek : { $subtract : [ "$date", {$multiply : [{ $subtract : [{$dayOfWeek : "$date"} , 1 ] }, 24, 60, 60, 1000 ]  } ] }
                      }
                  }
              ]
          ).pretty()
          

          文档

          > db.dd.find()
          { "_id" : ObjectId("5a62e2697702c6be61d672f4"), "date" : ISODate("2018-01-20T06:32:09.157Z") }
          

          一周开始

          {
              "_id" : ObjectId("5a62e2697702c6be61d672f4"),
              "date" : ISODate("2018-01-20T06:32:09.157Z"),
              "startOfWeek" : 1,
              "currentDayOfWeek" : 7,
              "daysToMinus" : 6,
              "startOfThisWeek" : ISODate("2018-01-14T06:32:09.157Z")
          }
          > 
          

          【讨论】:

          • 这可能是现有 mongodb 最简单的答案;也够复杂~不知道这样的 date_trunc 是不是很受欢迎,有人可以请求一个功能并可能在未来的版本中实现?
          • OP 要求 DateTrunc 到本周开始应该有类似 ISODate("2018-01-14T00:00:00.000Z");用你的方法肯定是可行的,只是会更复杂。
          猜你喜欢
          • 2018-08-23
          • 2013-06-04
          • 2012-01-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-12
          • 1970-01-01
          • 2010-11-13
          相关资源
          最近更新 更多