您的位置:首页 > 博客中心 > 数据库 >

MongoDB聚合管道

时间:2022-03-14 03:23

通过上一篇文章中,认识了MongoDB中四个聚合操作,提供基本功能的count、distinct和group,还有可以提供强大功能的mapReduce。

在MongoDB的2.2版本以后,聚合框架中多了一个新的成员,聚合管道,数据进入管道后就会经过一级级的处理,直到输出。

对于数据量不是特别大,逻辑也不是特别复杂的聚合操作,聚合管道还是比mapReduce有很多优势的:

  1. 相比mapReduce,聚合管道比较容易理解和使用
  2. 可以直接使用管道表达式操作符,省掉了很多自定义js function,一定程度上提高执行效率
  3. 和mapReduce一样,它也可以作用于分片集合

但是,对于数据量大,逻辑复杂的聚合操作,还是要使用mapReduce实现。

 

聚合管道

在聚合管道中,每一步操作(管道操作符)都是一个工作阶段(stage),所有的stage存放在一个array中。MongoDB文档中的描述如下:

db.collection.aggregate( [ { <stage> }, ... ] )

在聚合管道中,每一个stage都对应一个管道操作符,根据MongoDB文档,聚合管道可以支持以下管道操作符:

 

$project

$project主要用于数据投影,实现字段的重命名、增加和删除。下面例子中,重命名了"name"字段,增加了"birthYear"字段,删除了"_id"字段。

 1 > db.runCommand({
 2 ...     "aggregate": "school.students",
 3 ...     "pipeline": [
 4 ...                         {"$match": {"age": {"$lte": 22}}},
 5 ...                         {"$project": {"_id": 0, "studentName": "$name", "gender": 1, "birthYear": {"$subtract": [2014, "$age"]}}},
 6 ...                         {"$sort": {"birthYear":1}}
 7 ...                     ]
 8 ... })
 9 {
10         "result" : [
11                 {
12                         "gender" : "Female",
13                         "studentName" : "Will0",
14                         "birthYear" : 1992
15                 },
16                 {
17                         "gender" : "Male",
18                         "studentName" : "Will4",
19                         "birthYear" : 1993
20                 },
21                 {
22                         "gender" : "Male",
23                         "studentName" : "Will8",
24                         "birthYear" : 1993
25                 },
26                 {
27                         "gender" : "Female",
28                         "studentName" : "Will1",
29                         "birthYear" : 1994
30                 },
31                 {
32                         "gender" : "Male",
33                         "studentName" : "Will5",
34                         "birthYear" : 1994
35                 },
36                 {
37                         "gender" : "Female",
38                         "studentName" : "Will6",
39                         "birthYear" : 1994
40                 }
41         ],
42         "ok" : 1
43 }
44 >

 

$unwind

$unwind用来将数组拆分为独立字段。

 1 > db.runCommand({
 2 ...     "aggregate": "school.students",
 3 ...     "pipeline": [
 4 ...                         {"$match": {"age": 23}},
 5 ...                         {"$project": {"name": 1, "gender": 1, "age": 1, "classes": 1}},
 6 ...                         {"$unwind": "$classes"}
 7 ...                     ]
 8 ... })
 9 {
10         "result" : [
11                 {
12                         "_id" : ObjectId("54805220e31c9e1578ed0ccc"),
13                         "name" : "Will3",
14                         "gender" : "Male",
15                         "age" : 23,
16                         "classes" : "WPF"
17                 },
18                 {
19                         "_id" : ObjectId("54805220e31c9e1578ed0ccc"),
20                         "name" : "Will3",
21                         "gender" : "Male",
22                         "age" : 23,
23                         "classes" : "C"
24                 }
25         ],
26         "ok" : 1
27 }
28 >

 

$group

跟单个的group用法类似,用作分组操作。

使用$group时,必须要指定一个_id域,同时也可以包含一些算术类型的表达式操作符。

下面的例子就是用聚合管道实现得到男生和女生的平均年龄。

 1 > db.runCommand({
 2 ...     "aggregate": "school.students",
 3 ...     "pipeline": [
 4 ...                     {"$group":{"_id":"$gender", "avg": { "$avg": "$age" } }}
 5 ...                  ]
 6 ... })
 7 {
 8         "result" : [
 9                 {
10                         "_id" : "Male",
11                         "avg" : 21.8
12                 },
13                 {
14                         "_id" : "Female",
15                         "avg" : 22
16                 }
17         ],
18         "ok" : 1
19 }
20 >

 

查询男生和女生的最大年龄

 1 > db.runCommand({
 2 ...     "aggregate": "school.students",
 3 ...     "pipeline": [
 4 ...                         {"$group": {"_id": "$gender", "max": {"$max": "$age"}}}
 5 ...                     ]
 6 ... })
 7 {
 8         "result" : [
 9                 {
10                         "_id" : "Male",
11                         "max" : 24
12                 },
13                 {
14                         "_id" : "Female",
15                         "max" : 24
16                 }
17         ],
18         "ok" : 1
19 }
20 >

 

 

管道操作符、管道表达式和表达式操作符

上面的例子中,已经接触了这几个概念,下面进行进一步介绍。

管道操作符作为"键",所对应的"值"叫做管道表达式,例如"{"$group": {"_id": "$gender", "max": {"$max": "$age"}}}"

  • $group是管道操作符
  • {"_id": "$gender", "max": {"$max": "$age"}}是管道表达式
  • $gender在文档中叫做"field path",通过"$"符号来获得特定字段,是管道表达式的一部分
  • $max是表达式操作符,正是因为聚合管道中提供了很多表达式操作符,我们可以省去很多自定义js函数

下面列出了常用的表达式操作符,更详细的信息,

  • Boolean Expressions
    • $and, $or, $ not
  • Comparison Expressions
    • $cmp, $eq, $gt, $gte, $lt, $lte, $ne
  • Arithmetic Expressions
    • $add, $divide, $mod, $multiply, $subtract
  • String Expressions
    • $concat, $strcasecmp, $substr, $toLower, $toUpper

 

总结

聚合管道提供了一种mapReduce 的替代方案,mapReduce使用相对来说比较复杂,而聚合管道的拥有固定的接口,一系列可选择的表达式操作符,对于大多数的聚合任务,聚合管道一般来说是首选方法。

Ps: 文章中使用的例子可以通过以下链接查看

http://files.cnblogs.com/wilber2013/pipeline.js

 

热门排行

今日推荐

热门手游