导读:本期聚焦于大象创作的《MongoDB聚合管道$bottom怎么用?如何取出分组底部N个文档》,敬请观看详情。为什么分组聚合后总是拿不到最末尾的几条数据?MongoDB从5.0版本开始提供的$bottom操作符正是为此设计的。它能配合$group阶段,按照指定的排序规则取出每个分组中排在底部的N个文档,比传统先排序再取值的写法更简洁直观。本文将详细讲解$bottom的基本语法、sortSpec排序字段的作用机制、output输出字段的定义方式,并通过学生成绩排名、商品价格筛选等实例演示完整用法,同时对比$top、$first、$last等相近操作符的区别,帮你彻底掌握分组取尾部数据的技巧,避免常见踩坑。

MongoDB的聚合管道提供了丰富的累加器操作符,用于在$group阶段对分组数据进行汇总计算。从MongoDB 5.0开始,官方引入了$bottom和$top这一对操作符,专门解决"按排序规则取出分组内顶部或底部N个文档"的场景。相比传统的$push加上手动排序再截取的方式,$bottom不仅语义更清晰,性能也更优。本文将围绕$bottom的语法、使用场景以及与相近操作符的区别展开详细讲解。

MongoDB聚合管道$bottom怎么用?如何取出分组底部N个文档

一、$bottom的基本语法与参数说明

$bottom只能作为$group阶段的累加器使用,不能单独出现在其他阶段。它的语法结构由三个固定字段组成:sortBy、output和整型数字N。其中sortBy指定排序规则,output指定要返回的字段表达式,整型数字N表示要取出的文档数量,取值必须是正整数。

{ $group: {
    _id: "$classId",              // 分组字段,例如按班级分组
    lowestScores: {
        $bottom: {
            sortBy: { score: -1 }, // 按score降序排列
            output: "$score",      // 取出的内容
            n: 3                   // 取底部3个文档
        }
    }
}}

理解$bottom的关键在于弄清sortBy的排序方向。文档在分组内部会先按照sortBy排序,排完之后位于序列末尾的N个文档就是所谓的"底部"。因此sortBy为{ score: -1 }降序时,底部N个就是分数最低的N个;如果改为{ score: 1 }升序,底部N个就变成了分数最高的N个。这个方向性是新手最容易搞混的地方,写反了排序方向,取出来的结果会完全相反。

output部分非常灵活,既可以是一个简单字段如"$score",也可以是一个文档对象,把多个字段组合输出。例如写成{ name: "$name", score: "$score" },返回结果中每一条都会同时包含学生姓名和分数,方便后续阶段直接使用。

二、完整实例:取出每个班级分数最低的三名学生

下面通过一个完整例子演示$bottom的实际效果。假设集合students中存储了学生的成绩数据,每条文档包含姓名、班级ID和分数三个字段,现在需要统计每个班级分数最低的3名学生。

// 插入测试数据
db.students.insertMany([
    { name: "张三", classId: "A", score: 88 },
    { name: "李四", classId: "A", score: 92 },
    { name: "王五", classId: "A", score: 65 },
    { name: "赵六", classId: "A", score: 47 },
    { name: "孙七", classId: "A", score: 73 },
    { name: "周八", classId: "B", score: 55 },
    { name: "吴九", classId: "B", score: 81 },
    { name: "郑十", classId: "B", score: 39 }
]);

// 聚合查询
db.students.aggregate([
    { $group: {
        _id: "$classId",
        bottomThree: {
            $bottom: {
                sortBy: { score: -1 },
                output: { name: "$name", score: "$score" },
                n: 3
            }
        }
    }}
]);

执行上述聚合后,A班会返回张三、孙七、王五对应的文档,B组会返回吴九和周八对应的文档。注意当分组内文档数不足N个时,$bottom会返回该分组中全部文档,而不会报错或填充空值,这个特性在数据量不均匀的场景下非常实用。

输出结果是一个数组,数组内每个元素的结构与output定义保持一致。如果后续还需要对结果做进一步处理,比如过滤或统计,可以直接接上$match、$unwind等阶段,管道的组合完全不受限制。

三、$bottom与$top、$first、$last的区别

很多初学者容易把$bottom和$last混为一谈,实际上两者有本质区别。$last取的是分组内按照管道进入顺序的最后一个文档,只返回一个文档;而$bottom是根据显式指定的sortBy排序后取底部N个,返回的是数组。$first和$top的关系同理。

操作符排序依据返回数量引入版本
$bottom显式sortByN个(数组)5.0
$top显式sortByN个(数组)5.0
$first管道进入顺序1个2.6
$last管道进入顺序1个2.6

$top的语法与$bottom完全相同,只是取的是排序后头部的N个文档。两者组合使用还能实现一些巧妙的需求,例如同时取每个分组的最高分和最低分,只需在$group中定义两个字段分别使用$top和$bottom即可,一条聚合语句就能完成。

db.students.aggregate([
    { $group: {
        _id: "$classId",
        highest: { $top:    { sortBy: { score: -1 }, output: "$score", n: 1 } },
        lowest:  { $bottom: { sortBy: { score: -1 }, output: "$score", n: 1 } }
    }}
]);

需要特别提醒的是,$bottom要求sortBy字段在同一分组内的排序是确定的。如果存在排序键相同的文档,MongoDB不保证这些文档之间的输出顺序稳定,必要时应在sortBy中追加一个唯一字段作为次级排序键,例如{ score: -1, _id: 1 },这样结果就是可复现的。

四、使用注意事项与常见坑点

首先是版本兼容问题。$bottom是MongoDB 5.0新增的操作符,低版本执行会直接抛出不识别的错误。如果项目仍运行在4.x版本,只能退而求其次,使用$push把整个分组数据推入数组,再用$arrayElem0配合$sortArray截取,写法繁琐得多。

其次是内存限制。$group阶段默认允许使用100MB内存,当分组数量多、每个分组文档量大,且output定义的字段较宽时,可能触发内存超限错误。应对方式有两种:一是精简output字段,只保留必要的数据;二是允许管道使用磁盘临时文件,即在aggregate方法中设置{ allowDiskUse: true },用磁盘空间换取计算能力。

最后,如果 sortBy 指定的字段在部分文档中不存在,这些文档会被视为null值参与排序,null在升序时排在最前、降序时排在最后,这会直接影响底部N个的判定结果。因此在数据质量无法保证的集合上使用$bottom前,最好先通过$match过滤掉缺失排序字段的文档,确保统计结果的准确性。

总结来说,$bottom让分组取尾部数据从复杂的数组操作简化为一行声明式配置,配合$top使用几乎可以覆盖所有"分组内极值"类的查询需求。掌握它之后,类似排行榜、末位淘汰统计、异常值检测等业务场景的实现都会变得简洁高效。

MongoDB$bottom聚合管道修改时间:2026-08-31 03:38:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。