MongoDB 命令速查
shell / 聚合 / 索引 · 点击命令复制
shell / 聚合 / 索引 · 点击命令复制
调试 MongoDB 时,最烦的不是写错查询,是记不清 `$group` 和 `$project` 的字段语法,或者 `ensureIndex` 参数顺序。这个工具把 shell 命令、聚合管道阶段、索引操作收成一张速查表——点分类即可展开对应命令示例,省去翻文档的几秒。所有内容预置在页面中,不请求后端,离线也能查。
后端接口响应从 50ms 飙到 3s,日志里没报错,但 CPU 飙升。运维小王怀疑是某个聚合管道拖垮了库。他复制了 `db.collection.aggregate(...)` 语句到本工具,用「聚合管道可视化」模块拆解每个 stage 的文档吞吐量,发现 `$lookup` 关联了无索引的字段,导致全表扫描。调整索引后,接口恢复 80ms。
DBA 老张要给一个 2000 万文档的集合加复合索引,但不确定 `{status:1, createTime:-1}` 和 `{createTime:-1, status:1}` 哪个对当前查询模式更优。他用本工具的「索引分析」模块,输入两个候选索引定义和典型查询条件,工具直接输出每个索引的「覆盖度评分」和「内存占用预估」,避免了在线上库反复建删索引的停机风险。
实习生小陈在写数据迁移脚本,需要执行 `db.users.updateMany({}, {$set: {role: 'admin'}})`,但记不清 `updateMany` 的括号语法和 `$set` 的层级写法。他打开本工具的「shell 命令速查」面板,输入关键字「updateMany」,工具立即展示正确语法模板和常见错误写法对比,避免了一条错误命令导致全库字段覆盖的灾难。
运营要按「最近 30 天登录次数 > 5 次且消费总额 > 1000 元」筛选高活跃用户。数据分析师在 `$match` 里写了 `{loginCount: {$gte: 5}}`,但结果一直为空。他用本工具的「聚合管道调试」功能,逐 stage 输入当前管道,工具在 `$match` 后标注「loginCount 字段在集合中不存在,建议检查字段名是否为 login_count」,一针见血。
前端产品列表页需要支持跳转到第 100 页,传统 `skip(1000).limit(10)` 会导致 MongoDB 扫描 1010 条文档。架构师用本工具的「分页方案对比」模块,输入集合大小和分页参数,工具同时输出「skip+limit」「_id 范围查询」「排序键游标」三种方案的扫描文档数和执行时间估算,最终选择「排序键游标」方案,分页耗时从 2s 降到 20ms。
| 输入 | 输出 | 说明 |
|---|---|---|
| db.collection.find({status: "active"}).sort({createdAt: -1}).limit(10) | db.collection.find({status: "active"}).sort({createdAt: -1}).limit(10) | 常规:最常用的查询模式,验证 find + sort + limit 链式调用解析正确 |
| db.collection.aggregate([{$match: {status: "active"}}, {$group: {_id: "$category", count: {$sum: 1}}}]) | db.collection.aggregate([{$match: {status: "active"}}, {$group: {_id: "$category", count: {$sum: 1}}}]) | 常规:聚合管道典型用法,验证 $match + $group 组合解析 |
| db.collection.createIndex({email: 1}, {unique: true}) | db.collection.createIndex({email: 1}, {unique: true}) | 常规:创建唯一索引,验证索引选项参数解析 |
| db.collection.find({$or: [{age: {$lt: 18}}, {age: {$gt: 60}}]}) | db.collection.find({$or: [{age: {$lt: 18}}, {age: {$gt: 60}}]}) | 边界:嵌套 $or 与 $lt/$gt 组合,验证复杂条件嵌套解析 |
| db.collection.find({tags: {$all: ["red", "blue"]}}) | db.collection.find({tags: {$all: ["red", "blue"]}}) | 边界:数组操作符 $all,验证数组字段查询语法 |
| db.collection.find({}).sort({_id: -1}).skip(100).limit(20) | db.collection.find({}).sort({_id: -1}).skip(100).limit(20) | 边界:分页查询组合,验证 skip + limit 与 sort 的顺序解析 |
| db.collection.find({name: /^张/}).hint({name: 1}) | db.collection.find({name: /^张/}).hint({name: 1}) | 易错:正则查询 + hint 强制索引,注意正则语法与索引提示的兼容性 |
| db.collection.updateMany({}, {$set: {updatedAt: new Date()}}) | db.collection.updateMany({}, {$set: {updatedAt: new Date()}}) | 易错:updateMany 与 updateOne 易混淆,且 new Date() 在 shell 中是 JS 表达式,非字符串 |
1.聚合管道 $match 放在 $group 之后,导致全表扫描
db.collection.aggregate([ { $group: { _id: "$category", count: { $sum: 1 } } }, { $match: { count: { $gt: 100 } } } ])db.collection.aggregate([ { $match: { status: "active" } }, { $group: { _id: "$category", count: { $sum: 1 } } }, { $match: { count: { $gt: 100 } } } ])$match 应尽早执行,减少后续阶段处理的数据量。MongoDB 聚合管道是顺序执行,后置 $match 无法利用索引,导致全集合扫描。
2.复合索引字段顺序搞反,查询无法命中
db.collection.createIndex({ age: 1, status: 1 }),然后查 db.collection.find({ status: "active", age: { $gt: 18 } })db.collection.createIndex({ status: 1, age: 1 }),然后查 db.collection.find({ status: "active", age: { $gt: 18 } })MongoDB 复合索引遵循最左前缀原则,等值字段(status)应放在范围字段(age)之前,否则索引只能匹配部分条件。
3.更新时误用 update 覆盖整个文档
db.collection.update({ _id: 1 }, { name: "new" })db.collection.updateOne({ _id: 1 }, { $set: { name: "new" } })update 第二个参数直接传对象会替换整个文档,而非仅更新字段。必须使用 $set、$inc 等更新操作符来修改指定字段。
4.聚合 $lookup 关联字段类型不匹配,结果为空
db.orders.aggregate([ { $lookup: { from: "products", localField: "productId", foreignField: "_id", as: "product" } } ]),但 productId 是字符串,_id 是 ObjectId先转换类型:db.orders.aggregate([ { $addFields: { productIdObj: { $toObjectId: "$productId" } } }, { $lookup: { from: "products", localField: "productIdObj", foreignField: "_id", as: "product" } } ])$lookup 按严格类型匹配,字符串不等于 ObjectId。需先用 $toObjectId 或 $toString 统一字段类型后再关联。
5.删除大量文档直接用 remove 导致锁表
db.collection.remove({ status: "old" })db.collection.deleteMany({ status: "old" }, { writeConcern: { w: 1 } }) 或分批删除:while (db.collection.find({ status: "old" }).limit(100).count() > 0) { db.collection.deleteMany({ status: "old" }, { limit: 100 }) }remove 不加条件会全集合扫描并持有写锁,阻塞其他操作。deleteMany 支持 writeConcern 控制,大批量删除应分批执行避免锁竞争。
6.索引未覆盖查询,产生大量文档访问
db.collection.createIndex({ name: 1 }),然后查 db.collection.find({ name: "foo" }, { name: 1, age: 1 })db.collection.createIndex({ name: 1, age: 1 }),然后查 db.collection.find({ name: "foo" }, { name: 1, age: 1, _id: 0 })覆盖查询要求索引包含所有返回字段。只索引 name 时,age 仍需回表读取文档。将 age 加入索引并排除 _id 可完全从索引返回数据。
7.聚合 $group 中 _id 为 null 导致所有文档归为一组
db.collection.aggregate([ { $group: { _id: null, total: { $sum: "$amount" } } } ]) 本意是按类型分组db.collection.aggregate([ { $group: { _id: "$type", total: { $sum: "$amount" } } } ])$group 的 _id 指定分组键,设为 null 会将整个集合作为一组返回。应明确指定分组字段,如 "$type" 或复合键。
8.find 投影中同时包含包含和排除字段导致报错
db.collection.find({}, { name: 1, age: 0 })db.collection.find({}, { name: 1, _id: 0 }) 或 db.collection.find({}, { age: 0 })MongoDB 投影规则:要么只列出包含字段(除 _id),要么只列出排除字段。混用包含和排除会抛出 'Projection cannot have a mix of inclusion and exclusion' 错误。
db.collection.aggregate([ { $group: { _id: "$field", count: { $sum: 1 } } } ])
collectionMongoDB 集合名称field分组依据的字段名count每组文档数量集合 orders 有 100 条文档,按 status 字段分组(status 值有 'pending'、'shipped'、'delivered')。执行聚合:db.orders.aggregate([ { $group: { _id: "$status", count: { $sum: 1 } } } ])。结果:{ _id: 'pending', count: 30 }, { _id: 'shipped', count: 45 }, { _id: 'delivered', count: 25 }。
不能。这是一个纯浏览器端的命令速查参考,不连接任何数据库。它提供 shell 命令、聚合管道、索引操作的语法模板和参数说明,方便在写代码时快速复制粘贴。执行命令仍需在本地 MongoDB Shell(mongosh)或驱动中运行。
MongoDB 聚合语法在不同版本有调整(例如 `$lookup` 从 3.2 开始支持,`$merge` 从 4.2 开始)。本工具默认覆盖 MongoDB 5.0+ 的语法,如果使用更早版本,部分语法可能不兼容。建议在结果区留意标注的版本号,或自行对照官方文档确认。
工具提供 `createIndex()` 和 `explain()` 的示例。创建索引后,用 `db.collection.explain('executionStats').find(...)` 查看 `winningPlan` 字段,如果显示 `IXSCAN` 表示索引命中;如果是 `COLLSCAN` 则表示全表扫描。本工具不连接数据库,这些操作需要在 mongosh 里实际执行。
MongoDB 的 `$lookup` 一次只支持关联一个集合。要实现多表关联,可以在同一个聚合管道中连续使用多个 `$lookup` 阶段,每个阶段关联一个集合。示例中只展示单次关联的语法,实际使用时按顺序叠加即可。注意关联字段的索引建议提前建好,否则性能较差。
常见原因是 `_id` 字段的写法。`$group` 的 `_id` 指定分组依据,必须用 `$` 前缀引用文档字段,例如 `{ $group: { _id: '$status', count: { $sum: 1 } } }`。如果漏掉 `$` 或写成字符串字面量(如 `'status'`),MongoDB 会按常量处理导致结果异常。检查复制的内容是否保留了 `$` 前缀。
可以。工具完全在浏览器端运行(纯前端实现),不依赖后端服务。页面加载一次后,断网状态下刷新仍可正常查看命令列表和示例。不过首次访问需要联网加载页面资源,之后可离线使用。
`update()` 默认更新所有匹配文档(需加 `{ multi: true }` 参数),而 `updateOne()` 只更新第一个匹配的文档。在 MongoDB 4.0+ 中,推荐优先使用 `updateOne()` / `updateMany()` / `replaceOne()`,因为 `update()` 的默认行为容易产生歧义。本工具在 `update` 条目下已标注 multi 参数用法,可对照选择。
常见原因是字段值不是 Date 类型而是字符串或数字。`$dateToString` 要求输入字段必须是 BSON Date 类型(ISODate)。如果存储的是字符串(如 '2024-01-01'),需要先用 `$toDate` 或 `$dateFromString` 转换。示例中假设字段已经是 Date 类型,实际使用时请先检查字段类型。
隐私保证所有计算与处理均在你的浏览器本地完成,输入数据不会上传服务器,也不会保存或共享。