介绍
在本实验中,你将学习如何在 MongoDB 文档中操作数组。你将练习使用 MongoDB 强大的更新操作符来添加、移除和更新数组元素。本实验提供分步说明和实际示例,帮助你掌握 MongoDB 数据库中的数组管理。
你将首先使用 $push 操作符向数组添加元素。接下来,你将学习使用 $pull 和 $pullAll 操作符移除元素。你还将探索如何修改数组中的特定元素,以及如何使用 $addToSet 来确保数组只包含唯一元素。最后,你将学习如何使用聚合管道(aggregation pipeline)移除数组中已存在的重复项。
使用 $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 数组将包含对象,每个对象都有 name 和 grade 字段。
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" } }
])
让我们分解一下这个管道:
$match: 这个阶段过滤文档,只处理 "Michael Chen" 的文档。$project: 这个阶段重塑文档。我们保留name字段,并将skills数组替换为$setUnion: "$skills"的输出。$setUnion操作符接收一个数组,并返回一个只包含唯一元素的新数组。$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 构建动态且健壮的应用程序至关重要。

