资讯专栏INFORMATION COLUMN

【mongoDB查询进阶】聚合管道(二) -- 阶段操作符

flybywind / 3281人阅读

摘要:当在中使用时,累加器是针对每个分组使用的当在中使用时,累加器则是针对每个字面量起作用,具体用法下一篇文章阐述。另外再加以配合表达式操作符组成的表达式或者在或中使用累加器能查询统计的内容会更加的多样化。

上篇最后说到管道操作符,本篇文章将详细说一下管道操作符。

mongoDB查询进阶--聚合管道(一)回顾

什么是管道操作符(Aggregation Pipeline Operators)

mongoDB有4类操作符用于文档的操作,例如find查询里面会用到的$gte,$in等。操作符以$开头,分为查询操作符,更新操作符,管道操作符,查询修饰符4大类。其中管道操作符是用于聚合管道中的操作符。

管道操作符的分类

管道操作符可以分为三类:

阶段操作符(Stage Operators)

表达式操作符(Expression Operators)

累加器(Accumulators)

此处中文勉强翻译,以英文为准,欢迎大神给意见,谢谢。

参考MongoDB官网:https://docs.mongodb.com/manual/reference/operator/aggregation/#accumulators

阶段操作符(Stage Operators)

阶段操作符是使用于db.collection.aggregate方法里面,数组参数中的第一层。

db.collection.aggregate( [ { 阶段操作符:表述 }, { 阶段操作符:表述 }, ... ] )
表达式操作符(Expression Operators)

表达式操作符主要用于在管道中构建表达式时使用,使用类似于函数那样需要参数,主要用于$project操作符中,用于构建表达式,使用方法一般如下:

方法1:

{ : [ ,  ... ] }

方法2:

{ :  }
累加器(Accumulators)

累加器本来只能使用与$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 匹配操作符
说明:

用于重构每一个文档的字段,可以提取字段,重命名字段,甚至可以对原有字段进行操作后新增字段

用法:
{ $match: {  } }
示例:

查询用户年龄是18岁的用户

db.users.aggregate([{ $match : { age : "18" } }]);
$project 投射操作符
说明:

用于对文档集合进行筛选

用法:
{ $project: {  } }

specification的规则

规则 描述
<字段名>: 1 or true 选择需要返回什么字段
_id: 0 or false 不返回_id(默认返回)
<字段名>: 表达式 使用表达式,可以用于重命名字段,或对其值进行操作,或新增字段
<字段名>: 0 or false 选择需要不返回什么字段,注意:当使用这种用法时,就不要用上面的方法
示例1:

用户集合投射用户姓名

不返回_id

db.users.aggregate([{ $project : { name: 1 } }]);
示例2:

将_id重命名为userId

不返回_id_

db.users.aggregate([{ $project : { ueserId: "$_id", _id: 0 } }]);
示例3:

返回新字段username,并使用表达式让它的值为name的大写。

db.users.aggregate([ 
    { 
        $project : {
            name: 1, 
            username: { $toUpper: "$name" }, 
            _id: 0 
        } 
    } 
]);

关于管道表达式:最简单的“$project”表达式是包含和排除字段(如: { name: 1 }),以及字段名称$fieldname(如: { userId: "$_id" })。除此以外,还可以使用表达式操作符(如: $toUpper)构成更丰富的表达式,将多个字面量和变量组合在一起使用,得到更多有意思的值,更多表达式操作符的说明及使用在另外的篇章中详细阐述。

$sort 排序操作符
说明:

用于根据一个或多个字段对文档进行排序

用法:
{ $sort: { : , :  ... } }
示例:

users集合按照年龄age从低到高排序

db.users.aggregate([{ $sort : { age: 1 } }]);
$limit 限制操作符
说明:

用于限制返回文档的数量

用法:
{ $limit:  }
示例:

返回5篇article

db.articles.aggregate({ $limit : 3 });
$skip 跳过操作符
说明:

用于跳过指定数量的文档

用法:
{ $skip:  }
示例:

跳过1个文档

db.users.aggregate([{ $skip : 1 }]);
$count 统计操作符
说明:

用于统计文档的数量

用法:
{ $count:  }

string是统计之后输出统计结果的字段名

示例:

统计文章的总数,以totalArticle返回

db.articles.aggregate([{ totalArticle : 1 }]);
$group 分组操作符
说明:

用于对文档集合进行分组

用法:
{ $group: { _id: , : {  :  }, ... } }

_id是必须的,用作分组的依据条件

示例:

将用户(users)按性别(sex)分组

db.users.aggregate([{ $group : { _id: "$sex" } }]);

