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
阶段允许我们在同一个输入文档集上的单个阶段中 运行 多个聚合管道。每个子管道在输出文档中都有自己的字段,其结果存储为文档数组。
每个字段因此由其自己的聚合管道生成,其第一阶段 $sort
s 由 time
字段(以相反的顺序)记录,然后是 $limit
阶段'我将只保留第一项(由所选顺序定义的最小项)。
管道的第二部分($set
阶段)用于清理 $facet
输出格式。
请注意,$sort
后跟 $limit
阶段由 Mongo 优化(参见 here)。这允许排序操作在进行时仅保留前 n 个结果(在我们的例子中只有 1 个元素)。
另请注意,我们的 $sort
阶段将受益于 time
上的索引。
如何根据 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
阶段允许我们在同一个输入文档集上的单个阶段中 运行 多个聚合管道。每个子管道在输出文档中都有自己的字段,其结果存储为文档数组。
每个字段因此由其自己的聚合管道生成,其第一阶段 $sort
s 由 time
字段(以相反的顺序)记录,然后是 $limit
阶段'我将只保留第一项(由所选顺序定义的最小项)。
管道的第二部分($set
阶段)用于清理 $facet
输出格式。
请注意,$sort
后跟 $limit
阶段由 Mongo 优化(参见 here)。这允许排序操作在进行时仅保留前 n 个结果(在我们的例子中只有 1 个元素)。
另请注意,我们的 $sort
阶段将受益于 time
上的索引。