最近在工作中接触到了 MongoDB 数据库。作为 NoSQL 数据库,MongoDB 在使用上和平常用得比较多的 MySQL 等数据库存在着比较大的差别,因此我觉得不能直接沿用传统关系型数据库的设计思维,有必要学习一下 MongoDB 的设计模式。

在《MongoDB 权威指南》的第九章中介绍了 MongoDB 中常用的设计模式。然而在原文中,每个设计模式只用了简单的一段话概括,没有给出具体的使用示例。在这篇文章中我将先引用书中原文介绍设计模式,然后再给出具体详细的使用示例。这篇文章借助 ChatGPT 的帮助来完成。

多态模式

这种模式适用于集合中的所有文档具有类似但不完全相同结构的情况。它涉及识别跨文档的公共字段,而这些文档需要支持应用程序的公共查询。跟踪文档或子文档中的特定字段将有助于识别数据与不同代码路径或类/子类之间的差异,我们可以在应用程序中编码以管理二者的差异。这允许在文档不完全相同的单个集合中使用简单查询来提高查询性能。

我们构建一个通知系统,有三种类型的通知,最主要的区分方式是看它们的 type 字段:

  1. Email 通知
  2. SMS 通知
  3. App 推送通知
{
  "_id": ObjectId("..."),
  "type": "email",
  "to": "[email protected]",
  "subject": "Welcome!",
  "body": "Thank you for registering.",
  "createdAt": ISODate("2025-08-01T12:00:00Z")
}

{
  "_id": ObjectId("..."),
  "type": "sms",
  "phoneNumber": "+1234567890",
  "message": "Your code is 123456",
  "createdAt": ISODate("2025-08-01T12:01:00Z")
}

{
  "_id": ObjectId("..."),
  "type": "push",
  "deviceId": "abc123",
  "title": "New Message",
  "content": "You have a new message in your inbox.",
  "createdAt": ISODate("2025-08-01T12:02:00Z")
}

我们可以使用如下简单查询获取所有通知的公共信息:


db.notifications.find(
  { createdAt: { $gte: ISODate("2025-08-01T00:00:00Z") } },
  { type: 1, createdAt: 1 }
)

我们也可以通过 type 字段对文档进行分类处理:


// 查询所有 email 通知
db.notifications.find({ type: "email" })

// 查询所有 push 通知的内容字段
db.notifications.find(
  { type: "push" },
  { content: 1, deviceId: 1 }
)

属性模式

这种模式非常适合于文档中部分字段具有希望对其进行排序或查询的公共特性,或者需要排序的字段仅存在于部分文档中,或者这两个条件都满足。它包括将数据重塑为键–值对数组,并在该数组中的元素上创建索引。限定符可以作为附加字段添加到这些键–值对中。此模式有助于查询那些存在许多相似字段的文档,因此需要的索引更少,查询也更容易编写。

传统文档结构如下(字段静态):

{
  "_id": 1,
  "height": 180,
  "weight": 75,
  "blood_pressure": "120/80"
}

应用属性模式后改为: