MongoDB 基础教程

MongoDB 基础教程

实验环境 :华为云 ECS ecs-ff10-0002 · Ubuntu 24.04.4 LTS · MongoDB 8.0.26

实操时间 :2026-06-20

知识体系:数据库/集合基本操作 → 数据查询 → 文档操作 → 聚合与索引 → 高级查询与高级索引


一、MongoDB 概念

1.1 什么是 MongoDB

MongoDB 是一款面向文档的 NoSQL 数据库,数据以 BSON(Binary JSON)格式存储。与关系型数据库相比,它具有以下核心特点:

维度 关系型数据库(MySQL) MongoDB
数据模型 行与列(表格) 文档(JSON/BSON)
Schema 固定结构 灵活可变
扩展方式 垂直扩展为主 原生水平扩展(分片)
查询语言 SQL MQL(Mongo Query Language)
事务 完整 ACID 多文档事务(4.x+)
适用场景 强一致性业务 高并发/非结构化/快速迭代

1.2 核心术语对照

MySQL MongoDB 说明
database database 数据库
table collection 集合
row document 文档
column field 字段
index index 索引
JOIN $lookup 关联查询
primary key _id 主键(自动生成 ObjectId)

1.3 BSON 数据类型

MongoDB 支持丰富的数据类型:

复制代码
String        字符串
Integer       整数(32/64 位)
Double        浮点数
Boolean       布尔值
Date          日期时间
Array         数组
Object        嵌套文档
ObjectId      12 字节唯一标识符
Null          空值
Regex         正则表达式
BinData       二进制数据

二、安装 MongoDB

2.1 添加官方 apt 源(Ubuntu 24.04)

bash 复制代码
# 1. 导入 GPG 公钥
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc \
  | gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg --dearmor

# 2. 添加软件源
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] \
  https://repo.mongodb.org/apt/ubuntu noble/mongodb-org/8.0 multiverse" \
  | tee /etc/apt/sources.list.d/mongodb-org-8.0.list

# 3. 更新包列表并安装
apt-get update
apt-get install -y mongodb-org

2.2 启动服务并验证

bash 复制代码
systemctl enable mongod
systemctl start mongod
systemctl status mongod

实操输出:

复制代码
● mongod.service - MongoDB Database Server
     Loaded: loaded (/usr/lib/systemd/system/mongod.service; enabled; preset: enabled)
     Active: active (running) since Sat 2026-06-20 12:03:38 CST; 2s ago
   Main PID: 9677 (mongod)
bash 复制代码
mongod --version
复制代码
db version v8.0.26
Build Info: {
    "version": "8.0.26",
    "openSSLVersion": "OpenSSL 3.0.13 30 Jan 2024",
    "distmod": "ubuntu2404",
    "distarch": "x86_64"
}

2.3 连接 MongoDB Shell

bash 复制代码
mongosh                     # 连接默认本地实例
mongosh school              # 直接进入 school 数据库
mongosh "mongodb://host:27017/db"  # 连接远程

三、数据库与集合基本操作

3.1 数据库操作

javascript 复制代码
// 查看所有数据库
show dbs

// 切换/创建数据库(不存在则自动创建,插入数据后才真正落盘)
use school

// 查看当前数据库
db

// 查看数据库列表(带大小)
db.adminCommand({listDatabases:1}).databases.forEach(d =>
  print(d.name + "  " + d.sizeOnDisk + " bytes")
)

实操输出:

复制代码
admin  40KB
config  12KB
local  40KB
school  196KB
javascript 复制代码
// 删除数据库(在目标库内执行)
db.dropDatabase()

3.2 集合操作

javascript 复制代码
// 显式创建集合
db.createCollection('students')
db.createCollection('courses')

// 查看集合列表
show collections
db.getCollectionNames()

// 删除集合
db.students.drop()

// 重命名集合
db.courses.renameCollection('course_info')

// 集合统计信息
db.runCommand({collStats: "students"})

实操输出:

复制代码
文档数: 8  大小: 1783 bytes  索引数: 5

提示 :插入第一条文档时集合会自动创建,createCollection() 用于需要指定选项(如 capped 固定集合)的场景。


