管理 MongoDB 嵌入式文档

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习如何有效地管理 MongoDB 中的嵌入式文档。嵌入式文档,也称为嵌套文档,是 MongoDB 数据模型的核心特性,它允许你在单个文档中存储复杂、分层的数据。你将学习一系列技术,包括创建包含嵌套数据的文档、更新其中的特定字段、移除元素以及查询这些复杂结构。你还将学习如何使用模式验证(schema validation)来强制执行一致的数据结构。这些技能对于使用 MongoDB 构建健壮高效的应用程序至关重要。

这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 100%。获得了学习者 93% 的好评率。

创建包含嵌套数据的文档

在本步骤中,你将学习如何在 MongoDB 中创建具有嵌套结构的文档。这是在单个文档中建模相关数据的基本技能。

首先,打开 MongoDB Shell (mongosh) 以开始与数据库进行交互。本实验中后续的所有命令都将在该 shell 中运行,除非另有说明。

mongosh

接下来,切换到一个名为 bookstore 的新数据库。如果此数据库不存在,MongoDB 会在你首次向其中存储数据时为你创建它。

use bookstore

现在,你将创建一个名为 books 的集合并插入一个文档。此文档将包含嵌套对象和嵌套对象数组,代表一本书的详细信息。

db.books.insertOne({
    title: "Advanced MongoDB",
    author: {
        name: "Jane Smith",
        contact: {
            email: "jane.smith@example.com",
            phone: "+1-555-123-4567"
        }
    },
    published: {
        year: 2023,
        publisher: "Tech Publications"
    },
    chapters: [
        { number: 1, title: "Introduction to Nested Documents" },
        { number: 2, title: "Advanced Document Structures" }
    ]
})

在你刚刚创建的文档中:

  • authorpublished 是嵌套文档(对象)。
  • author 文档包含另一个嵌套文档 contact
  • chapters 是一个数组,其中包含多个嵌套文档,每个文档代表一个章节。

要查看文档并确认其结构,请使用 find() 方法。

db.books.find()

你应该会看到以下输出,确认文档已正确插入。_id 是 MongoDB 自动生成的唯一标识符。

[
  {
    _id: ObjectId("..."),
    title: 'Advanced MongoDB',
    author: {
      name: 'Jane Smith',
      contact: {
        email: 'jane.smith@example.com',
        phone: '+1-555-123-4567'
      }
    },
    published: {
      year: 2023,
      publisher: 'Tech Publications'
    },
    chapters: [
      { number: 1, title: 'Introduction to Nested Documents' },
      { number: 2, title: 'Advanced Document Structures' }
    ]
  }
]

更新嵌套字段和数组元素

创建文档后,你通常需要修改其中的部分内容。在本步骤中,你将学习如何更新嵌套文档和数组中的特定字段。你应该仍然处于上一步的 mongosh shell 中。

首先,让我们更新作者的联系信息。为此,你将使用 $set 操作符配合点表示法(dot notation)来指定要更改字段的确切路径。此命令将更改电话号码并添加一个新的 website 字段。

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $set: {
        "author.contact.phone": "+1-888-999-0000",
        "author.contact.website": "www.janesmith.com"
    } }
)

点表示法 "author.contact.phone" 告诉 MongoDB 进入 author 对象,然后进入 contact 对象,并更新 phone 字段。

接下来,你将使用 $push 操作符向 chapters 数组添加一个新章节。

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $push: {
        chapters: {
            number: 3,
            title: "MongoDB Advanced Techniques"
        }
    } }
)

你也可以更新数组中的特定元素。让我们修改第一个章节的标题。我们使用位置操作符 $,它充当匹配查询条件("chapters.number": 1)的第一个数组元素的占位符。

db.books.updateOne(
    { title: "Advanced MongoDB", "chapters.number": 1 },
    { $set: {
        "chapters.$.title": "Introduction to Nested Documents (Revised)"
    } }
)

为了验证所有更改,请再次检索文档。.pretty() 方法会格式化输出,使其更易于阅读。

db.books.find({ title: "Advanced MongoDB" }).pretty()

输出将显示更新后的电话号码、新的网站、添加的第三章以及修改后的第一章标题。

移除嵌套字段和数组元素

在本步骤中,你将学习如何删除文档的某些部分。这包括删除特定字段、整个嵌套文档以及数组中的元素。

首先,让我们使用 $unset 操作符从作者的联系信息中删除 website 字段。提供给 $unset 的值(在此例中为空字符串 "")无关紧要;该操作符仅删除指定的字段。

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $unset: { "author.contact.website": "" } }
)

接下来,你将从数组中删除一个元素。让我们使用 $pull 操作符从 chapters 数组中删除第三个章节(即 number: 3 的那个)。$pull 操作符会删除所有匹配指定条件的数组元素。

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $pull: {
        chapters: { number: 3 }
    } }
)

最后,你可以删除整个嵌套对象。让我们再次使用 $unset 操作符从 author 文档中删除 contact 对象。

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $unset: { "author.contact": "" } }
)

