MongoDB 按日期和状态双 $group 聚合

MongoDB double $group aggregation by date and status

我有几个文件看起来像这样(减去许多其他不相关的字段):

  [{
    status: 'open',
    createdDate: 2021-06-17T09:02:58.325Z
  },
  {
    status: 'declined',
    createdDate: 2021-07-25T09:09:15.851Z
  },
  {
    status: 'declined',
    createdDate: 2021-09-22T09:32:14.958Z
  },
  {
    status: 'open',
    createdDate: 2021-09-02T09:45:26.584Z
  },
  {
    status: 'referral',
    createdDate: 2021-09-05T09:46:02.764Z
  }]

对于集合的这个子组,我想汇总下一个结果:

{
    "2021-06" : { submitted: 1, referral: 0, declined: 0},
    "2021-07" : { submitted: 1, referral: 0, declined: 1},
    "2021-08" : { submitted: 0, referral: 0, declined: 0},
    "2021-09" : { submitted: 3, referral: 1, declined: 1},
}

已提交的文件总数(打开、推荐和拒绝)。 我尝试以多种方式使用 $group,但没有成功。 有什么建议么? 谢谢!

查询

  • 按日期分组,仅包含年份和月份
  • 计数 3 个累加器,第一个总是添加,第二个和第三个仅在看到状态打开(第二个)和状态(拒绝)第三个时才添加
  • 将根和数组替换为对象,使数据成为键,并将数据作为嵌套文档,就像您预期的输出一样

Test code here

aggregate(
[{"$group":
  {"_id":{"$dateToString":{"date":"$createdDate", "format":"%Y-%m"}},
   "submitted":{"$sum":1},
   "referral":{"$sum":{"$cond":[{"$eq":["$status", "open"]}, 1, 0]}},
   "declined":
   {"$sum":{"$cond":[{"$eq":["$status", "declined"]}, 1, 0]}}}},
 {"$replaceRoot":
  {"newRoot":
   {"$arrayToObject":
    [[{"k":"$_id",
       "v":
       {"submitted":"$submitted",
        "referral":"$referral",
        "declined":"$declined"}}]]}}}])

OP 要求一个解决方案,其中值成为键,例如2021-06: {...。本着良好数据设计的精神,我可能会建议一个更简单的管道,将日期值保留为一个值:

db.foo.aggregate([
    // The SAME as answer above!
    {$group: {
        "_id":{"$dateToString":{"date":"$createdDate", "format":"%Y-%m"}},
        "submitted":{"$sum":1},
        "referral":{"$sum":{"$cond":[{"$eq":["$status", "open"]}, 1, 0]}},
        "declined":{"$sum":{"$cond":[{"$eq":["$status", "declined"]}, 1, 0]}}
    }}
]);

屈服

{ "_id" : "2021-06", "submitted" : 1, "referral" : 1, "declined" : 0 }
{ "_id" : "2021-07", "submitted" : 1, "referral" : 0, "declined" : 1 }
{ "_id" : "2021-09", "submitted" : 3, "referral" : 1, "declined" : 1 }

现在您可以排序、过滤等,而无需通过 $objectToArray 等将键转换为 rvals。如果完全有必要使用比 _id 更具描述性的键名(而不是通过保留 _id) 造成混淆,然后附加这些阶段:

    ,{$addFields: {"date":"$_id"}}
    ,{$unset: "_id"}