处理 MongoDB 错误

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习处理 MongoDB 中常见错误的关键技术。使用 MongoDB Shell (mongosh),你将练习识别和解决各种问题,包括连接失败、重复键错误和数据验证错误。完成本实验后,你将对如何使数据库操作更可靠、更健壮有实际的理解。

连接 MongoDB 并处理错误

连接错误是使用任何数据库时都会遇到的常见问题。它们可能由于多种原因发生,例如服务器地址不正确、网络问题或数据库服务器未运行。在本步骤中,你将学习如何识别连接错误然后成功连接。

首先,尝试连接到一个 MongoDB 实例,但使用一个它未运行的端口。MongoDB 的默认端口是 27017。我们将尝试连接到端口 27018

在你的终端中执行以下命令:

mongosh "mongodb://localhost:27018"

此命令会失败并返回一个错误,因为 shell 在指定地址找不到 MongoDB 服务器。输出将类似于:

MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27018

MongoNetworkError 清楚地表明连接失败。ECONNREFUSED 部分告诉你目标机器主动拒绝了连接,这通常意味着没有服务在该端口上监听。

现在,让我们连接到正确的端口。MongoDB 服务在设置阶段已为你启动。要连接到它,请在不带任何参数的情况下运行 mongosh 命令。这将使用默认连接字符串 mongodb://127.0.0.1:27017

mongosh

成功连接后,你将看到欢迎消息和 test> 提示符,表明你已连接到默认的 test 数据库。

Current Mongosh Log ID: ...
Connecting to:          mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.2.6
Using MongoDB:          8.0.0
Using Mongosh:          2.2.6
...
test>

你现在已连接到 MongoDB 服务器。请保持此终端打开,并在 mongosh shell 中进行后续步骤。

处理重复键错误

维护数据完整性对任何应用程序都至关重要。最常见的数据完整性问题之一是重复记录。MongoDB 通过唯一索引来防止这种情况,这些索引确保索引字段不存储重复值。

你应该已经从上一步进入了 mongosh shell。首先,切换到一个名为 errorlab 的新数据库。当你首次在该数据库中存储数据时,数据库会自动创建。

use errorlab

接下来,在新的 users 集合的 email 字段上创建一个唯一索引。此命令告诉 MongoDB users 集合中的每个文档都必须有一个唯一的 email 字段值。

db.users.createIndex({ email: 1 }, { unique: true });

输出确认索引已成功创建。

{
  "numIndexesBefore": 1,
  "numIndexesAfter": 2,
  "createdCollectionAutomatically": true,
  "ok": 1
}

现在,向 users 集合插入一个文档。此操作将成功,因为集合是空的且电子邮件是唯一的。

db.users.insertOne({ name: "John Doe", email: "john@example.com" });

你将看到一条确认消息,其中包含插入文档的 ID:

{
  "acknowledged": true,
  "insertedId": ObjectId("...")
}

接下来,尝试插入另一个具有完全相同电子邮件地址的文档。

db.users.insertOne({ name: "Jane Doe", email: "john@example.com" });

这次,操作失败了。MongoDB 返回一个 MongoBulkWriteError,并带有特定的错误代码 E11000,这表示重复键错误。这是为了保护数据完整性而预期的行为。

MongoServerError: E11000 duplicate key error collection: errorlab.users index: email_1 dup key: { email: "john@example.com" }

使用 Upsert 解决重复键

防止重复是好的,但有时你希望在记录存在时更新它,如果不存在则创建它。这种“更新或插入”逻辑是一个常见的需求。MongoDB 提供了一种使用 upsert 选项来完成此操作的简洁方法。

让我们尝试更新电子邮件地址为 john@example.com 的用户。我们将使用 updateOne 方法,并将 upsert 选项设置为 true

db.users.updateOne(
  { email: "john@example.com" },
  { $set: { name: "John Doe Updated", lastUpdated: new Date() } },
  { upsert: true }
);

输出显示匹配并修改了一个文档。upsertedIdnull,因为更新的是现有文档,而不是插入新文档。

{
  "acknowledged": true,
  "matchedCount": 1,
  "modifiedCount": 1,
  "upsertedId": null
}

现在,让我们为尚不存在的用户 jane@example.com 运行一个类似的命令。

db.users.updateOne(
  { email: "jane@example.com" },
  { $set: { name: "Jane Doe", lastUpdated: new Date() } },
  { upsert: true }
);

这次,输出显示 matchedCount 为 0,但创建了一个新文档,如 upsertedId 所示。

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

为了验证结果,你可以查询集合以查看所有文档。pretty() 方法会格式化输出,使其更易于阅读。

db.users.find().pretty();

输出将显示两个文档:John 的文档(更新了姓名)和 Jane 新创建的文档。upsert 选项提供了一种强大且原子化的方式来处理“创建或更新”场景。

处理查询和验证错误

当你查询数据或插入的数据不符合预定义规则时,也可能发生错误。在此步骤中,你将探索一个查询语法错误和一个数据验证错误。

首先,让我们看看当你使用一个不存在的查询运算符时会发生什么。这是一种常见的拼写错误。

db.users.find({ name: { $invalidOperator: "John" } });

MongoDB 会立即返回一个错误,因为它不识别 $invalidOperator

MongoServerError[BadValue]: unknown operator: $invalidOperator

接下来,让我们探索一个更强大的功能:模式验证 (schema validation)。你可以定义文档在插入或更新到集合时必须遵循的规则。让我们创建一个新的 products 集合,并为其设置一个验证器,该验证器要求包含 name(字符串类型)和 price(数字类型)。

db.createCollection("products", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price"],
      properties: {
        name: {
          bsonType: "string",
          description: "must be a string and is required"
        },
        price: {
          bsonType: "number",
          description: "must be a number and is required"
        }
      }
    }
  }
});

现在,尝试插入一个违反此模式的文档,将 price 提供为字符串而不是数字。

db.products.insertOne({ name: "Laptop", price: "1200" });

该操作会因 MongoBulkWriteError 而失败。Document failed validation 的消息清楚地说明了原因,阻止了无效数据进入你的数据库。

MongoServerError: Document failed validation
...

最后,插入一个符合模式的有效文档。

db.products.insertOne({ name: "Laptop", price: 1200 });

此操作成功,因为文档是有效的。

{
  "acknowledged": true,
  "insertedId": ObjectId("...")
}

模式验证是一个强大的工具,用于在数据库内部直接强制执行数据一致性。

总结

在本实验中,你学习了如何使用 mongosh shell 处理几种常见的 MongoDB 错误。你首先识别并解决了连接错误。然后,你通过创建唯一索引来防止重复键错误,从而强制执行数据完整性。你还学会了如何使用 upsert 选项来优雅地处理“更新或插入”逻辑。最后,你探索了查询语法错误,并使用模式验证来防止无效数据保存到你的数据库。这些基本的错误处理技能对于使用 MongoDB 构建可靠和健壮的应用程序至关重要。