更新 MongoDB 记录

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习如何在 MongoDB 集合中更新文档。你将首先使用 updateOne() 方法修改单个文档。然后,你将学习使用 updateMany() 方法一次修改多个文档。你还将使用各种更新操作符,如 $set$inc$unset,来执行特定的修改。最后,你将探索 upsert 选项,该选项允许你更新现有文档或在文档不存在时创建新文档。本实验提供了一种实用的、动手实践的方法来掌握 MongoDB 中基本的数据操作技能。

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

使用 updateOne 更新单个文档

在本步骤中,你将学习如何在 MongoDB 集合中修改单个文档。我们将使用 updateOne() 方法和 $set 操作符来更改特定文档中的字段值。

首先,打开 MongoDB Shell 以便与你的数据库进行交互。

mongosh

进入 shell 后,切换到为你准备好的 mylab_database

use mylab_database

让我们查看 books 集合中的当前文档。.pretty() 方法会格式化输出,使其更易读。

db.books.find().pretty();

你应该会看到三个初始的图书文档。_id 值在你自己的环境中将是唯一的。

[
  {
    _id: ObjectId("..."),
    title: 'JavaScript Fundamentals',
    author: 'Mike Johnson',
    year: 2022,
    pages: 350
  },
  {
    _id: ObjectId("..."),
    title: 'Python Deep Dive',
    author: 'Sarah Williams',
    year: 2021,
    pages: 450
  },
  {
    _id: ObjectId("..."),
    title: 'Machine Learning Basics',
    author: 'John Doe',
    year: 2020,
    price: 39.99
  }
]

现在,让我们将 "JavaScript Fundamentals" 这本书的出版年份更新为 2023

db.books.updateOne(
  { title: "JavaScript Fundamentals" },
  { $set: { year: 2023 } }
);

让我们分解一下这个命令:

  • updateOne(): 此方法查找第一个匹配筛选条件的文档并更新它。
  • { title: "JavaScript Fundamentals" }: 这是筛选文档。它告诉 MongoDB 查找 title 字段为 "JavaScript Fundamentals" 的文档。
  • { $set: { year: 2023 } }: 这是更新文档。$set 操作符将 year 字段的值替换为 2023

该命令会返回一个结果对象,确认操作已执行。

{
  "acknowledged": true,
  "insertedId": null,
  "matchedCount": 1,
  "modifiedCount": 1,
  "upsertedCount": 0
}

matchedCount: 1 表明有一个文档匹配了我们的筛选条件,而 modifiedCount: 1 表明有一个文档已成功更新。

为了验证更改,再次查找该文档。

db.books.findOne({ title: "JavaScript Fundamentals" });

输出将显示更新后的文档,包含新的年份。

{
  _id: ObjectId("..."),
  title: 'JavaScript Fundamentals',
  author: 'Mike Johnson',
  year: 2023,
  pages: 350
}

使用 updateMany 更新多个文档

有时你需要一次性更新多个文档。为此,MongoDB 提供了 updateMany() 方法。在本步骤中,你将为所有在特定年份之前出版的书籍添加一个新字段。

让我们为所有在 2022 年之前出版的书籍添加一个 status 字段,值为 "Classic"

db.books.updateMany({ year: { $lt: 2022 } }, { $set: { status: "Classic" } });

以下是对该命令的解释:

  • updateMany(): 此方法更新所有匹配指定筛选条件的文档。
  • { year: { $lt: 2022 } }: 此筛选器选择 year 小于 2022 的文档。$lt 操作符代表“小于”(less than)。
  • { $set: { status: "Classic" } }: 此更新文档向所有匹配的文档添加一个新字段 status,其值为 "Classic"

输出将显示匹配和修改了多少文档。在本例中,有两本书是在 2022 年之前出版的。

{
  "acknowledged": true,
  "insertedId": null,
  "matchedCount": 2,
  "modifiedCount": 2,
  "upsertedCount": 0
}

为了验证两个文档是否都已更新,你可以查询所有 status"Classic" 的书籍。

db.books.find({ status: "Classic" }).pretty();

你将看到这两本书现在都包含了 status 字段。

[
  {
    _id: ObjectId("..."),
    title: 'Python Deep Dive',
    author: 'Sarah Williams',
    year: 2021,
    pages: 450,
    status: 'Classic'
  },
  {
    _id: ObjectId("..."),
    title: 'Machine Learning Basics',
    author: 'John Doe',
    year: 2020,
    price: 39.99,
    status: 'Classic'
  }
]

