应全局复用单个mongoclient实例,dao函数接收collectionname字符串并内部获取句柄;find返回游标需显式处理,findone须确保查询字段有索引;update/delete操作必须限定filter、慎用upsert和空条件,错误需判mongoservererror提取码。

怎么用原生 mongodb 驱动写可复用的 CRUD 函数
直接封装 collection.insertOne、collection.find 这些调用,别碰 ODM(比如 Mongoose),核心是把连接、db 实例、collection 名抽出来,让业务代码只关心“查什么”“改什么”。
常见错误:每次操作都重新 new MongoClient 或反复 client.db().collection() —— 连接池被绕过,压测时秒变超时。
- 全局复用一个
MongoClient实例,启动时连接,关机时close() - DAO 函数接收
collectionName字符串,内部通过db.collection(collectionName)获取句柄,不硬编码 collection - 所有方法返回 Promise,统一用
async/await,避免回调地狱和未捕获异常
find 和 findOne 的参数怎么传才不踩坑
findOne 和 find 看似一样,但行为差异直接影响性能和内存——尤其在分页或大结果集场景。
典型问题:用 find({}).toArray() 加载 10 万条记录到内存,Node 进程 OOM;或者 findOne 忘加 _id 索引,慢查询拖垮整个服务。
-
findOne只返回单个文档,适合查主键或唯一字段,必须确保查询字段有索引(比如{ userId: 1 }) -
find返回游标(Cursor),要显式调用.toArray()、.limit().skip()或.forEach();分页务必用.limit(n).skip(m),别先toArray()再切片 - 过滤条件一律用纯对象,别传字符串或函数;
$regex尽量配合^前缀,否则无法走索引
updateOne 和 updateMany 的写法差异影响数据一致性
用错更新方法会导致部分数据没改、或意外改多——尤其是带时间戳、计数器等需要原子操作的字段。
容易忽略:默认 upsert: false,查无此文档就静默失败;而 updateMany 如果条件太宽(比如漏写 status: 'active'),可能批量污染历史数据。
- 更新单条且确定存在时,用
updateOne+{ upsert: false }(显式声明,避免误创建) - 需原子自增/时间戳更新,必须用
$set、$inc、$currentDate操作符,别直接传整个新对象(会覆盖其他字段) - 涉及状态流转(如
orderStatus: 'paid' → 'shipped'),加filter限定原状态,防止重复发货
deleteOne 和 deleteMany 的安全边界怎么设
删库跑路不是玩笑,MongoDB 原生驱动不带软删、回收站或 SQL 式 WHERE 校验——删错就是物理删除。
高频事故:本地开发用 deleteMany({}) 清库,一不小心同步到测试环境;或忘记加 { _id: { $in: [...] } },删掉整张表。
- 永远不用裸
{}作为 filter;生产环境 DAO 层可强制校验 filter 对象的 key 数量 ≥ 1,或白名单允许字段(如只允许_id、tenantId) -
deleteOne优先用于带明确主键的操作;deleteMany必须搭配业务域隔离字段(如{ appId: 'xxx', deletedAt: null }) - 敏感操作(如用户注销级联删)建议包装成两步:先
updateMany打标记,再异步定时清理,留出回滚窗口
最麻烦的其实是错误处理粒度——MongoDB 报错信息藏在 error.errInfo 或 error.writeErrors 里,不是所有异常都带 message。写 DAO 时得专门判 error.name === 'MongoServerError' 并提取码,不然日志里全是 “E11000 duplicate key”。










