Bla*_*lam 1 grouping mongodb aggregation-framework
我的收藏中有以下文件。每个文档都包含有关特定位置的历史天气数据:
{
'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'}
]
}
Run Code Online (Sandbox Code Playgroud)
我想获取每个位置(或多或少)的最新天气数据列表,如下所示:
{
'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'}
}
Run Code Online (Sandbox Code Playgroud)
我很确定它需要某种 $group 聚合,但无法弄清楚如何通过$max:<field>. 例如,下面的查询只返回最大时间戳本身,没有任何伴随的字段。
db.collection.aggregate([{
'$unwind': '$history'
}, {
'$group': {
'_id': '$name',
'timestamp': {
'$max': '$history.timestamp'
}
}
}])
Run Code Online (Sandbox Code Playgroud)
返回
{ "_id" : "new york", "timestamp" : 1524560400 }
{ "_id" : "san franciscoeo", "timestamp" : 1524560400 }
{ "_id" : "miami", "timestamp" : 1524560400 }
Run Code Online (Sandbox Code Playgroud)
实际的集合和数组非常大,因此客户端处理不会很理想。任何帮助将非常感激。
正如您找到的答案的作者一样,我认为我们实际上可以使用现代 MongoDB 版本做得更好。
简而言之,我们实际上可以应用于$max您的特定情况,与$indexOfArray和$arrayElemAt一起使用来提取匹配的值:
db.collection.aggregate([
{ "$addFields": {
"history": {
"$arrayElemAt": [
"$history",
{ "$indexOfArray": [ "$history.timestamp", { "$max": "$history.timestamp" } ] }
]
}
}}
])
Run Code Online (Sandbox Code Playgroud)
这将返回您:
{
"_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"
}
}
Run Code Online (Sandbox Code Playgroud)
这当然实际上不需要“分组”任何东西,只需$max从每个文档中找到值,就像您试图做的那样。这避免了您需要通过强制通过 a$group或实际上是$unwind.
用法本质上是$max从指定的数组属性返回“最大值”值,因为这$history.timestamp是从数组对象中提取“仅那些值”的一种简短方式。
这用于与相同的“值列表”进行比较以确定匹配的“索引” via $indexOfArray,它将数组作为第一个参数,将要匹配的值作为第二个参数。
该$arrayElemAt运算符还接受一个数组作为它的第一个参数,这里我们使用完整"$history"数组,因为我们要提取“完整对象”。我们通过$indexOfArray运算符的“返回索引”值来完成。
当然,这对于“单个”匹配很好,但是如果您想将其扩展为具有相同$max值的“多个”匹配,那么您可以$filter改用:
db.collection.aggregate([
{ "$addFields": {
"history": {
"$filter": {
"input": "$history",
"cond": { "$eq": [ "$$this.timestamp", { "$max": "$history.timestamp" } ] }
}
}
}}
])
Run Code Online (Sandbox Code Playgroud)
这将输出:
{
"_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"
}
]
}
Run Code Online (Sandbox Code Playgroud)
主要的区别当然是该"history"属性仍然是一个“数组”,因为那是$filter会产生的。当然还要注意,如果实际上有“多个”条目具有相同的时间戳值,那么这当然会返回所有条目,而不仅仅是匹配的“第一个索引”。
比较基本上是针对“每个”数组元素进行的,以查看“当前”( "$$this") 对象是否具有与$max结果匹配的指定属性,并最终仅返回与提供的条件匹配的那些数组元素。
这些本质上是您的“现代”方法,它们避免了 的开销$unwind,实际上$sort以及$group可能不需要它们的地方。当然,它们不需要仅用于处理单个文档。
但是,如果您确实需要$group通过特定的分组键并考虑数组“内部”的值来跨越“多个文档”,那么您发现的初始方法实际上适合该场景,因为最终您“必须”$unwind处理以这种方式在数组“内部”使用项目。并且还考虑了“跨文档”。
所以要留意使用阶段像$group和只有在这里你真正需要并在“分组”是您的实际意图。如果您只是想在“文档中”找到一些东西,那么有更有效的方法可以做到这一点,而无需这些阶段为处理带来的所有额外开销。$unwind