MongoDB聚合运算符:$dateSubtract

文章目录

$dateSubtract聚合运算符将Date()对象按指定的时间单位递减。从版本5.0开始支持。

语法

js 复制代码
{
   $dateSubtract: {
      startDate: <Expression>,
      unit: <Expression>,
      amount: <Expression>,
      timezone: <tzExpression>
   }
}

返回一个日期对象Date()startDate可以是任何能被解析为日期、时间戳或对象Id的表达式,这三种类型都会返回Date()对象。

参数字段说明:

|字段|是否必须|描述|

|-|-|

|startDate|是|开始日期(UTC),可以是日期、时间戳或对象Id表达式|

|unit|是|要增加的时间的单位,单位可以是能被解析为下列值的表达式:yearquarterweekmonthdayhourminutesecondmillisecond|

|amount|是|以units为单位,在startDate基础上的增量,amount是可以被解析为整数、小数或双精度数的表达式|

|timezone|否|执行操作的时区,<tzExpression>必须是能被解析为奥尔森时区标识符格式的字符串或UTC偏移量,如果timezone不指定,返回值显示为UTC|

使用

时间测量

MongoDB遵循流行的数据库用法,以UTC为时间单位工作。dateSubtract表达式总是以 UTC 为起始日期,并以 UTC 为结果返回。如果指定了时区,计算将使用指定的时区进行。当计算涉及夏令时(DST)时,时区尤为重要。

如果单位是一个月或更大,操作会根据该月的最后一天进行调整。例如,在 10 月的最后一天增加一个月,这就是 "月末最后一天 "调整。

js 复制代码
{
   $dateSubtract:
      {
         startDate: ISODate("2021-03-31T12:10:05Z"),
         unit: "month",
         amount: 1
      }
}

注意:返回的日期ISODate("2020-11-30T12:10:05Z")是30日,而不是 31日,因为11月的天数比10月少。

时区

<timezone>字段中使用 Olson 时区标识符时,MongoDB 会应用 DST 偏移(如果适用于指定的时区)。

例如,包含以下文件的sales集合:

json 复制代码
{
   "_id" : 1,
   "item" : "abc",
   "price" : 20,
   "quantity" : 5,
   "date" : ISODate("2017-05-20T10:24:51.303Z")
}

下面的聚合说明了 MongoDB 如何处理 Olson 时区标识符的 DST 偏移量。示例使用$hour$minute操作符返回日期字段的相应部分:

js 复制代码
db.sales.aggregate([
{
   $project: {
      "nycHour": {
         $hour: { date: "$date", timezone: "-05:00" }
       },
       "nycMinute": {
          $minute: { date: "$date", timezone: "-05:00" }
       },
       "gmtHour": {
          $hour: { date: "$date", timezone: "GMT" }
       },
       "gmtMinute": {
          $minute: { date: "$date", timezone: "GMT" } },
       "nycOlsonHour": {
          $hour: { date: "$date", timezone: "America/New_York" }
       },
       "nycOlsonMinute": {
          $minute: { date: "$date", timezone: "America/New_York" }
       }
   }
}])

操作返回以下结果:

json 复制代码
{
   "_id": 1,
   "nycHour" : 5,
   "nycMinute" : 24,
   "gmtHour" : 10,
   "gmtMinute" : 24,
   "nycOlsonHour" : 6,
   "nycOlsonMinute" : 24
}

举例

减去固定的数量

下面是一组系统连接时间:

js 复制代码
db.connectionTime.insertMany(
  [
     {
        custId: 457,
        login: ISODate("2020-12-25T19:04:00"),
        logout: ISODate("2020-12-28T09:04:00")
     },
     {
        custId: 457,
        login: ISODate("2021-01-27T05:12:00"),
        logout: ISODate("2021-01-28T13:05:00")
     },
     {
        custId: 458,
        login: ISODate("2021-01-22T06:27:00"),
        logout: ISODate("2021-01-31T11:00:00")
     },
     {
        custId: 459,
        login: ISODate("2021-02-14T20:14:00"),
        logout: ISODate("2021-02-17T16:05:00")
     },
     {
        custId: 460,
        login: ISODate("2021-02-26T02:44:00"),
        logout: ISODate("2021-02-18T14:13:00")
        }
  ]
)

