首页/数据/NoSQL 速查/投影规则与字段裁剪
MongoDB99 条

投影规则与字段裁剪

投影 {field:1} 保留、{field:0} 排除;两者不可混用(_id 是例外),合理投影可减少磁盘回表与网络传输。

NoSQL 速查数据更新于 2026-10-13

投影是干什么的

投影是查询的第二个参数,决定返回文档里保留哪些字段:{field: 1} 为包含,{field: 0} 为排除,除 _id 外两者不能混用。它不只是"少显示几列",还直接影响磁盘回表、网络传输与客户端反序列化开销,并且是覆盖查询(covered query)成立的前提条件。

基本信息

包含写法{ name: 1, email: 1 }
排除写法{ password: 0, apiToken: 0 }
_id 例外可在包含模式里单独给 _id: 0
嵌套与数组{ "addr.city": 1 }、{ items: { $slice: 3 } }
生效位置find/findOne 第二参数;聚合里对应 $project 阶段

示例

示例
db.users.find({}, { name: 1, email: 1, _id: 0 })          # 只取需要的字段
db.users.find({}, { password: 0, apiToken: 0 })           # 排除敏感字段
db.users.find({ vip: true }, { name: 1, "addr.city": 1 })  # 嵌套字段用点号路径
db.orders.find({}, { items: { $slice: 3 } })              # 数组只取前 3 个元素
db.users.find({ role: "ops" }).projection({ name: 1, _id: 0 })  # 链式写法等价
db.users.find({}, { name: 1 }).explain("executionStats")   # 确认是否走了覆盖查询

典型使用场景

  • 接口返回:只查前端真正用到的字段,省带宽也省序列化时间
  • 安全兜底:默认排除 password、token 一类字段,避免日志或响应误带敏感数据
  • 列表页:大文档用 $slice 截断数组,防止单条响应几百 KB 拖慢整页
  • 覆盖查询:投影字段全部落在索引里并排除 _id,可实现不回表读取
  • 导出归档:只导出必要字段,减小 dump 体积与恢复时间
  • 排查数据问题:先小投影确认字段是否有值、类型是否与预期一致,再决定是否整篇取回

投影写法对照

写法含义注意
{a:1}包含模式:只返回 a 与 _id包含模式里不能出现 0 字段
{a:0}排除模式:返回除 a 外全部字段宽文档依旧很大
{_id:0}单独排除 _id覆盖查询必须带上
{$slice:n}数组投影取前 n 个元素仅对数组字段有效
覆盖查询成立还需要索引本身包含所有被查询与投影的字段;只投影了索引字段却漏掉 _id:0,执行计划仍会带 FETCH 阶段回表读文档,可用 explain 的 totalDocsExamined 是否为 0 来验证。
排除模式无法只取部分字段,宽文档传回客户端仍要付完整网络代价;接口类查询优先用包含模式做白名单。
投影只决定返回什么,不等于加密:用排除法"隐藏"的字段仍以明文存在集合里,且 profiler 与慢日志会记录原始查询语句与条件值,敏感数据该靠字段级加密或独立存储解决。

易错点

  • {a:1, b:0} 这类混用除 _id:0 外会直接报错,别凭直觉拼字段
  • 排除模式看似安全,但文档越大传输越贵;列表接口应用包含模式白名单而不是排除黑名单
  • 投影不影响排序结果,但排序字段未投影时无法核对顺序,排查排序问题时把排序字段一起投影出来
  • 在聚合 $project 里混用 1 与 0 同样报错,且 $project 会重新定义文档结构,链式后续阶段按新结构取字段

数据来源与参考

  • MongoDB Manual: Project Fields to Return
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