MongoDB Shell

MongoDB Shell(简称简称 mongomongosh)是 MongoDB 官方提供的交互式命令行工具,用于连接 MongoDB 数据库并执行各种操作(如查询、插入、更新数据,管理数据库 / 集合等)。它是开发者与 MongoDB 交互的最直接方式之一,其使用的命令是 MongoDB 操作的 “原生语法”

它的本质实际上是一个基于 JavaScript 的交互式环境,支持 JavaScript 语法和 MongoDB 扩展命令。你可以在其中输入命令,实时执行并获取结果,类似 MySQL 的 mysql 命令行工具。

  • 从 MongoDB 5.0 开始,官方推荐使用新一代 Shell 工具 mongosh(功能更完善,支持语法高亮、自动补全、更好的错误提示等),旧的 mongo 工具已逐步废弃。

也就是说,你可以使用 js 操作 MongoDB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 在 mongosh 中执行 JS 逻辑
const userList = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
];

// 批量插入文档(结合 JS 数组和循环)
userList.forEach(user => {
db.users.insertOne(user);
});

// 条件查询并处理结果(JS 函数)
const adults = db.users.find({ age: { $gt: 18 } }).toArray();
adults.forEach(adult => {
console.log(`${adult.name} is an adult`);
});

而且,还可以通过 Node.js 环境,借助 MongoDB 官方提供的 Node.js 驱动mongodb 包)来操作数据库。

1
npm install mongodb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const { MongoClient } = require('mongodb');

// 连接数据库
const client = new MongoClient('mongodb://localhost:27017');
async function run() {
try {
await client.connect();
const db = client.db('mydb'); // 指定数据库
const usersCollection = db.collection('users'); // 指定集合

// 1. 插入文档
await usersCollection.insertOne({ name: "Charlie", age: 28 });

// 2. 查询文档
const result = await usersCollection.find({ age: { $gt: 25 } }).toArray();
console.log("查询结果:", result);

// 3. 更新文档
await usersCollection.updateOne(
{ name: "Charlie" },
{ $set: { age: 29 } }
);

// 4. 删除文档
await usersCollection.deleteOne({ name: "Bob" });
} finally {
await client.close(); // 关闭连接
}
}

run().catch(console.error);

MongoDB 的文档模型基于 BSON(类 JSON 格式),与 JavaScript 的对象语法高度契合,数据结构转换成本极低。

MongoDB的基本操作

MongoDB 是基于文档模型的 NoSQL 数据库,核心操作围绕数据库(Database)、集合(Collection)、文档(Document) 三层结构展开

数据库操作

数据库是集合的容器,一个 MongoDB 实例可包含多个数据库,若未指定数据库,MongoDB 会默认使用 test 数据库。

操作目的 命令 说明
查看所有数据库 show dbsshow databases 仅显示包含数据的数据库(空库不显示)
切换 / 创建数据库 use <数据库名> 若数据库不存在,插入数据时会自动创建
查看当前所在数据库 db 无参数,直接返回当前数据库名称
删除当前数据库 db.dropDatabase() 谨慎操作,需先通过 db 确认当前库

数据库名可以是满足以下条件的任意 UTF-8 字符串

  1. 不能是空字符串
  2. 不得含有 空格.$/\\0 等符号
  3. 全部小写
  4. 最多 64 字节

而且adminlocalconfig是保留的三个数据库,可以直接访问这些有特殊作用的数据库

在上一篇中我们也说了,MongoDB 的存储方式是每个数据库在磁盘上对应一个独立的文件夹(位于 MongoDB 数据目录,默认 data/db),文件夹名与数据库名一致,数据文件(如 .wt 格式)存放在其中。

选择数据库和创建数据库

use <数据库名> 是最常用的数据库操作命令,功能是切换到指定数据库;若该数据库不存在,则在首次插入数据时自动创建

但是,仅执行 use 不会实际创建空数据库

1
2
3
4
5
// 切换到名为 "mydb" 的数据库(若不存在则准备创建)
use mydb

// 此时数据库并未实际创建,需插入数据才会生成
db.users.insertOne({name: "Alice"}) // 插入文档后,"mydb" 被实际创建

因为,空数据库(无任何集合和数据)不会显示在 show dbs 结果中,只有当插入数据后才会被持久化。

这也就是为什么,我们再 MongoDB Compass 中创建数据库的时候,需要让你插入一个集合

image-20251025151100395

查看当前数据库

db 命令无需参数,直接返回当前所在的数据库名称。

1
2
use mydb
db // 输出:mydb(表示当前在 "mydb" 数据库中)
image-20251025151214142

可以看到默认使用的是 test 库

查看所有数据库

show dbsshow databases,用于列出 MongoDB 实例中所有非空数据库(包含至少一个集合和数据),并显示每个数据库的磁盘占用大小。

