更新 MongoDB 数组

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习如何在 MongoDB 文档中操作数组。你将练习使用 MongoDB 强大的更新操作符来添加、移除和更新数组元素。本实验提供分步说明和实际示例,帮助你掌握 MongoDB 数据库中的数组管理。

你将首先使用 $push 操作符向数组添加元素。接下来,你将学习使用 $pull$pullAll 操作符移除元素。你还将探索如何修改数组中的特定元素,以及如何使用 $addToSet 来确保数组只包含唯一元素。最后,你将学习如何使用聚合管道(aggregation pipeline)移除数组中已存在的重复项。

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

使用 $push 向数组添加元素

在此步骤中,你将学习如何使用 $push 操作符向 MongoDB 文档中的数组添加新元素。此操作符会将指定的值追加到数组中。

首先,打开 MongoDB shell 以便与你的数据库进行交互。本实验中的所有数据库操作都将在 shell 中执行。

mongosh

进入 shell 后,切换到 arraylab 数据库。此数据库已在你设置过程中创建。

use arraylab

设置脚本还创建了一个 students 集合,其中包含一个文档。让我们查找此文档以查看其当前状态。

db.students.findOne({ name: "Alice Johnson" })

你应该会看到以下输出,显示 Alice 注册了两个课程:

{
  _id: ObjectId('...'),
  name: 'Alice Johnson',
  courses: [ 'Mathematics', 'Computer Science' ]
}

现在,让我们使用 $push 操作符将新课程 "Data Structures" 添加到 Alice 的 courses 数组中。

db.students.updateOne(
    { name: "Alice Johnson" },
    { $push: { courses: "Data Structures" } }
)

updateOne 方法会查找与筛选条件 { name: "Alice Johnson" } 匹配的文档,并应用更新 { $push: { courses: "Data Structures" } }$push 操作符会将新课程追加到 courses 数组中。

你还可以通过将 $push$each 修饰符结合使用来一次添加多个元素。让我们将 "Physics" 和 "Chemistry" 添加到数组中。

db.students.updateOne(
    { name: "Alice Johnson" },
    { $push: { courses: { $each: ["Physics", "Chemistry"] } } }
)

为了确认所有新课程都已添加,请再次运行 findOne 命令。

db.students.findOne({ name: "Alice Johnson" })

更新后的文档现在包含所有五门课程:

{
  _id: ObjectId('...'),
  name: 'Alice Johnson',
  courses: [
    'Mathematics',
    'Computer Science',
    'Data Structures',
    'Physics',
    'Chemistry'
  ]
}

从数组中移除元素

在此步骤中,你将学习如何使用 $pull$pullAll 操作符从数组中移除元素。$pull 会移除指定值的所有实例,而 $pullAll 会移除多个指定值的所有实例。

你应该仍然处于上一步的 mongosh shell 中。让我们开始使用 $pull 操作符从 Alice 的列表中移除 "Physics" 课程。

db.students.updateOne(
    { name: "Alice Johnson" },
    { $pull: { courses: "Physics" } }
)

此命令会查找 "Alice Johnson" 的文档,并从她的 courses 数组中移除字符串 "Physics"。

接下来,让我们一次移除多个课程。$pullAll 操作符非常适合此任务。我们将移除 "Mathematics" 和 "Chemistry"。

db.students.updateOne(
    { name: "Alice Johnson" },
    { $pullAll: { courses: ["Mathematics", "Chemistry"] } }
)

现在,让我们验证文档的最终状态,看看还剩下哪些课程。

db.students.findOne({ name: "Alice Johnson" })

输出显示 "Physics"、"Mathematics" 和 "Chemistry" 已被移除,只剩下两门课程。

{
  _id: ObjectId('...'),
  name: 'Alice Johnson',
  courses: [ 'Computer Science', 'Data Structures' ]
}

更新数组中的特定元素

在此步骤中,你将学习如何修改数组中的特定元素。当你的数组包含对象,并且你需要更新其中某个对象的字段时,这将非常有用。

首先,让我们插入一个新的学生文档。此文档的 courses 数组将包含对象,每个对象都有 namegrade 字段。

db.students.insertOne({
    name: "Bob Smith",
    courses: [
        { name: "Mathematics", grade: "B" },
        { name: "Computer Science", grade: "A" },
        { name: "Physics", grade: "C" }
    ]
})

假设我们想将 Bob 在 "Computer Science" 中的成绩从 "A" 改为 "A+"。我们可以使用位置操作符 $。此操作符充当匹配查询条件的第一个元素的占位符。

db.students.updateOne(
    { name: "Bob Smith", "courses.name": "Computer Science" },
    { $set: { "courses.$.grade": "A+" } }
)

在此命令中,查询 { "courses.name": "Computer Science" } 识别了正确的数组元素。然后,更新 { $set: { "courses.$.grade": "A+" } } 使用 $ 来引用该元素并更新其 grade 字段。

让我们验证一下更改。

db.students.findOne({ name: "Bob Smith" })

输出将显示更新后的成绩:

