验证 MongoDB 数据

MongoDBMongoDBBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

介绍

在本实验中,你将学习如何验证 MongoDB 的数据类型、处理无效数据以及防止不良输入,以维护 MongoDB 数据库中的数据完整性。你将首先探索 MongoDB 支持的不同数据类型,并创建一个具有特定数据类型要求的示例集合。然后,你将练习插入类型不正确的文档,并学习如何正确插入符合验证标准的数据。通过本实验,你将深入了解 MongoDB 的数据验证和错误处理技术。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL mongodb(("MongoDB")) -.-> mongodb/BasicOperationsGroup(["Basic Operations"]) mongodb(("MongoDB")) -.-> mongodb/QueryOperationsGroup(["Query Operations"]) mongodb(("MongoDB")) -.-> mongodb/DataTypesGroup(["Data Types"]) mongodb(("MongoDB")) -.-> mongodb/ErrorHandlingGroup(["Error Handling"]) mongodb/BasicOperationsGroup -.-> mongodb/create_database_collection("Create Database and Collection") mongodb/BasicOperationsGroup -.-> mongodb/insert_document("Insert Document") mongodb/BasicOperationsGroup -.-> mongodb/update_document("Update Document") mongodb/QueryOperationsGroup -.-> mongodb/find_documents("Find Documents") mongodb/DataTypesGroup -.-> mongodb/use_numeric_data_types("Use Numeric Data Types") mongodb/DataTypesGroup -.-> mongodb/use_string_data_types("Use String Data Types") mongodb/DataTypesGroup -.-> mongodb/work_with_array_data_types("Work with Array Data Types") mongodb/ErrorHandlingGroup -.-> mongodb/handle_write_errors("Handle Write Errors") subgraph Lab Skills mongodb/create_database_collection -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/insert_document -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/update_document -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/find_documents -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/use_numeric_data_types -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/use_string_data_types -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/work_with_array_data_types -.-> lab-422100{{"验证 MongoDB 数据"}} mongodb/handle_write_errors -.-> lab-422100{{"验证 MongoDB 数据"}} end

检查数据类型

在这一步骤中,你将学习如何检查和验证 MongoDB 中的数据类型,这对于维护数据完整性和防止数据库中的错误至关重要。

理解 MongoDB 中的数据类型

MongoDB 支持多种数据类型,包括:

  • String(字符串)
  • Integer(整数)
  • Double(双精度浮点数)
  • Boolean(布尔值)
  • Array(数组)
  • Object(对象)
  • Date(日期)
  • ObjectId(对象 ID)
  • Null(空值)

让我们首先打开 MongoDB shell 并探索数据类型验证:

mongosh

创建具有类型验证的示例集合

我们将创建一个具有特定数据类型要求的 users 集合:

use dataValidationLab

db.createCollection("users", {
   validator: {
      $jsonSchema: {
         bsonType: "object",
         required: ["name", "age", "email"],
         properties: {
            name: {
               bsonType: "string",
               description: "必须是一个字符串且为必填项"
            },
            age: {
               bsonType: "int",
               minimum: 18,
               maximum: 120,
               description: "必须是一个介于 18 和 120 之间的整数"
            },
            email: {
               bsonType: "string",
               pattern: "^.+@.+$",
               description: "必须是一个有效的电子邮件地址"
            }
         }
      }
   }
})

让我们分解一下验证规则:

  • bsonType: "string" 确保字段是一个字符串
  • bsonType: "int" 确保字段是一个整数
  • minimummaximum 设置范围约束
  • pattern 验证电子邮件格式

尝试插入类型不正确的文档

现在,让我们尝试插入文档以测试我们的验证规则:

// 由于年龄类型不正确,此操作将失败
db.users.insertOne({
  name: "John Doe",
  age: "25", // 字符串而不是整数
  email: "[email protected]"
});

// 由于电子邮件无效,此操作也将失败
db.users.insertOne({
  name: "Jane Smith",
  age: 30,
  email: "invalid-email"
});

