MongoDB 中聚合( aggregate )主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果,通常由聚合管道操作符和聚合表达式组合,完成数据处理。功能有点类似 Sql 语句中的 sum()、agv() 等。
目录
第1关:聚合管道操作符将文档定制格式输出(一)
常用聚合管道操作符
$project 修改文档结构输出
$match 筛选文档输出
编程要求
第2关:聚合管道操作符将文档定制格式输出(二)
$limit 限制文档数量输出
$skip 跳过前 n 条文档输出
$limit 与 $skip 可以组合使用
$unwind 拆分数组类型字段
$group 分组输出
$sort 排序输出
编程要求
第3关:2-2-3聚合表达式对文档数据进行统计
聚合表达式
编程要求
第1关:聚合管道操作符将文档定制格式输出(一)
一、本关任务:按照编程要求,对集合 educoder 进行相应的查询输出。
二、相关知识
为了完成本关任务,你需要掌握:
1.聚合管道操作符的作用; 2.$project 的用法; 3.$match 的用法。
常用聚合管道操作符
常用的几个聚合管道操作符:
操作符 | 作用 |
---|---|
$project | 修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档 |
$match | 用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作 |
$limit | 用来限制MongoDB聚合管道返回的文档数 |
$skip | 在聚合管道中跳过指定数量的文档,并返回余下的文档 |
$unwind | 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值 |
$group | 将集合中的文档分组,可用于统计结果 |
$sort | 将输入文档排序后输出 |
注意:以上操作不会修改集合的内容,只是将集合以指定形式输出。
例:在数据库 mydb1 有集合 educoder 内容如下:
_id | course | author | tags | learning_num |
---|---|---|---|---|
1 | Python表达式问题求解实训 | 李暾 | Python基础,求解 | 1882 |
2 | Java语言之基本语法 | 余跃 | Java基础,语法 | 814 |
3 | Python面向对象编程实训 | 李暾 | Python基础,面向对象 | 143 |
4 | Android综合实训之物联网移动应用开发(1) | prophet5 | Android,物联网,移动开发 | 207 |
$project 修改文档结构输出
有时候我们并不会用到文档的全部内容,只是使用其中几列,这时候就可以使用 $project 进行操作;或者有时候要重命名键值(列名),也可以使用 $project。
对集合 educoder 进行操作:
use mydb1db.educoder.insert([{_id:1,course:'Python表达式问题求解实训',author:'李暾',tags:['Python基础','求解'],learning_num:1882},{_id:2,course:'Java语言之基本语法',author:'余跃',tags:['Java基础','语法'],learning_num:814},{_id:3,course:'Python面向对象编程实训',author:'李暾',tags:['Python基础','面向对象'],learning_num:143},{_id:4,course:'Android综合实训之物联网移动应用开发(1)',author:'prophet5',tags:['Android','物联网','移动开发'],learning_num:207}])db.educoder.find()
此时可以看到: (我们已经成功插入4条数据)
只输出作者 author 和学习人数 learning_num 信息,
_id
也不要不显示(_id
默认是显示的):db.educoder.aggregate({$project:{_id:0,author:1,learning_num:1}})
0 为不显示,非 0 为显示。重命名字段名(把 learning_num 重命名为 num):
db.educoder.aggregate({$project:{course:1,authoe:1,tags:1,num:'$learning_num'}})
$match 筛选文档输出
有时候我们要在集合中筛选出符合特定条件的文档,这时候使用 $match 便可以很快实现。
只输出作者为“李暾”的文档:
db.educoder.aggregate({$match:{author:'李暾'}})
编程要求
为了减少你的工作量,给出以下文档内容:
{
_id:1,
course:'Python表达式问题求解实训',
author:'李暾',
tags:['Python基础','求解'],
learning_num:1882
},
{
_id:2,
course:'Java语言之基本语法',
author:'余跃',
tags:['Java基础','语法'],
learning_num:814
},
{
_id:3,
course:'Python面向对象编程实训',
author:'李暾',
tags:['Python基础','面向对象'],
learning_num:143
},
{
_id:4,
course:'Android综合实训之物联网移动应用开发(1)',
author:'prophet5',
tags:['Android','物联网','移动开发'],
learning_num:207
}
先在命令行中操作,将上述文档插入数据库 test1 中的集合 educoder 中,然后仿照例子中的查询方法,再在右侧代码栏 Begin-End 的中按要求输入符合以下两条要求的命令,以分号;
隔开(由于测试需要,请在代码里$
符号前添加转义符\
,不需要有空格,格式如:\$
,平时在命令行操作不需要):
文档输出时只保留 course 和 learning_num 字段(
_id
也不要保留);输出学习人数 learning_num 为1882档。
具体的返回结果,请查看测试集。
> use test1switched to db test1> db.educoder.insert({_id:1,course:'Python表达式问题求解实训',author:'李暾',tags:['Python基础','求解'],learning_num:1882})WriteResult({ "nInserted" : 1 })> db.educoder.insert({_id:2,course:'Java语言之基本语法',author:'余跃',tags:['Java基础','语法'],learning_num:814})WriteResult({ "nInserted" : 1 })> db.educoder.insert({_id:3,course:'Python面向对象编程实训',author:'李暾',tags:['Python基础','面向对象'],learning_num:143})WriteResult({ "nInserted" : 1 })> db.educoder.insert({_id:4,course:'Android综合实训之物联网移动应用开发(1)',author:'prophet5',tags:['Android','物联网','移动开发'],learning_num:207})WriteResult({ "nInserted" : 1 })> db.educoder.find(){ "_id" : 1, "course" : "Python表达式问题求解实训", "author" : "李暾", "tags" : [ "Python基础", "求解" ], "learning_num" : 1882 }{ "_id" : 2, "course" : "Java语言之基本语法", "author" : "余跃", "tags" : [ "Java基础", "语法" ], "learning_num" : 814 }{ "_id" : 3, "course" : "Python面向对象编程实训", "author" : "李暾", "tags" : [ "Python基础", "面向对象" ], "learning_num" : 143 }{ "_id" : 4, "course" : "Android综合实训之物联网移动应用开发(1)", "author" : "prophet5", "tags" : [ "Android", "物联网", "移动开发" ], "learning_num" : 207 }> db.educoder.aggregate({$project:{_id:0,course:1,learning_num:1}}){ "course" : "Python表达式问题求解实训", "learning_num" : 1882 }{ "course" : "Java语言之基本语法", "learning_num" : 814 }{ "course" : "Python面向对象编程实训", "learning_num" : 143 }{ "course" : "Android综合实训之物联网移动应用开发(1)", "learning_num" : 207 }> db.educoder.aggregate({$match:{learning_num:1882}}){ "_id" : 1, "course" : "Python表达式问题求解实训", "author" : "李暾", "tags" : [ "Python基础", "求解" ], "learning_num" : 1882 }
第2关:聚合管道操作符将文档定制格式输出(二)
一、本关任务:按照编程要求,对集合 educoder 进行相应的查询输出。
二、相关知识
上一关已经了解了 $project 和 $match 的用法,本关就介绍剩余管道操作符的用法。
例:在数据库 mydb1 有集合 educoder 内容如下:
_id | course | author | tags | learning_num |
---|---|---|---|---|
1 | Python表达式问题求解实训 | 李暾 | Python基础,求解 | 1882 |
2 | Java语言之基本语法 | 余跃 | Java基础,语法 | 814 |
3 | Python面向对象编程实训 | 李暾 | Python基础,面向对象 | 143 |
4 | Android综合实训之物联网移动应用开发(1) | prophet5 | Android,物联网,移动开发 | 207 |
先插入数据,方法见第一关;
$limit 限制文档数量输出
有时候集合中文档数量太大,我们只想选取前几行查看一下,这时候就可以用 $limit,输出前2条文档:
db.educoder.aggregate({$limit:2})
$skip 跳过前 n 条文档输出
与 $limit 相反,$skip 是跳过前 n 条文档,显示剩余文档。
将集合 educoder 中的前两条文档跳过,显示剩余文档:
db.educoder.aggregate({$skip:2})
- $skip 接受一个数字 n,表示丢弃结果集中的前 n 个文档;
$limit 与 $skip 可以组合使用
db.educoder.aggregate([{$skip:1},{$limit:2}]) #跳过第一条,显示前两条,也就是显示第2-3条文档
db.educoder.aggregate([{$limit:2},{$skip:1}])
#显示前两条,跳过第一条,也就是显示第2条文档
$unwind 拆分数组类型字段
将 educoder 中的 tags 字段拆分成多条,每条包含数组中的一个值:
db.educoder.aggregate({$unwind:'$tags'})
$group 分组输出
该操作比较常用,因为文档分组后利于我们的统计。比如,按照作者分组我们就可以统计出该集合总共有几个作者。
在集合 educoder 中,按作者分组:
db.educoder.aggregate({$group:{_id:'$author'}})
$sort 排序输出
按照学习人数 learning_num 降序排序输出文档:
db.educoder.aggregate({$sort:{learning_num:-1}})
-1 代表降序排序, 1 代表升序排序。
编程要求
为了减少你的工作量,给出以下文档内容:
{
_id:1,
course:'Python表达式问题求解实训',
author:'李暾',
tags:['Python基础','求解'],
learning_num:1882
},
{
_id:2,
course:'Java语言之基本语法',
author:'余跃',
tags:['Java基础','语法'],
learning_num:814
},
{
_id:3,
course:'Python面向对象编程实训',
author:'李暾',
tags:['Python基础','面向对象'],
learning_num:143
},
{
_id:4,
course:'Android综合实训之物联网移动应用开发(1)',
author:'prophet5',
tags:['Android','物联网','移动开发'],
learning_num:207
}
先在命令行中操作,将上述文档插入数据库 test2 中的集合 educoder 中,然后仿照例子中的查询方法,再在右侧代码栏 Begin-End 中按要求输入符合以下3条要求的命令,以分号;
隔开(由于测试需要,请在代码里$
符号前添加转义符\
,不需要有空格,格式如:\$
,平时在命令行操作不需要):
输出集合前3条文档;
将文档按照学习人数 learning_num 升序排序;
输出除了前两条以外的文档。
> use test2switched to db test2> db.educoder.insert({_id:1,course:'Python表达式问题求解实训',author:'李暾',tags:['Python基础','求解'],learning_num:1882})WriteResult({ "nInserted" : 1 })> db.educoder.insert({_id:2,course:'Java语言之基本语法',author:'余跃',tags:['Java基础','语法'],learning_num:814})WriteResult({ "nInserted" : 1 })> db.educoder.insert({_id:3,course:'Python面向对象编程实训',author:'李暾',tags:['Python基础','面向对象'],learning_num:143})WriteResult({ "nInserted" : 1 })> db.educoder.insert({_id:4,course:'Android综合实训之物联网移动应用开发(1)',author:'prophet5',tags:['Android','物联网','移动开发'],learning_num:207})WriteResult({ "nInserted" : 1 })> db.educoder.aggregate({$limit:3}){ "_id" : 1, "course" : "Python表达式问题求解实训", "author" : "李暾", "tags" : [ "Python基础", "求解" ], "learning_num" : 1882 }{ "_id" : 2, "course" : "Java语言之基本语法", "author" : "余跃", "tags" : [ "Java基础", "语法" ], "learning_num" : 814 }{ "_id" : 3, "course" : "Python面向对象编程实训", "author" : "李暾", "tags" : [ "Python基础", "面向对象" ], "learning_num" : 143 }> db.educoder.aggregate({$sort:{learning_num:1}}){ "_id" : 3, "course" : "Python面向对象编程实训", "author" : "李暾", "tags" : [ "Python基础", "面向对象" ], "learning_num" : 143 }{ "_id" : 4, "course" : "Android综合实训之物联网移动应用开发(1)", "author" : "prophet5", "tags" : [ "Android", "物联网", "移动开发" ], "learning_num" : 207 }{ "_id" : 2, "course" : "Java语言之基本语法", "author" : "余跃", "tags" : [ "Java基础", "语法" ], "learning_num" : 814 }{ "_id" : 1, "course" : "Python表达式问题求解实训", "author" : "李暾", "tags" : [ "Python基础", "求解" ], "learning_num" : 1882 }> db.educoder.aggregate({$skip:2}){ "_id" : 3, "course" : "Python面向对象编程实训", "author" : "李暾", "tags" : [ "Python基础", "面向对象" ], "learning_num" : 143 }{ "_id" : 4, "course" : "Android综合实训之物联网移动应用开发(1)", "author" : "prophet5", "tags" : [ "Android", "物联网", "移动开发" ], "learning_num" : 207 }
第3关:2-2-3聚合表达式对文档数据进行统计
一、本关任务:按照编程要求,使用aggregate()
方法,结合聚合管道操作符和聚合表达式对文档数据进行统计。
二、相关知识
为了完成本关任务,你需要掌握:
1.常用聚合表达式; 2.如何结合聚合管道操作符使用聚合表达式完成统计。
聚合表达式
常用的几个聚合表达式:
表达式 | 用法 |
---|---|
$sum | 计算总和 |
$avg | 计算平均值 |
$min | 获取集合中所有文档对应值的最小值 |
$max | 获取集合中所有文档对应值的最大值 |
$push | 在结果文档中插入值到一个数组中 |
$addToSet | 在结果文档中插入值到一个数组中,但不创建副本 |
$first | 根据资源文档的排序获取第一个文档数据 |
$last | 根据资源文档的排序获取最后一个文档数据 |
如何对文档进行统计
例:在数据库mydb2
有集合educoder
内容如下:
_id | course | author | tags | learning_num |
---|---|---|---|---|
1 | Python表达式问题求解实训 | 李暾 | Python基础,求解 | 1882 |
2 | Java语言之基本语法 | 余跃 | Java基础,语法 | 814 |
3 | Python面向对象编程实训 | 李暾 | Python基础,面向对象 | 143 |
4 | Android综合实训之物联网移动应用开发(1) | prophet5 | Android,物联网,移动开发 | 207 |
首先插入数据,见第一关;
现在我们通过
aggregate()
方法来获取每个作者拥有的实训数量,命名为:num_course
:db.educoder.aggregate([{$group:{_id:'$author',num_course:{$sum:1}}}])
命令解析:
先通过聚合管道操作符
$group
将author
字段数据分组;$sum:1
的含义:如果前面的情况出现一次,就加1
,如果后面为$sum:2
,那么前面条件每满足一次就加2
;
通过
aggregate()
方法来获取每个作者的实训学习总人数learning_sum
:db.educoder.aggregate([{$group:{_id:'$author',learning_sum:{$sum:'$learning_num'}}}])
编程要求
为了减少你的工作量,给出以下文档内容:
{
_id:1,
course:'Python表达式问题求解实训',
author:'李暾',
tags:['Python基础','求解'],
learning_num:1882
},
{
_id:2,
course:'Java语言之基本语法',
author:'余跃',
tags:['Java基础','语法'],
learning_num:814
},
{
_id:3,
course:'Python面向对象编程实训',
author:'李暾',
tags:['Python基础','面向对象'],
learning_num:143
},
{
_id:4,
course:'Android综合实训之物联网移动应用开发(1)',
author:'prophet5',
tags:['Android','物联网','移动开发'],
learning_num:207
}
先在命令行中操作,将例子中的文档插入数据库test3
中的集合educoder
中,然后仿照例子中的查询方法,再在右侧代码栏Begin-End
中按要求输入符合以下3
条要求的命令,以分号;
隔开(由于测试需要,请在代码里$
符号前添加转义符\
,不需要有空格,格式如:\$
,平时在命令行操作不需要):
将文档按照作者
author
分组,然后取得每个组的第一个实训名first_course
;查询跟随每个作者
author
的平均学习人数learning_avg
;统计集合中各
tags
拥有的课程数量course_num
(提示:先将tags
字段使用$unwind
拆分再统计)。
具体的返回结果,请查看测试集。
> use test3switched to db test3> db.educoder.insert([{_id:1,course:'Python表达式问题求解实训',author:'李暾',tags:['Python基础','求解'],learning_num:1882},{_id:2,course:'Java语言之基本语法',author:'余跃',tags:['Java基础','语法'],learning_num:814},{_id:3,course:'Python面向对象编程实训',author:'李暾',tags:['Python基础','面向对象'],learning_num:143},{_id:4,course:'Android综合实训之物联网移动应用开发(1)',author:'prophet5',tags:['Android','物联网','移动开发'],learning_num:207}])BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 4, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ]})