MongoDB: 通过 date/time 获取每个 id 的最新完整文档
MongoDB: get latest full document for each id by date/time
我需要获取基于 data/time 的 id 数组中的最新文档。我有以下查询执行此操作,但它仅 returns _id
和 acquiredTime
字段。我怎样才能得到 return 包含所有字段的完整文档?
db.trip.aggregate([
{ $match: { tripId: { $in: ["trip01", "trip02" ]}} },
{ $sort: { acquiredTime: -1} },
{ $group: { _id: "$tripId" , acquiredTime: { $first: "$acquiredTime" }}}
])
集合看起来像:
[{
"tripId": "trip01",
"acquiredTime": 1000,
"name": "abc",
"value": "abc"
},{
"tripId": "trip02",
"acquiredTime": 1000,
"name": "xyz",
"value": "xyz"
},{
"tripId": "trip01",
"acquiredTime": 2000,
"name": "def",
"value": "abc"
},{
"tripId": "trip02",
"acquiredTime": 2000,
"name": "ghi",
"value": "xyz"
}]
此刻我得到:
[{
"tripId": "trip01",
"acquiredTime": 2000
},{
"tripId": "trip02",
"acquiredTime": 2000
}]
我需要得到:
[{
"tripId": "trip01",
"acquiredTime": 2000,
"name": "def",
"value": "abc"
},{
"tripId": "trip02",
"acquiredTime": 2000,
"name": "ghi",
"value": "xyz"
}]
您的方法是正确的方法,但问题是 $group
and $project
只是行不通,需要您在结果中命名您想要的所有字段。
如果您不介意结构看起来有点不同,那么您始终可以在 MongoDB 2.6 及更高版本中使用 $$ROOT
:
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": { "acquiredTime": -1} },
{ "$group": { "_id": "$tripId" , "doc": { "$first": "$$ROOT" }}}
])
所以整个文档都在那里,但都作为子文档包含在结果中 "doc"。
对于其他任何内容或更漂亮的内容,您将必须指定所需的每个字段。它只是一个数据结构,因此您始终可以通过代码生成它。
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": { "acquiredTime": -1} },
{ "$group": {
"_id": "$tripId" ,
"acquiredTime": { "$first": "$acquiredTime" },
"name": { "$first": "$name" },
"value": { "$first": "$value" }
}}
])
根据我的理解,当要返回大量唯一文档时,上述解决方案会遇到性能和 RAM 问题,因为 $match 的输出排序在内存,不管你有什么索引。
参考:https://docs.mongodb.com/manual/tutorial/sort-results-with-indexes/
要最大化性能并最小化 RAM 使用:
- 创建唯一索引
[(tripId, 1), (acquiredTime, -1)]
- 有排序正好沿着索引
这当然会花费您一个索引,这会减慢插入速度 - 天下没有免费的饭:)
此外,将原始文档移动到子文档的美观问题可以使用 $replaceRoot
轻松解决,无需明确列出文档键。
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": SON([("tripId", 1), ("acquiredTime", -1)],
{ "$group": { "_id": "$tripId" , "doc": { "$first": "$$ROOT" }}},
{ "$replaceRoot": { "newRoot": "$doc"}}
])
最后,值得注意的是,如果 acquiredTime 只是您的服务器时间,您可以去掉它,因为 _id
已经嵌入了创建时间戳。因此唯一索引将继续 [(tripId, 1), (_id, -1)]
,查询变为:
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": SON([("tripId", 1), ("_id", -1)],
{ "$group": { "_id": "$tripId" , "doc": { "$first": "$$ROOT" }}},
{ "$replaceRoot": { "newRoot": "$doc"}}
])
这也更好,因为 MongoDB 中的日期对象具有 1 毫秒的分辨率,这取决于插入的频率 - 可能导致极难重现竞争条件,而自动生成的 _id
保证严格递增。
我需要获取基于 data/time 的 id 数组中的最新文档。我有以下查询执行此操作,但它仅 returns _id
和 acquiredTime
字段。我怎样才能得到 return 包含所有字段的完整文档?
db.trip.aggregate([
{ $match: { tripId: { $in: ["trip01", "trip02" ]}} },
{ $sort: { acquiredTime: -1} },
{ $group: { _id: "$tripId" , acquiredTime: { $first: "$acquiredTime" }}}
])
集合看起来像:
[{
"tripId": "trip01",
"acquiredTime": 1000,
"name": "abc",
"value": "abc"
},{
"tripId": "trip02",
"acquiredTime": 1000,
"name": "xyz",
"value": "xyz"
},{
"tripId": "trip01",
"acquiredTime": 2000,
"name": "def",
"value": "abc"
},{
"tripId": "trip02",
"acquiredTime": 2000,
"name": "ghi",
"value": "xyz"
}]
此刻我得到:
[{
"tripId": "trip01",
"acquiredTime": 2000
},{
"tripId": "trip02",
"acquiredTime": 2000
}]
我需要得到:
[{
"tripId": "trip01",
"acquiredTime": 2000,
"name": "def",
"value": "abc"
},{
"tripId": "trip02",
"acquiredTime": 2000,
"name": "ghi",
"value": "xyz"
}]
您的方法是正确的方法,但问题是 $group
and $project
只是行不通,需要您在结果中命名您想要的所有字段。
如果您不介意结构看起来有点不同,那么您始终可以在 MongoDB 2.6 及更高版本中使用 $$ROOT
:
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": { "acquiredTime": -1} },
{ "$group": { "_id": "$tripId" , "doc": { "$first": "$$ROOT" }}}
])
所以整个文档都在那里,但都作为子文档包含在结果中 "doc"。
对于其他任何内容或更漂亮的内容,您将必须指定所需的每个字段。它只是一个数据结构,因此您始终可以通过代码生成它。
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": { "acquiredTime": -1} },
{ "$group": {
"_id": "$tripId" ,
"acquiredTime": { "$first": "$acquiredTime" },
"name": { "$first": "$name" },
"value": { "$first": "$value" }
}}
])
根据我的理解,当要返回大量唯一文档时,上述解决方案会遇到性能和 RAM 问题,因为 $match 的输出排序在内存,不管你有什么索引。
参考:https://docs.mongodb.com/manual/tutorial/sort-results-with-indexes/
要最大化性能并最小化 RAM 使用:
- 创建唯一索引
[(tripId, 1), (acquiredTime, -1)]
- 有排序正好沿着索引
这当然会花费您一个索引,这会减慢插入速度 - 天下没有免费的饭:)
此外,将原始文档移动到子文档的美观问题可以使用 $replaceRoot
轻松解决,无需明确列出文档键。
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": SON([("tripId", 1), ("acquiredTime", -1)],
{ "$group": { "_id": "$tripId" , "doc": { "$first": "$$ROOT" }}},
{ "$replaceRoot": { "newRoot": "$doc"}}
])
最后,值得注意的是,如果 acquiredTime 只是您的服务器时间,您可以去掉它,因为 _id
已经嵌入了创建时间戳。因此唯一索引将继续 [(tripId, 1), (_id, -1)]
,查询变为:
db.trip.aggregate([
{ "$match": { "tripId": { "$in": ["trip01", "trip02" ]}} },
{ "$sort": SON([("tripId", 1), ("_id", -1)],
{ "$group": { "_id": "$tripId" , "doc": { "$first": "$$ROOT" }}},
{ "$replaceRoot": { "newRoot": "$doc"}}
])
这也更好,因为 MongoDB 中的日期对象具有 1 毫秒的分辨率,这取决于插入的频率 - 可能导致极难重现竞争条件,而自动生成的 _id
保证严格递增。