要查看这些删除操作的结果,请再次查询文档。

db.books.find({ title: "Advanced MongoDB" }).pretty()

输出将显示 website 字段和整个 contact 对象已消失,并且 chapters 数组现在只包含两个元素。

查询包含嵌套数据的文档

查询是数据库的基本操作。在本步骤中,你将学习如何根据嵌套对象和数组中的值来查询文档。为了确保我们有一组干净的数据用于这些示例,请先删除现有的 books 集合。

db.books.drop()

现在,插入几个具有不同嵌套结构的新文档以练习查询。

db.books.insertMany([
    {
        title: "MongoDB Essentials",
        author: {
            name: "John Doe",
            experience: { years: 5, specialization: "Database Design" }
        },
        tags: ["beginner", "database", "nosql"],
        chapters: [
            { number: 1, title: "Introduction", pages: 25 },
            { number: 2, title: "Advanced Concepts", pages: 45 }
        ]
    },
    {
        title: "Advanced Database Techniques",
        author: {
            name: "Jane Smith",
            experience: { years: 8, specialization: "Distributed Systems" }
        },
        tags: ["advanced", "distributed", "nosql"],
        chapters: [
            { number: 1, title: "System Design", pages: 35 },
            { number: 2, title: "Performance Optimization", pages: 55 }
        ]
    }
])

要按嵌套文档中的字段查找书籍,请使用点表示法(dot notation)。此查询查找由 "John Doe" 撰写的书籍。

db.books.find({ "author.name": "John Doe" })

你可以深入到更深的嵌套结构。此查询查找作者专业领域为 "Database Design" 的书籍。

db.books.find({ "author.experience.specialization": "Database Design" })

要查找数组包含特定值的文档,你可以直接查询数组字段。这会查找所有标记为 "nosql" 的书籍。

db.books.find({ tags: "nosql" })

你还可以查询数组元素满足特定条件的文档。此查询查找至少有一个章节页数超过 40 页的书籍,使用了 $gt(大于)操作符。

db.books.find({ "chapters.pages": { $gt: 40 } })

最后,你可以组合多个条件。此查询查找标记为 "advanced" 且作者拥有 5 年或以上经验($gte 表示大于或等于)的书籍。

db.books.find({
    "author.experience.years": { $gte: 5 },
    tags: "advanced"
})

通过 Schema 验证强制执行文档结构

为了保持数据一致性,MongoDB 允许你使用 JSON Schema validation 来强制执行集合中文档的特定结构。在本步骤中,你将创建一个名为 courses 的新集合,并为其设置一个 validator,以确保所有文档都符合预定义的结构。

首先,创建一个名为 courses 的新集合。在创建时,你将传递一个包含 schema 规则的 validator 选项。

db.createCollection("courses", {
   validator: {
      $jsonSchema: {
         bsonType: "object",
         required: ["title", "instructor", "duration", "topics"],
         properties: {
            title: {
               bsonType: "string",
               description: "must be a string and is required"
            },
            instructor: {
               bsonType: "object",
               required: ["name", "email"],
               properties: {
                  name: { bsonType: "string" },
                  email: {
                     bsonType: "string",
                     pattern: "^.+@.+$"
                  }
               }
            },
            duration: {
               bsonType: "int",
               minimum: 1,
               maximum: 100
            },
            topics: {
               bsonType: "array",
               minItems: 1,
               items: { bsonType: "string" }
            }
         }
      }
   }
})

此 schema 要求每个文档都包含 titleinstructordurationtopics。它还定义了每个字段的数据类型,包括 instructor 对象和 topics 数组的嵌套规则。

现在,尝试插入一个符合这些规则的文档。此操作应该会成功。

db.courses.insertOne({
   title: "MongoDB Advanced Techniques",
   instructor: {
      name: "Jane Smith",
      email: "jane.smith@example.com"
   },
   duration: 40,
   topics: ["Nested Documents", "Schema Validation"]
})

接下来,尝试插入一个违反 schema 规则的文档。此文档的 instructor.name 数据类型不正确,电子邮件格式无效,duration 超出了允许的范围,并且 topics 数组为空。

db.courses.insertOne({
   title: "Invalid Course",
   instructor: {
      name: 123,
      email: "invalid-email"
   },
   duration: 200,
   topics: []
})

此插入将失败,MongoDB 将返回 Document failed validation 错误。这可以防止不一致的数据被保存,并有助于维护数据库的完整性。

总结

在本实验中,你学习了管理 MongoDB 中嵌入式文档的基本技术。你首先创建了具有复杂嵌套结构(包括对象和数组)的文档。然后,你练习了使用点表示法(dot notation)和 $set$push 等操作符来更新特定字段。你还学会了如何使用 $unset$pull 来删除数据。此外,你还探索了如何对嵌套数据执行定向查询,并最终通过为集合定义和应用 JSON Schema validator 来强制执行数据完整性。这些技能对于构建能够有效利用 MongoDB 灵活文档模型的应用程序至关重要。