MongoDB 入门专栏
http://blog.youkuaiyun.com/column/details/19681.html
管道操作
mongodb 的数据聚合过程通常会配合管道操作,mongodb 的管道操作概念类似于 LInux 中的管道概念,mongodb 的聚合管道将 mongodb 文档在一个管道处理完毕后将结果传递给下一个管道处理,管道操作是可以以此重复的;
mongodb 管道使用 aggregte() 方法,使用管道表达式表示一个管道过程,表达式是无状态的,只能计算当前聚合管道的文档,不能处理其他文档,以下是常用的几种管道操作表达式:
聚合操作使用 aggregate
() 函数,该函数原型为:db.collection_name.aggregate(<opeation>)
表达式 | 说明 |
$project | 修改输入文档的结构,可以用于重命名、增加/删除域、创建计算结果、嵌套文档; |
$match | 用于过滤数据,只输出符合条件的文档,$match 使用MongoDB的标准查询操作; |
$limit | 用来限制MongoDB聚合管道返回的文档数; |
$skip | 在聚合管道中跳过指定数量的文档,并返回余下的文档; |
$count | 显示当前管道的文档数量 |
$size | 显示一个键(该键为数组类型)的数组长度; |
$unwind | 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值; |
$group | 将集合中的文档分组,可用于统计结果; |
$sort | 将输入文档排序后输出; |
$geoNear | 输出接近某一地理位置的有序文档; |
关于管道完整表达式列表的用法,参见:
https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
示
例使用:
以下为示例使用集合 blog 的文档结构:
{
_id: ObjectId("5a88f21ac05302a3b998abb9"),
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
author: 'assad',
url: 'http://blog.assad.artcile/1',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
_id: ObjectId("5a88f278c05302a3b998abba"),
title: 'NoSQL Overview',
description: 'No sql database is very fast',
author: 'Tim',
url: 'http://blog.assad.artcile/34',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 233
},
{
_id: ObjectId("5a88f299c05302a3b998abbb"),
title: 'SqlLite Overview',
description: 'SqlLite is no sql database',
author: 'assad',
url: 'http://blog.assad.artcile/213',
tags: ['SqlLite', 'database', 'NoSQL'],
likes: 666
}
示例:
# $project 示例:只显示 blog 集合的 title,author,likes 字段
> db.blog.aggregate(
... [ {$project:{_id:0,title:1,author:1,likes:1}} ]
... )
{ "author" : "assad", "likes" : 100, "title" : "article_title" }
{ "author" : "Tim", "likes" : 233, "title" : "article_title" }
{ "author" : "assad", "likes" : 666, "title" : "article_title" }
# $count 示例:显示 blog 集合中 likes>100, likes<500 的文档的数量
> db.blog.aggregate([
... { $match:{likes:{$gt:100,$lt:500}} },
... { $count:"result" }
... ])
{ "result" : 1 }
# 综合示例:显示 blog 集合中每一条文档的 tages 总数
> db.blog.aggregate([
... { $project:{_id:0,title:1,tages_count:{$size:"$tags"}} }
...])
{ "title" : "MongoDB Overview", "tages_count" : 3 }
{ "title" : "NoSQL Overview", "tages_count" : 3 }
{ "title" : "SqlLite Overview", "tages_count" : 3 }
# 综合示例:显示 blog 集合中 author="assad", likes>=100, likes<=800 的文档,按 likes 正序排序,只显示前 5 个文档,只显示 title,author,likes 字段
> db.blog.aggregate( [
... { $project:{title:1,author:1,likes:1} },
... { $match:{author:"assad",likes:{$gte:100,$lte:800}} },
... { $limit:5 },
... { $sort:{likes:1} }
... ]}
{ "_id" : ObjectId("5a88f21ac05302a3b998abb9"), "title" : "MongoDB Overview", "author" : "assad", "likes" : 100 }
{ "_id" : ObjectId("5a88f299c05302a3b998abbb"), "title" : "SqlLite Overview", "author" : "assad", "likes" : 666 }
# 综合示例:显示 blog 集合中 tags 键数组中各个值出现次数的统计
> db.blog.aggregate([
... { $unwind:"$tags" },
... { $group:{_id:"$tags",count:{$sum:1}}}
... ])
{ "_id" : "SqlLite", "count" : 1 }
{ "_id" : "NoSQL", "count" : 3 }
{ "_id" : "database", "count" : 3 }
{ "_id" : "mongodb", "count" : 2 }
$group 分组表达式
在 aggregate() 方法中使用 $group 表达式进行分组操作,类似于 SQL 中的 group by 子句,$group 常用的聚合表达式如下:
表达式 | 说明 |
$sum | 计算总和; |
$avg | 计算平均值; |
$max | 获取文档对应键值的最大值上限; |
$min | 获取文档对应键值的最小值; |
$first | 根据文档的排序获取第一个文档; |
$last | 根据文档的排序获取最后一个文档; |
$push | 在结果文档中插入一个数组; |
$addToSet | 在结果文档中插入一个数组,但是不创建副本; |
示例:
演示 $sum
# 显示每一个 author 各自的 likes 总数
> db.blog.aggregate(
... [ {$group:{_id:"$author",likes_count:{$sum:"$likes"} }} ]
...)
{ "_id" : "Tim", "likes_count" : 233 }
{ "_id" : "assad", "likes_count" : 766 }
# 显示所有文档的 likes 总数
> db.blog.aggregate(
... [ {$group:{_id:null, likes_count:{$sum:"$likes"} }} ]
...)
{ "_id" : null, "likes_count" : 999 }
演示 $avg
# 显示各个 author 的 likes 平均数
> db.blog.aggregate(
... [ {$group:{_id:"$author",likes_avg:{$avg:"$likes"} }} ]
...)
{ "_id" : "Tim", "likes_avg" : 233 }
{ "_id" : "assad", "likes_avg" : 383 }
# 显示所有文档 likes 的平均数
> db.blog.aggregate(
... [ {$group:{_id:null, likes_avg:{$avg:"$likes"} }} ]
...)
{ "_id" : null, "likes_avg" : 333 }
演示 $min,$max
# 显示每一个 author 的 likes 最小值记录
> db.blog.aggregate(
... [ {$group:{_id:"$author",likes_min:{$min:"$likes"} }} ]
... )
{ "_id" : "Tim", "likes_min" : 233 }
{ "_id" : "assad", "likes_min" : 100 }
# 显示所有文档记录中 likes 最小的记录
> db.blog.aggregate(
... [ {$group:{_id:null, likes_min:{$min:"$likes"} }} ]
... )
{ "_id" : null, "likes_min" : 100 }
# 显示所有文档记录中 likes 最大的记录
> db.blog.aggregate(
... [ {$group:{_id:null, likes_max:{$max:"$likes"} }} ]
... )
{ "_id" : null, "likes_max" : 666 }
演示
$push
# 显示各个 author 的 article title 和 likes 列表
> db.blog.aggregate(
... [ {$group:{_id:"$author",article_like:{$push:{title:"$title",likes:"$likes"}}}} ]
... )
{ "_id" : "Tim", "article_like" : [ { "title" : "NoSQL Overview", "likes" : 233 } ] }
{ "_id" : "assad", "article_like" : [ { "title" : "MongoDB Overview", "likes" : 100 }, { "article" : "SqlLite Overview", "likes" : 666 } ] }
按时间分组聚合
对于时间,按年月日,小时分钟秒进行分组聚合,可以使用以下表达式获取时间参数:
- $dayOfYear: 返回该日期是这一年的第几天(全年 366 天)。
- $dayOfMonth: 返回该日期是这一个月的第几天(1到31)。
- $dayOfWeek: 返回的是这个周的星期几(1:星期日,7:星期六)。
- $year: 返回该日期的年份部分。
- $month: 返回该日期的月份部分( 1 到 12)。
- $week: 返回该日期是所在年的第几个星期( 0 到 53)。
- $hour: 返回该日期的小时部分。
- $minute: 返回该日期的分钟部分。
- $second: 返回该日期的秒部分(以0到59之间的数字形式返回日期的第二部分,但可以是60来计算闰秒)。
- $millisecond:返回该日期的毫秒部分( 0 到 999)。
- $dateToString: { $dateToString: { format: , date: } }。
以下是示例用的 loginLog 集合内容:
{
"_id" : ObjectId("5a8969f54a7c036151cce245"),
"ip" : "25.12.4.42",
"date" : ISODate("2018-02-16T09:33:12Z")
}
{
"_id" : ObjectId("5a896a164a7c036151cce246"),
"ip" : "126:123:11:1",
"date" : ISODate("2018-02-17T10:20:00Z")
}
{
"_id" : ObjectId("5a896a2c4a7c036151cce247"),
"ip" : "23.88.44.22",
"date" : ISODate("2018-01-20T20:30:00Z")
}
{
"_id" : ObjectId("5a896a4a4a7c036151cce248"),
"ip" : "55.78.22.123",
"date" : ISODate("2018-02-19T21:59:20Z")
}
>
示例:
# 统计每个月分组的文档总数
> db.loginLog.aggregate([
... { $group:{_id:{$month:"$date"},log_count:{$sum:1}} }
... ])
{ "_id" : 1, "log_count" : 1 }
{ "_id" : 2, "log_count" : 3 }