1、聚合管道介绍
MongoDB 聚合管道是一种数据处理框架,可以对 MongoDB 中的文档进行复杂的数据处理和转换操作,以生成所需的数据汇总结果。聚合管道由一个或多个阶段组成,每个阶段都代表了一种数据处理操作,这些操作按顺序依次执行,最终生成最终的聚合结果。
聚合管道的基本原理是将输入文档作为输入,并通过一系列阶段逐步转换和处理这些文档,最终得到输出结果。每个阶段都可以对文档进行筛选、分组、投影、排序、限制等操作,从而实现复杂的数据聚合和分析功能。
1)常用管道命令
阶段 | 描述 |
$project | 重塑文档流。 $project 可以重命名、添加或删除字段,以及创建计算值和子文档。 |
$match | 过滤文档流,并只允许匹配的文档传递到下一个管道阶段。 $match 使用标准的 MongoDB 查询。 |
$limit | 限制聚合管道中文档的数量。 |
$skip | 跳过管道中指定数量的文档,并返回剩余的文档。 |
$unwind | 将一组文档拆分成单个文档流。 |
$group | 将文档分组,以便根据文档集合计算聚合值。 |
$lookup | 用于引入其它集合的数据 (表关联查询) |
$sort | 将所有输入文档按排序顺序返回。 |
$geoNear | 基于与地理位置点的接近程度,返回有序的文档流。 |
SQL | MongoDB | 表达式 |
WHERE | $match | 条件筛选 |
GROUP BY | $group | 分组聚合 |
HAVING | $match | 分组后条件筛选 |
SELECT | $project | 投影 |
ORDER BY | $sort | 排序 |
LIMIT | $limit | 限制数量 |
SUM() | $sum | 求和 |
COUNT() | $sum | 统计数量 |
join | $lookup | 联表查询 |
2)常用表达式
处理输入文档并输出
语法:表达式:‘$列名’
表达式操作符 | 描述 |
$addToSet | 将文档指定字段的值去重 |
$max | 文档指定字段的最大值 |
$min | 文档指定字段的最小值 |
$sum | 文档指定字段求和 |
$avg | 文档指定字段求平均 |
$gt | 大于给定值 |
$lt | 小于给定值 |
$eq | 等于给定值 |
2、MongoDB 聚合操作使用示例
文档结构:
{ "_id" : ObjectId("5f14d39e6d3c96242c09ce9b"), "Id" : 5, "title" : "Python 数据类型字典(Dictionary)", "relateId" : 31, "relateTitle" : "Java数据类型(基本类型和引用类型)", "scores" : 0.442981194961, "isActive" : true, "createTime" : ISODate("2020-07-20T15:13:34.555+08:00"), "updateTime" : ISODate("2020-07-20T15:13:34.555+08:00"), "createUser" : "levi", "updateUser" : "levi" }
1)$project
修改文档的结构,可以用来重命名、增加或删除文档中的字段;
例如,
db.getCollection('ArticleRelation').aggregate([ { $project:{title:1, relateTitle:1 } } ])
输出:
/* 1 */ { "_id" : ObjectId("5f14d39e6d3c96242c09ce9b"), "title" : "Python 数据类型字典(Dictionary)", "relateTitle" : "Java数据类型(基本类型和引用类型)" } /* 2 */ { "_id" : ObjectId("5f14d4816d3c96242c09cede"), "title" : "Js(Javascript)中的apply方法的使用", "relateTitle" : "Js(Javascript)中的call方法的使用" } /* 3 */ { "_id" : ObjectId("5f14d4816d3c96242c09cedf"), "title" : "Js(Javascript)中的apply方法的使用", "relateTitle" : "Js(Javascript)中的bind方法的使用" }
2)$match
用于过滤文档。用法类似于 find() 方法中的参数。
db.getCollection('ArticleRelation').aggregate([ { $project:{title:1, relateTitle:1,isActive:1 } }, { $match:{"isActive":{"$eq":true}}}])
输出结果:
/* 1 */ { "_id" : ObjectId("5f14d39e6d3c96242c09ce9b"), "title" : "Python 数据类型字典(Dictionary)", "relateTitle" : "Java数据类型(基本类型和引用类型)", "isActive" : true } /* 2 */ { "_id" : ObjectId("5f14d4816d3c96242c09cede"), "title" : "Js(Javascript)中的apply方法的使用", "relateTitle" : "Js(Javascript)中的call方法的使用", "isActive" : true } /* 3 */ { "_id" : ObjectId("5f14d4816d3c96242c09cedf"), "title" : "Js(Javascript)中的apply方法的使用", "relateTitle" : "Js(Javascript)中的bind方法的使用", "isActive" : true }
3)$group
将集合中的文档进行分组,可用于统计结果
db.getCollection("ArticleRelation").aggregate( [ { "$group" : { "_id" : { "Id" : "$Id", "relateId" : "$relateId" }, "COUNT(*)" : { "$sum" : NumberInt(1) }, "dups": {"$addToSet": '$_id'} } }, { "$project" : { "relateId" : "$_id.relateId", "Id" : "$_id.Id", "COUNT(*)" : "$COUNT(*)", "dups":"$dups", "_id" : NumberInt(0) } } ], { "allowDiskUse" : true } )
输出结果:
/* 1 */ { "relateId" : 3041, "Id" : 3051, "COUNT(*)" : 1, "dups" : [ ObjectId("64c0ec5bf9004501a2230504") ] } /* 2 */ { "relateId" : 3039, "Id" : 3051, "COUNT(*)" : 1, "dups" : [ ObjectId("64c0ec5af9004501a2230503") ] } /* 3 */ { "relateId" : 3035, "Id" : 3051, "COUNT(*)" : 1, "dups" : [ ObjectId("64c0ec57f9004501a22304ff") ] }
4)$sort
db.getCollection('ArticleRelation').aggregate([ { $project:{title:1, createTime:1,relateTitle:1,isActive:1 } }, { $match:{"isActive":{"$eq":true}}}, { $sort:{"createTime":-1}}])
输出结果:
/* 1 */ { "_id" : ObjectId("64c0f1d1f900450c9e3a8702"), "title" : "Linux系统简介及各发行版之间区别", "relateTitle" : ".NET Core 、 .NET 5、.NET 6和.NET 7 简介及区别", "isActive" : true, "createTime" : ISODate("2023-07-26T18:13:37.258+08:00") } /* 2 */ { "_id" : ObjectId("64c0f123f900450c9e3a8701"), "title" : "Linux系统简介及各发行版之间区别", "relateTitle" : "XML和JSON格式简介、区别及解析", "isActive" : true, "createTime" : ISODate("2023-07-26T18:10:43.731+08:00") } /* 3 */ { "_id" : ObjectId("64c0ec5bf9004501a2230504"), "title" : "Docker 常用命令详解", "relateTitle" : "Docker CLI docker wait 常用命令", "isActive" : true, "createTime" : ISODate("2023-07-26T17:50:19.347+08:00") }
5)$limit 和 $skip
$limit
限制结果的数量,$skip
跳过文档的数量。
db.getCollection('ArticleRelation').aggregate([ { $project:{title:1, createTime:1,relateTitle:1,isActive:1 } }, { $match:{"isActive":{"$eq":true}}}, { $sort:{"createTime":-1}}, { "$skip":1 }, { "$limit":2 } ])
输出结果:
/* 1 */ { "_id" : ObjectId("64c0f123f900450c9e3a8701"), "title" : "Linux系统简介及各发行版之间区别", "relateTitle" : "XML和JSON格式简介、区别及解析", "isActive" : true, "createTime" : ISODate("2023-07-26T18:10:43.731+08:00") } /* 2 */ { "_id" : ObjectId("64c0ec5bf9004501a2230504"), "title" : "Docker 常用命令详解", "relateTitle" : "Docker CLI docker wait 常用命令", "isActive" : true, "createTime" : ISODate("2023-07-26T17:50:19.347+08:00") }
6)$lookup
用于表关联
db.getCollection('ArticleRelation').aggregate([ {"$lookup": { from: "Articles", localField: "Id", foreignField: "Id", as: "articles" } } ])
输出结果:
/* 1 */ { "_id" : ObjectId("5f14d39e6d3c96242c09ce9b"), "Id" : 5, "title" : "Python 数据类型字典(Dictionary)", "relateId" : 31, "relateTitle" : "Java数据类型(基本类型和引用类型)", "scores" : 0.442981194961, "isActive" : true, "createTime" : ISODate("2020-07-20T15:13:34.555+08:00"), "updateTime" : ISODate("2020-07-20T15:13:34.555+08:00"), "createUser" : "levi", "updateUser" : "levi", "articles" : [ { "_id" : ObjectId("5ba0bf50f900453f2c303d76"), "Id" : 5, "title" : "Python 数据类型字典(Dictionary)", "keyword" : "python字典,python dic(dictionary),", "content" :"Python 数据类型字典(Dictionary)", "is_active" : true, "create_time" : ISODate("2018-09-18T17:03:12.000+08:00"), "update_time" : ISODate("2018-11-05T16:07:54.345+08:00"), "create_user" : "levi", "update_user" : "levi", } ] }
3、MongoDB 删除重复数据
var duplicates = []; var data = db.getCollection("ArticleRelation").aggregate( [ { "$group" : { "_id" : { "Id" : "$Id", "relateId" : "$relateId" }, "COUNT(*)" : { "$sum" : NumberInt(1) }, "dups": {"$addToSet": '$_id'} } }, { "$project" : { "relateId" : "$_id.relateId", "Id" : "$_id.Id", "COUNT(*)" : "$COUNT(*)", "dups":"$dups", "_id" : NumberInt(0) } }, { "$match" : { "COUNT(*)" : { "$gt" : NumberLong(1) } } } ], { "allowDiskUse" : true } ).forEach(function(doc) { doc.dups.shift();//删除重复数据中的第一条 doc.dups.forEach( function(dupId){ duplicates.push(dupId); // 获取所有需要删除的主键_id } ) }); printjson(duplicates); //删除数据 db.getCollection("ArticleRelation").remove({_id:{$in:duplicates}})