结论:find()的projection参数需用纯对象,值仅限1/true/0/false,不可混用包含与排除;\_id是唯一可单独排除的字段;数组精准筛选须用聚合管道$filter。

直接说结论:用 find() 第二个参数传 projection 对象,字段值写 1 表示要,0 表示不要;但不能混用,_id 是唯一能单独排除的例外。
projection 参数怎么写才合法
projection 必须是纯对象,键是字段名,值只能是 1、true、0 或 false。混用包含和排除会报错:Projection cannot have a mix of inclusion and exclusion。
- 只写包含(推荐):
{name: 1, age: 1}→ 返回name、age和默认的_id - 只写排除:
{password: 0, token: 0}→ 返回除password、token外的所有字段(含_id) - 想排除
_id?必须显式写:{_id: 0, name: 1, age: 1},否则它永远在结果里 - 嵌套字段用点号:
{"favorites.food": 1},但注意这不会展开整个favorites对象,只取food字段
数组字段怎么精准返回特定元素
普通 projection 里的 points.0 或 points.$ 只能返回第一个匹配项,不是你想要的“所有符合条件的数组元素”。这时候必须上聚合管道,$filter 是核心。
- 错误示范(只拿第一个):
{points: {$elemMatch: {points: {$gte: 80}}}}→ 结果里points还是完整数组,只是加了查询条件 - 正确做法(聚合 +
$filter):[{$project: {name: 1, points: {$filter: {input: "$points", cond: {$gte: ["$$this.points", 80]}}}}}] - 注意
$filter必须放在$project阶段,不能塞进find()的 projection 参数里 - 如果只查一个文档,用
findOne()配合聚合选项(如 Mongoose 的.aggregate())比find()更直接
为什么有时候字段没被过滤掉
常见原因是字段路径写错,或类型不匹配导致条件失效,最终 projection 拿到的是原始字段值。
-
"finished.0"要求finished真的是数组;如果存成字符串"[17,3]",点号语法就失效 - 用
{$size: 2}查询数组长度时,projection 仍需单独指定字段,查询条件和返回字段是两件事 - 驱动层(如 Go 的 mongo-go-driver)可能把
omitemptytag 当成隐式0,导致字段被跳过——这不是 MongoDB 行为,是序列化层干扰 - mongosh 中游标自动迭代前 20 条,容易误判“没生效”,实际用代码调用
toArray()或遍历游标才能确认真实返回内容
真正影响传输量的是投影后 BSON 文档的总字节数,而不是字段个数。一个 points 数组哪怕只返回 points.0,只要原始文档里它占几 MB,没做 $filter 或 $slice,网络开销照样大。











