MongoDB 的文档模型与 CRUD 实战

对于开发者而言,数据库的选择往往决定了应用程序的架构灵活性。不少开发者应该都熟悉传统的关系型数据库(如 MySQL 或 PostgreSQL),初次接触 MongoDB 时可能会感到一种思维模式的转变。

今天一起来探讨 MongoDB 为何被设计成现在的样子,以及如何进行基础的增删改查(CRUD)操作。

关系型数据库的世界

在关系型数据库中,数据被整齐地排列在行和列中,就跟 Excel 表格似的。表结构(Schema)在数据写入前就必须定义好。每一行都要遵循这个规则。

这种设计能确保数据的一致性和逻辑严密性。但在面对快速迭代的现代应用时,这种刚性可能成为瓶颈。如果业务需求变更,需要给现有的亿级大表增加一个字段,那迁移工作就复杂多了,还有潜在的停机风险。

NoSQL = Not Only SQL

NoSQL 并不是不要SQL,我觉得它更倾向于 Not Only SQL

并不是说要抛弃关系型数据库,而是在工具箱中增加一个新的选项。对于社交网络、游戏日志、物联网数据等非结构化或半结构化数据,我们需要一种更灵活的存储方式。MongoDB 正是其中的佼佼者,属于文档型数据库(Document-Oriented)

核心概念:文档与集合

MongoDB 不再将数据拆散存储在不同的行和列中,而是将一个完整的对象存储为一个文档(Document)

文档 (Document)

文档是 MongoDB 的最小数据单元。它看起来非常像 JSON 对象,但技术上,MongoDB 使用的是 BSON(Binary JSON)。BSON 是一种二进制格式,读写速度更快,且支持 JSON 无法表达的数据类型(如日期、二进制数据等)。

文档强就强在它能嵌套。开发者可以将列表、对象直接存入一个字段中,而不需要像 SQL 那样建立多张关联表。

代码示例:一个电商订单文档

以下代码展示了一个包含嵌套对象(收货地址)和数组(商品列表)的文档结构,这在 MongoDB 中是标准写法:

javascript 复制代码
{
  // 唯一标识符,相当于主键
  "_id": ObjectId("64b8f1e2a9b3c5d6e7f8a9b0"),
  // 支持存储具体的日期类型
  "created_at": ISODate("2024-05-20T09:30:00Z"),
  "status": "Processing",
  
  // 嵌套对象:无需拆分到另一张表
  "delivery_info": {
    "Country": "France",
    "city": "Paris",
    "detail": "Avenue des Champs Elysées"
  },
  
  // 数组结构:直接存储列表数据
  "cart_items": [
    {
      "sku": "KB-MECHANICAL-01",
      "count": 1,
      "unit_price": 599.00
    },
    {
      "sku": "MOUSE-PAD-XL",
      "count": 2,
      "unit_price": 49.50
    }
  ]
}

集合 (Collection)

如果说文档对应 SQL 中的"行",那么集合(Collection)就对应 SQL 中的"表"。

最大的区别是它的动态模式(Dynamic Schema) 。在同一个集合中,文档的结构可以不完全相同。例如,在 users 集合中,有的用户可能有 wechat_id 字段,而有的用户只有 email 字段。这种灵活性让数据建模更贴近面向对象编程的逻辑。

核心字段:_id

每个文档都必须有一个 _id 字段,作为主键。如果在插入数据时没有指定,MongoDB 会自动生成一个全局唯一的 ObjectId。这个 ID 包含了时间戳信息,因此甚至可以从 ID 中推算出数据的创建时间。


基础实战:CRUD 操作

假设我们已经连接到了数据库(使用 mongosh 或图形化工具),下面演示如何对一个名为 employees的集合进行操作。

新增 (Create)

使用 insertOne 插入单条数据,或 insertMany 插入多条数据。

  • 插入单条数据
javascript 复制代码
// 向 employees 集合中添加一名新员工
db.employees.insertOne({
  name: "Jack",
  department: "Engineering",
  is_manager: false,
  skills: ["Java", "Docker"],
  onboard_date: new Date()
})
  • 插入多条数据
javascript 复制代码
// 一次性添加多名实习生
db.employees.insertMany([
   {
     name: "Mike",
     role: "Intern",
     age: 21,
     mentor: "Jack"
   },
   {
     name: "Tommy",
     role: "Intern",
     age: 22,
     mentor: "Jack"
   }
])