正确的文档插入

以下是插入有效文档的方式:

db.users.insertOne({
  name: "Alice Johnson",
  age: NumberInt(28),
  email: "[email protected]"
});

注意使用 NumberInt() 显式创建一个整数。

验证必填字段

在这一步骤中,我们将基于之前的数据验证工作,重点确保 MongoDB 文档中始终包含必填字段。

理解必填字段

必填字段是每个文档中必须包含的关键信息。在之前的步骤中,我们创建了一个包含必填字段的验证模式。现在,我们将探讨如何强制执行和验证这些要求。

让我们继续在 MongoDB shell 中操作:

mongosh

切换到我们现有的数据库:

use dataValidationLab

定义严格的必填字段验证

我们将修改现有的 users 集合,以强制执行严格的必填字段验证:

db.runCommand({
  collMod: "users",
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "age", "email", "registrationDate"],
      properties: {
        name: {
          bsonType: "string",
          description: "姓名是必填项且必须为字符串"
        },
        age: {
          bsonType: "int",
          minimum: 18,
          maximum: 120,
          description: "年龄是必填项且必须为介于 18 和 120 之间的整数"
        },
        email: {
          bsonType: "string",
          pattern: "^.+@.+$",
          description: "电子邮件是必填项且必须为有效的电子邮件地址"
        },
        registrationDate: {
          bsonType: "date",
          description: "注册日期是必填项且必须为日期"
        }
      }
    }
  },
  validationLevel: "strict"
});

尝试插入缺少字段的文档

让我们尝试插入缺少必填字段的文档:

// 由于缺少 registrationDate,此操作将失败
db.users.insertOne({
  name: "Bob Wilson",
  age: NumberInt(35),
  email: "[email protected]"
});

// 由于缺少必填字段,此操作也将失败
db.users.insertOne({
  name: "Charlie Brown"
});

正确的文档插入

以下是插入符合所有要求的文档的方式:

db.users.insertOne({
  name: "Emma Thompson",
  age: NumberInt(42),
  email: "[email protected]",
  registrationDate: new Date()
});

检查验证错误

为了理解插入失败的原因,MongoDB 提供了验证错误信息:

try {
  db.users.insertOne({
    name: "Incomplete User"
  });
} catch (error) {
  print("Validation Error:", error.message);
}

这将帮助你准确了解缺少了哪些必填字段。

处理无效数据

在这一步骤中,我们将学习如何处理和管理 MongoDB 中的无效数据,重点关注错误捕获、日志记录以及实现数据验证策略。

准备环境

让我们继续在 MongoDB shell 中操作:

mongosh

切换到我们现有的数据库:

use dataValidationLab

创建错误处理集合

我们将创建一个具有高级验证和错误处理的新集合:

db.createCollection("products", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "price", "category"],
      properties: {
        name: {
          bsonType: "string",
          minLength: 2,
          maxLength: 100
        },
        price: {
          bsonType: ["double", "int"],
          minimum: 0,
          description: "价格必须为正数"
        },
        category: {
          enum: ["Electronics", "Clothing", "Books", "Food"],
          description: "类别必须是预定义值之一"
        }
      }
    }
  },
  validationAction: "error"
});

实现错误处理机制

我们将创建一个函数来处理无效数据的插入:

function safeInsertProduct(product) {
  try {
    db.products.insertOne(product);
    print("产品插入成功:", product.name);
  } catch (error) {
    print("插入产品时出错:", error.message);

    // 将无效数据记录到单独的集合中
    db.invalidProducts.insertOne({
      product: product,
      errorMessage: error.message,
      timestamp: new Date()
    });
  }
}

测试错误处理

让我们通过多种场景测试我们的错误处理机制:

// 有效的产品插入
safeInsertProduct({
  name: "Smartphone",
  price: 599.99,
  category: "Electronics"
});

// 无效的产品 - 价格类型错误
safeInsertProduct({
  name: "Laptop",
  price: "1000", // 字符串而不是数字
  category: "Electronics"
});