四、文档基本操作(CRUD)

4.1 插入文档

insertOne ------ 插入单条
javascript 复制代码
var result = db.students.insertOne({
  name: '张三',
  gender: 'M',
  age: 20,
  score: 88.5,
  hobbies: ['篮球', '编程'],          // Array 类型
  address: { city: '北京', district: '海淀' },  // 嵌套文档
  enrolled: true,                     // Boolean
  createAt: new Date('2026-06-20')    // Date
})

print(result.insertedId)  // ObjectId("6a361159ccbd98a6a59df8a3")
insertMany ------ 批量插入
javascript 复制代码
db.students.insertMany([
  { name: '李四', gender: 'F', age: 19, score: 92.0,
    hobbies: ['阅读','音乐'], address: { city: '上海', district: '浦东' },
    enrolled: true, createAt: new Date('2026-06-20') },
  { name: '王五', gender: 'M', age: 21, score: 75.3,
    hobbies: ['游泳'], address: { city: '广州', district: '天河' },
    enrolled: true, createAt: new Date('2026-06-20') },
  { name: '赵六', gender: 'F', age: 20, score: 96.8,
    hobbies: ['数学','编程'], address: { city: '深圳', district: '南山' },
    enrolled: true, createAt: new Date('2026-06-20') },
  // ... 更多文档
])

实操结果:total docs: 8

4.2 更新文档

updateOne / updateMany
javascript 复制代码
// $set ------ 更新指定字段
var r = db.students.updateOne(
  { name: '张三' },             // 查询条件
  { $set: { score: 91.0, remark: '班长' } }  // 更新操作
)
// updateOne matched:1 modified:1

// 批量更新:给所有 age<=19 的学生加 grade 字段
db.students.updateMany(
  { age: { $lte: 19 } },
  { $set: { grade: '大一' } }
)
// updateMany matched:2 modified:2
常用更新操作符
操作符 说明 示例
$set 设置字段值 {$set: {score: 90}}
$unset 删除字段 {$unset: {remark: ""}}
$inc 数值递增/递减 {$inc: {score: 5}}
$push 向数组追加元素 {$push: {hobbies: "钢琴"}}
$pull 从数组删除元素 {$pull: {hobbies: "游泳"}}
$addToSet 追加(去重) {$addToSet: {tags: "新"}}
$rename 重命名字段 {$rename: {old: "new"}}

实操演示:

javascript 复制代码
// $inc: 王五成绩 +5
db.students.updateOne({name: '王五'}, {$inc: {score: 5.0}})
// 王五 score after +5: 80.3

// $push: 赵六爱好添加钢琴
db.students.updateOne({name: '赵六'}, {$push: {hobbies: '钢琴'}})
// 赵六 hobbies: ["数学","编程","钢琴"]
replaceOne ------ 替换整个文档
javascript 复制代码
db.students.replaceOne(
  { name: '吴九' },
  { name: '吴九', gender: 'M', age: 20, score: 73.5,
    hobbies: ['足球'], address: { city: '南京', district: '玄武' },
    enrolled: true, remark: '成绩提升了' }
)
// 吴九 after replace: {"name":"吴九","score":73.5,"remark":"成绩提升了"}
findOneAndUpdate ------ 原子查询+更新
javascript 复制代码
var doc = db.students.findOneAndUpdate(
  { name: '张三' },
  { $set: { remark: '优秀班长' } },
  { returnDocument: 'after', projection: {_id:0, name:1, remark:1} }
)
// { name: '张三', remark: '优秀班长' }

4.3 删除文档

javascript 复制代码
// deleteOne ------ 删除第一条匹配文档
db.students.deleteOne({ name: '临时测试' })
// before delete count:9 → after deleteOne count:8

// deleteMany ------ 批量删除
db.students.deleteMany({ enrolled: false })

// 清空集合(保留集合定义和索引)
db.students.deleteMany({})

五、数据查询

5.1 基础查询

javascript 复制代码
// 查询所有文档
db.students.find()

// 条件查询
db.students.find({ gender: 'F' })

