gpt4 book ai didi

MongoDB : Aggregation framework : Get last dated document per grouping ID

转载 作者:行者123 更新时间:2023-12-02 02:12:55 25 4
gpt4 key购买 nike

我想获取所有其他字段的每个站的最后一个文档:

{
"_id" : ObjectId("535f5d074f075c37fff4cc74"),
"station" : "OR",
"t" : 86,
"dt" : ISODate("2014-04-29T08:02:57.165Z")
}
{
"_id" : ObjectId("535f5d114f075c37fff4cc75"),
"station" : "OR",
"t" : 82,
"dt" : ISODate("2014-04-29T08:02:57.165Z")
}
{
"_id" : ObjectId("535f5d364f075c37fff4cc76"),
"station" : "WA",
"t" : 79,
"dt" : ISODate("2014-04-29T08:02:57.165Z")
}

我需要有 t 和 station 以获得每个站的最新 dt。
使用聚合框架:
db.temperature.aggregate([{$sort:{"dt":1}},{$group:{"_id":"$station", result:{$last:"$dt"}, t:{$last:"$t"}}}])

返回
{
"result" : [
{
"_id" : "WA",
"result" : ISODate("2014-04-29T08:02:57.165Z"),
"t" : 79
},
{
"_id" : "OR",
"result" : ISODate("2014-04-29T08:02:57.165Z"),
"t" : 82
}
],
"ok" : 1
}

这是最有效的方法吗?

谢谢

最佳答案

直接回答您的问题,是的,这是最有效的方法。但我确实认为我们需要澄清为什么会这样。

正如替代方案中所建议的那样,人们正在关注的一件事是在传递给 之前对您的结果进行“排序”。 $group stage 和他们正在查看的是“timestamp”值,因此您需要确保所有内容都按“timestamp”顺序排列,因此形式如下:

db.temperature.aggregate([
{ "$sort": { "station": 1, "dt": -1 } },
{ "$group": {
"_id": "$station",
"result": { "$first":"$dt"}, "t": {"$first":"$t"}
}}
])

如前所述,您当然需要一个索引来反射(reflect)这一点,以提高排序效率:

然而,这才是真正的重点。其他人似乎忽略了(如果您自己不是这样)是所有这些数据都可能被插入 已经按时间顺序,因为每个读数都被记录为添加的。

所以这个美丽的是 _id字段(默认为 ObjectId )已经按“时间戳”顺序排列,因为它本身实际上包含一个时间值,这使得语句成为可能:

db.temperature.aggregate([
{ "$group": {
"_id": "$station",
"result": { "$last":"$dt"}, "t": {"$last":"$t"}
}}
])

快点。为什么?好吧,您不需要选择索引(要调用的附加代码),除了文档之外,您也不需要“加载”索引。

我们已经知道文件是有序的(按 _id )所以 $last 边界是完全有效的。无论如何,您都在扫描所有内容,您还可以在 _id 上“范围”查询值在两个日期之间同样有效。

这里唯一要说的是,在“现实世界”的用法中,对您来说可能更实用 $match 在进行这种累积而不是获取“第一个”和“最后一个”时的日期范围之间 _id值来定义“范围”或在您的实际使用中类似的东西。

那么这个证据在哪里呢?嗯,它很容易重现,所以我只是通过生成一些示例数据来做到这一点:

var stations = [ 
"AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL",
"GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA",
"ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
"NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK",
"OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
"VA", "WA", "WV", "WI", "WY"
];


for ( i=0; i<200000; i++ ) {

var station = stations[Math.floor(Math.random()*stations.length)];
var t = Math.floor(Math.random() * ( 96 - 50 + 1 )) +50;
dt = new Date();

db.temperatures.insert({
station: station,
t: t,
dt: dt
});

}

在我的硬件上(8GB 笔记本电脑,带旋转磁盘,虽然不是很好,但肯定足够了),运行每种形式的语句清楚地显示了使用索引和排序(索引上的键与排序语句相同)的版本的明显停顿。这只是一个小小的停顿,但差异很大,足以引起注意。

即使查看解释输出(版本 2.6 及更高版本,或者实际上在 2.4.9 中虽然没有记录),您也可以看到其中的差异,尽管 $sort 由于存在索引而被优化掉,所花费的时间似乎与索引选择然后加载索引条目有关。包括“覆盖”索引查询的所有字段没有区别。

同样对于记录,纯粹索引日期并且仅对日期值进行排序会给出相同的结果。可能稍微快一点,但仍然比没有排序的自然索引形式慢。

所以只要你能愉快地“范围”在第一个和最后一个 _id值,那么确实在插入顺序上使用自然索引实际上是最有效的方法。您的现实世界里程可能会因这对您是否实用而有所不同,并且最终可能会更方便地在日期上实现索引和排序。

但是,如果您对使用 _id 感到满意范围或大于“最后一个” _id在您的查询中,然后可能进行一次调整以获取值以及您的结果,以便您实际上可以在连续查询中存储和使用该信息:

db.temperature.aggregate([
// Get documents "greater than" the "highest" _id value found last time
{ "$match": {
"_id": { "$gt": ObjectId("536076603e70a99790b7845d") }
}},

// Do the grouping with addition of the returned field
{ "$group": {
"_id": "$station",
"result": { "$last":"$dt"},
"t": {"$last":"$t"},
"lastDoc": { "$last": "$_id" }
}}
])

如果您实际上是在“跟进”这样的结果,那么您可以确定 ObjectId 的最大值。从您的结果中提取并在下一个查询中使用它。

无论如何,玩得开心,但是再次是的,在这种情况下,查询是最快的方式。

关于MongoDB : Aggregation framework : Get last dated document per grouping ID,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23360551/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com