1
2
3
4
5
6
show dbs
// 输出示例:
// admin 40.00 KiB
// config 72.00 KiB
// local 40.00 KiB
// mydb 40.00 KiB
image-20251025151235182

删除当前数据库

删除当前所在的数据库,包括其中所有集合和数据,操作不可逆,需谨慎使用。

步骤

  1. 先通过 db 确认当前数据库,避免误删;
  2. 执行 db.dropDatabase() 命令。
1
2
3
4
5
6
// 确认当前数据库
db // 输出:mydb

// 删除当前数据库
db.dropDatabase()
// 输出成功信息:{ ok: 1, dropped: 'mydb' }
  • 若删除的是 adminlocal 等系统数据库,可能导致 MongoDB 功能异常,禁止随意删除。
  • 删除后,磁盘上对应的数据库文件夹会被移除(或标记为删除,后续由 MongoDB 清理)。

查看数据库状态(db.stats()

用于查看当前数据库的统计信息,包括数据量、集合数量、磁盘占用等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use mydb
db.stats()
// 输出示例(关键字段):
{
db: 'mydb', // 数据库名
collections: 2, // 集合数量
views: 0, // 视图数量
documents: 10, // 文档总数量
avgObjSize: 56, // 平均文档大小(字节)
dataSize: 560, // 数据总大小(字节)
storageSize: 40960, // 磁盘存储大小(字节,包含预分配空间)
indexes: 1, // 索引数量
indexSize: 40960, // 索引占用磁盘大小
ok: 1
}
image-20251025151257039

数据库权限与用户管理

数据库级别的权限控制通过创建用户实现,用户可被赋予对特定数据库的操作权限(如读、写、管理等)。

常用操作

  • 创建数据库用户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 切换到目标数据库(用户权限仅作用于当前库)
    use mydb

    // 创建用户,赋予读写权限
    db.createUser({
    user: "myuser", // 用户名
    pwd: "mypassword", // 密码
    roles: [{ role: "readWrite", db: "mydb" }] // 角色(readWrite 表示读写权限)
    })
  • 查看数据库用户

    1
    2
    use mydb
    show users // 列出当前数据库的所有用户
  • 删除数据库用户

    1
    2
    use mydb
    db.dropUser("myuser") // 删除指定用户

说明admin 数据库是权限管理的根数据库,在 admin 中创建的用户可拥有跨库的超级权限(如 root 角色)。

数据库连接配置

在连接 MongoDB 时,可通过连接字符串指定目标数据库,无需手动执行 use 命令。

1
2
# 连接到本地 MongoDB 实例的 "mydb" 数据库
mongosh "mongodb://localhost:27017/mydb"

数据库备份和恢复

备份与恢复可通过 mongodumpmongorestore 工具对单个数据库进行备份和恢复

1
2
3
4
5
# 备份 "mydb" 数据库到指定目录
mongodump --db mydb --out /backup

# 恢复 "mydb" 数据库
mongorestore --db mydb /backup/mydb

集合操作

集合是文档的容器(类似关系型数据库的 “表”),无需预先定义结构,插入文档时可自动创建。

操作目的 命令 说明
查看当前库的所有集合 show collectionsshow tables 显示当前数据库下所有非空集合
手动创建集合 db.createCollection("<集合名>") 可选参数(如 {capped: true, size: 1024} 表示固定大小集合)
删除指定集合 db.<集合名>.drop() 谨慎操作,删除后数据不可恢复

集合也有命名规范

  • 集合名不能是空字符串
  • 集合名不能含有 \0 字符(空字符),这个字符表示集合名的结尾
  • 集合名不能以 system.开头,这是为系统集合保留的前缀
  • 不能以 system. 开头(系统集合专用前缀,如 system.indexes 存储索引信息);
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$

集合的显式创建

没啥好说

1
db.createCollection('集合名称');

虽然集合可自动创建,但手动创建时可指定特殊属性(如固定大小、自动过期等)。

常用选项参数

  • capped: <boolean>:是否为固定大小集合(默认 false)。固定集合有最大容量限制,满后会覆盖最早的文档(类似环形缓冲区)。
  • size: <number>:固定集合的最大存储空间(字节),需与 capped: true 配合使用。
  • max: <number>:固定集合可存储的最大文档数(受 size 限制,若空间先满则优先按 size 覆盖)。
  • autoIndexId: <boolean>:是否自动为 _id 字段创建索引(默认 true)。
  • validator: <document>:文档验证规则(限制插入 / 更新的文档必须满足的条件)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 创建普通集合(无特殊属性,等价于插入文档时自动创建)
db.createCollection("users")

// 创建固定大小集合(最大 10MB,最多 1000 个文档)
db.createCollection("logs", {
capped: true,
size: 10 * 1024 * 1024, // 10MB
max: 1000
})

// 创建带文档验证的集合(要求插入的文档必须包含 "age" 字段且为数字)
db.createCollection("students", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["age"],
properties: {
age: { bsonType: "int", minimum: 0 }
}
}
}
})
image-20251025152952350

