需要在MongoDB集合中找到最大值及其时间戳。

3
考虑一个由以下形式的文档组成的集合:

{
"_id" : ObjectId("55f3600da9fb6e4f937a50a7"),
"timestamp" : ISODate("2010-01-01T08:10:00Z"),
"temperature" : 12.31
}
{
"_id" : ObjectId("55f3600da9fb6e4f937a50a8"),
"timestamp" : ISODate("2010-01-01T08:15:00Z"),
"temperature" : 12.48
}
...

我希望能够找到每天的最高温度,以及发生时间。使用管道聚合很容易实现第一部分:

[{"$group" : {"_id" : {"day": { "$dayOfYear": "$timestamp" }},
              "max_temperature": {"$max" : "$temperature"}}},
 {"$sort" : {"_id.day":1}}]

这为我提供了一个很好的结果集,其中包括每天的最高温度:
{u'max_temperature': 20.98, u'_id': {u'day': 1}}
{u'max_temperature': 24.15, u'_id': {u'day': 2}}
{u'max_temperature': 22.02, u'_id': {u'day': 3}}
...

但是,我如何获取每个日最大值发生的时间戳呢? 类似于:

{u'max_temperature': 20.98, u'time_of_max': ISODate("2010-01-01T15:11:12"), u'_id': {u'day': 1}}
{u'max_temperature': 24.15, u'time_of_max': ISODate("2010-01-02T16:03:42"), u'_id': {u'day': 2}}
{u'max_temperature': 22.02, u'time_of_max': ISODate("2010-01-03T16:33:59"), u'_id': {u'day': 3}}
...
2个回答

2

首先使用$sort,然后使用$first操作符代替$max。但需要注意的是,你需要先减少“day”细节:

[
    { "$project": {
        "day": { "$dayOfYear": "$timestamp" },
        "timestamp": 1,
        "temperature": 1
    }},
    { "$sort": { "day": 1, "temperature": -1 } },
    { "$group": {
        "_id" : "$day",
        "max_temperature": { "$first": "$temperature" },
        "timestamp": { "$first": "$timestamp" }
    }},
    { "$sort": { "_id":1 } }
]

一旦你有了“day”的预测值,就可以按照最大的“temperature”值对输入进行排序。然后$first分组操作符将从在分组边界上找到的“第一个”文档中选取字段。
因此,“temperature”是“最大值”,因为进行了排序,而其他字段将来自于发生该值的同一文档。
从技术上讲,这仍然有效:
[
    { "$sort": { "temperature": -1, "timestamp": 1 } },
    { "$group": {
        "_id" : { "$dayOfYear": "$timestamp" },
        "max_temperature": { "$first": "$temperature" },
        "timestamp": { "$first": "$timestamp" }
    }},
    { "$sort": { "_id":1 } }
]

但是,由于初始排序顺序实际上与分组键不匹配,因此整体效率可能会降低。


你的第一个建议绝对可行,而且是我没有想到的方法。使用原始方法查找每天的最高温度大约需要在我的 NUC 上花费 0.1 秒。使用你的方法添加时间需要大约 0.4 秒。很遗憾,没有一种保存满足 $max 聚合的文档的方法。 - TomK
@TomK 整个问题在于你所“请求的”是“匹配文档中的多个值”,这与 $max 的作用“相反”。 $max 运算符从分组条件“任何地方”找到“最大值”,但你需要“两个属性”出现在“最大值”分组边界处,这意味着必须进行 $sort。 这不是“我的”方法,这是唯一的方法,而没有保留单独的预聚合数据,我们通常出于性能原因而这样做。 - Blakes Seven

0
db.temp.aggregate([{$project:{date:{$dayOfYear:"$timestamp"},temp:"$temperature",ISODate:"$timestamp"}},{$group:{_id:"$date",maxtemp:{"$max":"$temp"},timestamp:{"$first":"$ISODate"}}},{"$sort":{_id:1}}])

如果我正确理解您的方法,这将返回每天的开始时间,而不是最高温度发生的时间。 - TomK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接