// 投影:只返回指定字段(1=显示,0=隐藏,_id 默认显示需显式排除)
db.students.find({}, { _id: 0, name: 1, score: 1 })

实操输出:

复制代码
张三 91
李四 92
王五 80.3
赵六 96.8
孙七 60
周八 85
吴九 73.5
郑十 99

5.2 比较操作符

操作符 含义 示例
$eq 等于 {score: {$eq: 92}}
$ne 不等于 {enrolled: {$ne: false}}
$gt 大于 {score: {$gt: 90}}
$gte 大于等于 {age: {$gte: 20}}
$lt 小于 {score: {$lt: 70}}
$lte 小于等于 {age: {$lte: 19}}
$in 在列表中 {age: {$in: [19, 21, 22]}}
$nin 不在列表中 {city: {$nin: ["北京"]}}
javascript 复制代码
// score >= 90
db.students.find({score: {$gte: 90}}, {_id:0, name:1, score:1})
// 李四 92 / 赵六 96.8 / 郑十 99

// age 在 19~20 之间
db.students.find({age: {$gte: 19, $lte: 20}}, {_id:0, name:1, age:1})
// 张三 20 / 李四 19 / 赵六 20 / 周八 19 / 吴九 20

// age IN [19, 21, 22]
db.students.find({age: {$in: [19, 21, 22]}}, {_id:0, name:1, age:1})

5.3 逻辑操作符

javascript 复制代码
// AND(隐式写法:多条件同一文档)
db.students.find({gender: 'M', score: {$gt: 80}})
// 张三 M 91

// $and(显式写法,可含复杂逻辑)
db.students.find({
  $and: [{age: {$gte: 20}}, {score: {$gte: 80}}, {enrolled: true}]
})
// 张三 / 王五 / 赵六 / 郑十

// $or:满足任一条件
db.students.find({
  $or: [{score: {$lt: 70}}, {score: {$gt: 95}}]
})
// 赵六 96.8 / 孙七 60 / 郑十 99

// $nor:均不满足(性别非 F 且分数 < 90)
db.students.find({
  $nor: [{gender: 'F'}, {score: {$gte: 90}}]
})
// 王五 / 孙七 / 吴九

// $not:取反
db.students.find({score: {$not: {$gte: 90}}})
// 王五 / 孙七 / 周八 / 吴九(score < 90)

5.4 排序、分页

javascript 复制代码
// 按 score 降序排序
db.students.find({}, {_id:0, name:1, score:1}).sort({score: -1})
// 郑十 99 → 赵六 96.8 → 李四 92 → 张三 91 → ...

// 分页:第 2 页(每页 3 条,跳过前 3 条)
db.students.find({}, {_id:0, name:1, score:1})
  .sort({score: -1})
  .skip(3)
  .limit(3)
// { name: '张三', score: 91 }
// { name: '周八', score: 85 }
// { name: '王五', score: 80.3 }

5.5 嵌套文档与数组查询

javascript 复制代码
// 嵌套字段查询(点表示法)
db.students.find({"address.city": "北京"}, {_id:0, name:1, "address.city":1})
// 张三(北京·海淀)/ 孙七(北京·朝阳)

// 数组包含某元素
db.students.find({hobbies: "编程"}, {_id:0, name:1, hobbies:1})
// 张三 / 赵六 / 郑十

// $in 查询数组包含任意一个
db.students.find({hobbies: {$in: ["编程", "数学"]}})

// $size:数组长度等于 1
db.students.find({hobbies: {$size: 1}}, {_id:0, name:1, hobbies:1})
// 王五 ["游泳"] / 孙七 ["游戏"] / 吴九 ["足球"]

5.6 其他查询

javascript 复制代码
// 正则表达式(name 以 张 开头)
db.students.find({name: /^张/})

// $exists:字段是否存在
db.students.find({remark: {$exists: true}}, {_id:0, name:1, remark:1})
// 张三(优秀班长)/ 吴九(成绩提升了)

// $type:字段类型
db.students.find({score: {$type: "double"}})

