综合练习
Atlas 集群 + 聚合。
30 分钟
综合练习
这一课你将能做到
- 为「在线判题学习平台」设计一套 MongoDB 文档模型,包含用户、课时、提交记录、排行榜四个集合
- 用索引优化三个高频查询,并用聚合管道输出一张「课时通过率 + 用户排名」双报表
- 用 Change Streams 监听新提交,触发排行榜实时更新,形成闭环
为什么要学
前 11 课是零件,这一课是整车。学习平台的数据形状天然适合 MongoDB:提交记录是文档,排行榜要实时,用户进度要快速更新。把 11 课的知识点串成一个可运行的最小系统,才算真正学会。
先建立图像
数据模型(4 个集合):
users lessons submissions leaderboard
├─ _id ├─ _id ├─ _id ├─ _id (userId)
├─ nickname ├─ slug ├─ userId ├─ nickname
├─ avatar ├─ title ├─ lessonSlug ├─ totalScore
└─ createdAt ├─ difficulty ├─ status ├─ passCount
├─ tags[] ├─ score ├─ lastSubmitAt
└─ trackSlug ├─ attempts └─ updatedAt
├─ cases[](内嵌)
└─ submittedAt
数据流:
提交 → submissions (insert) → Change Stream → 累加 leaderboard (upsert + $inc)
↘ Kafka → 异步通知/风控
查询排行榜 → leaderboard 单集合(无 JOIN) → 按 totalScore 倒序取前 10
跟着做
步骤 1:创建集合与初始数据
use codeforge
// 课时字典
db.lessons.drop()
db.lessons.insertMany([
{ slug: "mdbrm-intro", title: "MongoDB 概览", difficulty: 1, trackSlug: "mongodb-roadmap-essentials" },
{ slug: "mdbrm-types", title: "BSON 与类型", difficulty: 2, trackSlug: "mongodb-roadmap-essentials" },
{ slug: "mdbrm-crud", title: "CRUD 操作", difficulty: 2, trackSlug: "mongodb-roadmap-essentials" },
{ slug: "mdbrm-query", title: "查询运算符", difficulty: 3, trackSlug: "mongodb-roadmap-essentials" },
{ slug: "mdbrm-agg", title: "聚合管道", difficulty: 3, trackSlug: "mongodb-roadmap-essentials" }
])
// 用户
db.users.drop()
db.users.insertMany([
{ _id: 1024, nickname: "小林", avatar: "a.png", createdAt: new Date("2026-09-01") },
{ _id: 2048, nickname: "阿明", avatar: "b.png", createdAt: new Date("2026-09-15") },
{ _id: 4096, nickname: "老王", avatar: "c.png", createdAt: new Date("2026-10-01") }
])
// 提交记录(带内嵌 cases)
db.submissions.drop()
db.submissions.insertMany([
{ userId: 1024, lessonSlug: "mdbrm-intro", status: "accepted", score: 100, attempts: 1,
cases: [{name:"c1",passed:true,costMs:12},{name:"c2",passed:true,costMs:8}],
submittedAt: new Date("2026-10-01T08:00:00Z") },
{ userId: 1024, lessonSlug: "mdbrm-types", status: "failed", score: 40, attempts: 2,
cases: [{name:"c1",passed:true,costMs:15},{name:"c2",passed:false,costMs:300}],
submittedAt: new Date("2026-10-02T09:00:00Z") },
{ userId: 1024, lessonSlug: "mdbrm-types", status: "accepted", score: 88, attempts: 1,
cases: [{name:"c1",passed:true,costMs:10},{name:"c2",passed:true,costMs:25}],
submittedAt: new Date("2026-10-03T10:00:00Z") },
{ userId: 2048, lessonSlug: "mdbrm-intro", status: "accepted", score: 95, attempts: 1,
cases: [{name:"c1",passed:true,costMs:11},{name:"c2",passed:true,costMs:9}],
submittedAt: new Date("2026-10-04T11:00:00Z") },
{ userId: 2048, lessonSlug: "mdbrm-crud", status: "accepted", score: 78, attempts: 2,
cases: [{name:"c1",passed:true,costMs:20},{name:"c2",passed:false,costMs:180}],
submittedAt: new Date("2026-10-05T14:00:00Z") },
{ userId: 4096, lessonSlug: "mdbrm-intro", status: "failed", score: 30, attempts: 3,
cases: [{name:"c1",passed:false,costMs:200},{name:"c2",passed:false,costMs:400}],
submittedAt: new Date("2026-10-06T16:00:00Z") }
])
// 排行榜(初始为空,由 Change Stream 或聚合初始化)
db.leaderboard.drop()