在 MongoDB 中按年和月聚合查询

Posted

技术标签:

【中文标题】在 MongoDB 中按年和月聚合查询【英文标题】:Aggregate query by Year and Month in MongoDB 【发布时间】:2022-01-14 06:33:00 【问题描述】:

我正在使用 MongoDB 4.2。

我有以下集合和聚合查询,用于在日期范围内按年和月返回计数。

[
  
    "_id": ObjectId("60096afd7568e5a724966237"),
    "title": "One Hospitals",
    "createdAt": ISODate("2021-01-21T11:52:29.215Z")
  ,
  
    "_id": ObjectId("605492c632016f7bdd74c282"),
    "title": "Customer-5",
    "createdAt": ISODate("2021-07-19T12:02:14.134Z")
  ,
  
    "_id": ObjectId("60eeb32209434f1b24aff594"),
    "title": "UTR-007-HealthEndpoints",
    "createdAt": ISODate("2021-07-14T09:49:22.521Z")
  ,
  
    "_id": ObjectId("613a515cb24382575e7e766b"),
    "title": "UTR-004-005-Team",
    "createdAt": ISODate("2021-09-09T18:24:28.942Z")
  ,
  
    "_id": ObjectId("61605c5192467e75213a4374"),
    "title": "UTR-004-005-GC-Team",
    "createdAt": ISODate("2021-10-08T14:57:21.375Z")
  ,
  
    "_id": ObjectId("61826734c82e4e52c4663e1f"),
    "title": "Two Registry",
    "createdAt": ISODate("2021-11-03T10:40:52.611Z")
  ,
  
    "_id": ObjectId("61b090976a08dda345c15fb2"),
    "title": "Api Customer",
    "createdAt": ISODate("2021-12-08T11:01:43.011Z")
  
]

聚合查询如下。

db.collection.aggregate([
  
    $match: 
      $and: [
        
          "createdAt": 
            $gte: ISODate("2021-07-01")
          
        ,
        
          "createdAt": 
            $lte: ISODate("2021-12-31")
          
        
      ],
      
    
  ,
  
    $group: 
      _id: 
        $dateToString: 
          "date": "$createdAt",
          "format": "%Y-%m"
        
      ,
      Count: 
        $sum: 1
      ,
      
    
  
])

上述查询返回以下输出。

[
  
    "Count": 1,
    "_id": "2021-09"
  ,
  
    "Count": 1,
    "_id": "2021-12"
  ,
  
    "Count": 1,
    "_id": "2021-11"
  ,
  
    "Count": 2,
    "_id": "2021-07"
  ,
  
    "Count": 1,
    "_id": "2021-10"
  
]

谁能帮助实现以下目标?

    输入文档没有 2021-08 的任何数据,因此在匹配组中提到的日期范围内添加 0 个缺失月份 添加一个名为 TITLE 且 MMM YYYY 的新字段(如 2021 年 7 月) 根据上面的_id对输出进行排序

总的来说,预期的输出应该如下。

[
  
    "Count": 2,
    "_id": "2021-07",
    "Title": "Jul 2021"
  ,
  
    "Count": 0,
    "_id": "2021-08",
    "Title": "Aug 2021"
  ,
  
    "Count": 1,
    "_id": "2021-09",
    "Title": "Sep 2021"
  ,
  
    "Count": 1,
    "_id": "2021-10",
    "Title": "Oct 2021"
  ,  
    "Count": 1,
    "_id": "2021-11",
    "Title": "Nov 2021"
  ,  
  
    "Count": 1,
    "_id": "2021-12",
    "Title": "Dec 2021"
  
]

我这里有MongoDB Playground 供您参考。

非常感谢您的帮助。

【问题讨论】:

【参考方案1】:

我建议升级到 Mongo 5.0,它提供了一些不错的功能,$dateTrunc$dateAdd$dateDiff 在这种情况下非常有用。如果您没有它们可用,那么您需要处理毫秒、$dateFromParts$dateToParts。但它需要更多代码。

MongoDB 不支持本地化日期格式,例如月份名称。因此,您需要使用一些 javascript 库,例如moment.js

或者您可以使用 $switch 构建月份名称的查找

var ret = db.collection.aggregate([
    $match:  createdAt:  $gte: ISODate("2021-07-01"), $lte: ISODate("2021-12-31")   ,
   
      $group: 
         _id:  $dateTrunc:  date: "$createdAt", unit: "month"  ,
         Count:  $count:  
      
   ,
   
      $group: 
         _id: null,
         data:  $push: "$$ROOT" 
      
   ,
   
      $set: 
         month: 
            $dateDiff: 
               startDate: ISODate("2021-07-01"),
               endDate: ISODate("2021-12-31"),
               unit: "month"
            
         
      
   ,
   
      $set: 
         data: 
            $map: 
               input:  $range: [0,  $add: ["$month", 1] ] ,
               as: "m",
               in: 
                  $let: 
                     vars: 
                        month: 
                           $dateAdd: 
                              startDate: ISODate("2021-07-01"),
                              unit: "month",
                              amount: "$$m"
                           
                        
                     ,
                     in: 
                        _id: "$$month",
                        Count:  $filter:  input: "$data", cond:  $eq: ["$$this._id", "$$month"]   
                     
                  
               
            
         
      
   ,
   
      $set: 
         data: 
            $map: 
               input: "$data",
               in:  _id: "$$this._id", Count:  $ifNull: [ $first: "$$this.Count.Count" , 0]  
            
         
      
   ,
    $unwind: "$data" ,
    $replaceWith: "$data" ,
    $sort:  _id: 1  
]).toArray();

ret.forEach(function (doc)  doc.Title = moment(doc._id).format('MMM YYYY') )
print(tojsononeline(ret))

【讨论】:

以上是关于在 MongoDB 中按年和月聚合查询的主要内容,如果未能解决你的问题,请参考以下文章

我需要从结构为 yyyyMMddHHmmss 20170227141500 的时间戳中按年和月分组

如何在oracle sql developer中按年和月分组获取当前财政年度的数据?

如何使用 CouchDB 按年和月对条目进行分组?

按年和月分组并获得一个月的最小值,日期

python urllib Python模块:按年和月分列的ARIN PPML档案

如何使用 Flume 按年和月对 txt/csv 文件中的数据进行分区?是不是可以使 HDFS 路径动态化?