$size只能用于find中精确匹配数组长度,无法统计或比较大小;在聚合中需配合$isarray和$cond防护使用,否则遇缺失/null/非数组字段会报错17124。

$size 只能用于精确匹配数组长度,不能直接统计或比较大小。想查每篇文档的数组字段长度,必须走聚合管道 $project 配合 $size,而不是用 find 查询。
在聚合中用 $size 计算字段长度
直接对数组字段用 $size 会报错:如果字段缺失、为 null 或不是数组类型,$size 就会失败(错误码 17124,提示 "The argument to $size must be an array, but was of type: missing")。
正确做法是先用 $isArray 判断字段是否为数组,再用 $cond 控制分支:
db.persons.aggregate([
{
$project: {
name: 1,
booksLength: {
$cond: {
if: { $isArray: "$books" },
then: { $size: "$books" },
else: 0
}
}
}
}
])
-
$isArray: "$books"是必须加的防护,否则遇到没有books字段或值为null的文档就中断 - 别写成
$size: "$books.length"—— MongoDB 不支持点号取 length 属性 - 返回结果里
booksLength是数字,可后续用于$match、$sort或$group
为什么不能在 find 中用 $size 统计长度
$size 在 find 查询里只支持「等于」语义,比如 { books: { $size: 4 } },它不返回长度值,也不接受表达式参数。
- 写
{ books: { $size: { $gt: 3 } } }会直接报语法错误 ——$size后面只能跟数字字面量 - 想查「数组长度大于 3」,必须用聚合 +
$match,或退而求其次用$where(性能差、无法走索引) - Laravel jenssegers 等 ORM 封装通常只透传
find语法,所以动态生成$gt类条件时,$size本质不可用
替代方案:提前存长度字段并维护一致性
如果频繁按数组长度查询(比如「收藏数 > 10 的用户」),硬扛聚合开销不现实。更稳的做法是业务层写入/更新时,同步维护一个长度字段,例如 booksCount: 4。
- 这个字段可建索引,
find({ booksCount: { $gt: 3 } })能高效执行 - 避免在聚合里反复计算,尤其当集合大、数组深时,
$size在 pipeline 里是逐文档执行的 - 风险点在于:如果绕过应用逻辑直接操作数据库(如用
update改了books却没更新booksCount),数据就错位了
真正容易被忽略的是字段存在性检查 —— 大多数人第一次用 $size 都倒在 missing 或 null 上,而不是语法或性能。宁可多写两行 $cond,也别让整个聚合 pipeline 因单个脏数据崩掉。











