
本文介绍如何正确使用 mongoose 获取集合中文档总数,避免因误用 `findone()` 导致 `nan` 错误,并安全实现基于计数的自增 id 逻辑。
在构建类似“猜 Emoji”这类需要唯一序号标识(如 ID: 1, 2, 3...)的应用时,一个常见误区是试图通过 model.findOne() 获取文档后直接调用 .length 属性来统计数量——但 findOne() 返回的是单个文档对象(或 null),而非数组,因此 data.length 恒为 undefined,参与数学运算后即得 NaN。
✅ 正确做法是使用 Mongoose 提供的聚合计数方法:
// ✅ 推荐:高效、原子性好、支持条件过滤
const count = await model.countDocuments();
// 创建新文档,ID 为现有总数 + 1
await model.create({ ID: count + 1, /* 其他字段 */ });⚠️ 注意事项:
- countDocuments() 是 Mongoose 5.3+ 推荐的替代方案(取代已弃用的 count()),它基于 $collStats 或 count 命令,准确且兼容分片集群;
- 若需带条件统计(例如仅统计未删除的记录),可传入查询对象:countDocuments({ deleted: false });
- 避免竞态条件(Race Condition):在高并发场景下,countDocuments() + create() 两步操作非原子性,可能导致重复 ID。如需强一致性,建议改用 MongoDB 原生 ObjectId、$inc 自增字段(配合单独计数器集合),或使用事务封装。
? 补充:若你确实需要「严格递增且不跳号」的整数 ID,推荐建立专用计数器集合(如 counters),配合 findOneAndUpdate 实现原子自增:
const counter = await db.collection('counters').findOneAndUpdate(
{ _id: 'emojiId' },
{ $inc: { seq: 1 } },
{ upsert: true, returnDocument: 'after' }
);
await model.create({ ID: counter.seq, emoji: '?', ... });总之,用对 API 是基础:findOne() 查数据,countDocuments() 数数量——二者语义与返回值截然不同,切勿混用。










