免责声明
在阅读其余答案之前,请阅读https://docs.mongodb.com/manual/core/aggregation-pipeline-limits/
问题中的结果文档应包含属于特定年龄组的所有文档的数组。
该数组的大小不能超过 16MB,因此下面的代码仅适用于非常小的微型文档集合。
代码:
db.collection.aggregate([
{ $sort: { age: 1 } },
{ $group: {
_id: null,
ages: { $push: "$age" }
} },
{ $addFields: {
ranges: { $reduce: {
input: { $range: [ 1, { $size: "$ages" }, 1 ] },
initialValue: [ [ { $arrayElemAt: [ "$ages", 0 ] } ] ],
in: { $cond: {
if: { $gt: [
{ $subtract: [ { $arrayElemAt: [ "$ages", "$$this" ] }, { $arrayElemAt: [ "$ages", { $subtract: [ "$$this", 1 ] } ] } ] },
2
] },
then: { $concatArrays: [ "$$value", [ [ { $arrayElemAt: [ "$ages", "$$this" ] } ] ] ] },
else: { $concatArrays: [
{ $slice: [ "$$value" , { $subtract: [ { $size: "$$value" }, 1 ] } ] },
[ { $concatArrays: [
{ $arrayElemAt: [ { $slice: [ "$$value" , -1 ] }, 0 ] } ,
[ { $arrayElemAt: [ "$ages", "$$this" ] } ]
] } ]
] }
} }
} }
} },
{ $unwind: "$ranges" },
{ $lookup: {
from: "collection",
localField: "ranges",
foreignField: "age",
as: "group"
} },
{ $project: { _id: 0, group: 1 } }
])
可能需要解释的部分是如何计算年龄组。
为此,我们将使用 $group 的所有年龄放入单个数组,然后将 $addFields“范围” - 年龄组的 2D 数组,其中年轻组中最年长的人和年长组中最年轻的人之间存在差距大于 2 年。
该数组是使用 $range 数组中的 $reduce 计算得出的,该数组包含所有年龄的索引,但首先是初始值。
reduce 表达式是一个 $cond,它计算所有年龄数组的当前和前一个 ($subtract) 元素之间的差异。
如果大于 2,则使用 $concatArrays 添加新的年龄组。否则,年龄将添加到最旧的组中,使用$slice 推送到范围数组中的最后一个组,并使用$setUnion 消除重复项。
在计算年龄组时,我们将$lookup同一个集合按年龄分组到“group”数组中。