综合练习
课程评论/日志集合设计;可选 Spring 读写 API。
综合练习
这一课你将能做到
- 独立设计并落地
lesson_logs与comments两个集合:文档形状、索引策略、批量数据 - 用聚合产出课时热度 Top 5,用
explain证明常用查询无COLLSCAN,并用countDocuments对账 - 完整陈述「
cf_track留 MySQL、日志与评论进 Mongo」的选型理由,可选通过 Spring 或 Node 完成读写闭环
为什么要学
前面的课是一个个零件:文档建模、CRUD、查询投影、索引、聚合、Spring/Node 集成、Docker 环境。工程能力体现在把它们串成一条可演示路径:建模 → 写入 → 查询 → 索引验证 → 聚合统计 →(可选)API 读写。
这道综合题也是把本 track 变成简历素材的一步:面试聊到 MongoDB 时,你有亲手跑通的集合设计、索引决策、聚合报表和选型论述——全是能展开讲的实战细节。
人话:打勾式刷课不算会;这一课的验收标准是 mongosh(和可选代码)跑出正确且可验证的结果。
先建立图像
任务一 lesson_logs(做题日志) 任务二 comments(课程评论)
形状:谁在何时对哪课做了什么 形状:评论 + 作者快照 + mentions[]
索引:{ lessonSlug, at } 索引:{ trackSlug, createdAt }
{ userId, at }
聚合:热度 Top5 + 去重用户 查询:最新 10 条(投影瘦身)
可选任务三:Spring POST/GET 或 Node 导入脚本
红线:绝不改 MySQL TrackController 权威路径
| 集合 | 核心问题 | 成功标准 |
|---|---|---|
lesson_logs | 谁在何时对哪课做了什么 | ≥ 20 条数据;两个复合索引;聚合 Top5 与手工计数一致 |
comments | 某课程下的评论流 | 索引就位;投影省流量;能说出为何独立成集合 |
跟着做
步骤 1:重置实验库命名空间
use coderoadmap_lab
db.lesson_logs.deleteMany({}) // 仅实验库!
db.comments.deleteMany({})
db.lesson_logs.getIndexes() // 观察一下遗留索引
步骤 2:任务一——建模 + 批量造数 + 索引
文档形状(与 Spring / Node 课的字段契约完全一致):
{
lessonSlug: "mongo-crud",
trackSlug: "mongodb-essentials",
userId: "u_1001",
action: "view", // view | complete
at: ISODate("2026-03-01T10:00:00Z")
}
按查询场景先建索引(回忆 ESR:等值在前、排序在后):
db.lesson_logs.createIndex({ lessonSlug: 1, at: -1 }) // 课维度时间线
db.lesson_logs.createIndex({ userId: 1, at: -1 }) // 用户维度时间线
db.lesson_logs.createIndex(
{ at: 1 },
{ expireAfterSeconds: 7776000 } // TTL:90 天过期
)
用循环批量造数(至少 3 个用户、5 门课、两种 action 混合):
const users = ["u_1001", "u_1002", "u_1003"];
const lessons = ["mongo-intro", "mongo-crud", "mongo-queries",
"mongo-indexes", "mongo-aggregation"];
const actions = ["view", "complete"];
const batch = [];
for (let i = 0; i < 40; i++) {
batch.push({
lessonSlug: lessons[i % lessons.length],
trackSlug: "mongodb-essentials",
userId: users[i % users.length],
action: actions[i % 2],
at: ISODate("2026-03-0" + ((i % 7) + 1) + "T" +
String(8 + (i % 12)).padStart(2, "0") + ":00:00Z")
});
}
db.lesson_logs.insertMany(batch)
db.lesson_logs.countDocuments() // 40