MongoDB:按日期/时间获取每个id的最新完整文档

时间:2015-01-22 05:44:24

标签: mongodb mongodb-query aggregation-framework

我需要根据数据/时间获取一系列ID中的最新文档。我有以下查询执行此操作,但它只返回_idacquiredTime字段。如何让它返回包含所有字段的完整文档?

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"
}]

2 个答案:

答案 0 :(得分:2)

您的方法是正确的方法,但问题是$group$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" }
   }}
])

答案 1 :(得分:0)

对于我的理解,当要返回大量唯一文档时,上述解决方案会遇到性能和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严格递增。