$map 是 mongodb 聚合管道中用于对数组每个元素执行相同操作并返回新数组的表达式操作符,适用于查询时批量格式转换(如 id 映射、时间戳转字符串、添加序号等),需配合 $project 或 $addfields 使用,要求 input、as、in 三参数齐全,不支持过滤或累加,应与 $filter/$reduce 配合使用。

什么是 $map,它适合解决什么问题
$map 是 MongoDB 聚合管道中的一个表达式操作符,用于对数组字段的每个元素执行相同的操作并返回新数组。它不修改原数据,只在聚合阶段临时转换——所以别指望用它直接更新文档里的数组;它真正有用的地方是:查的时候就完成格式转换,比如把 userIds 数组批量转成带 name 和 id 的对象、把时间戳转成日期字符串、给每个元素加序号等。
注意:$map 必须配合 $project 或 $addFields 使用,不能单独出现在 pipeline 里。
基本语法和必须传的参数
$map 接收三个键: input(源数组)、as(当前元素的变量名,不带 $$ 前缀)、in(对每个元素做的表达式)。漏掉任意一个都会报错 "$map requires 'input' field" 或 "'as' is required"。
常见写法示例:
{
$map: {
input: "$tags",
as: "tag",
in: { name: "$$tag", length: { $strLenCP: "$$tag" } }
}
}
这里 $$tag 才是实际引用变量的方式(双美元符号),as: "tag" 只是声明名字,不是赋值语句。
-
input必须是数组,如果字段可能为null或缺失,先用$ifNull包一层,否则整个表达式会失败 -
as值只能是合法标识符(不能含点、空格、特殊字符),且不能与外层字段名冲突(比如外层有item字段,就别设as: "item") -
in里所有字段引用都必须加$$,写成$tag会被当成字段路径解析,结果通常是null
和 $reduce / $filter 混用时的典型陷阱
想在 $map 里做条件过滤或累加?别硬套——$map 本身不支持跳过元素或中断循环。需要“筛选后映射”,得先 $filter 再 $map;需要“边遍历边累计”,得换 $reduce。
比如想把数组中大于 10 的数翻倍,错误写法:
{
$map: {
input: "$numbers",
as: "n",
in: { $cond: [{ $gt: ["$$n", 10] }, { $multiply: ["$$n", 2] }, "$$n"] }
}
}
这其实没过滤,只是做了条件替换。真要过滤,得这样:
{
$map: {
input: {
$filter: {
input: "$numbers",
cond: { $gt: ["$$this", 10] }
}
},
as: "n",
in: { $multiply: ["$$n", 2] }
}
}
-
$filter的cond里用$$this引用当前项,而$map里必须用$$<as></as>,两者变量机制不同,不能混用 - 嵌套太深(比如
$map里再套$map)容易触发 BSON 大小限制(16MB),尤其输入数组大时,建议提前在应用层分页或降维
性能和兼容性要注意的点
$map 在 3.4+ 支持,但低版本驱动可能默认发旧协议,导致语法报错。确认 MongoDB 版本的同时,也检查 driver 是否启用了 maxWireVersion 兼容模式。
性能方面:$map 是内存操作,数组越大越吃内存。实测单次处理超过 10 万元素可能触发 "Sort exceeded memory limit" 类错误(即使没排序),尤其当 in 表达式里还调了 $lookup 或复杂字符串函数时。
- 生产环境慎用
$map处理 > 1k 元素的数组,优先考虑应用层处理或预计算字段 - 避免在
in中调用$function(4.4+)或$let嵌套过深,解释开销明显上升 - 如果只是简单取字段(如
in: "$$item.name"),MongoDB 通常能优化,但一旦涉及计算、类型转换、条件分支,就要做好性能预期
最常被忽略的是:数组为空时 $map 返回空数组,不是 null,但上层逻辑如果没处理空数组,可能引发前端解构失败或聚合阶段后续 $unwind 报错。