使用更多更新操作符:$inc$unset

MongoDB 提供了各种操作符以支持不同类型的更新。在本步骤中,你将学习使用 $inc 来修改数值,以及使用 $unset 来从文档中移除字段。

首先,让我们使用 $inc 操作符将 "Python Deep Dive" 这本书的页数增加 50。$inc 操作符会按指定值递增一个字段。

db.books.updateOne({ title: "Python Deep Dive" }, { $inc: { pages: 50 } });

为了确认更改,请检索该文档。

db.books.findOne({ title: "Python Deep Dive" });

此时 pages 字段应为 500(450 + 50)。

{
  _id: ObjectId("..."),
  title: 'Python Deep Dive',
  author: 'Sarah Williams',
  year: 2021,
  pages: 500,
  status: 'Classic'
}

接下来,让我们从 "Machine Learning Basics" 文档中移除 price 字段。$unset 操作符会删除指定的字段。提供给 $unset 的值(在此例中为 "")无关紧要,可以是任何值。

db.books.updateOne(
  { title: "Machine Learning Basics" },
  { $unset: { price: "" } }
);

让我们验证一下 price 字段是否已被移除。

db.books.findOne({ title: "Machine Learning Basics" });

输出将显示该文档不再包含 price 字段。

{
  _id: ObjectId("..."),
  title: 'Machine Learning Basics',
  author: 'John Doe',
  year: 2020,
  status: 'Classic'
}

使用 upsert 创建文档

"Upsert" 是一种特殊的更新操作,如果文档存在,则对其进行更新;如果文档不存在,则插入一个新文档。这有助于避免单独的“检查后插入”逻辑,并确保数据库操作的原子性。在以下场景中,Upsert 特别有用:

  • 确保数据一致性: 无需先检查文档是否存在,然后决定是插入还是更新,你可以执行一个原子性操作来同时处理这两种情况。
  • 处理并发操作: 在多用户环境中,upsert 可以防止在你的检查和插入操作之间,另一个进程插入相同文档而导致的竞态条件(race conditions)。
  • 简化应用逻辑: 减少应用代码中复杂条件逻辑的需要,使其更易于维护且不易出错。

你可以通过向更新命令添加 upsert: true 选项来启用此行为。

我们尝试添加一本新书 "Cloud Computing Essentials"。由于这本书不存在于我们的集合中,upsert 操作将创建它。

db.books.updateOne(
  { title: "Cloud Computing Essentials" },
  { $set: { author: "David Lee", year: 2023, pages: 300 } },
  { upsert: true }
);

该命令包含三个部分:

  1. 筛选条件(filter):{ title: "Cloud Computing Essentials" }
  2. 更新操作(update):{ $set: { ... } }
  3. 选项(options):{ upsert: true }

由于没有文档匹配筛选条件,因此创建了一个新文档。结果对象通过包含 upsertedId 来反映这一点。

{
  acknowledged: true,
  matchedCount: 0,
  modifiedCount: 0,
  upsertedCount: 1,
  upsertedId: ObjectId("...")
}

现在,我们再次运行相同的命令。这次,MongoDB 将找到我们刚刚创建的文档并对其进行更新。

db.books.updateOne(
  { title: "Cloud Computing Essentials" },
  { $set: { author: "David Lee", year: 2023, pages: 300 } },
  { upsert: true }
);

结果现在显示 matchedCount: 1。由于我们设置的数据与现有数据相同,modifiedCount0。如果我们更改了某个值,modifiedCount 将为 1

{
  "acknowledged": true,
  "insertedId": null,
  "matchedCount": 1,
  "modifiedCount": 0,
  "upsertedCount": 0
}

你可以验证新书是否已存在于集合中。

db.books.findOne({ title: "Cloud Computing Essentials" });

输出将显示新创建的文档。

{
  _id: ObjectId("..."),
  title: 'Cloud Computing Essentials',
  author: 'David Lee',
  year: 2023,
  pages: 300
}

完成后,你可以退出 MongoDB Shell。

exit;

总结

在本实验中,你学习了在 MongoDB 中更新文档的基本技术。你练习了使用 updateOne 修改单个文档,以及使用 updateMany 修改多个文档。你还探索了强大的更新操作符,包括用于更改字段值的 $set,用于递增数字的 $inc,以及用于移除字段的 $unset。最后,你学习了如何使用 upsert 选项在文档不存在时创建它。这些技能是管理和维护任何 MongoDB 应用程序中数据的基本功。