findone() 返回单个文档或null,非游标;不支持链式调用sort/limit,须通过options参数指定排序;projection控制字段但不能混用包含与排除;未匹配时返回null而非空对象。

findOne() 返回的是文档,不是游标
调用 findOne() 时,你拿到的是一个具体的文档对象(或 null),不是像 find() 那样返回可遍历的游标。这意味着不能链式调用 .limit(1) 或 .sort() —— 它们对 findOne() 无效。
常见错误是误以为 db.collection.findOne().sort({date: -1}) 能生效,实际会报错或静默忽略。排序必须通过 options 参数传入。
- 正确写法:
db.collection.findOne({status: "active"}, {sort: {createdAt: -1}}) - 错误写法:
db.collection.findOne({status: "active"}).sort({createdAt: -1}) - 不加
sort时,结果取决于当前查询计划(索引扫描 or collection scan),顺序不可靠
projection 参数控制字段,但 _id 默认总出现
findOne() 的第二个参数是 projection,用于指定返回哪些字段。规则和 find() 一致:不能混用 1 和 0(_id 是唯一例外)。
- 只返回
name和email:findOne({}, {name: 1, email: 1})→ 自动带_id - 排除
_id:findOne({}, {_id: 0, name: 1, email: 1}) - 排除
password字段:findOne({username: "alice"}, {password: 0}) - 混用
{name: 1, password: 0}会直接报错:Projection cannot have a mix of inclusion and exclusion.
没匹配到时返回 null,不是空对象或空数组
这是最容易踩坑的一点:很多开发者习惯性地对返回值做 .length、.map() 或解构,结果在 null 上报错。
- 安全访问示例(JavaScript):
const user = await collection.findOne({uid: 123}); if (user) { console.log(user.name); } - Python 中注意:
result = collection.find_one({...}),检查用if result is not None:,不是if result:(因为空文档{}也是 falsy) - 别依赖
findOne()做“是否存在”的布尔判断——它本意是取数据,不是判空;真要判存在,用count_documents({...}, limit=1)更轻量
想稳定取“最新一条”,必须显式加 sort + 索引
很多人以为 findOne({status: "done"}) 总是返回最后插入的那条,其实不然。MongoDB 不保证自然顺序在所有场景下都等价于插入时间,尤其在分片集群、有删除/更新、或使用覆盖索引时。
- 可靠做法:
findOne({status: "done"}, {sort: {updatedAt: -1}}),并确保updatedAt字段上有索引 - 如果字段名是
_id(ObjectId 类型),可用:findOne({status: "done"}, {sort: {_id: -1}}),因为 ObjectId 包含时间戳,且默认有索引 - 没建对应索引时,
sort可能触发 collection scan,性能陡降,日志里会出现EXECUTOR_ERROR: Sort exceeded memory limit
sort 必须进 options 对象,而不是拼在 query 里;还有就是把 null 当成假值直接解构,一跑就崩。