// $expr:字段间比较(score > age * 4)
db.students.find({$expr: {$gt: ["$score", {$multiply: ["$age", 4]}]}})
// 张三 / 李四 / 赵六 / 周八 / 郑十

// distinct:获取字段唯一值
db.students.distinct("address.city")
// ["上海", "北京", "南京", "广州", "成都", "杭州", "深圳"]

// 文档计数
db.students.countDocuments({score: {$gte: 80}})  // 6(精确,带过滤条件)
db.students.estimatedDocumentCount()              // 8(快速,全集合估算)

六、索引

6.1 索引类型

类型 创建语法 说明
单字段索引 createIndex({score: 1}) 升序(-1 降序)
复合索引 createIndex({gender:1, score:-1}) 多字段联合索引
唯一索引 createIndex({name:1}, {unique:true}) 值不可重复
文本索引 createIndex({name:"text", remark:"text"}) 全文搜索
TTL 索引 createIndex({expireAt:1}, {expireAfterSeconds:0}) 到期自动删除
2dsphere createIndex({location:"2dsphere"}) 地理空间查询

6.2 索引操作实操

javascript 复制代码
// 创建单字段索引
db.students.createIndex({score: 1})

// 创建复合索引
db.students.createIndex({gender: 1, score: -1})

// 创建唯一索引
db.students.createIndex({name: 1}, {unique: true})

// 查看所有索引
db.students.getIndexes().forEach(idx =>
  print(JSON.stringify({name: idx.name, key: idx.key, unique: idx.unique || false}))
)

实操输出:

json 复制代码
{"name":"_id_","key":{"_id":1},"unique":false}
{"name":"score_1","key":{"score":1},"unique":false}
{"name":"gender_1_score_-1","key":{"gender":1,"score":-1},"unique":false}
{"name":"name_1","key":{"name":1},"unique":true}
javascript 复制代码
// 删除指定索引
db.students.dropIndex({score: 1})

// 删除全部索引(除 _id)
db.students.dropIndexes()

6.3 EXPLAIN 验证索引

javascript 复制代码
var expl = db.students.find({score: {$gte: 90}}).explain("executionStats")
print("winningPlan stage: " + expl.queryPlanner.winningPlan.stage)
print("totalDocsExamined: " + expl.executionStats.totalDocsExamined)
print("nReturned: " + expl.executionStats.nReturned)

实操输出:

复制代码
winningPlan stage: FETCH
totalDocsExamined: 4
nReturned: 4

解读 :FETCH 阶段使用了 score_1 索引,仅扫描 4 条文档即返回 4 条结果,避免全集合扫描(COLLSCAN)。

6.4 文本索引

javascript 复制代码
// 创建文本索引(支持中文需配置分词器,默认英文分词)
db.students.createIndex({name: "text", remark: "text"})

// $text 全文搜索
db.students.find({$text: {$search: "班长"}}, {_id:0, name:1, remark:1})

6.5 TTL 自动过期索引

javascript 复制代码
db.sessions.createIndex(
  {createdAt: 1},
  {expireAfterSeconds: 3600}  // 1小时后自动删除
)

实操(courses 集合):

javascript 复制代码
db.courses.createIndex({expireAt: 1}, {expireAfterSeconds: 0})
// TTL 索引已创建(expireAfterSeconds=0,到期自动删除)
// expireAt_1 expire=0

6.6 地理空间索引(2dsphere)

javascript 复制代码
// 存储 GeoJSON 格式的坐标
db.students.updateOne({name: '张三'}, {
  $set: {location: {type: "Point", coordinates: [116.397, 39.917]}}
})

db.students.createIndex({location: "2dsphere"})

// $near 查询:北京天安门周边 5km 范围内的学生
db.students.find({
  location: {
    $near: {
      $geometry: {type: "Point", coordinates: [116.4, 39.9]},
      $maxDistance: 5000
    }
  }
})
// 北京周边5km范围: ["张三"]

七、聚合框架

7.1 聚合管道概念

复制代码
db.collection.aggregate([
  { $stage1: {...} },   // 第一个管道阶段
  { $stage2: {...} },   // 第二个管道阶段
  ...
])

