projection 参数在 find() 或 findone() 中作为第二个参数生效,值为 1 表示包含字段、0 表示排除,但除 _id 外不可混用 1 和 0;支持点号路径投影嵌套字段,$ 操作符可返回匹配的第一个数组元素,聚合中则用 $project 阶段实现更复杂字段处理。

投影(projection)是 MongoDB 查询时控制返回字段的核心机制,不加投影默认返回全部字段(包括 _id),但多数场景下你只需要几个字段——用对投影能显著减少网络传输、内存占用和序列化开销。
projection 参数怎么写才生效?
在 find() 或 findOne() 中,第二个参数就是 projection。它是一个对象,键为字段名,值为 1(包含)或 0(排除)。注意:_id 是唯一允许显式设为 0 的字段;其他字段不能混用 1 和 0(除 _id 外)。
-
{ name: 1, email: 1 }→ 只返回name和email,自动包含_id -
{ name: 1, email: 1, _id: 0 }→ 返回name和email,不返回_id -
{ tags: 0, comments: 0 }→ 排除tags和comments,其余字段(含_id)都返回 -
{ name: 1, tags: 0 }→ ❌ 报错:Projection cannot have a mix of inclusion and exclusion
嵌套字段和数组怎么投影?
用点号(.)路径语法可精确控制嵌套字段。对数组,可用 $ 操作符返回匹配的第一个元素,或用 $elemMatch 配合投影做条件筛选。
-
{ "address.city": 1, "profile.age": 1, _id: 0 }→ 只取嵌套的city和age -
{ "scores.$": 1 }→ 若查询条件含{ "scores": { $gte: 90 } },只返回第一个 ≥90 的分数项 -
{ "comments": { $elemMatch: { "by": "user123" } } }→ 返回comments数组中第一个匹配by === "user123"的对象
注意:$ 投影只适用于查询条件中已用对应字段做过匹配;否则可能返回空数组或 null。
聚合管道里怎么用投影?
在 aggregate() 中,$project 阶段功能更强大,支持重命名、计算、条件表达式,且没有 1/0 混用限制。
-
{ $project: { fullName: { $concat: ["$firstName", " ", "$lastName"] }, _id: 0 } }→ 拼接字段并丢弃_id -
{ $project: { status: { $cond: { if: { $gt: ["$score", 80] }, then: "pass", else: "fail" } } } }→ 动态计算字段 - 想等价于
find({},{name:1,email:1})?写成{ $project: { name: 1, email: 1, _id: 0 } }即可
性能提示:如果只是字段裁剪,优先用 find() 的 projection;只有需要计算、重命名或结构转换时,才上 $project 阶段——它会触发文档重建,开销更高。
容易被忽略的边界情况
投影不是“过滤器”,它不改变查询匹配逻辑,只影响返回内容。几个关键盲区:
- 即使投影了
{ status: 1 },查询条件仍需完整写{ status: "active" },不能简写成{ status: { $eq: "active" } }然后靠投影“省事” - 索引覆盖(covered query)要求:查询条件 + 投影字段必须全部命中同一个索引,且不能含
_id: 0(因为索引条目自带_id,排除它就无法覆盖) - 使用
countDocuments()时传 projection 会被忽略——计数不关心返回哪些字段 - Node.js 驱动里,projection 对象的字段顺序不影响结果,但某些旧版驱动对
_id: 0放在前面可能有兼容问题,建议统一放最后
真正要压低响应体积,光靠投影不够;得配合合理索引、避免 N+1 查询、以及评估是否真需要从 MongoDB 直接返回大文本或二进制字段。