由于服务问题,需要从2021年1月的每个注销时间中减去3个小时,可以在聚合管道中使用$dateSubtract来递减注销时间。

js 复制代码
db.connectionTime.aggregate(
   [
      {
         $match:
            {
               $expr:
                  {
                     $eq:
                        [
                            { $year: "$logout" },
                              2021
                        ]
                  },
               $expr:
                  {
                     $eq:
                        [
                            { $month: "$logout" },
                              1
                        ]
                   }
             }
       },
       {
          $project:
             {
                logoutTime:
                   {
                      $dateSubtract:
                         {
                            startDate: "$logout",
                            unit: "hour",
                            amount: 3
                         }
                   }
              }
        },
        {
           $merge: "connectionTime"
        }
   ]
)

$match阶段进行了两次类似的比较,首先,$year$month操作符分别从logoutTime日期对象中提取年份和月份;然后检查月份和年份是否与选择目标相匹配;由于"January"被编码为"1",因此当年和月等于($eq)"2021"和 "1"时,$expr为真。

$project阶段使用$dateSubtract从每个选定文档的logoutTime中减去3个小时。

最后,$merge阶段会更新集合,为修改后的文档写入新的logoutTime

**注意:**与$out不同,$merge阶段只更新匹配的文档,并保留集合的其他部分。

结果文档如下:

json 复制代码
{
 "_id" : ObjectId("603dd94b044b995ad331c0b5"),
 "custId" : 457,
 "login" : ISODate("2020-12-25T19:04:00Z"),
 "logout" : ISODate("2020-12-28T09:04:00Z")
}
{
 "_id" : ObjectId("603dd94b044b995ad331c0b6"),
 "custId" : 457,
 "login" : ISODate("2021-01-27T05:12:00Z"),
 "logout" : ISODate("2021-01-28T13:05:00Z"),
 "logoutTime" : ISODate("2021-01-28T10:05:00Z")
}
{
 "_id" : ObjectId("603dd94b044b995ad331c0b7"),
 "custId" : 458,
 "login" : ISODate("2021-01-22T06:27:00Z"),
 "logout" : ISODate("2021-01-31T11:00:00Z"),
 "logoutTime" : ISODate("2021-01-31T08:00:00Z")
}
{
 "_id" : ObjectId("603dd94b044b995ad331c0b8"),
 "custId" : 459,
 "login" : ISODate("2021-02-14T20:14:00Z"),
 "logout" : ISODate("2021-02-17T16:05:00Z")
}
{
 "_id" : ObjectId("603dd94b044b995ad331c0b9"),
 "custId" : 460,
 "login" : ISODate("2021-02-26T02:44:00Z"),
 "logout" : ISODate("2021-02-18T14:13:00Z")
}

根据相对日期筛选

假如要向过去一周内使用过服务的客户发送调查问卷,$dateSubtract表达式可以创建一个相对于查询执行时间的范围过滤器。:

js 复制代码
db.connectionTime.aggregate(
   [
      {
         $match:
            {
               $expr:
                  {
                     $gt:
                        [
                           "$logoutTime",
                            {
                               $dateSubtract:
                                  {
                                     startDate: "$$NOW",
                                     unit: "week",
                                     amount: 1
                                  }
                            }
                        ]
                  }
             }
      },
      {
         $project:
            {
               _id: 0,
               custId: 1,
               loggedOut:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d",
                           date: "$logoutTime"
                        }
                  }
            }
      }
   ]
)

内置聚合变量$$NOW返回ISODate格式的当前日期时间,$match阶段使用$$NOW中的值获取今天的日期;然后,比较表达式 ($expr) 使用大于 ($gt)和$dateSubtract过滤集合,以匹配注销时间在过去一周内的文档。

$project阶段使用$dateToString表达式将日期转换为更易读的格式,如果不进行转换,MongoDB将以ISODate格式返回日期,输出结果显示上周有两名客户注销。

json 复制代码
{ "custId" : 459, "loggedOut" : "2021-02-17" }
{ "custId" : 460, "loggedOut" : "2021-02-18" }

调整夏令时

所有日期在内部都以UTC时间存储。如果指定了时区,$dateSubtract将使用当地时间进行计算。计算结果以UTC显示。

本例假如客户分布在多个时区,按天或按小时计费,现在要了解夏令时对计费期的影响。