// 无效的产品 - 无效类别
safeInsertProduct({
  name: "T-Shirt",
  price: 29.99,
  category: "Accessories" // 不在预定义类别中
});

// 无效的产品 - 名称过短
safeInsertProduct({
  name: "A",
  price: 10,
  category: "Books"
});

查看无效数据日志

让我们检查无效产品的日志:

print("无效产品日志:");
db.invalidProducts.find();

错误处理的最佳实践

  1. 在插入前始终验证数据
  2. 使用 try-catch 块
  3. 记录无效数据以便后续审查
  4. 在模式级别实现验证
  5. 使用适当的验证操作

修复数据问题

在这一步骤中,我们将学习如何识别和纠正 MongoDB 中的数据问题,重点关注数据清理和转换策略。

准备环境

让我们继续在 MongoDB shell 中操作:

mongosh

切换到我们现有的数据库:

use dataValidationLab

创建数据清理集合

我们将创建一个可能包含问题数据的集合:

db.createCollection("customers", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["name", "email", "age"],
      properties: {
        name: {
          bsonType: "string",
          minLength: 2
        },
        email: {
          bsonType: "string",
          pattern: "^.+@.+$"
        },
        age: {
          bsonType: "int",
          minimum: 18,
          maximum: 120
        }
      }
    }
  }
});

// 插入一些可能存在问题数据的示例数据
db.customers.insertMany([
  { name: "John Doe", email: "john@example", age: "35" },
  { name: "A", email: "invalid-email", age: 15 },
  { name: "Jane Smith", email: "[email protected]", age: 25 }
]);

数据清理函数

让我们创建一个函数来清理和验证客户数据:

function cleanCustomerData() {
  // 查找并修复无效文档
  const invalidCustomers = db.customers.find({
    $or: [
      { age: { $type: "string" } },
      { email: { $not: /^.+@.+$/ } },
      {
        name: { $exists: true, $expr: { $lt: [{ $strLenBytes: "$name" }, 2] } }
      }
    ]
  });

  invalidCustomers.forEach((customer) => {
    // 修复年龄:转换为整数
    const fixedAge =
      typeof customer.age === "string"
        ? parseInt(customer.age)
        : customer.age < 18
          ? 18
          : customer.age;

    // 修复电子邮件:如果缺少域名则添加
    const fixedEmail = customer.email.includes("@")
      ? customer.email
      : `${customer.email}@example.com`;

    // 修复名称:填充过短的名称
    const fixedName =
      customer.name.length < 2 ? customer.name.padEnd(2, "X") : customer.name;

    // 使用修正后的数据更新文档
    db.customers.updateOne(
      { _id: customer._id },
      {
        $set: {
          age: NumberInt(fixedAge),
          email: fixedEmail,
          name: fixedName
        }
      }
    );

    print(`修复客户: ${customer.name}`);
  });

  // 验证清理后的数据
  print("清理数据验证:");
  db.customers.find().forEach(printjson);
}

// 运行清理函数
cleanCustomerData();

清理后的数据验证

让我们验证清理后的数据是否符合我们的验证标准:

function validateCustomers() {
  const invalidCustomersCount = db.customers
    .find({
      $or: [
        { age: { $not: { $type: "int" } } },
        { age: { $lt: 18, $gt: 120 } },
        { email: { $not: /^.+@.+$/ } },
        {
          name: {
            $exists: true,
            $expr: { $lt: [{ $strLenBytes: "$name" }, 2] }
          }
        }
      ]
    })
    .count();

  print(`剩余无效客户数量: ${invalidCustomersCount}`);
  return invalidCustomersCount === 0;
}

validateCustomers();

数据修复的最佳实践

  1. 在数据转换前后始终进行验证
  2. 谨慎使用类型转换
  3. 为关键字段设置默认值或最小值
  4. 记录数据更改以便审计
  5. 在进行大规模数据清理前创建备份

