【问题标题】:Group and count by month按月分组和计数
【发布时间】:2015-02-06 14:14:34
【问题描述】:

我有一个预订表,我想获得一个月内的预订数量,即按月分组。

我很困惑如何从日期中获取月份。

这是我的架构:

{
    "_id" : ObjectId("5485dd6af4708669af35ffe6"),
    "bookingid" : 1,
    "operatorid" : 1,
    ...,
    "bookingdatetime" : "2012-10-11T07:00:00Z"
}
{
    "_id" : ObjectId("5485dd6af4708669af35ffe7"),
    "bookingid" : 2,
    "operatorid" : 1,
    ...,
    "bookingdatetime" : "2014-07-26T05:00:00Z"
}
{
    "_id" : ObjectId("5485dd6af4708669af35ffe8"),
    "bookingid" : 3,
    "operatorid" : 2,
    ...,
    "bookingdatetime" : "2014-03-17T11:00:00Z"
}

这是我尝试过的:

db.booking.aggregate([
  { $group: {
    _id: new Date("$bookingdatetime").getMonth(),
    numberofbookings: { $sum: 1 }
  }}
])

但它会返回:

{ "_id" : NaN, "numberofbookings" : 3 }

我哪里错了?

【问题讨论】:

  • "bookingdatetime" : "2012-10-11T07:00:00Z" 是字符串吗?
  • 您需要将其转换为 ISODate,然后对其进行聚合。否则,使用 Map/Reduce
  • 使用 new Date($bookingdate) 将字符串转换为 ISODate。 new Date("2012-10-11T07:00:00Z") 这是输出 ISODate("2012-10-11T07:00:00Z")
  • 我有这个查询:db.booking.aggregate({$project: {month:{$month:new Date("$bookingdatetime")}}}, {$group:{_id: {month:"$month"},numberofbookings:{$sum:1}}} 但它给了我错误的输出:{ "_id" : { "month" : 8 }, "numberofbookings" : 3 }

标签: mongodb date grouping


【解决方案1】:

如果您打算按月分组,即使您的数据跨越多年,您也可以使用$dateFromString$dateToString 的组合(以便将日期格式化为"%Y-%m"(例如2012-10)) :

// { date: "2012-10-11T07:00:00Z" }
// { date: "2012-10-23T18:30:00Z" }
// { date: "2012-11-02T21:30:00Z" }
// { date: "2013-01-11T18:30:00Z" }
// { date: "2013-10-07T14:15:00Z" }
db.bookings.aggregate([
  { $group: {
    _id: {
      $dateToString: {
        date: { $dateFromString: { dateString: "$date" } },
        format: "%Y-%m"
      }
    },
    bookings: { $count: {} } // or { $sum: 1 } prior to Mongo 5
  }}
])
// { _id: "2012-10", bookings: 2 }
// { _id: "2012-11", bookings: 1 }
// { _id: "2013-01", bookings: 1 }
// { _id: "2013-10", bookings: 1 }

这个:

  • 首先将字符串日期转换为字符串:$dateFromString: { dateString: "$date" }
  • 为了将日期格式化为%Y-%m$dateToString: { date: { }, format: "%Y-%m" }
  • 其中的组合($dateFromString/$dateToString)用作我们的组密钥
  • 最后我们用$count(或{ $sum: 1 }Mongo 5之前)计算我们的分组预订

【讨论】:

    【解决方案2】:

    Mongo 4 开始,您可以使用$toDate 运算符将您的字符串转换为日期(基于Will Shaver 给出的answer):

    // { date: "2012-10-11T07:00:00Z" }
    // { date: "2012-10-23T18:30:00Z" }
    // { date: "2012-11-02T21:30:00Z" }
    db.bookings.aggregate([
      { $group: {
        _id: { month: { $month: { $toDate: "$date" } } },
        bookings: { $sum: 1 }
      }}
    ])
    // { "_id" : { "month" : 10 }, "bookings" : 2 }
    // { "_id" : { "month" : 11 }, "bookings" : 1 }
    

    【讨论】:

      【解决方案3】:

      您需要在您的组中使用 $month 关键字。您的new Date().getMonth() 调用只会发生一次,并会尝试从字符串“$bookingdatetime”中创建一个月。

      db.booking.aggregate([
          {$group: {
              _id: {$month: "$bookingdatetime"}, 
              numberofbookings: {$sum: 1} 
          }}
      ]);
      

      【讨论】:

      • 它给出了这个异常“异常:无法从 BSON 类型字符串转换为日期”
      • 我没有看到您将日期存储为字符串。这让您望而却步,没有简单的方法可以在聚合语句中进行此转换。已经有很多人在 SO 上遇到过这个问题:stackoverflow.com/questions/15473772/…
      • @user1584253 可能忘记了字段前导“$”(例如 $bookingdatetime)
      • 我猜 OP 不希望所有 10 月都合并到同一个组中,所以我建议 _id: { month: { $month: "$bookingdatetime" }, year: { $year: "$bookingdatetime" } }
      • 查询错误会导致MongoError: FieldPath field names may not start with '$'。请参阅 Eb Staubs 评论以获得正确的解决方案。
      【解决方案4】:

      您不能在聚合管道中包含任意 JavaScript,因此因为您将 bookingdatetime 存储为 string 而不是 Date,所以您不能使用 $month 运算符。

      但是,由于您的日期字符串遵循严格的格式,您可以使用$substr 运算符从字符串中提取月份值:

      db.test.aggregate([
          {$group: {
              _id: {$substr: ['$bookingdatetime', 5, 2]}, 
              numberofbookings: {$sum: 1}
          }}
      ])
      

      输出:

      {
          "result" : [ 
              {
                  "_id" : "03",
                  "numberofbookings" : 1
              }, 
              {
                  "_id" : "07",
                  "numberofbookings" : 1
              }, 
              {
                  "_id" : "10",
                  "numberofbookings" : 1
              }
          ],
          "ok" : 1
      }
      

      【讨论】:

      • @JohnnyHK ,有一个与此相同的问题。但不同的是,如果某个月份没有值,它需要返回 0 .....请检查这个并给出答案。 stackoverflow.com/questions/38437797/…
      • 假设您只对当前年份感兴趣,这不会在年份结束后立即崩溃吗?
      • @colefner 如果需要,您可以在管道的开头添加一个$match 阶段以将文档过滤到当前年份。
      猜你喜欢
      • 2017-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 2015-08-03
      相关资源
      最近更新 更多