创建billing集合:

js 复制代码
db.billing.insertMany(
   [
      {
         location: "America/New_York",
         login: ISODate("2021-03-13T10:00:00-0500"),
         logout: ISODate("2021-03-14T18:00:00-0500")
      },
      {
         location: "America/Mexico_City",
         login: ISODate("2021-03-13T10:00:00-00:00"),
         logout: ISODate("2021-03-14T08:00:00-0500")
      }
   ]
)

首先从每个文件的login日期中减去 1 天,然后再减去 24 小时。

js 复制代码
db.billing.aggregate(
   [
      {
         $project:
            {
               _id: 0,
               location: 1,
               start:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d %H:%M",
                           date: "$login"
                        }
                  },
               days:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d %H:%M",
                           date:
                              {
                                 $dateSubtract:
                                    {
                                       startDate: "$login",
                                       unit: "day",
                                       amount: 1,
                                       timezone: "$location"
                                    }
                              }
                        }
                  },
               hours:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d %H:%M",
                           date:
                              {
                                 $dateSubtract:
                                 {
                                    startDate: "$login",
                                    unit: "hour",
                                    amount: 24,
                                    timezone: "$location"
                                 }
                              }
                        }
                  },
               startTZInfo:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d %H:%M",
                           date: "$login",
                           timezone: "$location"
                        }
                  },
               daysTZInfo:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d %H:%M",
                           date:
                              {
                                 $dateSubtract:
                                    {
                                       startDate: "$login",
                                       unit: "day",
                                       amount: 1,
                                       timezone: "$location"
                                    }
                              },
                           timezone: "$location"
                        }
                  },
               hoursTZInfo:
                  {
                     $dateToString:
                        {
                           format: "%Y-%m-%d %H:%M",
                           date:
                              {
                                 $dateSubtract:
                                    {
                                       startDate: "$login",
                                       unit: "hour",
                                       amount: 24,
                                       timezone: "$location"
                                    }
                              },
                           timezone: "$location"
                        }
                  },
            }
      }
   ]
).pretty()

$dateToString表达式对输出进行了重新格式化,以提高可读性。结果汇总如下:

|字段|纽约|墨西哥城|

|-|-|

|start|2021-03-14 15:00|2021-03-14 15:00|

|Start, TZ Info|2021-03-14 11:00|2021-03-14 04:00|

|1 Day|2021-03-13 16:00|2021-03-13 15:00|

|1 Day, TZ Info|2021-03-13 11:00|2021-03-13 09:00|

|24 Hours|2021-03-13 15:00|2021-03-13 15:00|

|24 Hours, TZ Info|2021-03-13 10:00|2021-03-13 09:00|

上表强调了几点:

  • 未格式化的日期以 UTC 返回。纽约的$login是 UTC-5,但开始、天数和小时行显示的时间是 UTC 时间。

  • 3月14日是纽约的夏令时开始日,但不是墨西哥的夏令时开始日。当某地切换到DST并从某一天跨入下一天时,计算的时间会进行调整。

  • 夏令时改变的是一天的长度,而不是小时。夏令时不会改变小时数。只有当测量单位为日或更大且计算跨越指定时区的时钟变化时,才会对 DST 进行调整。

相关推荐
老邓计算机毕设1 分钟前
SSM智慧社区家政服务系统80q7o(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架
松涛和鸣1 小时前
72、IMX6ULL驱动实战:设备树(DTS/DTB)+ GPIO子系统+Platform总线
linux·服务器·arm开发·数据库·单片机
likangbinlxa1 小时前
【Oracle11g SQL详解】UPDATE 和 DELETE 操作的正确使用
数据库·sql
r i c k2 小时前
数据库系统学习笔记
数据库·笔记·学习
野犬寒鸦2 小时前
从零起步学习JVM || 第一章:类加载器与双亲委派机制模型详解
java·jvm·数据库·后端·学习
IvorySQL3 小时前
PostgreSQL 分区表的 ALTER TABLE 语句执行机制解析
数据库·postgresql·开源
·云扬·3 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
IT邦德3 小时前
Oracle 26ai DataGuard 搭建(RAC到单机)
数据库·oracle
惊讶的猫3 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
不爱缺氧i3 小时前
完全卸载MariaDB
数据库·mariadb