{
  _id: ObjectId('...'),
  name: 'Bob Smith',
  courses: [
    { name: 'Mathematics', grade: 'B' },
    { name: 'Computer Science', grade: 'A+' },
    { name: 'Physics', grade: 'C' }
  ]
}

你还可以使用所有位置操作符 $[ ] 一次更新数组中的所有元素。让我们为 Bob 的所有课程添加一个 semester 字段。

db.students.updateOne(
    { name: "Bob Smith" },
    { $set: { "courses.$[].semester": "Fall 2023" } }
)

检查最终文档以查看结果。

db.students.findOne({ name: "Bob Smith" })

数组中的所有课程对象现在都具有 semester 字段。

{
  _id: ObjectId('...'),
  name: 'Bob Smith',
  courses: [
    { name: 'Mathematics', grade: 'B', semester: 'Fall 2023' },
    { name: 'Computer Science', grade: 'A+', semester: 'Fall 2023' },
    { name: 'Physics', grade: 'C', semester: 'Fall 2023' }
  ]
}

使用 $addToSet 确保元素唯一性

在此步骤中,你将学习使用 $addToSet 操作符。此操作符仅当元素尚不存在于数组中时才将其添加到数组,从而防止重复条目。

首先,让我们添加一个具有技能数组的新学生。

db.students.insertOne({
    name: "Emma Wilson",
    skills: ["Python", "JavaScript"]
})

现在,让我们尝试使用 $addToSet 再次添加 "Python"。由于 "Python" 已存在于数组中,此操作不会更改文档。

db.students.updateOne(
    { name: "Emma Wilson" },
    { $addToSet: { skills: "Python" } }
)

让我们检查文档以确认。

db.students.findOne({ name: "Emma Wilson" })

你将看到 skills 数组没有改变,因为 "Python" 已经存在。

{
  _id: ObjectId('...'),
  name: 'Emma Wilson',
  skills: [ 'Python', 'JavaScript' ]
}

$push 类似,$addToSet 可以与 $each 修饰符结合使用以添加多个值。它只会添加数组中尚不存在的值。

db.students.updateOne(
    { name: "Emma Wilson" },
    { $addToSet: {
        skills: {
            $each: ["React", "Node.js", "Python", "TypeScript"]
        }
    } }
)

让我们检查最终文档。

db.students.findOne({ name: "Emma Wilson" })

新的、唯一的技能已被添加,而重复的 "Python" 被忽略了。

{
  _id: ObjectId('...'),
  name: 'Emma Wilson',
  skills: [ 'Python', 'JavaScript', 'React', 'Node.js', 'TypeScript' ]
}

移除数组中的现有重复项

虽然 $addToSet 可以防止添加新的重复项,但有时你的文档中的数组可能已经包含重复值。在本步骤中,你将学习如何使用聚合管道移除现有重复项。

首先,让我们插入一个包含重复项的 skills 数组的文档。

db.students.insertOne({
    name: "Michael Chen",
    skills: ["Python", "JavaScript", "Python", "React", "JavaScript", "Node.js"]
})

为了移除这些重复项,我们可以使用一个包含 $merge 阶段的聚合管道。这个管道将读取文档,创建一个包含唯一元素的新数组,然后更新原始文档。

首先,我们需要在 name 字段上创建一个索引,以确保 $merge 操作能够正确工作:

db.students.createIndex({ name: 1 }, { unique: true })

现在,运行以下聚合命令:

db.students.aggregate([
    { $match: { name: "Michael Chen" } },
    { $project: {
        name: 1,
        skills: { $setUnion: "$skills" }
    } },
    { $merge: { into: "students", on: "name", whenMatched: "replace" } }
])

让我们分解一下这个管道:

  1. $match: 这个阶段过滤文档,只处理 "Michael Chen" 的文档。
  2. $project: 这个阶段重塑文档。我们保留 name 字段,并将 skills 数组替换为 $setUnion: "$skills" 的输出。$setUnion 操作符接收一个数组,并返回一个只包含唯一元素的新数组。
  3. $merge: 这个阶段将管道的结果写回 students 集合。它根据 name 字段 (on: "name") 查找要更新的文档,并用管道中的新文档替换整个文档 (whenMatched: "replace")。我们创建的索引确保了高效的匹配和更新。

现在,让我们验证重复项是否已被移除。

db.students.findOne({ name: "Michael Chen" })

输出将显示只包含唯一元素的 skills 数组。元素的顺序可能会有所不同。

{
  _id: ObjectId('...'),
  name: 'Michael Chen',
  skills: [ 'JavaScript', 'Node.js', 'Python', 'React' ]
}

总结

在此次实验中,你学习了 MongoDB 中管理数组的几种基本技术。你练习了使用 $push 添加元素,使用 $pull$pullAll 移除元素,以及使用位置 $ 操作符更新特定元素。你还探索了如何使用 $addToSet 在数组中维护唯一元素,以及如何使用包含 $setUnion$merge 的聚合管道清理现有重复项。这些技能对于使用 MongoDB 构建动态且健壮的应用程序至关重要。