首页/数据/NoSQL 速查/聚合 $project 投影与计算
MongoDB99 条

聚合 $project 投影与计算

$project 决定输出字段:保留、排除、重命名与表达式计算都在这里完成,是控制下游数据形状的开关。

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

$project 做什么

$project 是聚合管道里控制输出形状的阶段:可以保留字段、排除字段、重命名字段,也可以用表达式计算新字段。它决定后续阶段能拿到哪些数据;把它放在管道前段还能提前裁掉不需要的字段,减少后续阶段的计算量。

基本信息

保留{ sku: 1 }
排除{ internal: 0 }
重命名{ name: "$title" }
计算{ total: { $multiply: ["$qty", "$price"] } }
混用限制保留式与排除式不能混用,_id 是唯一例外

典型使用场景

  • 裁剪输出字段,减小返回体积
  • 重命名字段,统一对外接口的字段名
  • 计算派生字段:总价、比例、条件标记
  • 把嵌套字段提到顶层,便于下游处理
  • 放在管道前段裁剪,减少后续阶段的数据量

常用写法

常用写法
// 计算总价并标记大额订单
 db.orders.aggregate([
   { $project: {
       _id: 0,
       sku: 1,
       total: { $multiply: ["$qty", "$price"] },
       isBig: { $gt: ["$qty", 10] }
   } }
 ])

// 重命名并提取嵌套字段
 db.users.aggregate([
   { $project: { _id: 0, nick: "$profile.nickname", city: "$addr.city" } }
 ])

// 输出常量必须用 $literal,否则会被当成字段路径
 db.users.aggregate([
   { $project: { source: { $literal: "import" }, name: 1 } }
 ])

// 条件标记
 db.orders.aggregate([
   { $project: { level: { $cond: [ { $gte: ["$amount", 1000] }, "high", "normal" ] } } }
 ])

写法与语义

写法语义注意
{ sku: 1 }保留该字段与其他排除式写法不能混用
{ internal: 0 }排除该字段_id 可以单独排除
{ name: "$title" }重命名或引用字段不带 $ 时会被当作常量字符串
{ total: { $multiply: [...] } }表达式计算除 _id 外字段不能重复输出
{ level: { $cond: [...] } }条件表达式两个分支的返回值类型要一致
$literal 用于输出常量值:直接写字符串会被当成字段路径解析,需要固定文本时一定要加上它。
保留式与排除式写法不能混用(_id 是唯一例外);字段较多时统一采用保留式写法,输出结构一眼就能看清。
$project 与 $addFields/$set 的取舍:后者保留原有字段、只做增补,$project 则是白名单式的精确控制;要同时保留原字段又加计算字段时,用 $addFields/$set 能少写一屏字段清单。
$project 里可以直接嵌表达式:算术 $add/$multiply、比较 $gt/$eq、条件 $cond、字符串 $concat/$substr、日期 $year/$dateToString,组合起来就能把内部结构整成对外接口需要的形状。
字段一旦被 $project 排除,后面的阶段就再也取不到它;管道中段确实需要临时字段时,先算出来用着,最后再收口裁剪。

易错点

  • 直接写字符串期望输出常量,结果被当成字段路径解析,需要常量值时必须用 $literal
  • 把保留式与排除式写法混在一起(除 _id 外),聚合会直接报错,团队内应统一一种风格
  • 在管道后段才做裁剪,前面的阶段仍然处理了全部字段,把 $project 提前能明显降低计算量
  • 输出字段与已有字段重名会造成覆盖或报错,重命名时要核对最终的字段列表

数据来源与参考

  • MongoDB Manual: $project
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