Двойная агрегация $group в MongoDB по дате и статусу

У меня есть несколько документов, которые выглядят так (за исключением множества других нерелевантных полей):

        [{
    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 несколькими способами, но это не сработало. Какие-либо предложения? Спасибо!

2 ответа

ОП попросил решение, в котором значения становятся ключами, например 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 }

Теперь можно сортировать, фильтровать и т.д., не прибегая к превращению ключей в rval через $objectToArrayи т. д. Если совершенно необходимо иметь более описательное имя ключа, чем (и не вызывать путаницы, сохраняя _id), затем добавьте эти этапы:

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

Запрос

  • группировать по дате в виде строки только с годом и месяцем
  • считайте 3 аккумулятора, первый всегда добавляет, второй и третий добавляют только если видят статус открытый(второй) и статус(отклонен) третий
  • замените корень и массив на объект, чтобы сделать ключ данных и данные как вложенный документ, как в ожидаемом результате

Тестовый код здесь

      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"}}]]}}}])
Другие вопросы по тегам