首页 > 解决方案 > 返回组,包括 MongoDB 聚合中的零条目

问题描述

那里的所有答案似乎都已经很老了,所以我会再次提出这个问题:

我正在尝试按日期对集合进行分组和计数,如下所示:

db.getCollection('mycollection').aggregate([
  {
    $group : {
               _id :{ $dateToString: { format: "%Y-%m-%d", date: "$timestamp"} },
               count: { $sum: 1 }
    }
  }
])

它工作得很好,即使在大型集合中,除了它不返回零计数的日期。今天解决这个问题的最佳做法是什么?有没有办法总体上做到这一点?

当然,我总是可以创建一个日期数组并逐个循环遍历它,但这在性能方面似乎相当昂贵,所以必须有更好的方法,对吧?

或者:将聚合与集合中从第一个到最后一个条目的所有日期匹配到一个数组,并添加那些不存在的?

标签: mongodb

解决方案


看来,在 MongoDB 中仍然没有办法做到这一点,所以这是我关于如何按天对集合进行分组、计算结果文档并重组输出以使其包含所有天的解决方案,包括计数为零的天。

从集合中获取您的数据:

db.getCollection('mycollection').aggregate([{
    $match : { 
       timestamp: {"$gte": new Date(2020, 1, 2), "$lt": new Date(2020, 2, 1)}}
   },
   {
    $group : {
       _id :{ $dateToString: { format: "%Y-%m-%d", date: "$timestamp"} },
      count: { $sum: 1 }
      }
}])

这将返回给定时间范围内(2020 年 2 月)计数高于零的所有天数(以下代码段中的 mongo_return)要重组数组,使其包含2 月的所有天数,您可以执行以下操作:

// MONGODB RESULT
var mongo_return = [
  {
    _id: "2020-02-02",
    count: 294.0
  },
  {
    _id: "2020-02-16",
    count: 243.0
  },
  {
    _id: "2020-02-18",
    count: 153.0
  }
];

// INSERT MONGODAYS INTO ARRAY
var mongoDays = mongo_return.map(function(item) {
  return item._id;
});

// SET TIMEFRAME
var start = new Date(2020, 1, 1);
var end = new Date(2020, 2, 0);

// PRODUCE ALL DAYS IN TIMEFRAME
for (var d = start; d <= end; d.setDate(d.getDate() + 1)) {
  let MyDate = new Date(d);
  let MyDateString =
    MyDate.getFullYear() +
    "-" +
    ("0" + (MyDate.getMonth() + 1)).slice(-2) +
    "-" +
    ("0" + MyDate.getDate()).slice(-2);
  // CHECK IF DAY IS IN MONGODAYS
  // AND ADD TO MONGORESULT WITH ZERO IF NOT
  if (!mongoDays.includes(MyDateString))
    mongo_return.push({ _id: MyDateString, count: 0 });
}

// ORDER ARRAY
const result = mongo_return.sort(function(a, b) {
  return a._id < b._id ? -1 : a._id > b._id ? 1 : 0;
});

console.log(result);

这肯定行得通,但会受到惩罚,数据集越大。如果您有更好的解决方案,请发布。但是:这与 js 无关,我知道上面的脚本可以缩短,但这不是我的问题。我想知道通过某种标志或选项或解决方法在 MongoDB 中是否以及如何实现这一点。

我不敢相信这应该是这么复杂。


推荐阅读