1. MongoDB简介
MongoDB是一款面向文档的开源数据库系统,由C++语言编写,旨在为WEB应用提供可扩展、高性能、完全无操作数据库的解决方案。在MongoDB中,数据存储在类似于JSON的BSON格式中,BSON是一种二进制编码格式,能够高效地存储数据。MongoDB的特点是高性能、高可扩展性、丰富的查询语言、支持的复杂数据结构以及灵活的数据模型。
2. MongoDB联表查询
MongoDB是一款NoSQL数据库,相对于关系型数据库而言,它对于联表查询有自己独特的处理方式。在关系型数据库中,为了实现两张表的关联查询,需要使用JOIN操作,而在MongoDB中,可以通过嵌套数据结构和引用方式完成多张表的关联操作。
2.1. 嵌套数据结构
在MongoDB中,可以将一个文档对象嵌套在另一个文档对象中,从而实现对多个集合的关联操作。例如:
// 学生集合
{
"_id": ObjectId("5e3f7e72c287e604696a0e96"),
"name": "张三",
"age": 18,
"class": "1班",
"grade": {
"math": 90,
"english": 80,
"chinese": 85
}
}
// 班级集合
{
"_id": ObjectId("5e3f7e72c287e604696a0e97"),
"name": "1班",
"teacher": "李老师"
}
上述代码中,可以看到学生集合中的class属性和班级集合中的name属性可以建立起联系,达到关联查询的目的。在查询时,可以使用$lookup操作符完成两张表之间的关联操作,示例如下:
db.students.aggregate([
{
$lookup: {
from: "classes",
localField: "class",
foreignField: "name",
as: "class_info"
}
}
])
其中,$lookup操作符将学生集合与班级集合进行关联,通过localField和foreignField指定关联的字段,as用来指定关联之后的输出结果。执行完成后,将返回下面的结果:
{
"_id": ObjectId("5e3f7e72c287e604696a0e96"),
"name": "张三",
"age": 18,
"class": "1班",
"grade": {
"math": 90,
"english": 80,
"chinese": 85
},
"class_info": [
{
"_id": ObjectId("5e3f7e72c287e604696a0e97"),
"name": "1班",
"teacher": "李老师"
}
]
}
2.2. 引用方式
除了嵌套数据结构,MongoDB还支持引用方式进行多张表的关联操作。在引用方式中,通过在一个集合中添加另一个集合文档的id字段,实现两张表之间的关联。例如:
// 学生集合
{
"_id": ObjectId("5e3f7e72c287e604696a0e96"),
"name": "张三",
"age": 18,
"class_id": ObjectId("5e3f7e72c287e604696a0e97"),
"grade": {
"math": 90,
"english": 80,
"chinese": 85
}
}
// 班级集合
{
"_id": ObjectId("5e3f7e72c287e604696a0e97"),
"name": "1班",
"teacher": "李老师"
}
在上述代码中,学生集合中的class_id属性和班级集合中的_id属性可以建立起联系,达到关联查询的目的。在查询时,可以使用$lookup操作符完成两张表之间的关联操作,示例如下:
db.students.aggregate([
{
$lookup: {
from: "classes",
localField: "class_id",
foreignField: "_id",
as: "class_info"
}
}
])
其中,$lookup操作符将学生集合与班级集合进行关联,通过localField和foreignField指定关联的字段,as用来指定关联之后的输出结果。执行完成后,将返回下面的结果:
{
"_id": ObjectId("5e3f7e72c287e604696a0e96"),
"name": "张三",
"age": 18,
"class_id": ObjectId("5e3f7e72c287e604696a0e97"),
"grade": {
"math": 90,
"english": 80,
"chinese": 85
},
"class_info": [
{
"_id": ObjectId("5e3f7e72c287e604696a0e97"),
"name": "1班",
"teacher": "李老师"
}
]
}
3. 同库联表查询方法示例
在MongoDB中,同库联表查询可以通过$lookup操作符和嵌套数据结构的方式来实现。下面是使用嵌套数据结构方式进行同库联表查询的示例:
// 创建订单集合
db.createCollection("orders")
// 向订单集合中插入数据
db.orders.insert([
{
"_id": ObjectId("5e3f7e72c287e604696a0e98"),
"product": "牛仔裤",
"price": 200.0,
"quantity": 2,
"customer": {
"name": "张三",
"address": "北京市朝阳区"
}
},
{
"_id": ObjectId("5e3f7e72c287e604696a0e99"),
"product": "衬衫",
"price": 100.0,
"quantity": 1,
"customer": {
"name": "李四",
"address": "上海市浦东新区"
}
}
])
// 查询订单集合并关联顾客集合
db.orders.aggregate([
{
$lookup: {
from: "orders",
localField: "customer",
foreignField: "_id",
as: "customer_info"
}
}
])
上述代码中,插入了两条订单数据,每条订单都包含一个顾客对象。在执行查询时,$lookup操作符将订单集合与顾客集合进行关联,通过localField和foreignField指定关联的字段,as用来指定关联之后的输出结果。执行完成后,将返回下面的结果:
{
"_id": ObjectId("5e3f7e72c287e604696a0e98"),
"product": "牛仔裤",
"price": 200.0,
"quantity": 2,
"customer": {
"name": "张三",
"address": "北京市朝阳区"
},
"customer_info": []
},
{
"_id": ObjectId("5e3f7e72c287e604696a0e99"),
"product": "衬衫",
"price": 100.0,
"quantity": 1,
"customer": {
"name": "李四",
"address": "上海市浦东新区"
},
"customer_info": []
}
由于顾客集合并不存在,因此返回的结果中,customer_info数组为空。
3.1. $unwind操作符
在上面的示例中,如果将顾客对象改成数组格式,结果如下:
db.orders.insert([
{
"_id": ObjectId("5e3f7e72c287e604696a0e98"),
"product": "牛仔裤",
"price": 200.0,
"quantity": 2,
"customer": [
{
"name": "张三",
"address": "北京市朝阳区"
}
]
},
{
"_id": ObjectId("5e3f7e72c287e604696a0e99"),
"product": "衬衫",
"price": 100.0,
"quantity": 1,
"customer": [
{
"name": "李四",
"address": "上海市浦东新区"
}
]
}
])
执行同样的查询操作,结果如下:
{
"_id": ObjectId("5e3f7e72c287e604696a0e98"),
"product": "牛仔裤",
"price": 200.0,
"quantity": 2,
"customer": [
{
"name": "张三",
"address": "北京市朝阳区"
}
]
},
{
"_id": ObjectId("5e3f7e72c287e604696a0e99"),
"product": "衬衫",
"price": 100.0,
"quantity": 1,
"customer": [
{
"name": "李四",
"address": "上海市浦东新区"
}
]
}
可以看到,查询结果并没有关联顾客信息。这是因为如果字段值为数组,则需要使用$unwind操作符将数组拆分。例如:
db.orders.aggregate([
{ $unwind: "$customer" },
{
$lookup: {
from: "orders",
localField: "customer",
foreignField: "_id",
as: "customer_info"
}
}
])
执行上述操作后,将返回如下结果:
{
"_id": ObjectId("5e3f7e72c287e604696a0e98"),
"product": "牛仔裤",
"price": 200.0,
"quantity": 2,
"customer": {
"name": "张三",
"address": "北京市朝阳区"
},
"customer_info": []
},
{
"_id": ObjectId("5e3f7e72c287e604696a0e99"),
"product": "衬衫",
"price": 100.0,
"quantity": 1,
"customer": {
"name": "李四",
"address": "上海市浦东新区"
},
"customer_info": []
}
$unwind操作符将顾客对象拆分成单独的文档,然后关联订单集合得到最终的查询结果。
4. 总结
在MongoDB中,联表查询有嵌套数据结构和引用方式两种方式。如果需要关联的字段为对象,则可以使用嵌套数据结构方式;如果需要关联的字段为数组,则需要使用$unwind操作符拆分成单独的文档,然后再使用嵌套数据结构方式关联查询。$lookup操作符是MongoDB中关联查询操作的关键操作符,通过它可以实现对多张表的关联查询操作。