logo头像

aferica

mongoDB中聚合aggregate使用(二)

本文于1614天之前发表,文中内容可能已经过时。

在之前的博文mongoDB中聚合aggregate使用中,我简单的介绍了一些常用的aggregate方法,在这篇文章中,我主要介绍另外的几个比较常用方法。

好文推荐

在使用和测试方法时,我在网上找到了这篇教程,很详细,且是中文文档,值得一读
MongoDB学习笔记

方法介绍

$lookup

  • 用于对同一数据库中的另外一个非分片集合执行左外连接操作,相当于left join

用法

1
2
3
4
5
6
7
8
{
$lookup: {
from: '被左外连接的集合',
localField: '输入文档中用于匹配(等于)的字段',
foreignField: '被连接集合中用于匹配的字段',
as: '被连接文档在输出中对应的字段名'
}
}

测试数据

订单表

1
2
3
4
5
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },
{ "_id" : 3 }
])

物品表

1
2
3
4
5
6
7
8
db.inventory.insert([
{ "_id" : 1, "sku" : "almonds", description: "product 1", "instock" : 120 },
{ "_id" : 2, "sku" : "bread", description: "product 2", "instock" : 80 },
{ "_id" : 3, "sku" : "cashews", description: "product 3", "instock" : 60 },
{ "_id" : 4, "sku" : "pecans", description: "product 4", "instock" : 70 },
{ "_id" : 5, "sku": null, description: "Incomplete" },
{ "_id" : 6 }
])

示例

1
2
3
4
5
6
7
8
9
10
11
# 订单左外连接库存
db.orders.aggregate([
{
$lookup: {
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
1
2
3
4
5
6
7
8
9
10
11
// 输出文档示例(只示例一条结果)
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2,
// 匹配的被连接文档,数组形式
"inventory_docs" : [
{ "_id" : 1, "sku" : "abc", "description": "product 1", "instock" : 120 }
]
}

$replaceRoot

  • 提升输入文档中的一个内嵌文档,将其作为根文档,代替原有文档,简单理解就是使用结果代替原有文档后继续操作,相当于mysql之类将查询到的结果生成一个虚拟表,再进行操作
  • 多用于一个Object字段
  • $mergeObjects$lookup配合使用更佳

用法

1
2
3
4
5
{ 
$replaceRoot: {
newRoot: <replacementDocument>
}
}

测试数据

用户表

1
2
3
4
5
db.people.insert([
{ "_id" : 1, "name" : "Arlene", "age" : 34, "pets" : { "dogs" : 2, "cats" : 1 } },
{ "_id" : 2, "name" : "Sam", "age" : 41, "pets" : { "cats" : 1, "fish" : 3 } },
{ "_id" : 3, "name" : "Maria", "age" : 25 }
])

示例

pets字段信息提取出来形成一张表

1
2
3
db.people.aggregate([
{ $replaceRoot: { newRoot: { $mergeObjects: [ { dogs: 0, cats: 0, birds: 0, fish: 0 }, "$pets" ] }} }
])
1
2
3
4
// 输出文档示例
{ "dogs" : 2, "cats" : 1, "birds" : 0, "fish" : 0 }
{ "dogs" : 0, "cats" : 1, "birds" : 0, "fish" : 3 }
{ "dogs" : 0, "cats" : 0, "birds" : 0, "fish" : 0 }

## $mergeObjects > 3.6以上版本才可以使用
  • 顾名思义,用于合并对象
  • $replaceRoot$lookup配合使用更佳

用法

1
2
3
4
# 对单个字段或对象使用
{ $mergeObjects: <document> }
# 对多个字段或对象使用
{ $mergeObjects: [ <document1>, <document2>, ... ] }

官方演示:
20190808111929.png

测试数据

使用上述$lookup测试数据

示例

上述使用$lookup结果如下

1
2
3
4
5
6
7
8
9
10
11
// 输出文档示例(只示例一条结果)
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2,
// 匹配的被连接文档,数组形式
"inventory_docs" : [
{ "_id" : 1, "sku" : "abc", "description": "product 1",, "instock" : 120 }
]
}

添加使用$mergeObjects

1
2
3
4
5
6
7
8
9
10
11
12
13
db.orders.aggregate([
{
$lookup: {
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
},
{
$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$inventory_docs", 0 ] }, "$$ROOT" ] } }
}
])
1
2
3
4
5
6
7
8
9
10
11
// 输出文档示例(只示例一条结果)
{
"_id" : 1,
"item" : "abc",
"price" : 12,
"quantity" : 2,
"_id" : 1,
"sku" : "abc",
"description": "product 1",
"instock" : 120
}

$addFields

  • 为每个输入文档添加额外的字段,可以用来处理数据

用法

1
{ $addFields: { <newField>: <expression>, ... } }

测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
db.scores.insert([
{
_id: 1,
student: "Maya",
homework: [ 10, 5, 10 ],
quiz: [ 10, 8 ],
extraCredit: 0
}
{
_id: 2,
student: "Ryan",
homework: [ 5, 6, 5 ],
quiz: [ 8, 8 ],
extraCredit: 8
}
])

示例

1
2
3
4
5
6
7
8
9
10
11
12
db.scores.aggregate([
{
$addFields: {
totalHomework: { $sum: "$homework" } ,
totalQuiz: { $sum: "$quiz" }
}
},
{
$addFields: { totalScore:
{ $add: [ "$totalHomework", "$totalQuiz", "$extraCredit" ] } }
}
])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"_id" : 1,
"student" : "Maya",
"homework" : [ 10, 5, 10 ],
"quiz" : [ 10, 8 ],
"extraCredit" : 0,
"totalHomework" : 25,
"totalQuiz" : 18,
"totalScore" : 43
}
{
"_id" : 2,
"student" : "Ryan",
"homework" : [ 5, 6, 5 ],
"quiz" : [ 8, 8 ],
"extraCredit" : 8,
"totalHomework" : 16,
"totalQuiz" : 16,
"totalScore" : 40
}

示例2

在使用$lookup将数据中的字符串转为ObjectId

1
{ $addFields: { "新字段名称": { "$toObjectId": "$字段名" }}},

$sortByCount

  • 根据指定表达式的值对输入文档进行分组,并且计算每个分组中文档的数量,相当于$group$sort组合使用

用法

1
2
3
4
{ $sortByCount:  <expression> } # 字段名,需要添加$
# 等价于以下两个Stage的组合:
{ $group: { _id: <expression>, count: { $sum: 1 } } },
{ $sort: { count: -1 } }
微信打赏

你的赞赏是对我最大的鼓励