MongoDB 获取聚合查询中的第一个和最后一个文档

MongoDB get first and last document in aggregate query

如何根据 time 字段获取第一个和最后一个文档。 我可以使用 $group 并获取 $first$last 文档,但我不需要在这里分组,只需获取第一个和最后一个完整文档。也许我可以使用 slice?此查询无效:

{
  "aggregate": "353469045637980_data",
  "pipeline": [
    {
      "$match": {
        "$and": [
          {
            "time": {
              "$gte": 1461369600
            }
          },
          {
            "time": {
              "$lt": 1461456000
            }
          }
        ]
      }
    },
    {
      "$project": {
        "first": {
          "$slice": 1
        },
        "last": {
          "$slice": -1
        }
      }
    }
  ]
}

嗯,你需要 $group,但你可以简单地使用一个常量(例如 null,参见 the docs) for its id so that it results in a single group. $$ROOT 然后引用你可以与 $first 一起使用的文档本身$last 像这样

$group: {
  _id: null,
  first: { $first: "$$ROOT" },
  last: { $last: "$$ROOT" }
}

当然,您可以进一步引入 $project 阶段,将数据整形为数组(正如您提到的,您需要一个列表)等。

作为旁注,您可能需要引入一个 $sort 阶段以确保 $first$last 具有正确的含义。

我知道这并没有直接回答问题,但是当我遇到这个问题时会帮助我很多。

您可以(如 DAXaholic 解释的那样)使用以下查询来获取相应的文档:

[
  $sort: {...},
  $group: {
    _id: null,
    first: { $first: "$$ROOT" },
    last: { $last: "$$ROOT" }
  }
]

我遇到的问题是出现以下错误:Sort exceeded memory limit of 104857600 bytes, but did not opt in to external sorting. Aborting operation. Pass allowDiskUse:true to opt in.

要解决此问题(不使用磁盘进行排序),您可以使用:

[
  $group: {
    _id: null,
    first: { $min: "$time" },
    last: { $max: "$time" }
  }
]

优点是您不需要排序,因此可能不会 运行 在 100 MB 内存限制内。缺点是它不会 return 整个文档(其中 $min / $max 匹配),而只是 属性 的值(在本例中 time ).

您可以使用 $facet 阶段,结合 $sort/$limit 阶段:

// { time: ISODate("2021-12-04"), b: "hello" }
// { time: ISODate("2021-12-07"), b: "world" }
// { time: ISODate("2021-12-05"), b: "oups"  }
db.collection.aggregate([
  { $facet: {
    first: [{ $sort: { time: 1 } }, { $limit: 1 }],
    last:  [{ $sort: { time: -1 } }, { $limit: 1 }]
  }},
  { $set: { first: { $first: "$first" }, last: { $last: "$last" } } }
])
// { first: { time: ISODate("2021-12-04"), b: "hello" }, last: { time: ISODate("2021-12-07"), b: "world" } }

$facet 阶段允许我们在同一个输入文档集上的单个阶段中 运行 多个聚合管道。每个子管道在输出文档中都有自己的字段,其结果存储为文档数组。

每个字段因此由其自己的聚合管道生成,其第一阶段 $sorts 由 time 字段(以相反的顺序)记录,然后是 $limit 阶段'我将只保留第一项(由所选顺序定义的最小项)。

管道的第二部分($set 阶段)用于清理 $facet 输出格式。


请注意,$sort 后跟 $limit 阶段由 Mongo 优化(参见 here)。这允许排序操作在进行时仅保留前 n 个结果(在我们的例子中只有 1 个元素)。

另请注意,我们的 $sort 阶段将受益于 time 上的索引。