MongoDB 聚合 $size 遇到缺失字段时报 must be an array
用 $isArray 和 $cond 区分数组与缺失、null、错误类型,保留异常标记;避免仅用 ifNull 或批量改文档掩盖数据问题。
聚合管道用 $size 统计数组长度,部分记录却让整条查询报错:
The argument to $size must be an array, but was of type: missing
同一字段在某些文档里是数组,在另一些文档里缺失、为 null 或保存了字符串,都会影响这条表达式。$size 只接受数组;空数组是合法输入,缺失字段并不是空数组。
下面针对 MongoDB 聚合表达式,示例用于支持 $isArray 的当前版本。它不是查询条件里用于匹配固定数组长度的 $size 写法。
先确认送进表达式的值
例如,products 中的 result.template 应当是模板名称数组。先保留实际查询的租户、权限和业务范围条件,再查看少量文档的该字段,区分以下形态:
{ result: { template: ["a", "b"] } }
{ result: { template: [] } }
{ result: { template: null } }
{ result: {} }
{ result: { template: "a,b" } }
前两种可以直接计算长度,后三种不能。还要检查前面的管道阶段是否已经投影掉字段,或把它改成其他类型。原始文档存在数组,不代表经过前面几步后仍然存在。
用数组判断保护长度计算
如果接口规定“非数组记为零,但同时保留类型异常标记”,可使用下面的只读聚合。集合名与路径均为示例:
db.products.aggregate([
{ $limit: 20 },
{
$project: {
templateIsArray: { $isArray: "$result.template" },
templateCount: {
$cond: {
if: { $isArray: "$result.template" },
then: { $size: "$result.template" },
else: 0
}
}
}
}
])
这里先由 $cond 选择分支,只有数组才进入 $size。空数组返回零且 templateIsArray 为 true;缺失、null、字符串等非数组返回零,但标记为 false,避免把坏数据悄悄等同于正常空数组。
$limit 只限制这次示例的检查数量,不代表样本能证明整个集合都符合模型。正式查询应保留业务范围,并按实际排序、分页要求安排阶段,不能照搬这个限制来计算全量排名。
业务需要区分“没有值”和“长度为零”时,可把 else: 0 改成 else: null,同步调整下游的排序和展示约定。不要为了得到一个数字而自动更新所有文档,把原来的字符串或对象改成空数组。
仅用 ifNull 不能处理所有错误类型
下面的表达式能把 null 或缺失字段替换成空数组:
{ $size: { $ifNull: ["$result.template", []] } }
但非空字符串、对象等值不是 null,仍会原样传入 $size 并报错。只有已经保证字段取值范围为数组、null 或缺失时,才适合采用这个简化版本。
也不要把“字段存在”当作“字段是数组”。一个存在的字符串同样会失败。若决定直接排除非数组文档,结果集合会变小,这与保留文档并返回零是不同的业务行为,需要明确选择。
按数据形态核对结果
在隔离样本里分别检查两元素数组、空数组、null、缺失和字符串。预期前两项的长度为 2、0,标记都为 true;后三项走约定的兜底分支且标记为 false。再核对前置投影、排序和分页没有改变需要保留的文档范围。
这项修复只改变查询表达式。保存原管道,输出约定不合适时恢复查询即可,不涉及文档数据回滚。若实际还需要清洗错误类型,应另行保留原值、制定转换规则并备份,不能把查询不再报错当作数据已经修复。