查询 (Read)

  • 查询单条 ( findOne ) :返回匹配条件的第一条数据。常用于根据 ID 或唯一字段查找。
javascript 复制代码
// 查找名字是 'Jack' 的员工
db.employees.findOne({ name: 'Jack' })
  • 查询多条 ( find ) :返回所有匹配的数据。支持复杂的筛选操作符。
javascript 复制代码
// 查询所有年龄大于 21 岁的员工
// $gt 表示 greater than (大于)
db.employees.find({ age: { $gt: 21 } })

更新 (Update)

更新操作通常包含两部分:筛选条件更新动作 (例如 $set)。

  • 更新单条 ( updateOne )
javascript 复制代码
// 找到名字是 'Tommy' 的员工,将其职位修改为 'Junior Developer'
db.employees.updateOne(
  { name: 'Tommy' },       // 筛选条件
  { $set: { role: 'Junior Developer' } } // 更新动作
)
  • 更新多条 ( updateMany )
javascript 复制代码
// 为所有没有 'location' 字段的员工,添加默认办公地点
// $exists: false 用于判断字段不存在
db.employees.updateMany(
  { location: { $exists: false } }, 
  { 
    $set: { 
      "location": "New York",
      "wfh_allowed": true
    } 
  }
);

删除 (Delete)

  • 删除单条 ( deleteOne )
javascript 复制代码
// 删除第一个匹配到的名字为 'Mike' 的记录
db.employees.deleteOne({ name: "Mike" })
  • 删除多条 ( deleteMany )
javascript 复制代码
// 删除所有实习生记录
db.employees.deleteMany({ role: "Intern" });

注意 :如果执行 db.employees.deleteMany({})(空筛选条件),将会清空整个集合,操作时需格外谨慎。


搭建高效的本地开发环境

理解了理论和基础指令后,下一步就是在本地环境中进行实战演练。

对于开发者来说,维护本地数据库环境比较繁琐。可能需要安装 Docker,编写复杂的 Compose 文件,或者在系统中通过命令行安装不同版本的数据库,还要处理端口冲突和版本依赖问题。

但这些问题在ServBay面前都是小Case,因为ServBay 能够极大简化这一流程。它不仅支持一键安装 MongoDB,更解决了多版本共存的痛点。

ServBay 的核心优势:

  • 多版本同时运行:ServBay支持运行 MongoDB 5.0 到 8.0 的实例,互不干扰。这对于维护不同历史时期的项目非常方便,无需反复卸载重装。

  • 全栈数据库支持:除了 MongoDB,ServBay 还囊括了 MySQL、PostgreSQL、MariaDB 以及 Redis、Memcached 等主流 NoSQL 数据库。

  • 实例隔离:支持同时运行多个不同类型的数据库实例,为每个项目提供独立的沙盒环境。

  • 极简配置:告别繁杂的配置文件,通过直观的图形界面即可管理服务状态和端口。

如果你希望跳过繁琐的环境配置,直接进入代码开发和数据库结构设计的核心环节,ServBay 会是一个得力的助手。它让数据库的安装和管理变得像手机安装 App 一样简单,让你能专注于构建更优秀的应用程序。

相关推荐
ITMr.罗2 小时前
深入理解EF Core更新机制(开发中因为省事遇到的问题)
服务器·数据库·c#·.net
哈哈哈笑什么2 小时前
Sleuth+Zipkin 与 OpenSearch 结合是企业级分布式高并发系统的“王炸组合”
分布式·后端·spring cloud
开心猴爷2 小时前
App HTTPS 抓包实战解析,从代理调试到真实网络流量观察的完整抓包思路
后端
梁萌2 小时前
MySQL索引的使用技巧
数据库·mysql·索引·b+tree
shengjk12 小时前
为什么按 Ctrl+D 会退出终端?—— 从电传打字机到现代 macOS 的完整旅程
后端
白宇横流学长2 小时前
基于SpringBoot医院复查开药网站和微信小程序的设计
spring boot·后端·微信小程序
小二·2 小时前
MyBatis基础入门《十》Spring Boot 整合 MyBatis:从单数据源到多数据源实战
spring boot·后端·mybatis
x10n92 小时前
OceanBase 参数对比工具 附源码
数据库·vscode·oceanbase·腾讯云ai代码助手
RestCloud3 小时前
如何用ETL做实时风控?从交易日志到告警系统的实现
数据库·数据仓库·kafka·数据安全·etl·数据处理·数据集成