设计 MongoDB 订单 Schema

MongoDBBeginner
立即练习

介绍

在本实验中,你将学习如何为电商应用设计 MongoDB 模式。目标是为每个订单创建一个单一、全面的文档,使其易于管理和查询。你将从创建一个基础订单结构开始,然后通过嵌入详细的客户信息、订单项列表、支付详情和状态历史来逐步丰富它。这种方法展示了 MongoDB 文档模型在实际应用中的强大功能。

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

创建基础订单文档

在第一步中,你将连接到 MongoDB 并创建一个新数据库。然后,你将插入你的第一个订单文档,它将作为我们将在后续步骤中构建的模式的基础。

首先,打开 MongoDB Shell。这个交互式命令行界面允许你与你的 MongoDB 数据库进行通信。

mongosh

进入 shell 后,你将看到一个 > 提示符。现在,切换到一个名为 ecommerce 的新数据库。如果数据库不存在,MongoDB 会在你首次存储数据时为你创建它。

use ecommerce

接下来,你将通过向名为 orders 的集合中插入一个文档来创建它。这个文档将代表一个单独的订单,并包含基本信息,如订单 ID、客户详情、订单日期和当前状态。

执行以下命令插入文档:

db.orders.insertOne({
  order_id: "ORD001",
  order_date: new Date("2023-10-26T10:00:00Z"),
  customer_id: "CUST123",
  status: "pending",
  total: 150.0
});

此命令执行以下操作:

  • db.orders: 指定当前数据库中的 orders 集合。
  • insertOne(): 一个 MongoDB 方法,用于插入单个文档。
  • 文档本身是一个类似 JSON 的对象,包含 order_idorder_datecustomer_idstatustotal 的键值对。

成功插入后,MongoDB 将返回一个确认信息以及新创建文档的唯一 _id

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

你现在已经创建了订单的基础结构。在下一步中,你将用更详细的信息来增强此文档。

嵌入客户详情以增强功能

一个简单的 customer_id 通常是不够的。在实际应用中,你在检索订单时经常需要客户的姓名和地址。与其执行一个单独的查询到 customers 集合,不如将这些信息直接嵌入到订单文档中。这是 MongoDB 中一种常见且强大的模式,可以提高读取性能。

在这一步中,你将更新订单文档以包含详细的、嵌套的客户信息。如果你在上一步中退出了 mongosh shell,请重新启动它并运行 use ecommerce

使用 updateOne() 方法查找 order_id: "ORD001" 的文档并进行修改。$set 操作符将一个字段的值替换为指定的值,而 $unset 则完全移除一个字段。

db.orders.updateOne(
  { order_id: "ORD001" },
  {
    $set: {
      customer: {
        customer_id: "CUST123",
        first_name: "John",
        last_name: "Doe",
        email: "john.doe@example.com",
        shipping_address: {
          street: "123 Main St",
          city: "Anytown",
          state: "CA",
          zip_code: "12345"
        }
      }
    },
    $unset: {
      customer_id: ""
    }
  }
);

在此命令中:

  • 第一个参数 { order_id: "ORD001" } 是用于选择要更新的文档的过滤器。
  • 第二个参数包含更新操作符:
    • $set: 我们正在设置一个新字段 customer,它是一个包含详细信息的嵌入式文档。
    • $unset: 我们正在移除顶层的旧 customer_id 字段,以避免数据冗余。

为了验证你的更改,你可以使用 findOne() 检索更新后的文档:

db.orders.findOne({ order_id: "ORD001" });

输出现在将显示嵌套的 customer 文档,并且顶层的 customer_id 字段将消失。这种嵌入式结构将相关数据保存在单个文档中。

添加订单商品数组

一个订单通常包含一个或多个产品。在单个订单文档中建模这种一对多关系的最佳方式是使用嵌入式文档的数组。数组中的每个元素将代表订单中的一个项。