文档在管道中依次流经各阶段,每个阶段对数据进行转换。

7.2 常用聚合阶段

阶段 说明
$match 过滤(等同 WHERE)
$group 分组统计
$sort 排序
$limit 限制条数
$skip 跳过 N 条
$project 投影/字段计算
$unwind 展开数组
$lookup 左连接(关联查询)
$count 计数
$addFields 添加/覆盖字段

7.3 聚合实操

$group ------ 分组统计
javascript 复制代码
db.students.aggregate([
  {$group: {
    _id: "$gender",
    count: {$sum: 1},
    avgScore: {$avg: "$score"},
    maxScore: {$max: "$score"}
  }}
])

输出:

json 复制代码
{ "_id": "F", "count": 4, "avgScore": 93.2, "maxScore": 99 }
{ "_id": "M", "count": 4, "avgScore": 76.2, "maxScore": 91 }
match + group ------ 条件聚合
javascript 复制代码
db.students.aggregate([
  {$match: {enrolled: true}},
  {$group: {_id: null, count: {$sum: 1}, avg: {$avg: "$score"}}}
])
// { _id: null, count: 7, avg: 88.23 }
sort + limit ------ Top N
javascript 复制代码
db.students.aggregate([
  {$sort: {score: -1}},
  {$limit: 3},
  {$project: {_id:0, name:1, score:1}}
])
// 郑十 99 → 赵六 96.8 → 李四 92
project + cond ------ 计算字段
javascript 复制代码
db.students.aggregate([
  {$project: {
    _id: 0,
    name: 1,
    score: 1,
    level: {
      $cond: [
        {$gte: ["$score", 90]}, "优秀",
        {$cond: [{$gte: ["$score", 75]}, "良好", "待提升"]}
      ]
    }
  }}
])

输出:

复制代码
张三 91 优秀 / 李四 92 优秀 / 王五 80.3 良好
赵六 96.8 优秀 / 孙七 60 待提升 / 周八 85 良好
吴九 73.5 待提升 / 郑十 99 优秀
$unwind ------ 展开数组统计爱好
javascript 复制代码
db.students.aggregate([
  {$unwind: "$hobbies"},
  {$group: {_id: "$hobbies", count: {$sum: 1}}},
  {$sort: {count: -1}},
  {$limit: 5}
])

输出:

复制代码
编程×3 / 数学×2 / 篮球×1 / 足球×1 / 音乐×1
$lookup ------ 集合关联
javascript 复制代码
// 查询学生及其选课信息
db.students.aggregate([
  {$lookup: {
    from: "course_info",    // 关联集合
    localField: "courseId", // 本集合字段
    foreignField: "_id",    // 目标集合字段
    as: "courseDetail"      // 输出字段名
  }}
])

八、高级查询

8.1 逻辑组合进阶

javascript 复制代码
// $and 显式写法(多条件)
db.students.find({
  $and: [{age: {$gte: 20}}, {score: {$gte: 80}}, {enrolled: true}]
})

// $nor:所有条件均不满足
db.students.find({
  $nor: [{gender: "F"}, {score: {$gte: 90}}]
})

// $not 对单个条件取反
db.students.find({score: {$not: {$gte: 90}}})

8.2 $expr ------ 字段间表达式比较

javascript 复制代码
// 找出 score > age * 4 的学生(用表达式比较字段)
db.students.find({
  $expr: {$gt: ["$score", {$multiply: ["$age", 4]}]}
})
// 张三(20×4=80<91) / 李四(19×4=76<92) / 赵六(20×4=80<96.8) / 周八(19×4=76<85) / 郑十(21×4=84<99)

8.3 数组高级查询

javascript 复制代码
// $elemMatch:数组元素满足复杂条件
db.students.find({
  scores: {$elemMatch: {$gte: 80, $lt: 90}}
})

// $all:数组同时包含所有元素
db.students.find({hobbies: {$all: ["编程", "数学"]}})

// $size:数组长度精确匹配
db.students.find({hobbies: {$size: 1}})
// 王五 ["游泳"] / 孙七 ["游戏"] / 吴九 ["足球"]

