在关系型数据库中,外键是一种非常常见的概念,它可以用于建立表之间的关联关系。但是,在 MongoDB 这种非关系型数据库中,没有外键的概念。那么,在 MongoDB 中如何使用外键呢?
MongoDB 中的文档关系
在 MongoDB 中,文档之间的关系可以通过嵌套文档或者引用文档的方式实现。
嵌套文档
嵌套文档是指一个文档中包含另一个文档。例如,我们可以在一个订单文档中嵌套一个用户文档:
// www.javascriptcn.com code example
{
"_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0c"),
"user": {
"_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0b"),
"name": "张三",
"age": 20
},
"products": [
{ "name": "商品1", "price": 100 },
{ "name": "商品2", "price": 200 }
]
}在这个例子中,订单文档中嵌套了一个用户文档,可以通过 order.user.name 来访问用户的名字。
引用文档
引用文档是指一个文档中包含了另一个文档的 _id 值。例如,我们可以在一个订单文档中引用一个用户文档:
{
"_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0c"),
"user": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0b"),
"products": [
{ "name": "商品1", "price": 100 },
{ "name": "商品2", "price": 200 }
]
}在这个例子中,订单文档中引用了一个用户文档的 _id 值,可以通过 db.users.findOne({ _id: order.user }) 来访问用户文档。
如何在 MongoDB 中实现外键
虽然 MongoDB 没有外键的概念,但是我们可以通过一些方式来实现类似外键的效果。
手动维护引用关系
我们可以手动在程序中维护文档之间的引用关系。例如,在上面的例子中,我们可以在订单文档中添加一个 user_id 字段,用于存储用户文档的 _id 值。然后,在查询订单文档时,可以通过 db.users.findOne({ _id: order.user_id }) 来访问用户文档。
这种方式的优点是简单易懂,缺点是需要手动维护引用关系,容易出错。
使用 $lookup 操作
MongoDB 3.2 版本以后,可以使用 $lookup 操作实现类似外键的效果。$lookup 操作可以在一个文档中查找另一个集合中的文档,并将它们合并成一个新的文档。
例如,我们可以通过以下代码查询订单文档,并将用户文档合并到订单文档中:
// www.javascriptcn.com code example
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user"
}
}
])这个操作会在 orders 集合中查找每个订单文档的 user_id 字段对应的 _id 值,并在 users 集合中查找对应的用户文档。然后,它会将每个订单文档和它对应的用户文档合并成一个新的文档,并将它们放在一个名为 user 的数组中。
这种方式的优点是不需要手动维护引用关系,缺点是需要使用聚合操作,效率可能较低。
示例代码
以下是一个使用 $lookup 操作实现类似外键的示例代码:
// www.javascriptcn.com code example
// 创建 users 集合
db.users.insertMany([
{ "_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0b"), "name": "张三", "age": 20 },
{ "_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0d"), "name": "李四", "age": 30 }
])
// 创建 orders 集合
db.orders.insertMany([
{ "_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0c"), "user_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0b"), "products": [{ "name": "商品1", "price": 100 }, { "name": "商品2", "price": 200 }] },
{ "_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0e"), "user_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0d"), "products": [{ "name": "商品3", "price": 300 }, { "name": "商品4", "price": 400 }] }
])
// 查询订单并将用户合并到订单中
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "user_id",
foreignField: "_id",
as: "user"
}
}
])这个示例代码会输出以下结果:
// www.javascriptcn.com code example
[
{
"_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0c"),
"user_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0b"),
"products": [
{ "name": "商品1", "price": 100 },
{ "name": "商品2", "price": 200 }
],
"user": [
{ "_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0b"), "name": "张三", "age": 20 }
]
},
{
"_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0e"),
"user_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0d"),
"products": [
{ "name": "商品3", "price": 300 },
{ "name": "商品4", "price": 400 }
],
"user": [
{ "_id": ObjectId("5f2b2c2f7d2c2c4b7e0f4d0d"), "name": "李四", "age": 30 }
]
}
]可以看到,每个订单文档都包含了对应的用户文档。
Source: FunTeaLearn,Please indicate the source for reprints https://funteas.com/post/67d9a489a941bf7134155d14