具有缺失键的Mongo组命令

时间:2014-07-27 09:56:54

标签: mongodb

我使用Mongo作为tick数据库,我定期将数据放入时间戳。后来我使用这些数据来查询有关系统的统计信息。

我有一个这样的集合:

[
  {
    "date": ISODate("2014-06-16T03:58:58.152Z"),
    "min": 4,
    "max": 25
  },
  {
    "date": ISODate("2014-06-18T03:48:58.152Z"),
    "min": 3,
    "max": 8
  },
  {
    "date": ISODate("2014-06-18T03:48:58.152Z"),
    "min": 8,
    "max": 12
  }
]

我使用group命令按天分组所有文档。

db.stats.group({
    keyf: function (doc) {
        var date = new Date(doc.date);
        date.setHours(0, 0, 0, 0);    
        return { date: date };
    },
    reduce: function (curr, result) {
        result.max = Math.max(curr.max, result.max || 0);
        result.min = Math.min(curr.min, result.min || 0);
    },
    initial: {}
});

这给了我以下结果:

[
  {
    "date": ISODate("2014-06-16T21:00:00.000Z"),
    "min": 4,
    "max": 25
  },
  {
    "date": ISODate("2014-06-18T21:00:00.000Z"),
    "min": 3,
    "max": 12
  }
]

对我来说问题是,由于2014-06-17没有条目,我需要以某种方式创建一个min: 0 and max: 0,所以结果应该是这样的:

[
  {
    "date": ISODate("2014-06-16T21:00:00.000Z"),
    "min": 4,
    "max": 25
  },
  {
    "date": ISODate("2014-06-17T21:00:00.000Z"),
    "min": 0,
    "max": 0
  },
  {
    "date": ISODate("2014-06-18T21:00:00.000Z"),
    "min": 3,
    "max": 12
  }
]

如果我能以某种方式给group命令一个日期数组作为键可以工作。如果这可能在Mongo?

1 个答案:

答案 0 :(得分:2)

  

如果我能以某种方式给group命令一个日期数组作为键可以工作。如果这可能在Mongo?

您不能group()关于不存在的数据,因此您必须每天插入一些数据。

有效的方法是pre-aggregating data,以确保您的日期范围内每天都有样本。

  

对我来说问题是,由于2014-06-17没有参赛作品,我需要以某种方式创建一个min:0和max:0

鉴于您正在计算每日最小值/最大值,一种方法是利用MongoDB 2.6中的新$min$max更新运算符,并将值插入到每日集合中。

由于您只希望每天获得混合/最大值,因此每日文档可能如下所示:

{
    "date": "yyyy-mm-dd",
    "min": 0,
    "max": 0
}

注意:为了清楚起见,我选择将日期格式化为ISO8601-ish字符串,但是如果您愿意,也可以使用Date字段。完整的BSON Date将包含时间和时区信息,因此您需要确保截断您的查询/更新。

以下是mongo shell中使用Moment.js(方便日期操作)迭代一系列日期并添加任何缺失值的示例:

var nextDate = moment("2014-06-01");
var lastDate = moment("2014-07-31");

while (nextDate <= lastDate) { 
    db.daily.findAndModify({
        query: {
            date: nextDate.format("YYYY-MM-DD")
        },
        update: {
            $max: { min: 0, max: 0 },
        },
        upsert: true
    });
    nextDate.add(1, 'day');
}

由于这是使用upsert标志:

  • 将插入不存在的每日文档,默认最小值/最大值为0
  • 现有的每日文档将使用现有minmax字段值的$ max进行更新(即现有值将被保留)

您可以使用相同的更新查询来维护每日最小值/最大值,因为插入了新数据(通过提供最小值/最大值而不是0的当前值),或者计算每日最小值/最大值使用排序顺序的原始样本:

// Daily minimum for 2014-06-18 (assuming ticks are using standard `Date` fields)
db.ticks.find(

    // query criteria
    { date: { $gte: ISODate("2014-06-18"), $lt: ISODate("2014-06-19") } },

    // projection
    { min: 1, _id: 0 }

).sort({min: 1}).limit(1)

// Daily maximum for 2014-06-18
db.ticks.find(

    // query criteria
    { date: { $gte: ISODate("2014-06-18"), $lt: ISODate("2014-06-19") } },

    // projection
    { max: 1, _id: 0 }

).sort({max: -1}).limit(1)