介绍
在本实验中,你将学习如何使用 MongoDB 的引用来设计数据关系并管理父子文档之间的关系。你将从一个简单的图书馆管理系统开始,包含书籍和作者,然后探索如何链接父子文档、更新子文档引用、查询父子关系以及维护引用完整性。本实验涵盖了 MongoDB 中文档引用的关键原则,并通过实际示例帮助你有效理解和应用这些概念。
在本实验中,你将学习如何使用 MongoDB 的引用来设计数据关系并管理父子文档之间的关系。你将从一个简单的图书馆管理系统开始,包含书籍和作者,然后探索如何链接父子文档、更新子文档引用、查询父子关系以及维护引用完整性。本实验涵盖了 MongoDB 中文档引用的关键原则,并通过实际示例帮助你有效理解和应用这些概念。
在这一步骤中,我们将通过创建一个链接文档的实际示例,探索如何在 MongoDB 中设计数据关系。我们将为一个简单的图书馆管理系统建模,包含书籍和作者。
MongoDB 提供了两种主要方式来创建文档之间的关系:
对于我们的图书馆系统,我们将使用文档引用来演示如何在集合之间链接相关数据。
首先,启动 MongoDB shell:
mongosh
接下来,创建我们的数据库和集合:
use library_database
db.authors.insertOne({
_id: ObjectId("author1"),
name: "Jane Austen",
nationality: "British",
birthYear: 1775
})
db.books.insertOne({
title: "Pride and Prejudice",
author_id: ObjectId("author1"),
published: 1813,
genre: "Classic Literature"
})
让我们分析一下我们做了什么:
authors
集合,其中包含一个唯一的 _id
books
集合,通过 author_id
引用作者author_id
字段包含对应作者文档的 _id
为了验证我们的引用,可以查询文档:
db.authors.findOne({ name: "Jane Austen" })
db.books.findOne({ title: "Pride and Prejudice" })
{
_id: ObjectId("author1"),
name: 'Jane Austen',
nationality: 'British',
birthYear: 1775
}
{
_id: ObjectId(...),
title: 'Pride and Prejudice',
author_id: ObjectId("author1"),
published: 1813,
genre: 'Classic Literature'
}
_id
创建文档之间的链接在这一步骤中,我们将通过创建更复杂的父子文档关系来扩展我们的图书馆数据库。我们将演示如何将多本书链接到一位作者,并有效管理这些关系。
让我们继续使用现有的数据库,并为 Jane Austen 添加更多书籍:
db.books.insertMany([
{
title: "Sense and Sensibility",
author_id: ObjectId("author1"),
published: 1811,
genre: "Classic Literature"
},
{
title: "Emma",
author_id: ObjectId("author1"),
published: 1815,
genre: "Classic Literature"
}
])
要查找 Jane Austen 的所有书籍,我们可以使用 author_id
:
db.books.find({ author_id: ObjectId("author1") })
[
{
_id: ObjectId(...),
title: 'Pride and Prejudice',
author_id: ObjectId("author1"),
published: 1813,
genre: 'Classic Literature'
},
{
_id: ObjectId(...),
title: 'Sense and Sensibility',
author_id: ObjectId("author1"),
published: 1811,
genre: 'Classic Literature'
},
{
_id: ObjectId(...),
title: 'Emma',
author_id: ObjectId("author1"),
published: 1815,
genre: 'Classic Literature'
}
]
我们还可以统计特定作者的书籍数量:
db.books.countDocuments({ author_id: ObjectId("author1") })
3
让我们使用聚合框架来获取更详细的信息:
db.books.aggregate([
{ $match: { author_id: ObjectId("author1") } },
{ $group: {
_id: "$author_id",
totalBooks: { $sum: 1 },
earliestPublished: { $min: "$published" },
latestPublished: { $max: "$published" }
}}
])
[
{
_id: ObjectId("author1"),
totalBooks: 3,
earliestPublished: 1811,
latestPublished: 1815
}
]
在这一步骤中,我们将学习如何在 MongoDB 中更新父子文档之间的引用。我们将探索不同的技术来修改文档引用并维护数据完整性。
首先,让我们向数据库中添加另一位作者:
db.authors.insertOne({
_id: ObjectId("author2"),
name: "Charles Dickens",
nationality: "British",
birthYear: 1812
})
让我们更新一本书以更改其作者引用:
db.books.updateOne(
{ title: "Emma" },
{ $set: { author_id: ObjectId("author2") } }
)
检查更新后的书籍的作者引用:
db.books.findOne({ title: "Emma" })
{
_id: ObjectId(...),
title: 'Emma',
author_id: ObjectId("author2"),
published: 1815,
genre: 'Classic Literature'
}
我们还可以一次性更新多个文档:
db.books.updateMany(
{ author_id: ObjectId("author1") },
{ $set: { genre: "Romantic Novel" } }
)
验证类型更新:
db.books.find({ author_id: ObjectId("author1") })
[
{
_id: ObjectId(...),
title: 'Pride and Prejudice',
author_id: ObjectId("author1"),
published: 1813,
genre: 'Romantic Novel'
},
{
_id: ObjectId(...),
title: 'Sense and Sensibility',
author_id: ObjectId("author1"),
published: 1811,
genre: 'Romantic Novel'
}
]
我们可以使用 upsert 选项来更新文档,如果文档不存在则创建它:
db.books.updateOne(
{ title: "Oliver Twist" },
{ $set: {
author_id: ObjectId("author2"),
published: 1837,
genre: "Historical Fiction"
}},
{ upsert: true }
)
在这一步骤中,我们将探索高级查询技术,以在 MongoDB 中检索父子集合之间的相关文档。我们将演示如何有效地查找和连接相关数据。
首先,查找特定作者的书籍:
db.books.find({ author_id: ObjectId("author1") })
使用多个过滤器查询书籍:
db.books.find({
author_id: ObjectId("author1"),
published: { $gt: 1812 }
})
此查询查找 Jane Austen 在 1812 年后出版的书籍。
使用聚合框架连接作者和书籍信息:
db.books.aggregate([
{ $lookup: {
from: "authors",
localField: "author_id",
foreignField: "_id",
as: "author_details"
}},
{ $match: {
"author_details.nationality": "British"
}},
{ $project: {
title: 1,
published: 1,
"author_name": "$author_details.name"
}}
])
[
{
_id: ObjectId(...),
title: 'Pride and Prejudice',
published: 1813,
author_name: ['Jane Austen']
},
{
_id: ObjectId(...),
title: 'Emma',
published: 1815,
author_name: ['Charles Dickens']
}
]
按出版年份查询并排序书籍:
db.books.find()
.sort({ published: 1 })
.limit(2)
此操作检索最早出版的两本书。
查找标题包含特定单词的书籍:
db.books.find({
title: { $regex: /Sense/, $options: 'i' }
})
$options: 'i'
使搜索不区分大小写。
统计每位作者的书籍数量:
db.books.aggregate([
{ $group: {
_id: "$author_id",
book_count: { $sum: 1 }
}},
{ $lookup: {
from: "authors",
localField: "_id",
foreignField: "_id",
as: "author_info"
}},
{ $project: {
author_name: "$author_info.name",
book_count: 1
}}
])
[
{
_id: ObjectId("author1"),
author_name: ['Jane Austen'],
book_count: 2
},
{
_id: ObjectId("author2"),
author_name: ['Charles Dickens'],
book_count: 2
}
]
在这最后一步中,我们将探讨在 MongoDB 中维护文档引用的策略,重点关注数据完整性、清理以及管理集合之间关系的最佳实践。
有时,当父文档被删除时,引用可能会变得过时。让我们演示如何管理这种情况:
db.authors.insertOne({
_id: ObjectId("author3"),
name: "Mark Twain",
nationality: "American",
birthYear: 1835
})
db.books.insertOne({
title: "The Adventures of Tom Sawyer",
author_id: ObjectId("author3"),
published: 1876,
genre: "Classic Literature"
})
删除一位作者并检查孤立的书籍:
db.authors.deleteOne({ _id: ObjectId("author3") })
## 查找具有不存在作者引用的书籍
db.books.find({
author_id: { $nin: db.authors.distinct("_id") }
})
创建一个函数来清理孤立的引用:
db.books.deleteMany({
author_id: { $nin: db.authors.distinct("_id") }
})
在插入文档时使用验证规则:
db.createCollection("books", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["title", "author_id"],
properties: {
title: {
bsonType: "string",
description: "must be a string and is required"
},
author_id: {
bsonType: "objectId",
description: "must be a valid author reference"
}
}
}
}
})
在引用字段上创建索引:
db.books.createIndex({ author_id: 1 })
db.books.getIndexes()
[
{ v: 2, key: { _id: 1 }, name: '_id_' },
{ v: 2, key: { author_id: 1 }, name: 'author_id_1' }
]
## 由于无效的 author_id,此操作将失败
db.books.insertOne({
title: "Invalid Book",
author_id: ObjectId("invalid_id")
})
在本实验中,我们学习了如何使用文档引用在 MongoDB 中设计数据关系。我们创建了一个图书馆管理系统的实际示例,模拟了书籍和作者的关系。我们探讨了在 MongoDB 中创建关系的两种主要方式:嵌入式文档和文档引用。对于我们的图书馆系统,我们选择使用文档引用来跨集合链接相关数据。我们学习了文档引用的关键原则,例如使用 _id
创建链接、保持引用简单一致,以及在数据量大或频繁变化时选择引用。我们还通过创建更复杂的父子文档关系扩展了图书馆数据库。