集合的隐式创建

当向不存在的集合插入文档时,MongoDB 会自动创建该集合

和上面提到的数据库类似

查看集合(show collectionsshow tables

用于列出当前数据库中所有非空集合(包含数据的集合)。

示例:

1
2
3
4
5
6
use mydb // 切换到目标数据库
show collections // 或 show tables
// 输出示例:
// users
// logs
// students

查看集合状态与信息

  • 查看集合统计信息db.<集合名>.stats()

    包含文档数量、索引、存储空间等信息。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    db.users.stats()
    // 输出示例(关键信息):
    {
    ns: "mydb.users", // 集合全名(数据库.集合)
    count: 50, // 文档数量
    size: 8192, // 数据总大小(字节)
    storageSize: 32768, // 磁盘存储大小
    indexes: 1, // 索引数量
    indexDetails: { ... } // 索引详情
    }
    image-20251025153113607
  • 查看集合元数据db.getCollectionInfos()

    列出当前数据库所有集合的元数据(如名称、是否固定集合、验证规则等)。

    1
    db.getCollectionInfos({ name: "users" }) // 查看指定集合的元数据
    image-20251025153151424
  • 重命名集合(db.<旧集合名>.renameCollection()

    用于修改集合名称,可跨数据库移动集合(需权限)。

    1
    db.<旧集合名>.renameCollection(<新集合名>, [选项])

    示例

    1
    2
    3
    4
    5
    // 在当前数据库内重命名
    db.users.renameCollection("user_profiles")

    // 移动到其他数据库(需有目标库权限)
    db.logs.renameCollection("archive.logs_2023") // 将 logs 移动到 archive 库,命名为 logs_2023

    注意

    • 若新集合名已存在,会抛出错误(可通过 { dropTarget: true } 选项强制删除目标集合后重命名,但需谨慎)。
    • 固定集合(capped)重命名后仍保持固定属性。

删除集合(db.<集合名>.drop()

永久删除集合及其中所有文档和索引,操作不可逆,需谨慎。

1
2
3
4
5
// 确认集合存在
show collections // 确保要删除的集合在列表中

// 删除集合
db.old_data.drop() // 成功返回 true,失败返回 false
image-20251025153401058

drop() 删除集合本身,而 deleteMany({}) 仅删除集合内的文档(集合结构和索引保留)。

集合文档验证规则(validator

手动创建集合时可指定验证规则,限制插入 / 更新的文档必须满足条件(默认仅在插入 / 更新时验证,不影响已有文档)。

1
2
3
4
5
6
7
8
9
10
11
12
// 创建时指定验证规则(要求文档必须有 "score" 字段且≥0)
db.createCollection("exams", {
validator: { $expr: { $gte: ["$score", 0] } },
validationLevel: "strict", // 严格模式(所有操作都验证,默认)
validationAction: "error" // 不满足规则时抛错(默认,可选 "warn" 仅警告)
})

// 修改已有集合的验证规则
db.runCommand({
collMod: "exams", // 集合名
validator: { $expr: { $and: [{ $gte: ["$score", 0] }, { $lte: ["$score", 100] }] } }
})

其中有一类用到的比较多的特殊集合,TTL 集合(Time-To-Live)

可自动删除过期文档(通过在日期字段上创建 TTL 索引实现),无需手动清理。

1
2
// 创建 TTL 索引(文档插入后 3600 秒自动删除)
db.sessions.createIndex({ "createTime": 1 }, { expireAfterSeconds: 3600 })

文档操作

文档(Document)是 MongoDB 中最小的数据单元(类似关系型数据库的 “行”),格式为 BSON(二进制 JSON),支持丰富的数据类型(如字符串、数字、数组、日期等)。对文档的操作是 MongoDB 的核心,涵盖插入(Create)、查询(Read)、更新(Update)、删除(Delete) 四大类(即 CRUD 操作)。

文档是键值对的集合,格式类似 JSON

1
2
3
4
5
6
7
8
{
_id: ObjectId("650a9f9e9f1b2c3d4e5f6a7b"), // 自动生成的唯一标识
name: "Alice",
age: 25,
hobbies: ["reading", "hiking"], // 数组类型
address: { city: "Beijing", district: "Haidian" }, // 嵌套文档
createTime: ISODate("2023-09-21T08:00:00Z") // 日期类型
}

注意,文档

  1. _id 字段:每个文档默认生成 _id(ObjectId 类型),唯一标识文档,不可修改。

  2. 操作原子性:单文档操作(如 insertOneupdateOne)是原子的,多文档操作需通过事务保证(MongoDB 4.0+ 支持多文档事务)。

  3. 数据类型:BSON 支持丰富类型(如数组、日期、ObjectId),例如 {tags: ["a", "b"], createTime: new Date()}

插入文档

用于向集合中添加新文档,支持单文档插入和多文档插入。

  • 单文档插入(insertOne()

    1
    db.<集合名>.insertOne(<文档对象>)

    其中,完整的语句如下

    1
    2
    3
    4
    5
    6
    7
    db.collection.insertOne(
    <document or array of documents>,
    {
    writeConcern: <document>,
    ordered: <boolean>
    }
    );
    • collection:插入文档的集合名称
    • <document or array of documents>:要插入的一个文档或者文档数组
    • writeConcern:可选参数,用于指定写入的安全级别
    • ordered:可选参数,默认为 true。如果设置为 true,MongoDB会按照数组中的顺序插入文档,并在遇到错误时停止。如果设置为 false,MongoDB会尝试插入所有文档,即使某些文档插入失败

    例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 向 users 集合插入一个文档(自动生成 _id)
    const result = db.users.insertOne({
    name: "Bob",
    age: 30,
    email: "bob@example.com"
    });

    // 输出结果(包含插入的文档 ID)
    printjson(result);
    // {
    // acknowledged: true,
    // insertedId: ObjectId("650aa1f39f1b2c3d4e5f6a7c")
    // }
    image-20251025154848166
  • 多文档插入(insertMany()

    1
    db.<集合名>.insertMany([<文档1>, <文档2>, ...])
    • ordered: <boolean>:默认 true,表示按顺序插入,若某文档出错则终止后续插入;false 表示并行插入,忽略错误继续执行。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 批量插入 3 个文档
    const result = db.users.insertMany([
    { name: "Charlie", age: 28, email: "charlie@example.com" },
    { name: "Diana", age: 26, email: "diana@example.com" },
    { name: "Eve", age: 32, email: "eve@example.com" }
    ], { ordered: false }); // 允许并行插入,忽略错误

    // 输出结果(包含所有插入的文档 ID)
    printjson(result.insertedIds);
    image-20251025154947026

    因为批量插入由于数据较多容易出现失败,可以使用 try catch 进行异常捕捉处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    try {
    db.comment.insertMany([
    {
    _id: "1",
    article_id: "100001",
    content: "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。",
    user_id: "1002",
    nickname: "相忘于江湖",
    create_time: new Date("2019-08-05T22:08:15.522Z"),
    like_number: NumberInt(1000),
    state: "1"
    },
    {
    _id: "2",
    article_id: "100001",
    content: "我夏天空腹喝凉开水,冬天喝温开水",
    user_id: "1005",
    nickname: "伊人憔悴",
    create_time: new Date("2019-08-05T23:58:51.485Z"),
    like_number: NumberInt(888),
    state: "1"
    },
    {
    _id: "3",
    article_id: "100001",
    content: "我一直喝凉开水,冬天夏天都喝。",
    user_id: "1004",
    nickname: "杰克船长",
    create_time: new Date("2019-08-06T01:05:06.321Z"),
    like_number: NumberInt(666),
    state: "1"
    },
    {
    _id: "4",
    article_id: "100001",
    content: "专家说不能空腹吃饭,影响健康。",
    user_id: "1003",
    nickname: "凯撒",
    create_time: new Date("2019-08-06T08:18:35.288Z"),
    like_number: NumberInt(2000),
    state: "1"
    },
    {
    _id: "5",
    article_id: "100001",
    content: "研究表明,刚烧开的水千万不能喝,因为烫嘴。",
    user_id: "1003",
    nickname: "凯撒",
    create_time: new Date("2019-08-06T11:01:02.521Z"),
    like_number: NumberInt(3000),
    state: "1"
    }
    ]);
    } catch (e) {
    print(e);
    };

    • 若集合不存在,插入文档时会自动创建集合。

      image-20251025155049942
    • 若文档中包含 _id 字段,需确保其值在集合中唯一,否则会抛出重复键错误(E11000)。

    • 插入操作是原子的(单文档插入要么完全成功,要么失败)。

查询文档

用于从集合中检索文档,支持条件筛选、排序、分页、投影(只返回指定字段)等。

基础查询(find()findOne()

  • find(<条件>):返回所有满足条件的文档(默认返回所有字段)。

  • findOne(<条件>):返回满足条件的第一个文档(自动格式化输出,无需 .pretty())。

    1
    2
    3
    4
    5
    6
    7
    8
    // 查询 users 集合中所有文档(无筛选条件)
    db.users.find().pretty(); // .pretty() 用于美化输出格式

    // 查询 number=10 的文档
    db.users.find({ number: 10 }).pretty();

    // 查询第一个 age>30 的文档
    db.users.findOne({ age: { $gt: 30 } });
    image-20251025155123185

image-20251025155315750

条件查询文档

MongoDB 提供丰富的查询操作符,用于复杂条件筛选:

操作符 说明 示例
$eq 等于 { age: { $eq: 25 } }
$ne 不等于 { age: { $ne: 25 } }
$gt 大于 { age: { $gt: 25 } }
$gte 大于等于 { age: { $gte: 25 } }
$lt 小于 { age: { $lt: 25 } }
$lte 小于等于 { age: { $lte: 25 } }
$in 在指定数组内 { age: { $in: [25, 30, 35] } }
$nin 不在指定数组内 { age: { $nin: [25, 30] } }
$and 逻辑与 { $and: [{ age: { $gt: 25 } }, { name: "Alice" }] }
$or 逻辑或 { $or: [{ age: { $lt: 20 } }, { age: { $gt: 40 } }] }
$not 逻辑非 { age: { $not: { $gt: 25 } } }
$exists 字段是否存在 { email: { $exists: true } }
$type 字段类型匹配 { age: { $type: "int" } }

整体的查询规则是这样的:db.<集合名>.find(<查询条件>)

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 查询年龄在 25~30 之间(含)的文档
db.users.find({ age: { $gte: 25, $lte: 30 } });

// 查询爱好包含 "reading" 或年龄小于 25 的文档
db.users.find({
$or: [
{ hobbies: "reading" },
{ age: { $lt: 25 } }
]
});

// 查询存在 email 字段且年龄不是 25 的文档
db.users.find({
email: { $exists: true },
age: { $ne: 25 }
});
image-20251025155612834

如果条件查询之间需要连接,需要使用 $and 操作符将条件进行关联(相当于 SQL 的 and)

1
$and:[ { },{ },{ } ]

如果两个以上条件之间是或者的关系,我们使用 $or 操作符进行关联,与前面 $and 的使用方式相同

1
$or:[ { },{ },{ } ]

例如,查询评论集合中 like_number 大于等于 700 并且小于等于 2000 的文档

1
2
3
4
5
6
7
db.comment.find({
$and: [
{ like_number: { $gte: NumberInt(700) } },
{ like_number: { $lte: NumberInt(2000) } }
]
})

数组查询

针对数组类型字段的查询,常用操作符:

操作符 说明 示例
$all 数组包含所有指定元素 { hobbies: { $all: ["reading", "hiking"] } }
$elemMatch 数组元素满足所有条件(嵌套) { scores: { $elemMatch: { $gt: 80, $lt: 90 } } }
$size 数组长度等于指定值 { hobbies: { $size: 2 } }

示例

1
2
3
4
5
// 查询 hobbies 数组同时包含 "reading" 和 "hiking" 的文档
db.users.find({ hobbies: { $all: ["reading", "hiking"] } });

// 查询 scores 数组中至少有一个元素在 80~90 之间的文档
db.students.find({ scores: { $elemMatch: { $gt: 80, $lt: 90 } } });

嵌套文档查询

查询嵌套在文档中的字段,需用 . 符号访问:

1
2
3
4
5
6
// 文档结构:{ name: "Alice", address: { city: "Beijing", district: "Haidian" } }
// 查询 address.city 为 "Beijing" 的文档
db.users.find({ "address.city": "Beijing" });

// 查询 address.district 不等于 "Chaoyang" 的文档
db.users.find({ "address.district": { $ne: "Chaoyang" } });

投影(只返回指定字段)

通过第二个参数指定返回的字段(1 表示返回,0 表示不返回,_id 默认返回,需显式设为 0 才不返回)。

1
2
3
4
5
// 只返回 name 和 age 字段(不返回 _id)
db.users.find({ age: { $gt: 25 } }, { name: 1, age: 1, _id: 0 });

// 返回所有字段,除了 email
db.users.find({}, { email: 0 });

排序、分页与限制

  • 排序(sort()1 升序,-1 降序。
  • 限制返回条数(limit(N):最多返回 N 条。
  • 跳过前 N 条(skip(N):用于分页(第 page 页:skip((page-1)*size).limit(size))。

skip()、limilt()、sort() 三个放在一起执行的时候,执行的顺序是先 sort()、skip()、limit(),和命令编写顺序无关

1
2
3
4
5
// 按 age 降序排序,取前 3 条
db.users.find().sort({ age: -1 }).limit(3);

// 分页:跳过前 2 条,取接下来的 2 条(第 2 页,每页 2 条)
db.users.find().skip(2).limit(2);

模糊查询

也就是,在 find 中填入对应字段的正则表达式匹配

1
db.集合.find({字段:/正则表达式/})

正则表达式是 js 的语法,直接量的写法

例如:查询评论内容包含 开水 的所有文档

1
db.comment.find({content:/开水/})

更新文档

用于修改已存在的文档,支持单文档更新和多文档更新,需指定更新条件更新操作

而更新操作涉及到的操作符如下

操作符 说明 示例
$set 设置字段值(新增或修改) { $set: { age: 26, email: "new@example.com" } }
$unset 删除字段 { $unset: { email: "" } }
$inc 数值字段自增 / 减 { $inc: { age: 1 } }(age+1)
$push 向数组添加元素 { $push: { hobbies: "swimming" } }
$addToSet 向数组添加元素(去重) { $addToSet: { hobbies: "reading" } }
$pull 从数组移除满足条件的元素 { $pull: { hobbies: "hiking" } }
$rename 重命名字段 { $rename: { "oldField": "newField" } }
单文档更新(updateOne()

只更新第一个满足条件的文档。

1
2
3
4
db.<集合名>.updateOne(
<查询条件>, // 筛选要更新的文档
<更新操作> // 定义更新逻辑(使用更新操作符)
)

详细来说就是下面这样

1
2
3
4
5
6
7
8
9
10
11
db.collection.updateOne(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [<filterdocument1>, ...]
}
);

image-20251025165353412

例如

1
2
3
4
5
6
7
8
9
10
11
// 更新单文档:将 name=Alice 的文档 age 改为 26,添加 address 字段
db.users.updateOne(
{ name: "Alice" },
{ $set: { age: 26, address: { city: "Shanghai" } } }
);

// 数组更新:向 name=Bob 的文档 hobbies 数组添加 "gaming"(去重)
db.users.updateOne(
{ name: "Bob" },
{ $addToSet: { hobbies: "gaming" } }
);
多文档更新(updateMany()

更新所有满足条件的文档。

1
2
3
4
db.<集合名>.updateMany(
<查询条件>,
<更新操作>
)
1
2
3
4
5
// 更新多文档:将所有 age<25 的文档添加 status: "young"
db.users.updateMany(
{ age: { $lt: 25 } },
{ $set: { status: "young" } }
);

详细来说就是下面这样

1
2
3
4
5
6
7
8
9
10
11
db.collection.updateMany(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [<filterdocument1>, ...]
}
);

image-20251025170103187

替换文档(replaceOne()

用新文档完全替换第一个满足条件的文档(保留 _id 字段)。

1
db.<集合名>.replaceOne(<查询条件>, <新文档>)
1
2
3
4
5
// 替换文档:用新文档替换 name=Charlie 的文档
db.users.replaceOne(
{ name: "Charlie" },
{ name: "Charlie Brown", age: 29, job: "Engineer" } // 新文档(保留 _id)
);

覆盖修改

值得注意的是,在高版本的 MongoDB 中,覆盖修改似乎已经被禁掉了,执行后会直接报错

如果我们想修改 _id 为 1 的记录,将该记录的点赞量修改为 1001,输入以下语句:

1
2
3
4
5
db.comment.updateOne(
{ _id: "1" },
{ like_number: NumberInt(1001) }
);

执行后,我们会发现,这条文档除了 like_number 字段其它字段都不见了

局部修改

局部需要使用修改器 $set 来实现

例如,我们修改 _id 为 2 的记录,游戏名改成博德之门

1
2
3
4
5
6
7
8
db.game.updateOne(
{ _id: "2" },
{$set: {
name: "博德之门"
}
}
);

删除文档

用于移除集合中的文档,支持单文档删除和多文档删除。

单文档删除(deleteOne()

删除第一个满足条件的文档。

1
db.<集合名>.deleteOne(<查询条件>)

例如:

1
2
// 删除 name=Eve 的第一个文档
db.users.deleteOne({ name: "Eve" });
多文档删除(deleteMany()

删除所有满足条件的文档。

1
db.<集合名>.deleteMany(<查询条件>)

示例:

1
2
3
4
5
// 删除所有 age>40 的文档
db.users.deleteMany({ age: { $gt: 40 } });

// 删除集合中所有文档(谨慎!条件为空)
db.users.deleteMany({});

删除注意事项

  • 删除操作不可逆,执行前需确认条件正确性。
  • deleteMany({}) 会删除集合中所有文档,但保留集合结构和索引;若需删除集合本身,用 db.<集合名>.drop()
  • 删除操作是原子的(单文档删除要么完全成功,要么失败)。

常用辅助操作

MongoDB 的常用辅助操作是指除了核心的数据库、集合、文档、索引操作之外,用于辅助管理、监控、调试数据库的实用功能。

统计文档数量(countDocuments()estimatedDocumentCount()

评价为夯,能忍住不用的也是神人

countDocuments(<条件>):精确统计满足条件的文档数量(会扫描符合条件的文档)。

1
2
// 统计 users 集合中 age>25 的文档数
db.users.countDocuments({ age: { $gt: 25 } });

estimatedDocumentCount():快速估算集合的总文档数(基于元数据,不扫描文档,适用于粗略统计,可能存在问题)。

1
2
// 估算 users 集合的总文档数
db.users.estimatedDocumentCount();

countDocuments 支持条件筛选且精确,但性能略低;estimatedDocumentCount 无筛选,速度快但结果是估算值(适用于对精度要求不高的场景)。

查看数据库 / 集合状态(stats()

非分布式下用到的不多,分布式下有更好代替,基本不手搓,但是是基础,评价为一般

数据库状态db.stats() 查看当前数据库的统计信息(如集合数量、总数据量、磁盘占用等)。

1
2
use mydb;
db.stats(); // 输出:数据库名、集合数、文档总数、数据大小、存储大小等

集合状态db.<集合名>.stats() 查看指定集合的详细信息(如文档数、索引数、平均文档大小等)。

1
db.users.stats(); // 输出:集合名、文档数、索引信息、存储空间等

查看集合元数据(getCollectionInfos()

获取当前数据库中所有集合的元数据(如名称、是否为固定集合、验证规则等),支持按条件筛选。

1
2
3
4
5
// 查看所有集合的元数据
db.getCollectionInfos();

// 查看名称包含 "log" 的集合元数据
db.getCollectionInfos({ name: /log/ });

数据导入与导出

速度很快,而且内部,人上人

MongoDB 提供命令行工具(非 mongosh 内部命令)用于数据的导入导出,方便数据迁移和备份。

导出数据(mongoexport

将集合数据导出为 JSON 或 CSV 格式。

1
2
3
4
5
# 导出为 JSON(默认)
mongoexport --db <数据库名> --collection <集合名> --out <输出文件路径>

# 导出为 CSV(需指定字段)
mongoexport --db mydb --collection users --type csv --fields name,age --out users.csv

例如

1
2
# 导出 mydb 库中 users 集合的数据到 users.json
mongoexport --db mydb --collection users --out /backup/users.json
导入数据(mongoimport

将 JSON 或 CSV 格式的数据导入集合。

1
2
3
4
5
# 导入 JSON 文件
mongoimport --db <数据库名> --collection <集合名> --file <输入文件路径>

# 导入 CSV 文件(需指定字段)
mongoimport --db mydb --collection users --type csv --headerline --file users.csv

示例:

1
2
# 将 users.json 导入到 mydb 库的 users 集合(若集合不存在则自动创建)
mongoimport --db mydb --collection users --file /backup/users.json

数据库备份与恢复

人上人

通过 mongodumpmongorestore 工具进行数据库级别的备份和恢复,支持完整备份或指定库 / 集合。

备份数据库(mongodump

创建数据库的二进制备份(包含数据和索引)。

1
2
3
4
5
6
7
8
# 备份所有数据库(默认保存到 dump 目录)
mongodump

# 备份指定数据库
mongodump --db <数据库名> --out <备份目录>

# 备份指定集合
mongodump --db mydb --collection users --out /backup

例如

1
2
# 备份 mydb 数据库到 /backup/mydb_backup 目录
mongodump --db mydb --out /backup/mydb_backup
恢复数据库(mongorestore

mongodump 生成的备份文件恢复数据。

1
2
3
4
5
6
7
8
# 恢复所有数据库(从 dump 目录)
mongorestore

# 恢复指定数据库
mongorestore --db <目标数据库名> <备份文件路径>

# 恢复指定集合
mongorestore --db mydb --collection users /backup/mydb/users.bson

例如

1
2
# 将 /backup/mydb_backup/mydb 恢复到 mydb 数据库
mongorestore --db mydb /backup/mydb_backup/mydb

查询优化与分析

查看查询执行计划(explain()

夯都感觉少了

分析查询的执行过程,判断是否使用索引、扫描文档数量等,用于优化查询性能。

语法:

1
db.<集合名>.find(<查询条件>).explain(<模式>);

常用模式:

  • queryPlanner(默认):返回查询计划的详细信息(如候选计划、最优计划)。
  • executionStats:返回执行统计信息(如扫描文档数、耗时)。
  • allPlansExecution:返回所有候选计划的执行信息(用于对比)。

示例:

1
2
// 分析查询 { age: { $gt: 25 } } 的执行情况
db.users.find({ age: { $gt: 25 } }).explain("executionStats");

关键指标:

  • executionStats.executionSuccess:查询是否成功。
  • executionStats.totalDocsExamined:扫描的文档总数(越小越好)。
  • executionStats.totalKeysExamined:扫描的索引键总数(使用索引时不为 0)。
  • winningPlan.inputStage.stage:执行阶段(IXSCAN 表示使用索引,COLLSCAN 表示全表扫描,需优化)。
查看索引使用情况($indexStats

通过聚合管道的 $indexStats 阶段,查看集合中各索引的使用频率,识别冗余索引。

示例:

1
2
// 查看 users 集合所有索引的使用情况
db.users.aggregate([{ $indexStats: {} }]);

输出包含:

  • name:索引名称。
  • accesses:包含 ops(索引操作次数)和 since(统计开始时间)。
  • 长期 ops=0 的索引可考虑删除,减少写入开销。

查看当前连接信息(db.currentOp()

一般,可有可无

查看数据库当前执行的操作(如查询、更新、删除),用于诊断慢查询或阻塞问题。

1
2
3
4
5
// 查看所有当前操作
db.currentOp();

// 筛选慢查询(执行时间超过 1000 毫秒的操作)
db.currentOp({ "secs_running": { $gt: 1 } });

终止操作(db.killOp()

绝对神存在,这个真不会卡

终止长时间运行的操作(需通过 db.currentOp() 获取操作 ID)。

1
2
// 假设通过 currentOp() 得到操作 ID 为 12345
db.killOp(12345);

复制集合(copyTo()

工具底层,只能给个一般

将当前集合的数据复制到同一数据库的另一个集合(仅适用于小数据量,大数据量建议用 aggregate 或备份工具)。

1
2
// 将 users 集合复制到 users_backup 集合
db.users.copyTo("users_backup");

批量操作

批量操作允许在一个请求中执行多个插入、更新、删除操作,减少网络往返次数,提升处理大量数据的效率。MongoDB 提供两种批量操作模式:有序执行(Ordered)无序执行(Unordered)

批量写入

这是批量操作的核心方法:bulkWrite()

bulkWrite() 是集合级别的方法,接收一个操作数组作为参数,支持的操作类型包括:

  • insertOne:插入单文档
  • updateOne:更新单文档
  • updateMany:更新多文档
  • replaceOne:替换单文档
  • deleteOne:删除单文档
  • deleteMany:删除多文档

一次性执行多个插入、更新、删除操作,减少网络交互,提高效率。

1
2
3
4
5
db.<集合名>.bulkWrite([
{ <操作类型1>: { <操作内容> } },
{ <操作类型2>: { <操作内容> } },
// ... 更多操作
], { <选项> });
  • ordered: boolean:默认 true(有序执行)
    • ordered: true:按顺序执行操作,若某步失败则终止后续操作。
    • ordered: false:并行执行操作(无序),某步失败不影响其他操作(适合对顺序无要求的场景)。

假如对 users 集合执行以下的操作

  1. 插入两个新用户;
  2. 更新 name: "Alice" 的年龄;
  3. 删除 name: "Bob" 的文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const result = db.users.bulkWrite([
// 1. 插入操作
{ insertOne: { document: { name: "Charlie", age: 28 } } },
{ insertOne: { document: { name: "Diana", age: 32 } } },
// 2. 更新操作(更新第一个匹配的文档)
{ updateOne: {
filter: { name: "Alice" },
update: { $set: { age: 26 } }
}
},
// 3. 删除操作(删除第一个匹配的文档)
{ deleteOne: { filter: { name: "Bob" } } }
], { ordered: false }); // 无序执行

printjson(result); // 输出操作结果

事务

与mysql一样,事务用于保证多个操作的原子性:当一组操作中任何一步失败,所有操作都会回滚(恢复到初始状态);只有所有操作成功,才会提交并持久化。

MongoDB 4.0+ 支持多文档事务,确保一组操作要么全部成功,要么全部失败(适用于复杂业务场景,如转账)。

基本流程也和 mysql 一样

  1. 创建会话(startSession());
  2. 启动事务(session.startTransaction());
  3. 在会话中执行一系列操作(所有操作需指定 { session } 参数);
  4. 若所有操作成功,提交事务(session.commitTransaction());
  5. 若发生错误,回滚事务(session.abortTransaction());
  6. 结束会话(session.endSession())。

以 “转账” 场景为例:用户 A 向用户 B 转账 100 元,需保证扣减 A 的余额和增加 B 的余额两个操作同时成功或失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 1. 创建会话
const session = db.getMongo().startSession();

try {
// 2. 启动事务
session.startTransaction();

// 3. 执行事务内操作(所有操作需指定 session)
// 扣减 A 的余额
const updateA = db.users.updateOne(
{ name: "Alice" },
{ $inc: { balance: -100 } },
{ session } // 绑定会话
);

// 增加 B 的余额
const updateB = db.users.updateOne(
{ name: "Bob" },
{ $inc: { balance: 100 } },
{ session }
);

// 检查操作是否成功(可选,根据业务需求判断)
if (updateA.modifiedCount !== 1 || updateB.modifiedCount !== 1) {
throw new Error("转账失败:用户不存在或余额不足");
}

// 4. 提交事务
session.commitTransaction();
print("转账成功!");
} catch (error) {
// 5. 发生错误时回滚事务
session.abortTransaction();
print(`转账失败,已回滚:${error.message}`);
} finally {
// 6. 结束会话
session.endSession();
}

但是我一直感觉 MongoDB 不适合事务

注意,MongoDB 仅支持 WiredTiger 存储引擎(MongoDB 默认引擎)。事务中的操作可跨多个集合甚至多个数据库,但不能跨不同的 MongoDB 实例。

事务中的读操作默认使用 primary 读偏好(仅从主节点读取),确保读取到最新数据。这个和别的不太一样