为了使用聚合框架从数组中获取$first 和$last 值,您需要首先使用$unwind 将数组“反规范化”为单个文档。还有另一个技巧可以将它们放回数组中。
假设一个像这样的文档
{
"type": "abc",
"values": [
{ "time": ISODate("2014-06-12T22:35:42.260Z"), "value": "ZZZ" },
{ "time": ISODate("2014-06-12T22:36:45.921Z"), "value": "KKK" },
{ "time": ISODate("2014-06-12T22:37:18.237Z"), "value": "AAA" }
]
}
假设您的数组已经排序,您会这样做:
如果您不关心数组中的结果,只需 $unwind 和 $group:
db.junk.aggregate([
{ "$unwind": "$values" },
{ "$group": {
"_id": "$type",
"ftime": { "$first": "$values.time" },
"fvalue": { "$first": "$values.value" },
"ltime": { "$last": "$values.time" },
"lvalue": { "$last": "$values.value" },
}}
])
对于数组中的那些结果,有一个技巧:
db.collection.aggregate([
{ "$unwind": "$values" },
{ "$project": {
"type": 1,
"values": 1,
"indicator": { "$literal": ["first", "last"] }
}},
{ "$group": {
"_id": "$type",
"ftime": { "$first": "$values.time" },
"fvalue": { "$first": "$values.value" },
"ltime": { "$last": "$values.time" },
"lvalue": { "$last": "$values.value" },
"indicator": { "$first": "$indicator" }
}},
{ "$unwind": "$indicator" },
{ "$project": {
"values": {
"time": {
"$cond": [
{ "$eq": [ "$indicator", "first" ] },
"$ftime",
"$ltime"
]
},
"value": {
"$cond": [
{ "$eq": [ "$indicator", "first" ] },
"$fvalue",
"$lvalue"
]
}
}
}},
{ "$group": {
"_id": "$_id",
"values": { "$push": "$values" }
}}
])
如果您的数组未排序,则在第一个 $group 之前放置一个额外的 $sort 阶段,以确保您的项目按照您希望它们被 $first 和 $last 评估的顺序。 “时间”字段的逻辑顺序,所以:
{ "$sort": { "type": 1, "values.time": 1 } }
$literal 声明了一个数组来标识“first”和“last”的值,这些值稍后会“展开”以创建每个分组文档的两个副本。然后使用$cond 运算符对这些值进行评估,以重新分配给“值”的单个字段,最后使用$push 将其推回数组中。
请记住,在管道中首先尝试$match,以便将您正在处理的文档数量减少到您合理需要的数量。您几乎不想对整个集合执行此操作,尤其是当您在数组上使用 $unwind 时。
正如最后一点,$literal 在 MongoDB 2.6 及更高版本中被引入/公开。对于以前的版本,您可以将其与未记录的 $const 互换。