Mongodb 如何从组中仅查询具有最新时间戳的文档?

Mongodb 如何从组中仅查询具有最新时间戳的文档?,mongodb,mongoose,mongodb-query,aggregation-framework,Mongodb,Mongoose,Mongodb Query,Aggregation Framework,在我查询的MongoDB集合中,每个文档在特定时间代表一个项。更新文档时,将创建具有相同项目id和新时间戳的新文档。所有项目都有唯一的项目ID 为了说明,请考虑这个例子。我们从项目的一个修订版开始: { _id: x, itemId: 123, createdOn: ISODate("2013-01-30T11:16:20.102Z"), field1: "foo", field2: "bar } 更新之后,我们对该项进行了两次修订,具有相同的itemI

在我查询的MongoDB集合中,每个文档在特定时间代表一个项。更新文档时,将创建具有相同项目id和新时间戳的新文档。所有项目都有唯一的项目ID

为了说明,请考虑这个例子。我们从项目的一个修订版开始:

{
    _id: x,
    itemId: 123,
    createdOn: ISODate("2013-01-30T11:16:20.102Z"),
    field1: "foo",
    field2: "bar
}
更新之后,我们对该项进行了两次修订,具有相同的itemId和不同的时间戳

[{
  _id: x,
  itemId: 123,
  createdOn: ISODate("2013-01-30T11:16:20.102Z"),
  field1: "foo",
  field2: "bar"
},
{
  _id: y,
  itemId: 123,
  createdOn: ISODate("2014-02-09T14:26:20.102Z"),
  field1: "baz",
  field2: "fiz"
}]
如何找到最近修订版中满足特定查询的所有项目?

我目前(错误的)方法是首先找到匹配的文档,然后按时间戳排序,按itemId对它们进行分组,然后从组中的第一个文档返回值:

ItemModel.aggregate({ $match: { field1: "foo"} }).sort({createdOn: -1}).group(
    {
        _id: '$itemId', // grouping key
        createdOn: {$first: '$createdOn'},
        field1: {$first: '$field1'},
        field2: {$first: '$field2'}
    }).exec(...);

这是错误的,因为它与项目的旧版本相匹配。只有项目的最新版本才应匹配。在上面的示例中,此方法返回项目“123”,而正确的结果是一个空结果集

当您可以在聚合管道中执行所有操作时,您在这里混合了一些方法。否则,只需按照正确的顺序进行步骤:

db.collection.aggregate([
    {$sort: { createdOn: -1 }},
    {$group: { _id: "$itemId", 
        createdOn: {$first: "$createdOn"},
        field1: {$first: "$field1" },
        field2: {$first: "$field2" }
    }},
    {$match: { field1: "foo" }}
])

因此,首先对最新文档进行排序。在
itemId
上分组(订单将首先维护$first),如果必须,则使用$match进行筛选。但是您的分组文档将是最新的。

当您可以在聚合管道中执行所有操作时,您在这里混合了一些方法。否则,只需按照正确的顺序进行步骤:

db.collection.aggregate([
    {$sort: { createdOn: -1 }},
    {$group: { _id: "$itemId", 
        createdOn: {$first: "$createdOn"},
        field1: {$first: "$field1" },
        field2: {$first: "$field2" }
    }},
    {$match: { field1: "foo" }}
])

因此,首先对最新文档进行排序。在
itemId
上分组(订单将首先维护$first),如果必须,则使用$match进行筛选。但是您的分组文档将是最新的文档。

可以考虑更改文档的模式,以更好地适合您的查询,并减少聚合的开销。您可以将修订子文档推送到数组中,并在父文档中维护最新修订,而不是为每个修订创建新文档;例如:

{
    _id: x,
    itemId: 123,
    createdOn: ISODate("2014-02-09T14:26:20.102Z"),
    field1: "baz",
    field2: "fiz,
    revisions: [
        {createdOn: ISODate("2013-01-30T11:16:20.102Z"), field1: "foo", field2: "bar"},
        {createdOn: ISODate("2014-02-09T14:26:20.102Z"), field1: "baz", field2: "fiz"}
    ]
}
请记住,MongoDB强制执行16MB的文档大小限制;这应该足以满足大多数用例。这将使您的查询非常简单:db.collection.find({field1:foo})


另一种方法…

< P>可以考虑更改文档的模式,以更好地适合查询,并减少聚合的开销。您可以将修订子文档推送到数组中,并在父文档中维护最新修订,而不是为每个修订创建新文档;例如:

{
    _id: x,
    itemId: 123,
    createdOn: ISODate("2014-02-09T14:26:20.102Z"),
    field1: "baz",
    field2: "fiz,
    revisions: [
        {createdOn: ISODate("2013-01-30T11:16:20.102Z"), field1: "foo", field2: "bar"},
        {createdOn: ISODate("2014-02-09T14:26:20.102Z"), field1: "baz", field2: "fiz"}
    ]
}
请记住,MongoDB强制执行16MB的文档大小限制;这应该足以满足大多数用例。这将使您的查询非常简单:db.collection.find({field1:foo})


只是另一种方法…

谢谢!这是一个好主意,因为当前模式在计数查询时也很麻烦。我必须运行整个聚合管道才能获得正确的项目计数。谢谢!这是一个好主意,因为当前模式在计数查询时也很麻烦。我必须运行整个聚合管道才能获得正确的项目计数。谢谢,我还没有意识到聚合管道有多灵活。我在开头和结尾都进行了匹配步骤,因为这样可以通过减少通过管道的文档数量来提高性能。谢谢,我还没有意识到聚合管道有多灵活。我在开头和结尾都使用了匹配步骤,因为这样可以减少通过管道传输的文档数量,从而提高性能。