必须用 $isArray 防护 $size,否则缺失字段或 null 会触发错误 17124;聚合中需先判断数组存在性再计算长度,find 中 $size 仅支持精确匹配,无法用于范围查询。

直接用 $size 会崩,必须加 $isArray 防护,否则遇到缺失字段或 null 就报错 17124。
聚合里用 $size 计算数组长度必须带存在性检查
很多人写完 { $size: "$tags" } 就跑,结果 pipeline 在某条 tags: null 或根本没有 tags 字段的文档上直接中断。MongoDB 不容忍类型错误 —— $size 要求输入必须是数组,否则抛出 "The argument to $size must be an array, but was of type: missing"。
正确姿势是先用 $isArray 判断,再用 $cond 分支:
db.posts.aggregate([
{
$project: {
title: 1,
tagCount: {
$cond: {
if: { $isArray: "$tags" },
then: { $size: "$tags" },
else: 0
}
}
}
}
])
-
$isArray: "$tags"是硬性前置条件,不能省 - 别写成
$size: "$tags.length"—— MongoDB 不支持点号取 length -
else: 0可按需改成null或$size: [],但数值型更利于后续$sort或$match
$size 在 find() 里只能精确匹配,不能返回长度值
想查「数组长度大于 3」?别在 find({ tags: { $size: { $gt: 3 } } }) 里试 —— 这语法直接报错。因为 $size 在 find 中只接受数字字面量,仅支持 { tags: { $size: 4 } } 这种「等于」语义。
- 替代方案只有两个:走聚合 +
$match(适合偶发查询),或用$where(性能差、无法走索引) - Laravel 的
jenssegers/mongodb等 ORM 默认只透传find语法,所以动态生成$gt条件时,$size实质不可用 - 如果高频按长度筛选(比如「收藏数 > 10」),建议业务层写入时同步维护
tagsCount: 3字段,并建索引
统计全局数组元素总数要用 $sum + $size
不是查每条文档的长度,而是算整个集合里某个数组字段一共有多少个元素(比如所有用户的 hobbies 加起来共多少项),就得用 $sum 聚合累加:
db.users.aggregate([
{ $match: { status: "active" } },
{ $group: { _id: null, totalHobbies: { $sum: { $size: "$hobbies" } } } }
])
- 这里
$size仍需防护 —— 如果hobbies可能缺失,得在外层$project先处理,或在$sum前套$cond - 注意
$sum是对每个文档计算后的数值再累加,不是对原始数组直接 flatten 后计数 - 若数组嵌套深(如
comments.replies),$size只算第一层,不递归
真正容易被忽略的是字段存在性 —— 大多数人第一次用 $size 都卡在 missing 或 null 上,而不是语法或性能。宁可多写两行 $cond,也别让整个 pipeline 因单个脏数据崩掉。


