8.4 findOneAndXxx ------ 原子操作

javascript 复制代码
// findOneAndUpdate:查询并更新,返回文档
db.students.findOneAndUpdate(
  {name: "张三"},
  {$set: {remark: "优秀班长"}},
  {returnDocument: "after", projection: {_id:0, name:1, remark:1}}
)
// { name: '张三', remark: '优秀班长' }

// findOneAndDelete:查询并删除,返回被删文档
db.students.findOneAndDelete({name: "临时测试"})

// findOneAndReplace:查询并替换
db.students.findOneAndReplace(
  {name: "xxx"},
  {name: "xxx", score: 0},
  {upsert: true}  // 不存在则插入
)

8.5 upsert ------ 存在则更新,不存在则插入

javascript 复制代码
db.students.updateOne(
  {name: "新同学"},
  {$set: {gender: "M", age: 18, score: 72.0}},
  {upsert: true}
)
// 找不到 name="新同学" 时自动 insert

九、完整实验:成绩管理系统

整合以上知识,构建一个完整的成绩管理数据库:

javascript 复制代码
// 使用 school 数据库
use school

// 课程集合
db.createCollection("course_info")
db.course_info.insertMany([
  {name: "高等数学", credit: 4, teacher: "刘老师"},
  {name: "英语",     credit: 3, teacher: "陈老师"},
  {name: "数据库原理", credit: 3, teacher: "张老师"},
  {name: "操作系统",  credit: 3, teacher: "王老师"}
])

// 成绩集合(每条记录含学生名+课程名,模拟非范式化设计)
db.createCollection("scores")
db.scores.insertMany([
  {student: "张三", course: "高等数学", score: 82, examDate: new Date("2026-01-10")},
  {student: "张三", course: "英语",     score: 79, examDate: new Date("2026-01-11")},
  {student: "郑十", course: "高等数学", score: 99, examDate: new Date("2026-01-10")},
  // ...
])

// 统计每门课程平均分
db.scores.aggregate([
  {$group: {_id: "$course", avgScore: {$avg: "$score"}, count: {$sum: 1}}},
  {$sort: {avgScore: -1}}
])

// 统计每位学生的平均分排名
db.scores.aggregate([
  {$group: {_id: "$student", avg: {$avg: "$score"}, total: {$sum: "$score"}}},
  {$sort: {avg: -1}},
  {$project: {_id:0, name:"$_id", avg:{$round:["$avg",2]}, total:1}}
])

十、常用命令速查

Shell 命令

命令 说明
show dbs 列出所有数据库
use <db> 切换数据库
show collections 列出当前库集合
db.stats() 数据库统计
db.<coll>.stats() 集合统计
exit / Ctrl+D 退出 shell

常用查询链式调用

javascript 复制代码
db.collection
  .find(filter, projection)
  .sort({field: 1})   // 1 升序,-1 降序
  .skip(n)
  .limit(n)
  .toArray()

操作符速查

复制代码
比较:$eq $ne $gt $gte $lt $lte $in $nin
逻辑:$and $or $nor $not
元素:$exists $type
数组:$size $all $elemMatch $push $pull $addToSet
更新:$set $unset $inc $rename $push $pull
聚合:$match $group $sort $limit $skip $project $unwind $lookup

总结

章节 核心知识点
MongoDB 概念 文档数据库、BSON 类型、与 SQL 对比
安装 apt 源安装、systemctl 管理、mongosh 连接
数据库/集合 createCollection、drop、rename、stats
文档 CRUD insertOne/Many、find、updateOne/Many、deleteOne/Many
查询语法 比较符、逻辑符、嵌套查询、数组查询、正则、$expr
索引 单字段、复合、唯一、文本、TTL、2dsphere、explain
聚合管道 match/match/match/group/sort/sort/sort/project/unwind/unwind/unwind/lookup
高级查询 nor/nor/nor/not/size/size/size/elemMatch、findOneAndUpdate、upsert

实操集群:ecs-ff10-0002 · Ubuntu 24.04.4 · MongoDB 8.0.26 · 2026-06-20