MongoDB快速入门:一篇学会基本操作与CRUD – wiki基地


MongoDB快速入门:一篇学会基本操作与CRUD

在当今数据驱动的世界里,数据库是任何应用程序的基石。传统的关系型数据库(如 MySQL, PostgreSQL)以其结构化和事务性在许多场景下表现出色,但随着互联网应用的复杂化和数据规模的爆炸式增长,一种更灵活、可扩展性更强的数据存储方案应运而生——NoSQL数据库。而在众多 NoSQL 数据库中,MongoDB 以其独特的文档模型、强大的查询能力和卓越的横向扩展性,成为了最受欢迎的选择之一。

本文是一篇为初学者量身打造的 MongoDB 入门指南,旨在通过详尽的讲解和丰富的实例,带您从零开始,一步步掌握 MongoDB 的核心概念和最基本的CRUD(创建、读取、更新、删除)操作。无论您是后端开发者、数据分析师,还是对数据库技术充满好奇的学生,读完本文,您将能够自信地开始使用 MongoDB 进行数据操作。

一、 核心概念:理解 MongoDB 的世界观

在动手操作之前,我们必须先理解 MongoDB 的基本构成和术语。这就像学习一门新语言前,先要掌握它的字母和基本词汇。MongoDB 的核心概念与关系型数据库有很好的对应关系,这有助于我们理解。

MongoDB 概念 关系型数据库 (SQL) 对应概念 解释
Database (数据库) Database (数据库) 数据库是一个物理容器,包含了多个集合。每个数据库都有自己独立的文件集。
Collection (集合) Table (表) 集合是一组 MongoDB 文档的容器,类似于关系型数据库中的表。
Document (文档) Row (行) 文档是 MongoDB 中数据的基本单元,由一组键值对(key-value)构成,类似于 JSON 对象。
Field (字段) Column (列) 字段是文档中的一个键值对,代表了数据的一个属性。
_id Primary Key (主键) 每个文档都有一个特殊的 _id 字段,在集合中必须是唯一的,作为文档的主键。
Index (索引) Index (索引) 与 SQL 类似,用于提升查询性能。

关键不同点:

  1. 数据模型: MongoDB 是文档导向的。它的数据单元(文档)是类似 JSON 的 BSON(Binary JSON)格式。这意味着你可以存储复杂的、嵌套的数据结构,比如数组和子文档,这在一个文档中就能表示复杂的关系,而不需要像 SQL 那样进行多表连接(JOIN)。

  2. 无模式(Schema-less): 这是 MongoDB 最显著的特点之一。一个集合里的文档不需要有相同的结构。你可以随时向文档中添加或删除字段,而无需预先定义表结构。这种灵活性使得它非常适合快速迭代的敏捷开发。

一个简单的文档示例:

假设我们要存储一个用户信息,在 MongoDB 中,它可能看起来是这样的:

json
{
"_id": ObjectId("63a5a7e8e4b1c2d3e4f5a6b7"),
"name": "张三",
"age": 30,
"email": "[email protected]",
"status": "active",
"hobbies": ["编程", "阅读", "健身"],
"address": {
"city": "北京",
"street": "中关村大街"
},
"join_date": ISODate("2022-12-23T10:00:00Z")
}

注意看,hobbies 是一个数组,address 是一个嵌套的子文档。这种丰富的数据结构是 MongoDB 强大能力的基础。

二、 环境准备:安装与启动

