上篇最后说到管道操做符,本篇文章将详细说一下管道操做符。mongodb
mongoDB有4类操做符用于文档的操做,例如find查询里面会用到的$gte,$in等。操做符以$开头,分为查询操做符,更新操做符,管道操做符,查询修饰符4大类。其中管道操做符是用于聚合管道中的操做符。express
管道操做符能够分为三类:json
此处中文勉强翻译,以英文为准,欢迎大神给意见,谢谢。segmentfault
参考MongoDB官网:https://docs.mongodb.com/manual/reference/operator/aggregation/#accumulators数组
阶段操做符是使用于db.collection.aggregate方法里面,数组参数中的第一层。函数
db.collection.aggregate( [ { 阶段操做符:表述 }, { 阶段操做符:表述 }, ... ] )
表达式操做符主要用于在管道中构建表达式时使用,使用相似于函数那样须要参数,主要用于$project操做符中,用于构建表达式,使用方法通常以下:this
方法1:翻译
{ <operator>: [ <argument1>, <argument2> ... ] }
方法2:code
{ <operator>: <argument> }
累加器原本只能使用与$groud下,可是版本3.2或以上,部分累加器还能使用于$project。当在$group中使用时,累加器是针对每一个分组使用的;当在$project中使用时,累加器则是针对每一个字面量起做用,具体用法下一篇文章阐述。
因为操做符比较多,本篇文章先说第一类阶段操做符,后面两类在下一篇再说。
操做符 | 简述 |
---|---|
$match | 匹配操做符,用于对文档集合进行筛选 |
$project | 投射操做符,用于重构每个文档的字段,能够提取字段,重命名字段,甚至能够对原有字段进行操做后新增字段 |
$sort | 排序操做符,用于根据一个或多个字段对文档进行排序 |
$limit | 限制操做符,用于限制返回文档的数量 |
$skip | 跳过操做符,用于跳过指定数量的文档 |
$count | 统计操做符,用于统计文档的数量 |
$group | 分组操做符,用于对文档集合进行分组 |
$unwind | 拆分操做符,用于将数组中的每个值拆分为单独的文档 |
$lookup | 链接操做符,用于链接同一个数据库中另外一个集合,并获取指定的文档,相似于populate |
更多操做符介绍详见官网:https://docs.mongodb.com/manual/reference/operator/aggregation/
假设有一个保存用户的集合Users,一个文章的集合Articles,数据大体以下:
users:
[ { name: 'John', age: 16, sex: male, city: guangzhou, _id: 1, ...}, { name: 'Rose', age: 18, sex: female, city: beijing, _id: 2, ...}, { name: 'Jack', age: 29, sex: male, city: guangzhou, _id: 3, ...}, { name: 'Allen', age: 18, sex: female, city: beijing, _id: 4, ...}, { name: 'Cruz', age: 22, sex: male, city: guangzhou, _id: 5, ...}, { name: 'Peter', age: 18, sex: male, city: guangzhou, _id: 6, ...}, { name: 'Kelly', age: 23, sex: female, city: shanghai, _id: 7, ...}, ... ]
articles:
[ { title: 'this is article A', author: 'John', _id: 1, ... }, { title: 'this is article B', author: 'Jack', _id: 2, ... }, { title: 'this is article C', author: 'Rose', _id: 3, ... }, { title: 'this is article D', author: 'John', _id: 4, ... }, { title: 'this is article E', author: 'John', _id: 5, ... }, ... ]
用于重构每个文档的字段,能够提取字段,重命名字段,甚至能够对原有字段进行操做后新增字段
{ $match: { <query> } }
db.users.aggregate([{ $match : { age : "18" } }]);
用于对文档集合进行筛选
{ $project: { <specification(s)> } }
specification的规则
规则 | 描述 |
---|---|
<字段名>: 1 or true | 选择须要返回什么字段 |
_id: 0 or false | 不返回_id(默认返回) |
<字段名>: 表达式 | 使用表达式,能够用于重命名字段,或对其值进行操做,或新增字段 |
<字段名>: 0 or false | 选择须要不返回什么字段,注意:当使用这种用法时,就不要用上面的方法 |
db.users.aggregate([{ $project : { name: 1 } }]);
db.users.aggregate([{ $project : { ueserId: '$_id', _id: 0 } }]);
db.users.aggregate([ { $project : { name: 1, username: { $toUpper: '$name' }, _id: 0 } } ]);
关于管道表达式:最简单的“$project”表达式是包含和排除字段(如: { name: 1 }),以及字段名称$fieldname(如: { userId: '$_id' })。除此之外,还可使用表达式操做符(如: $toUpper)构成更丰富的表达式,将多个字面量和变量组合在一块儿使用,获得更多有意思的值,更多表达式操做符的说明及使用在另外的篇章中详细阐述。
用于根据一个或多个字段对文档进行排序
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
db.users.aggregate([{ $sort : { age: 1 } }]);
用于限制返回文档的数量
{ $limit: <positive integer> }
db.articles.aggregate({ $limit : 3 });
用于跳过指定数量的文档
{ $skip: <positive integer> }
db.users.aggregate([{ $skip : 1 }]);
用于统计文档的数量
{ $count: <string> }
string是统计以后输出统计结果的字段名
db.articles.aggregate([{ totalArticle : 1 }]);
用于对文档集合进行分组
{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }
_id是必须的,用做分组的依据条件
db.users.aggregate([{ $group : { _id: '$sex' } }]);
返回结果:
[ { _id: 'male' }, { _id: 'female' } ]
db.users.aggregate([ { $group : { _id: '$sex', avgAge: { $avg: '$age' }, conut: { $sum: 1 } } } ]);
返回结果:
[ { _id: 'male', avgAge: <男性平均年龄>, count: <男性人数> }, { _id: 'female', avgAge: <女性平均年龄>, count: <女性人数> } ]
此处用到的表达式 { $avg: '$age' } 用于求平均年龄,$avg是求均值的操做符,$sum用于汇总, 都只能在$group中使用的累加器,mongoDB3.2以上版本则还能够在$project中使用,详细会在另外的篇章中阐述。
用于将数组中的每个值拆分为单独的文档
{ $unwind: <field path> }
增长icludeArrayIndex,preserveNullAndEmptyArrays两个可选配置
{ $unwind: { path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
字段 | 类型 | 描述 |
---|---|---|
path | string | 必填,数组的字段名,指定须要拆分的字段 |
includeArrayIndex | string | 可选,定义返回的字段名,返回的值是拆分前值在原数组的位置 |
preserveNullAndEmptyArrays | boolean | 可选,配置在path的值为空或缺失的状况下是否拆分, 默认false |
假设articles文档集合是这样:
{ title: 'this is article A', author: 'John', _id: 1, comments: ['a', 'b', 'c']}
db.articles.aggregate([{ $unwind: '$comments' }]);
结果:
[ { title: 'this is article A', author: 'John', _id: 1, comments: 'a'}, { title: 'this is article A', author: 'John', _id: 1, comments: 'b'}, { title: 'this is article A', author: 'John', _id: 1, comments: 'c'}, ]
假设articles文档集合是这样:
[ { title: 'this is article A', author: 'John', _id: 1, comments: ['a', 'b', 'c'] } { title: 'this is article B', author: 'Jack', _id: 2 }, { title: 'this is article C', author: 'Amy', _id: 3, comments: [] }, { title: 'this is article D', author: 'Lam', _id: 4, comments: null }, ]
操做:
db.articles.aggregate([ { $unwind: { path: '$comments', includeArrayIndex: 'arrayIndex', } } ]);
结果:
[ { title: 'this is article A', author: 'John', _id: 1, comments: 'a', arrayIndex: NumberLong(0) }, { title: 'this is article A', author: 'John', _id: 1, comments: 'b', arrayIndex: NumberLong(1) }, { title: 'this is article A', author: 'John', _id: 1, comments: 'c', arrayIndex: NumberLong(2) }, ]
操做:
db.articles.aggregate([ { $unwind: { path: '$comments', preserveNullAndEmptyArrays: true, } } ]);
结果:
[ { title: 'this is article A', author: 'John', _id: 1, comments: 'a' }, { title: 'this is article A', author: 'John', _id: 1, comments: 'b' }, { title: 'this is article A', author: 'John', _id: 1, comments: 'c' }, { title: 'this is article B', author: 'Jack', _id: 2 }, { title: 'this is article C', author: 'Amy', _id: 3 }, { title: 'this is article C', author: 'Amy', _id: 3, comments: null } ]
用于链接同一个数据库中另外一个集合,并获取指定的文档,相似于populate
{ $lookup: { from: <collection to join>, localField: <field from the input documents>, foreignField: <field from the documents of the "from" collection>, as: <output array field> } }
字段 | 描述 |
---|---|
from | 须要关联的集合名 |
localField | 本集合中须要查找的字段 |
foreignField | 另一个集合中须要关联的字段 |
as | 输出的字段名 |
db.articles.aggregate([ { $lookup: { from: "users", localField: "author", foreignField: "name", as: "author" } } ])
结果:
[ { title: 'this is article A', author: { name: 'John', age: 16, sex: male, city: guangzhou, _id: 1, ... }, _id: 1, ... }, { title: 'this is article B', author: { name: 'Jack', age: 29, sex: male, city: guangzhou, _id: 3, ... }, _id: 2, ... }, { title: 'this is article C', author: { name: 'Rose', age: 18, sex: male, city: beijing, _id: 2, ... }, _id: 3, ... }, { title: 'this is article D', author: { name: 'John', age: 16, sex: male, city: guangzhou, _id: 1, ... }, _id: 4, ... }, { title: 'this is article E', author: { name: 'John', age: 16, sex: male, city: guangzhou, _id: 1, ... }, _id: 5, ... }, ... ]
找出发表文章最多的5位做者,按发表文章排序,显示他的发表文章的总次数,和他本身的信息
db.articles.aggregate([ { $group: { _id: "$author", count: { $sum: 1 }, } }, { $sort: { count: -1 } }, { $skip: 5 }, { $lookup: { from: "users", localField: "author", foreignField: "name", as: "author" } }, { $project: { _id: 0, } } ])
本文介绍了几个使用聚合管道查询时经常使用的管道操做符的用法,熟练地综合使用以上操做符能够对数据进行多样的处理,组合,统计,得出多样化的数据。另外再加以配合表达式操做符(Expression Operators)组成的表达式, 或者在$project或$group中使用累加器(Accumulators)能查询统计的内容会更加的多样化。
感谢阅读~