在按MongoDB中的字段分组后选择最新文档

时间:2019-10-18 14:35:03

标签: mongodb mongodb-query aggregation-framework

我有一个问题,我希望它很简单,但是我无法弄清楚。我想做的是这样:

  • 查找集合中的所有文档,然后:
    • 按特定日期字段对文档进行排序
    • 在其他字段之一上应用distinct但返回整个文档

最佳示例显示。

这是一个模拟输入:

[
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("1998-11-04T18:46:14.000Z")
  },
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("1970-05-09T20:16:37.000Z")
  },
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
  },
  {
    "commandName" : "migration_b",
    "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
  }
]

预期输出为:

[
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
  },
  {
    "commandName" : "migration_b",
    "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
  }
]

或者,换句话说:

  • 通过commandName字段对输入数据进行分组
  • 在每个组内对文档进行排序
  • 从每个组中返回最新文档

我编写此查询的尝试失败:

  • distinct()函数将仅返回我要区分的字段的值,而不是整个文档。这使它不适合我的情况。

  • 试图编写一个aggregate查询,但是遇到了一个问题,即如何从每个组的内部对单个文档进行排序和选择? sort的聚集阶段将groups彼此排序,这不是我想要的。

我对Mongo不太了解,这就是我碰壁的地方。关于继续的任何想法?


作为参考,这是我正在尝试扩展的正在进行的聚合查询:

db.getCollection('some_collection').aggregate([
{ $group: { '_id': '$commandName', 'docs': {$addToSet: '$$ROOT'} } }, 
{ $sort: {'_id.docs.???': 1}}
])

后解析的修改

谢谢您的回答。我得到了我所需要的。供将来参考,这是完整的查询,它将执行请求的操作,并返回已过滤文档的列表,而不是组

db.getCollection('some_collection').aggregate([
{ $sort: {'executionDate': 1}},
{ $group: { '_id': '$commandName', 'result': { $last: '$$ROOT'} } },
{ $replaceRoot: {newRoot: '$result'} }
])

没有$replaceRoot阶段的查询结果 为:

[
  {
    "_id": "migration_a",
    "result": {
      "commandName" : "migration_a",
      "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
    }
  },
  {
    "_id": "migration_b",
    "result": {
      "commandName" : "migration_b",
      "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
    }
  }
]

外部_id_result只是我想要的实际文档的“组包装器”,它们嵌套在result键下。使用$replaceRoot阶段将嵌套文档移至结果的根。使用该阶段时的查询结果是:

[
  {
    "commandName" : "migration_a",
    "executionDate" : ISODate("2005-11-08T11:58:52.000Z")
  },
  {
    "commandName" : "migration_b",
    "executionDate" : ISODate("2016-06-02T19:48:34.000Z")
  }
]

3 个答案:

答案 0 :(得分:1)

尝试一下:

db.getCollection('some_collection').aggregate([
 { $sort: {'executionDate': -1}},
 { $group: { '_id': '$commandName', 'doc': {$first: '$$ROOT'} } }
])

答案 1 :(得分:1)

我相信这将为您提供所需的东西:

db.collection.aggregate([
  {
    $group: {
      "_id": "$commandName",
      "executionDate": {
        "$last": "$executionDate"
      }
    }
  }
])

您可以签出here

当然,如果您想完全匹配预期的输出,则可以添加一个排序(由于您的目标是简单地从每个组中返回最新文档,所以可能不需要这样做):

{
 $sort: {
  "executionDate": 1
 }
}

您可以here.签出该版本

答案 2 :(得分:1)

问题$last 聚合操作员文档中几乎涵盖了问题提出的用例。

摘要:

  

$group阶段应该在$sort阶段之后才能进行输入   文档以定义的顺序。由于$last仅选择了最后一个   一组文档。

查询: Link

db.collection.aggregate([
  {
    $sort: {
      executionDate: 1
    }
  },
  {
    $group: {
      _id: "$commandName",
      executionDate: {
        $last: "$executionDate"
      }
    }
  }
]);