必须先用$parseJson将JSON字符串转为BSON对象,再配合$cond兜底处理非法输入,之后才能在$group、$sum等阶段正常使用解析后的字段。

字符串字段里存了 JSON,不能直接用 $group 或 $sum?
不能。MongoDB 聚合管道不会自动解析字符串内容——哪怕它看起来像 {"price": 129.99, "qty": 5},在管道里它就是纯字符串,$price 字段路径无效,$sum: "$price" 会返回 null 或报错。
必须先把它转成 BSON 对象,才能继续后续操作。核心是 $jsonSchema 不行(那是校验用的),真正能用的是 $function(已废弃)或 $convert + $cond 组合,但最稳、最通用的方式是:用 $parseJson。
-
$parseJson是 MongoDB 4.4+ 引入的聚合操作符,专为这个场景设计 - 它接受一个字符串字段,返回解析后的 BSON 对象;如果字符串非法,可配合
$cond设默认值或跳过 - 注意:该操作符在部分旧版驱动或 mongosh 低版本中可能未启用,需确认服务端版本 ≥ 4.4
怎么安全调用 $parseJson 避免 pipeline 中断?
$parseJson 遇到非法 JSON 会直接报错终止整个聚合,所以不能裸用。必须包裹在 $cond 或 $ifNull 里兜底:
{
$project: {
parsedData: {
$cond: {
if: { $eq: [{ $type: "$jsonStr" }, "string"] },
then: {
$parseJson: {
json: "$jsonStr",
onError: null,
onNull: null
}
},
else: null
}
}
}
}
-
onError和onNull必须显式设为null(不能省略),否则非法输入仍会中断 - 先用
$type判断字段是否为 string,避免对null或数字调用$parseJson报错 - 解析后字段(如
parsedData.price)才能被$group、$sum等正常使用
解析后字段嵌套深、键名含点号或 $ 怎么办?
如果原始 JSON 字符串里有 {"user.name": "Alice", "$score": 95} 这类字段,$parseJson 能成功解析,但后续用 "$parsedData.user.name" 会出错——MongoDB 字段路径不支持点号在键名里。
这时不能靠管道修复,得在应用层预处理或改用 $getField(5.0+)动态取值:
{
$project: {
score: {
$getField: {
field: "$score",
input: "$parsedData"
}
}
}
}
-
$getField允许把字段名作为变量传入,绕过点号限制 - 若字段名本身来自数据(比如
"$parsedData.fieldName"存着真实键名),可用$let+$getField动态提取 - 含
$开头的键(如$score)MongoDB 默认拒绝插入,但$parseJson解析后允许读取——只是你无法用标准字段路径访问,必须走$getField
为什么导出结果时 JSON 格式选“默认扩展 JSON”而不是“宽松”?
聚合结果里如果有 ObjectId、ISODate、NumberLong 这类 BSON 类型,直接用 JSON.stringify() 会丢失精度或变成字符串(比如 ISODate("2026-10-20T13:00:00Z") → "2026-10-20T13:00:00.000Z")。
MongoDB Compass 导出选“默认扩展 JSON”,会保留 { "$oid": "..." }、{ "$date": "..." } 这类标记,下游系统(如 Node.js 的 mongodb 驱动)能原样还原为对应 BSON 类型。
- “宽松扩展 JSON”看着清爽,但丢类型——比如
NumberLong("5000000000")变成普通数字,超 JS 安全整数范围就出错 - 如果导出后要再导入回 MongoDB,必须用“默认”或“规范”格式,否则
$oid等字段会被当普通字符串存进去,再也变不回 ObjectId
真正卡住人的不是解析动作本身,而是错误处理和类型保真——$parseJson 后的字段路径、导出时的 JSON 格式选择、BSON 类型在字符串化过程中的隐式降级,这三处漏掉任何一环,数据就 quietly 损坏。


















