经过前面的小节,我们已经了解了 mongodb 的大部分功能,今天这节我们来进一步挖掘 mongodb 聚合查询的功能。
分组操作对应的关键字为“$group”,它的作用是根据给出的字段 key,把所有key的值相同的记录放在一起进行运算。这些运行包括常见的“求和($sum)”“计算平均数($avg)”“最大值($max)”“最小值 ($min)”等。
为了便于演示,我们新建一个集合并插入一些数据:
// 创建集合 info
db.createCollection("info");
// 向 info 集合中插入数据
db.info.insertMany(
[
{"name":"Lucy","hire_date":"2022/7/1","score":50},
{"name":"Lili","hire_date":"2022/7/1","score":57},
{"name":"Hanmeimei","hire_date":"2022/7/2","score":64},
{"name":"Lucy","hire_date":"2022/7/3","score":78},
{"name":"Hanmeimei","hire_date":"2022/7/4","score":77},
{"name":"Lili","hire_date":"2022/7/8","score":82},
{"name":"Lucy","hire_date":"2022/7/8","score":88},
{"name":"Hanmeimei","hire_date":"2022/8/8","score":95}
]
);
db.info.aggregate([{"$group":{"_id":"$name"}}]);

2. 去重并选择最新或最老的数据
// 以name为基准去重,然后取各个字段的最新数据
db.info.aggregate(
[
{"$group": {
"_id":"$name",
"date": {
"$last":"$hire_date"
},
"score": {
"$last":"$score"
}
}
}
]
);

这里的关键字“$last”表示取最后一条记录。在 mongodb 中,老数据先插入,新数据后插入,所以每一组的最后一条就是最新插入的数据。
“last”的反义词是“first”,所以关键字“$first”的意思是取 第一条,即是最早插入的数据。
db.info.aggregate(
[
{"$group": {
"_id":"$name",
"date": {
"$first":"$hire_date"
},
"score": {
"$first":"$score"
}
}
}
]
);

3. 分组并计算统计值
db.getCollection("info").aggregate(
[
{
"$group": {
"_id": "$name",
"max_score": {"$max":"$socre"},
"min_score": {"$min":"$score"},
"sum_score": {"$sum":"$score"},
"savg_score": {"$avg":"$score"}
}
}
]
);

“$sum”的值还可以使用数字“1”,这样查询语句就变成了统 计每一个分组内有多少条记录。
4. 拆分数组
之前我们看到了 user 集合中的 price 和 intersts 值的类型是数组,其实我们可以把这个数据中的每个元素拆分为一条记录,这里就需要用到 KaTeX parse error: Expected '}', got 'EOF' at end of input: …o.aggregate([{"unwind":“$字段名”}])
db.user.aggregate([{"$unwind":"$price"}]);

“$unwind”一次只能拆开一个数组,如果还要把“price”字段拆开, 则可以让第一次运行的结果再走一次“$unwind”阶段。
db.user.aggregate([{"$unwind":"$price"},{"$unwind":"$intersts"}]);

联集合查询相当于SQL中的联表查询。在某些情况下,一些相关的数据需要保存到多个集合中,然后使用某一个字段来进行关联,联集合查询的关键字为“$lookup”,它的语法如下:
主集合.aggregate{
[
{
"$lookup": {
"from":"被查集合名",
"localField":"主集合字段",
"foreignField":"被查集合字段",
"as": "保存查询结果的字段名"
}
}
]
}
为了方便演示,我们给 user 集合和 info 集合分别新增一个 id 和 uid 字段用以关联两个集合。
// 为 user 集合新增 id 字段
db.user.updateOne({_id:ObjectId("62e7759f6959971a547354d4")},{"$set":{"id":1}});
db.user.updateOne({_id:ObjectId("62e7759f6959971a547354d5")},{"$set":{"id":2}});
db.user.updateOne({_id:ObjectId("62e775a06959971a547354d6")},{"$set":{"id":3}});
db.user.updateOne({_id:ObjectId("62e775ac6959971a547354d8")},{"$set":{"id":4}});
db.user.updateOne({_id:ObjectId("62e775ac6959971a547354d9")},{"$set":{"id":5}});
db.user.updateOne({_id:ObjectId("62e7ac84a617f839c6d097bf")},{"$set":{"id":6}});
db.user.updateOne({_id:ObjectId("62e7ac84a617f839c6d097c0")},{"$set":{"id":7}});
// 为 info 集合新增 uid 字段
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d2d")},{"$set":{"uid":1}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d2e")},{"$set":{"uid":2}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d2f")},{"$set":{"uid":3}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d30")},{"$set":{"uid":4}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d31")},{"$set":{"uid":5}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d32")},{"$set":{"uid":6}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d33")},{"$set":{"uid":7}});
db.info.updateOne({_id:ObjectId("62e7df2c475e726c7b533d34")},{"$set":{"uid":8}});
准备工作完成,我们可以使用联集合查询的语法查询数据看看效果:
db.user.aggregate(
[
{
"$lookup":{
"from":"info",
"localField":"id",
"foreignField":"uid",
"as" : "result"
}
}
]
);

我们看到,result 字段就是我们查询的结果,它就是被关联集合里查询到的数据:
db.user.aggregate(
[
{
"$lookup":{
"from":"info",
"localField":"id",
"foreignField":"uid",
"as" : "result"
}
},
{"$project":{"result":1,"_id":0}},
{
"$unwind":"$result"
},
{
"$project":{
"name":"$result.name",
"date":"$result.hire_date",
"score":"$result.score",
}
}
]
);

有了之前的基础,上面的语句也就不难理解了,我们拆解出来 result 字段的数据,发现是符合我们预期的。
上面我们以 user 集合为基准查看,那如果我们换为以 info 表为基准又是什么样呢?
db.info.aggregate(
[
{
"$lookup":{
"from":"user",
"localField":"uid",
"foreignField":"id",
"as" : "result"
}
},
{
"$unwind":"$result"
}
]
);

我们看到这里查询出来的数据也是7条,这说明这里的联集合查询实现的是 mysql 中 inner join 的效果。
本节的内容就到这里了,关于 mongo 的基础操作也就到这里了,下节也是最后一节我们学习怎么优化 mongodb。
由于本人也是处于学习阶段,所有内容是看过资料以后自己实验得出,如有不妥之处还望各位批评指正,在下感激不尽。