
本文介绍如何使用 mongodb 聚合管道(特别是 `$map` 与 `$size`)将存储的二维数组(如用户答题结果)高效转换为对应子数组长度组成的一维数值数组,适用于动态长度的选项场景。
在构建在线测验(quiz)系统时,一种常见数据建模方式是将用户对各选项的选择结果以二维数组形式存储,例如:
{ "results": [["10938381", "10938382"], [], ["10938383", "10938384", "10938385"], ["10938386"]] }其中 results[i] 表示第 i 个答案选项被选中的用户 ID 列表;数组长度即该选项的投票数。业务上常需快速统计每个选项的响应人数——即把上述结构映射为 [2, 0, 3, 1]。
MongoDB 原生支持函数式转换操作符,推荐使用 $map 配合 $size 实现该需求。$map 可遍历输入数组的每个元素(此处为每个子数组),而 $size 可安全计算任意数组长度(对空数组返回 0,无需额外判断)。
✅ 正确聚合表达式如下(兼容 Mongoose .aggregate()):
YourModel.aggregate([
{
$project: {
results: {
$map: {
input: "$results",
in: { $size: "$$this" }
}
}
}
}
])? 关键说明:
- $$this 是 $map 的当前迭代变量,代表 results 中的每一个子数组;
- $size 对非数组字段会报错,但本例中 results 明确定义为二维数组,因此安全;
- 该方案完全动态:无论子数组数量是 2、4 还是 5 个,输出数组长度始终与输入一致,无需硬编码字段索引或使用 $switch 等冗余逻辑;
- 在 Mongoose 中可直接链式调用,例如:Quiz.findOne().aggregate([...]).exec()。
⚠️ 注意事项:
- 确保 results 字段在文档中始终为数组类型(建议在 Schema 中定义为 [[String]] 或 Array
>,并启用 typeKey: '$type' 验证); - 若存在 null 或缺失 results 字段,可添加 $ifNull 防御:input: { $ifNull: ["$results", []] };
- 如需同时返回原始选项列表(如 ["Strawberries", "Apples", ..."]),可在同一 $project 阶段一并保留或关联查表。
该方法简洁、高效、可读性强,是处理此类“数组长度映射”问题的标准实践。