返回结果:

[
  { _id: "male" },
  { _id: "female" }
]
进阶示例:

将用户(users)按性别(sex)分组

分组后使用计算各自性别的平均年龄

统计不同的性别的人数,并以count返回

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 拆分操作符
说明:

用于将数组中的每一个值拆分为多带带的文档

用法:
{ $unwind:  }
3.2+版本的用法:

增加icludeArrayIndex,preserveNullAndEmptyArrays两个可选配置

{
  $unwind:
    {
      path: ,
      includeArrayIndex: ,
      preserveNullAndEmptyArrays: 
    }
}
字段 类型 描述
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"},
]
进阶示例(v3.2+):

假设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 }
]
$lookup 连接操作符
说明:

用于连接同一个数据库中另一个集合,并获取指定的文档,类似于populate

用法:
{
   $lookup:
     {
       from: ,
       localField: ,
       foreignField: ,
       as: 
     }
}
字段 描述
from 需要关联的集合名
localField 本集合中需要查找的字段
foreignField 另外一个集合中需要关联的字段
as 输出的字段名
示例:

ariticles中的author关联到user表

authoer字段返回详细的用户的信息

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位作者,按发表文章排序,显示他的发表文章的总次数,和他自己的信息

文章按照作者分组,统计次数

按照次数从高到低排序

截取头5名

关联用户信息

不输出文章_id

操作
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)能查询统计的内容会更加的多样化。

感谢阅读~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84867.html

相关文章

  • mongoDB查询进阶聚合管道() -- 阶段作符

    摘要:当在中使用时,累加器是针对每个分组使用的当在中使用时,累加器则是针对每个字面量起作用,具体用法下一篇文章阐述。另外再加以配合表达式操作符组成的表达式或者在或中使用累加器能查询统计的内容会更加的多样化。 上篇最后说到管道操作符,本篇文章将详细说一下管道操作符。 mongoDB查询进阶--聚合管道(一)回顾 什么是管道操作符(Aggregation Pipeline Operators) ...

    brianway 评论0 收藏0
  • mongoDB查询进阶聚合管道(四) -- 累加器(Accumulators)

    摘要:累加器累加器本来只能使用与下,但是版本或以上,部分累加器还能使用于。当在中使用时,累加器是针对每个分组使用的当在中使用时,累加器则是针对每个字面量起作用。 回顾 相关文章回顾 mongoDB查询进阶--聚合管道(一)回顾mongoDB查询进阶--聚合管道(二)回顾mongoDB查询进阶--聚合管道(三)回顾 管道操作符的分类 管道操作符可以分为三类: 阶段操作符(Stage Oper...

    tomato 评论0 收藏0
  • mongoDB查询进阶聚合管道(四) -- 累加器(Accumulators)

    摘要:累加器累加器本来只能使用与下,但是版本或以上,部分累加器还能使用于。当在中使用时,累加器是针对每个分组使用的当在中使用时,累加器则是针对每个字面量起作用。 回顾 相关文章回顾 mongoDB查询进阶--聚合管道(一)回顾mongoDB查询进阶--聚合管道(二)回顾mongoDB查询进阶--聚合管道(三)回顾 管道操作符的分类 管道操作符可以分为三类: 阶段操作符(Stage Oper...

    wmui 评论0 收藏0
  • mongoDB查询进阶聚合管道(三)--表达式作符

    摘要:用法取反操作符,返回表达式中取反后的布尔值。用法示例假设有一个关于考试成绩的集合操作如下返回结果数学操作符操作符简述求绝对值操作符,于版新加入。用法进一法取整操作符,取于版新加入。用法切割操作符,用于对字符串进行分切。 回顾 相关文章回顾 mongoDB查询进阶--聚合管道(一)回顾mongoDB查询进阶--聚合管道(二)回顾 管道操作符的分类 管道操作符可以分为三类: 阶段操作符(...

    wenhai.he 评论0 收藏0
  • mongoDB查询进阶聚合管道(三)--表达式作符

    摘要:用法取反操作符,返回表达式中取反后的布尔值。用法示例假设有一个关于考试成绩的集合操作如下返回结果数学操作符操作符简述求绝对值操作符,于版新加入。用法进一法取整操作符,取于版新加入。用法切割操作符,用于对字符串进行分切。 回顾 相关文章回顾 mongoDB查询进阶--聚合管道(一)回顾mongoDB查询进阶--聚合管道(二)回顾 管道操作符的分类 管道操作符可以分为三类: 阶段操作符(...

    v1 评论0 收藏0

发表评论

0条评论

flybywind

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<