Добавление недостающих дат в агрегацию MongoDB - PullRequest
1 голос
/ 29 мая 2020

В этой задаче я хочу получить подписку пользователя на следующий временной диапазон

  • Сегодня
  • На этой неделе
  • В этом месяце
  • В этом году

Для этого временного диапазона я хочу отобразить $group всех возможных дат.

[
  { Dates: '2020-05-21', Count: 3, AverageIncome: 30 },
  { Dates: '2020-05-22', Count: 10, AverageIncome: 30 },
  { Dates: '2020-05-24', Count: 1, AverageIncome: 37 },
  { Dates: '2020-05-25', Count: 1, AverageIncome: 30 },
  { Dates: '2020-05-26', Count: 2, AverageIncome: 65 }
]

Я также хочу добавить недостающие даты с данными аккумулятора, установленными на 0. Например, это должно быть

[
  { Dates: '2020-05-21', Count: 3, AverageIncome: 30 },
  { Dates: '2020-05-22', Count: 10, AverageIncome: 30 },
  { Dates: '2020-05-23', Count: 0, AverageIncome: 0 },
  { Dates: '2020-05-24', Count: 1, AverageIncome: 37 },
  { Dates: '2020-05-25', Count: 1, AverageIncome: 30 },
  { Dates: '2020-05-26', Count: 2, AverageIncome: 65 },
  { Dates: '2020-05-27', Count: 0, AverageIncome: 0 },
  { Dates: '2020-05-28', Count: 2, AverageIncome: 0 },
]

PS: В это время UT C время будет 28 мая, 23:45

Стадии конвейера агрегации, которые я использую

const $agg = await Subscription.aggregate([
    {
      $match: {
        createdOn: {
          $gte: moment(req.query.from).toDate(),
          $lte: moment(req.query.to).toDate()
        },
        isCancelled: false
      }
    },

    {
      $group: {
        _id: {
          $dateToString: {
            format: "%Y-%m-%d",
            date: "$createdOn"
          }
        },
        count: { $sum: 1 },
        avgAmount: { $avg: "$amountPaid" }
      }
    },
    {
      $project: {
        _id: false,
        sortKey: {
          $dateFromString: {
            dateString: "$_id"
          }
        },
        Dates: "$_id",
        Count: "$count",
        AverageIncome: "$avgAmount"
      }
    },
    {
      $sort: {
        sortKey: 1
      }
    },
    {
      $project: {
        sortKey: false
      }
    }
  ]);

1 Ответ

1 голос
/ 29 мая 2020

Некоторое время назад я столкнулся с подобной проблемой, и вот подход, которому я следовал:

function fillEmptyDates($agg, $defaultData, from, to) {
  const $aggObj = {}; //Taking this as an object for easy checking

  $agg.map((agg) => {
    $aggObj[agg.Date] = agg; //Making an object from array entries with Date as key
  });

  const $loopDate = moment(from);
  const $endDate = moment(to);

  //Starting from from date to to date, checking if any date does not have entry in $aggObje
  while ($endDate.isSameOrAfter($loopDate)) {
    let $aggDate = $loopDate.format("YYYY-MM-DD");

    if (!$aggObj.hasOwnProperty($aggDate)) {
      //If any date does not have entry, creating a new entry from default aggregates and giving it the date as current date
      $aggObj[$aggDate] = {
        ...$defaultData,
        Date: $aggDate
      };
    }
    $loopDate.add(1, "day"); //Incrementing aggregate date
  }

  //Converting back to array and sorting by date
  return Object.values($aggObj).sort((a, b) =>
    moment(a.Date).isBefore(moment(b.Date)) ? -1 : 1
  );
};

//You can call this function like so
$agg = fillEmptyDates(
            $agg, 
            { Count: 0, AverageIncome: 0 },
            req.query.from,
            req.query.to,
        )
...