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}})