$exists不能直接与$group配合计算字段存在比例,因其仅为查询操作符、仅在$match阶段生效;必须用$cond将$exists布尔结果转为0/1数值后,再通过$sum聚合。
$exists 和 $group 配合能直接算出字段存在比例,但必须先用 $cond 把布尔结果转为数值(0/1),再用 $sum 累加——否则 $group 无法对布尔值做聚合。
MongoDB 的 $exists 是查询操作符,只在 $match 阶段生效;它不能作为表达式被 $group 引用。试图写 {_id: null, ratio: {$avg: "$field exists"}} 会报错或返回空值。
常见错误现象:
$exists 当作字段值参与计算,导致聚合阶段跳过该文档或输出 null
$cond 转换,直接对 $exists 结果求 $sum,实际得到的是 0 或未定义核心思路是:在 $project 阶段生成一个临时字段(如 has_field),用 $cond 检查 $field 是否存在($exists: true),存在则赋 1,否则 0。之后再进 $group 统计。
示例:统计 users 集合中 email 字段存在的比例
db.users.aggregate([ { $project: { has_email: { $cond: { if: { $exists: ["$email", true] }, then: 1, else: 0 } } } }, { $group: { _id: null, total: { $sum: 1 }, has_count: { $sum: "$has_email" } } }, { $project: { _id: 0, ratio: { $divide: ["$has_count", "$total"] } } }])
说明:
$exists: ["$email", true] 是合法表达式写法(注意数组形式),不能写成 $exists: "$email"
$sum: 1 是统计总文档数的惯用写法,比先 $count 再 $lookup 更轻量$divide 可能因 $total 为 0 报错,生产环境建议加 $cond 判断分母使用 Document 构建时,$exists 表达式容易写错格式,尤其在嵌套 $cond 里。
关键点:
$exists 必须作为 $cond 的 if 子句,且参数是数组:new Document("$exists", Arrays.asList("$email", true))
"$exists: '$email'" —— 这会被当作文本字面量,不解析$sum: 1 要写成 new Document("$sum", 1),不是 "1"
null 但存在(即 {"email": null}),$exists 仍返回 true;如需“非空且存在”,得额外加 $ne: null 判断这个模式看似简单,但在大集合上要注意两点:
$exists 判断,全表扫描不可避免;若高频统计某字段存在率,可考虑加稀疏索引:db.users.createIndex({email: 1}, {sparse: true})
"profile.phone")时,$exists 仍可用,但要确保路径完整,"$profile.phone" 才有效,"$profile" 不代表子字段存在ratio 是 double 类型,精度约 15 位,别拿它做严格相等判断(比如 == 1.0)真正麻烦的从来不是怎么写对,而是你忘了 $exists 在聚合管道里根本不是“值”,它只是个开关——得先把它撬开,变成数字,才能塞进 $group 的齿轮里转起来。