要开始实践,首先需要一个 MongoDB 环境。

  1. 安装 MongoDB Community Server
    访问 MongoDB 官网(https://www.mongodb.com/try/download/community)下载适合您操作系统的社区版。安装过程基本是“下一步”即可。安装完成后,MongoDB 服务(mongod)通常会作为系统服务在后台运行。

  2. 使用 MongoDB Shell (mongosh)
    mongosh 是 MongoDB 的现代交互式命令行工具,是与数据库交互的主要方式。在安装服务器时,它通常会一并安装。打开你的终端或命令行提示符,输入 mongosh 并回车。

    bash
    mongosh

    如果连接成功,你会看到欢迎信息和提示符 >,这表示你已经成功连接到本地运行的 MongoDB 服务了。

三、 数据库和集合的基本操作

mongosh 中,我们可以执行一些基本的管理命令。

  • 查看所有数据库

    javascript
    show dbs

    刚安装完可能只会看到 admin, config, local 这几个系统自带的数据库。

  • 切换或创建数据库
    使用 use 命令。如果该数据库不存在,MongoDB 不会立即创建它,而是在你向其中插入第一条数据时自动创建。

    javascript
    // 切换到名为 my_first_db 的数据库
    use my_first_db

    执行后,即使 my_first_db 不存在,系统也会显示 switched to db my_first_db

  • 查看当前数据库

    javascript
    db

  • 查看当前数据库中的集合

    javascript
    show collections

    刚创建的数据库是空的,所以这个命令不会有任何输出。

  • 删除当前数据库
    这个操作非常危险,请谨慎使用。

    javascript
    db.dropDatabase()

四、 CRUD 核心操作详解

CRUD 是所有数据库操作的核心。我们将围绕一个 users 集合来演示这些操作。

C – Create (创建数据)

创建操作就是向集合中插入新的文档。

1. 插入单个文档 (insertOne)

insertOne() 方法用于向集合中插入一个文档。

“`javascript
// 切换到我们的数据库
use my_first_db

// 向 users 集合插入一个用户文档
db.users.insertOne({
name: “李四”,
age: 28,
email: “[email protected]”,
hobbies: [“电影”, “音乐”],
status: “active”
})
“`

执行后,你会收到一个确认回执,包含了操作是否成功以及新插入文档的 _id

json
{
"acknowledged": true,
"insertedId": ObjectId("63a5aa87e4b1c2d3e4f5a6b8")
}

注意:我们没有手动创建 users 集合。在第一次向其插入数据时,MongoDB 自动为我们创建了它。这就是 MongoDB 的便捷之处。

2. 插入多个文档 (insertMany)

insertMany() 方法可以一次性插入一个文档数组,效率更高。

javascript
db.users.insertMany([
{
name: "王五",
age: 35,
email: "[email protected]",
hobbies: ["旅游", "摄影"],
status: "active",
address: { city: "上海" }
},
{
name: "赵六",
age: 22,
email: "[email protected]",
hobbies: ["游戏", "动漫"],
status: "inactive"
},
{
name: "孙七",
age: 35,
email: "[email protected]",
hobbies: ["烹饪"],
status: "active",
address: { city: "广州" }
}
])

返回结果将包含一个 insertedIds 数组,列出了所有新插入文档的 _id

R – Read (读取数据)

读取是数据库最频繁的操作。MongoDB 提供了强大而灵活的 find() 方法。

1. 查询所有文档

不带任何参数的 find() 会返回集合中的所有文档。

javascript
db.users.find()

mongosh 默认会以格式化的方式显示结果。如果结果太多,它会提示你输入 it (iterate) 来查看更多。

2. 按条件查询

find() 的第一个参数是一个查询过滤器文档,用于指定查询条件。

  • 等值匹配:查找 status"active" 的所有用户。

    javascript
    db.users.find({ status: "active" })

  • 查询嵌套文档中的字段:查找地址在上海的用户。使用点表示法(dot notation)。

    javascript
    db.users.find({ "address.city": "上海" })

3. 使用查询操作符

为了实现更复杂的查询(如大于、小于、在…之内),MongoDB 提供了一系列以 $ 开头的查询操作符。

  • 比较操作符$eq(等于), $ne(不等于), $gt(大于), $gte(大于等于), $lt(小于), $lte(小于等于)。

    “`javascript
    // 查找年龄大于 30 岁的用户
    db.users.find({ age: { $gt: 30 } })

    // 查找年龄小于等于 28 岁的用户
    db.users.find({ age: { $lte: 28 } })
    “`

  • 逻辑操作符$and, $or, $not, $nor

    “`javascript
    // 查找年龄大于 25 岁,并且状态为 “active” 的用户
    // $and 是默认的逻辑,可以省略
    db.users.find({ age: { $gt: 25 }, status: “active” })

    // 查找地址在 “北京” 或 “上海” 的用户
    db.users.find({
    $or: [
    { “address.city”: “北京” },
    { “address.city”: “上海” }
    ]
    })
    “`
    (为了让这个查询有结果,你可以自己先插入一个北京的用户)

  • 数组操作符$in (在…之中), $nin (不在…之中), $all (包含所有)。

    “`javascript
    // 查找爱好包含 “电影” 或 “旅游” 的用户
    db.users.find({ hobbies: { $in: [“电影”, “旅游”] } })

    // 查找爱好中同时包含 “游戏” 和 “动漫” 的用户
    db.users.find({ hobbies: { $all: [“游戏”, “动漫”] } })
    “`

4. 投影 (Projection)

默认情况下,find() 返回文档的全部字段。我们可以使用第二个参数(投影文档)来指定返回哪些字段。1 表示包含,0 表示排除。

javascript
// 只返回用户的 name 和 email 字段,同时排除默认会返回的 _id 字段
db.users.find(
{ status: "active" }, // 查询条件
{ name: 1, email: 1, _id: 0 } // 投影
)

5. 排序、跳过和限制

这些方法通常链式调用在 find() 之后,用于分页和排序。

  • sort(): 1 表示升序,-1 表示降序。
  • skip(): 跳过指定数量的文档。
  • limit(): 限制返回的文档数量。

javascript
// 查找所有用户,按年龄降序排列,跳过第1条,只返回 2 条记录
db.users.find().sort({ age: -1 }).skip(1).limit(2)

这个组合是实现后端分页功能的经典模式。

6. 查询单个文档 (findOne)

如果你确定只需要一个结果(或者只关心第一个匹配的结果),使用 findOne() 会更方便,它直接返回一个文档对象,而不是一个游标。

javascript
// 查找名字为 "李四" 的用户
db.users.findOne({ name: "李四" })

U – Update (更新数据)

更新操作用于修改集合中已存在的文档。

1. 更新单个文档 (updateOne)

updateOne() 匹配第一个符合条件的文档并进行更新。它需要两个参数:一个查询过滤器,一个更新操作文档。

关键:必须使用更新操作符!(如 $set, $inc, $push等),否则会用新文档替换整个旧文档。

  • 使用 $set 修改字段值
    $set 用于修改或添加字段。这是最常用的更新操作符。

    javascript
    // 将名字为 "李四" 的用户的年龄修改为 29,并添加一个新的 "vip" 字段
    db.users.updateOne(
    { name: "李四" }, // 查询条件
    { $set: { age: 29, vip: true } } // 更新操作
    )

  • 使用 $inc 增加数值
    $inc 用于对数字字段进行增减。

    javascript
    // 将 "李四" 的年龄增加 1
    db.users.updateOne({ name: "李四" }, { $inc: { age: 1 } })

  • 使用 $push 向数组添加元素

    javascript
    // 给 "李四" 的爱好列表里添加 "跑步"
    db.users.updateOne({ name: "李四" }, { $push: { hobbies: "跑步" } })

    如果想避免添加重复元素,可以使用 $addToSet

  • 使用 $unset 删除字段

    javascript
    // 删除 "赵六" 的 email 字段
    db.users.updateOne({ name: "赵六" }, { $unset: { email: 1 } })

2. 更新多个文档 (updateMany)

updateMany() 会更新所有匹配查询条件的文档。

javascript
// 将所有 status 为 "active" 的用户的状态更新为 "active_user",并添加一个 last_updated 字段
db.users.updateMany(
{ status: "active" },
{
$set: {
status: "active_user",
last_updated: new Date()
}
}
)

D – Delete (删除数据)

删除操作是不可逆的,务必小心。

1. 删除单个文档 (deleteOne)

deleteOne() 删除匹配条件的第一个文档。

javascript
// 删除名字为 "赵六" 的用户
db.users.deleteOne({ name: "赵六" })

2. 删除多个文档 (deleteMany)

deleteMany() 删除所有匹配条件的文档。

javascript
// 删除所有状态为 "inactive" 的用户
db.users.deleteMany({ status: "inactive" })

高危警告:如果给 deleteMany() 一个空的查询文档 {},它会删除集合中的所有文档!

db.users.deleteMany({}) // 清空整个 users 集合!

五、 索引:提升查询性能的利器

当数据量变大时,如果没有索引,每次查询 MongoDB 都需要扫描整个集合(全集合扫描),效率极低。索引通过预先排序数据,使得查询可以快速定位到目标文档,就像书的目录一样。

  • 创建单字段索引
    name 字段上创建一个升序索引。

    javascript
    db.users.createIndex({ name: 1 })

    这里的 1 表示升序,-1 表示降序。对于单字段索引,升降序影响不大,但对于复合索引则至关重要。

  • 创建复合索引
    如果你经常需要按 status 过滤,然后按 age 排序,那么创建一个复合索引会非常高效。

    javascript
    db.users.createIndex({ status: 1, age: -1 })

    索引的顺序很重要,应将最常用于精确匹配的字段放在前面。

  • 查看集合上的索引

    javascript
    db.users.getIndexes()

创建索引会占用额外的磁盘空间和一些写操作的性能开销,但对于读操作的性能提升是巨大的。合理地创建索引是 MongoDB 性能优化的关键。

总结与展望

恭喜你!通过本文的学习,你已经掌握了 MongoDB 的基本世界观,学会了如何安装和连接数据库,并对最核心的 CRUD 操作有了全面而深入的了解。我们涵盖了:

  • 核心概念:数据库、集合、文档、字段的关系。
  • 创建:使用 insertOneinsertMany 添加数据。
  • 读取:使用 findfindOne,结合查询操作符、投影、排序、分页等技巧进行复杂查询。
  • 更新:使用 updateOneupdateMany,配合 $set, $inc, $push 等更新操作符修改数据。
  • 删除:使用 deleteOnedeleteMany 移除数据。
  • 索引:了解了索引的基本概念和创建方法,为性能优化打下基础。

这仅仅是 MongoDB 强大功能的冰山一角。接下来,你可以继续探索更高级的主题,例如:

  • 聚合管道 (Aggregation Pipeline):用于处理复杂的数据聚合、转换和分析,是 MongoDB 的“瑞士军刀”。
  • 事务 (Transactions):在需要保证多文档操作原子性的场景下使用。
  • 复制集 (Replica Sets):通过数据冗余实现高可用性。
  • 分片 (Sharding):通过水平扩展将数据分布到多个服务器,以应对海量数据。

数据库技术是通往高级开发的必经之路。希望这篇详尽的入门指南能为你打开 MongoDB 的大门,让你在数据存储的道路上走得更远、更稳。现在,就动手实践吧!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部