我的收藏中有以下文件。每个文档都包含有关特定位置的历史天气数据:
{
'location':'new york',
'history':[
{'timestamp':1524542400, 'temp':79, 'wind_speed':1, 'wind_direction':'SW'}
{'timestamp':1524548400, 'temp':80, 'wind_speed':2, 'wind_direction':'SW'}
{'timestamp':1524554400, 'temp':82, 'wind_speed':3, 'wind_direction':'S'}
{'timestamp':1524560400, 'temp':78, 'wind_speed':4, 'wind_direction':'S'}
]
},
{
'location':'san francisco',
'history':[
{'timestamp':1524542400, 'temp':80, 'wind_speed':5, 'wind_direction':'SW'}
{'timestamp':1524548400, 'temp':81, 'wind_speed':6, 'wind_direction':'SW'}
{'timestamp':1524554400, 'temp':82, 'wind_speed':7, 'wind_direction':'S'}
{'timestamp':1524560400, 'temp':73, 'wind_speed':8, 'wind_direction':'S'}
]
},
{
'location':'miami',
'history':[
{'timestamp':1524542400, 'temp':84, 'wind_speed':9, 'wind_direction':'SW'}
{'timestamp':1524548400, 'temp':85, 'wind_speed':10, 'wind_direction':'SW'}
{'timestamp':1524554400, 'temp':86, 'wind_speed':11, 'wind_direction':'S'}
{'timestamp':1524560400, 'temp':87, 'wind_speed':12, 'wind_direction':'S'}
]
}
我想获得每个地点(或多或少)的最新天气数据列表,如下所示:
{
'location':'new york',
'history':{'timestamp':1524560400, 'temp':78, 'wind_speed':4, 'wind_direction':'S'}
},
{
'location':'san francisco',
'history':{'timestamp':1524560400, 'temp':73, 'wind_speed':8, 'wind_direction':'S'}
},
{
'location':'miami',
'history':{'timestamp':1524560400, 'temp':87, 'wind_speed':12, 'wind_direction':'S'}
}
我很确定它需要某种$ group聚合但无法弄清楚如何通过$max:<field>
选择整个对象。例如,以下查询仅返回最大时间戳本身,没有任何附带字段。
db.collection.aggregate([{
'$unwind': '$history'
}, {
'$group': {
'_id': '$name',
'timestamp': {
'$max': '$history.timestamp'
}
}
}])
返回
{ "_id" : "new york", "timestamp" : 1524560400 }
{ "_id" : "san franciscoeo", "timestamp" : 1524560400 }
{ "_id" : "miami", "timestamp" : 1524560400 }
实际的集合和数组非常大,因此客户端处理并不理想。任何帮助将不胜感激。
答案 0 :(得分:1)
作为您找到的答案的作者,我认为现在的MongoDB版本实际上可以做得更好。
简而言之,我们实际上可以将$max
应用于您的特定情况,与$indexOfArray
和$arrayElemAt
一起使用以提取匹配的值:
db.collection.aggregate([
{ "$addFields": {
"history": {
"$arrayElemAt": [
"$history",
{ "$indexOfArray": [ "$history.timestamp", { "$max": "$history.timestamp" } ] }
]
}
}}
])
哪会回复你:
{
"_id" : ObjectId("5ae9175564de8a00a66b3974"),
"location" : "new york",
"history" : {
"timestamp" : 1524560400,
"temp" : 78,
"wind_speed" : 4,
"wind_direction" : "S"
}
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3975"),
"location" : "san francisco",
"history" : {
"timestamp" : 1524560400,
"temp" : 73,
"wind_speed" : 8,
"wind_direction" : "S"
}
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3976"),
"location" : "miami",
"history" : {
"timestamp" : 1524560400,
"temp" : 87,
"wind_speed" : 12,
"wind_direction" : "S"
}
}
当然,这实际上并不需要“分组”任何内容,只需在每个文档中找到$max
值,就像您似乎要尝试的那样。这可以避免您需要通过强制$group
或$unwind
来“破坏”任何其他文档输出。
用法本质上是$max
从指定的数组属性返回“最大”值,因为$history.timestamp
是一种简短的方法,表示从对象内提取“只是那些值”。阵列。
这用于与相同的“值列表”进行比较,以通过$indexOfArray
确定匹配的“索引”,它将数组作为第一个参数,将匹配的值作为第二个参数。
$arrayElemAt
运算符也将数组作为它的第一个参数,这里我们使用完整的"$history"
数组,因为我们想要提取“完整对象”。我们通过$indexOfArray
运算符的“返回索引”值来执行此操作。
当然,这对于“单一”匹配很好,但是如果您想将其扩展为具有相同$max
值的“多个”匹配,那么您将使用$filter
代替:
db.collection.aggregate([
{ "$addFields": {
"history": {
"$filter": {
"input": "$history",
"cond": { "$eq": [ "$$this.timestamp", { "$max": "$history.timestamp" } ] }
}
}
}}
])
哪个会输出:
{
"_id" : ObjectId("5ae9175564de8a00a66b3974"),
"location" : "new york",
"history" : [
{
"timestamp" : 1524560400,
"temp" : 78,
"wind_speed" : 4,
"wind_direction" : "S"
}
]
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3975"),
"location" : "san francisco",
"history" : [
{
"timestamp" : 1524560400,
"temp" : 73,
"wind_speed" : 8,
"wind_direction" : "S"
}
]
}
{
"_id" : ObjectId("5ae9175564de8a00a66b3976"),
"location" : "miami",
"history" : [
{
"timestamp" : 1524560400,
"temp" : 87,
"wind_speed" : 12,
"wind_direction" : "S"
}
]
}
主要的区别当然是"history"
属性仍然是一个“数组”,因为那是$filter
将产生的。当然还要注意,如果事实上“多个”条目具有相同的时间戳值,那么这当然会返回它们而不仅仅是匹配的“第一个索引”。
比较基本上是针对“each”数组元素进行的,以查看“current”("$$this"
)对象是否具有与$max
结果匹配的指定属性,并最终仅返回那些数组与所提供条件匹配的元素。
这些基本上是您的“现代”方法,可避免$unwind
的开销,实际上$sort
和$group
可能不需要它们。当然,只需处理单个文档就不需要它们。
但是,如果您确实需要通过特定分组键跨“多个文档”$group
并考虑数组“内部”的值,那么您发现的初始方法实际上适合该场景,最终你“必须”$unwind
以这种方式处理数组“内部”的项目。并考虑“跨文件”。
因此,请注意使用$group
和$unwind
等等阶段,而实际需要的地方和“分组”是您的实际意图。如果您只是想在文档中找到某些内容,那么可以采用更有效的方法来实现这一目标,而不会产生这些阶段带来的所有额外开销。