让我们更新订单文档以包含一个项目列表。我们将添加一个 items 字段,它将是一个数组。数组中的每个对象将包含有关产品的详细信息,例如其 ID、名称、价格和数量。

执行以下 updateOne 命令:

db.orders.updateOne(
  { order_id: "ORD001" },
  {
    $set: {
      items: [
        {
          product_id: "PROD01",
          name: "Laptop",
          price: 1200.0,
          quantity: 1
        },
        {
          product_id: "PROD02",
          name: "Mouse",
          price: 25.0,
          quantity: 1
        }
      ],
      total: 1225.0
    }
  }
);

在这里,我们再次使用 $set 来添加 items 数组。我们还更新了 total 字段以匹配项目价格的总和。将计算出的总计直接存储在文档中是另一种性能优化方式,因为它避免了每次读取时都需要进行聚合。

让我们再次检查文档以查看新的 items 数组。

db.orders.findOne({ order_id: "ORD001" });

你将看到 items 数组嵌入在订单文档中。这种设计允许你通过一次数据库查询检索一个完整的订单,包括其所有项目。

嵌入支付信息

支付详情是订单的另一个关键部分。与客户信息和订单项类似,支付数据也可以直接嵌入到订单文档中。这包括支付方式、交易 ID 和状态。

在这一步中,你将向订单添加一个 payment 子文档。

db.orders.updateOne(
  { order_id: "ORD001" },
  {
    $set: {
      payment: {
        method: "credit_card",
        transaction_id: "TXN54321",
        status: "completed"
      }
    }
  }
);

此命令添加了一个包含三个字段的 payment 对象:methodtransaction_idstatus。嵌入这些信息可确保与单个交易相关的所有数据都位于一个地方。

让我们查看一下订单文档的结构。

db.orders.findOne({ order_id: "ORD001" });

该文档现在包含有关订单、客户、订单项和支付的全面信息,所有这些都可以通过一次读取操作进行访问。

跟踪订单状态历史记录

订单会经历各种阶段,例如“待处理”(pending)、“处理中”(processing)、“已发货”(shipped)和“已送达”(delivered)。虽然顶级的 status 字段显示当前状态,但维护所有状态更改的日志通常很有用。你可以通过添加一个 status_history 数组来实现这一点。

在最后一步中,你将把订单状态更新为“处理中”(processing),并开始构建 status_history 数组。$push 操作符用于将一个值追加到数组中。

首先,让我们将初始的“待处理”(pending)状态添加到历史记录中,并将当前状态更新为“处理中”(processing)。

db.orders.updateOne(
  { order_id: "ORD001" },
  {
    $set: { status: "processing" },
    $push: {
      status_history: {
        status: "pending",
        timestamp: new Date("2023-10-26T10:00:00Z")
      }
    }
  }
);

现在,让我们将新的“处理中”(processing)状态添加到历史记录中,以保持日志的完整性。

db.orders.updateOne(
  { order_id: "ORD001" },
  {
    $push: {
      status_history: {
        status: "processing",
        timestamp: new Date("2023-10-26T11:30:00Z")
      }
    }
  }
);

这种方法提供了订单生命周期的完整审计跟踪。你可以随时查询文档以查看其完整的历史记录。

db.orders.findOne({ order_id: "ORD001" });

该文档现在包含一个 status_history 数组,让你能够全面了解订单的整个过程。要退出 MongoDB shell,请键入 exit 并按 Enter。

总结

在本实验中,你学习了如何为电子商务订单设计一个实用且高效的 MongoDB schema。你从一个基础文档开始,通过嵌入相关数据逐步丰富它。你已成功创建了一个包含客户详情、订单项数组、支付信息和完整状态历史的单个文档。这种嵌入式文档模式是 MongoDB schema 设计中的一个核心概念,通过减少对复杂连接(joins)和多次查询的需求,有助于创建高性能和可扩展的应用程序。