大家好,又见面了,我是你们的朋友全栈君。
在使用MongoDB存储数据的时候,我们查询的时候,有时候难免会需要进行连表查询。但是MongoDB本身是非关系性数据库,连表查询,很多时候,需要我们自己在代码里手工操作。但是从 MongoDB 3.2 版本过后,我们可以使用 $lookup 进行连表查询。下面就简单介绍一下 MongoDB 的 $lookup 的简单使用。
比如现在我们有两张表, user 和 order 表。其中 user 表中的字段有 _id、uid、name、age;order 表中的字段有:_id、uid、product、money; 两张表存储的数据为:
代码语言:javascript复制users = [{
_id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小红", age: 26 }, { _id: ObjectId("5af2b2c6b138c267e414c073"), uid: "uid001", name: "小芳", age: 27 }]
代码语言:javascript复制orders = [{
_id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "产品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c072"), uid: "uid000", product: "产品2", money: 200 }, { _id: ObjectId("4af2b2c6b138c267e414c073"), uid: "uid001", product: "产品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c074"), uid: "uid001", product: "产品2", money: 200 }]
假如现在有两个需求:
- 查询用户信息并且显示该用户的总消费金额(用户名、年龄、总消费金额)
- 查询用户的订单信息(订单id、产品、价格、用户名)
1. 首先来看第一个需求:
这个需求如果我们不考虑连表,只考虑关联的话,应该是
- 先查询出用户表所有的数据
- 在订单表中求出每一个用户的消费总金额
- 遍历用户和订单数据,然后一一通过 uid 进行匹配对应。
如果按照我们的数据库连表来说:那应该是我们查询 user 表关联到 order 表,然后分组根据 uid 统计求和;下面来看一看具体的实现方式。
1.1 连表查询
代码语言:javascript复制db.user.aggregate([{
$lookup: { // 左连接
from: "order", // 关联到order表
localField: "uid", // user 表关联的字段
foreignField: "uid", // order 表关联的字段
as: "orders"
}
}]);
这个时候出来的结果应该为:
代码语言:javascript复制users = [{
_id: ObjectId("5af2b2c6b138c267e414c072"), uid: "uid000", name: "小红", age: 26, orders: [{ _id: ObjectId("4af2b2c6b138c267e414c071"), uid: "uid000", product: "产品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c072"), uid: "uid000", product: "产品2", money: 200 }] }, { _id: ObjectId("5af2b2c6b138c267e414c073"), uid: "uid001", name: "小芳", age: 27, orders: [{ _id: ObjectId("4af2b2c6b138c267e414c073"), uid: "uid001", product: "产品1", money: 100 }, { _id: ObjectId("4af2b2c6b138c267e414c073"), uid: "uid001", product: "产品1", money: 200 }] }]
1.2 拆分 orders 数组
代码语言:javascript复制{
$unwind: { // 拆分子数组
path: "$orders",
preserveNullAndEmptyArrays: true // 空的数组也拆分
}
}
这个时候的数据结果应该是这样的
代码语言:javascript复制[{
_id: ObjectId("5af2b2c6b138c267e414c072"),
uid: "uid000",
name: "小红",
age: 26,
orders: {
_id: ObjectId("4af2b2c6b138c267e414c071"),
uid: "uid000",
product: "产品1",
money: 100
}
}, {
_id: ObjectId("5af2b2c6b138c267e414c072"),
uid: "uid000",
name: "小红",
age: 26,
orders: {
_id: ObjectId("4af2b2c6b138c267e414c072"),
uid: "uid000",
product: "产品2",
money: 200
}
} …… ]
1.3 分组求和并返回字段数据
代码语言:javascript复制{
$group: { // 分组查询
_id: "$_id",
name: { $first: "$name" },
age: { $first: "$age" },
money: {$sum: "$orders.money"}
}
}
这样就查询出了我们所需要的数据。将代码总结一下为:
代码语言:javascript复制db.user.aggregate([{
$lookup: { // 左连接
from: "order", // 关联到order表
localField: "uid", // user 表关联的字段
foreignField: "uid", // order 表关联的字段
as: "orders"
}
}, {
$unwind: { // 拆分子数组
path: "$orders",
preserveNullAndEmptyArrays: true // 空的数组也拆分
}
}, { // 分组求和并返回
$group: { // 分组查询
_id: "$_id",
name: { $first: "$name" },
age: { $first: "$age" },
money: {$sum: "$orders.money"}
}
}]);
2. 查询用户的订单信息
2.1 连表查询
这个时候的连表是 order 表 跟 user 表关联(上一个是 user 表 和 order 表关联)
代码语言:javascript复制{
$lookup: {
from: "users",
localField: "openid",
foreignField: "openid",
as: "u"
}
}
2.2 拆分子数组
代码语言:javascript复制{ $unwind: "$u" }
2.3 只返回需要的字段
将 user 中需要返回的字段,提到子目录来
代码语言:javascript复制{$addFields: { name: "$u.name" }}
2.4 返回最终需要的字段结果
代码语言:javascript复制{
$project: {
_id: 1,
product: 1,
money: 1,
name: 1
}
}
最终的代码为:
代码语言:javascript复制db.order.aggregate([{
$lookup: {
from: "users",
localField: "openid",
foreignField: "openid",
as: "u"
}
}, {
$unwind: "$u"
}, {
$addFields: { name: "$u.name" }
}, {
$project: {
_id: 1,
product: 1,
money: 1,
name: 1
}
}]);
代码语言:javascript复制虽然在 MongoDB 3.2 后我们能够进行连表查询了,方便了很多。但是其实 MongoDB 本身是非关系性数据库。如果需要进行频繁的这种连表查询,我们可以考虑优化我们的数据库表。比如在订单表里面,每一条的订单记录都把我们的用户信息放进去。
[{
_id: ObjectId("4af2b2c6b138c267e414c071"),
uid: "uid000",
product: "产品1",
money: 100,
user: {
_id: ObjectId("5af2b2c6b138c267e414c072"),
uid: "uid000",
name: "小红",
age: 26
}
}, {
_id: ObjectId("4af2b2c6b138c267e414c071"),
uid: "uid000",
product: "产品1",
money: 100,
user: {
_id: ObjectId("5af2b2c6b138c267e414c072"),
uid: "uid000",
name: "小红",
age: 26
}
}]
这个时候,在实现两个需求就很简单了:
代码语言:javascript复制// 1. 查询用户信息并且显示该用户的总消费金额(用户名、年龄、总消费金额)
db.order.aggregate([{ // 根据 uid 求和
$group: {
_id: '$user.uid',
money: {
$sum: "$money"
},
name: { $first: "$user.name" },
age: { $first: "$user.age" }
}
}]);
// 2. 查询用户的订单信息(订单id、产品、价格、用户名)
db.order.aggregate([{
{$addFields: { name: "$user.name" }}
}, { // 根据 uid 求和
$project: {
_id: 1,
money: 1,
product: 1,
name: 1
}
}]);
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。