防止不良输入

在这最后一步中,我们将探索通过全面的验证策略来防止不良输入并增强 MongoDB 中的数据完整性的高级技术。

准备环境

让我们继续在 MongoDB shell 中操作:

mongosh

切换到我们现有的数据库:

use dataValidationLab

创建健壮的验证模式

我们将为 registrations 集合创建一个全面的验证模式:

db.createCollection("registrations", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["username", "email", "password", "registrationDate"],
      properties: {
        username: {
          bsonType: "string",
          minLength: 3,
          maxLength: 20,
          pattern: "^[a-zA-Z0-9_]+$",
          description: "用户名必须为字母数字,长度为 3-20 个字符"
        },
        email: {
          bsonType: "string",
          pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
          description: "必须为有效的电子邮件地址"
        },
        password: {
          bsonType: "string",
          minLength: 8,
          description: "密码必须至少为 8 个字符",
          pattern:
            "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$"
        },
        registrationDate: {
          bsonType: "date",
          description: "注册日期必须为有效日期"
        }
      }
    }
  },
  validationAction: "error",
  validationLevel: "strict"
});

实现输入预防中间件

创建一个函数以在插入前验证输入:

function safeRegister(userData) {
  // 清理并验证输入
  const sanitizedData = {
    username: userData.username.trim().toLowerCase(),
    email: userData.email.trim().toLowerCase(),
    password: userData.password,
    registrationDate: new Date()
  };

  // 额外的自定义验证
  if (sanitizedData.username.length < 3) {
    throw new Error("用户名过短");
  }

  if (db.registrations.findOne({ username: sanitizedData.username })) {
    throw new Error("用户名已存在");
  }

  if (db.registrations.findOne({ email: sanitizedData.email })) {
    throw new Error("电子邮件已注册");
  }

  try {
    // 尝试插入并触发 MongoDB 验证
    db.registrations.insertOne(sanitizedData);
    print("注册成功:", sanitizedData.username);
  } catch (error) {
    print("注册失败:", error.message);

    // 记录失败的注册尝试
    db.registrationAttempts.insertOne({
      attemptData: userData,
      errorMessage: error.message,
      timestamp: new Date()
    });
  }
}

// 测试输入预防
function testRegistrations() {
  const testCases = [
    // 有效注册
    {
      username: "john_doe",
      email: "[email protected]",
      password: "Strong!Pass123"
    },

    // 无效案例
    { username: "ab", email: "invalid-email", password: "short" },
    { username: "john_doe!", email: "[email protected]", password: "WeakPass" },
    {
      username: "special_chars!",
      email: "invalid@email",
      password: "NoSpecialChar123"
    }
  ];

  testCases.forEach((testCase) => {
    print("\n测试注册:");
    printjson(testCase);
    safeRegister(testCase);
  });
}

// 运行注册测试
testRegistrations();

查看注册尝试记录

检查记录的注册尝试:

print("失败的注册尝试:");
db.registrationAttempts.find();

输入预防的最佳实践

  1. 使用全面的模式验证
  2. 实现服务器端输入清理
  3. 使用正则表达式进行严格验证
  4. 记录并跟踪无效输入尝试
  5. 实现额外的自定义验证逻辑

总结

在本实验中,你学习了如何检查和验证 MongoDB 中的数据类型,以维护数据完整性并防止数据库中的错误。你探索了 MongoDB 支持的各种数据类型,包括字符串(string)、整数(integer)、双精度浮点数(double)、布尔值(boolean)、数组(array)、对象(object)、日期(date)、ObjectId 和 null。你创建了一个具有特定数据类型要求的示例用户集合(users collection),例如确保名称是字符串、年龄是介于 18 到 120 之间的整数,以及电子邮件是有效的电子邮件地址。你还学习了如何处理无效数据,通过尝试插入类型不正确的文档并理解错误信息。

最后,你练习了插入具有正确数据类型的文档,以成功将它们添加到用户集合中。