
本文介绍如何使用 mongodb 聚合管道(特别是 `$map` 与 `$size`)将存储用户答题结果的二维数组(如 `results: [["u1","u2"], [], ["u3","u4","u5"], ["u6"]]`)高效转换为对应子数组长度组成的一维数组(如 `[2, 0, 3, 1]`),完全适配动态题型(2–5 个选项)。
在构建在线测验系统时,常将用户选择以二维数组形式结构化存储:外层数组每个位置对应一个选项,内层数组存放选中该选项的用户 ID。例如:
{ "results": [["10938381", "10938382"], [], ["10938383", "10938384", "10938385"], ["10938386"]] }目标是将其聚合为各选项被选次数的统计数组 [2, 0, 3, 1] —— 这一需求不依赖固定长度,必须兼容 2 至 5 个选项的任意配置。
MongoDB 原生支持函数式数组变换,推荐使用 $map 配合 $size 实现零侵入、高性能的转换:
Model.aggregate([
{
$project: {
results: {
$map: {
input: "$results",
in: { $size: "$$this" }
}
}
}
}
]);- $map 遍历 "$results" 的每一项(即每个用户 ID 子数组);
- $$this 指代当前遍历的子数组;
- $size 直接返回该子数组的元素个数(空数组返回 0);
- 最终输出字段 results 被重写为纯数字数组,长度与原始外层数组一致。
✅ 优势说明:
- ✅ 完全动态:无需预设子数组数量,自动适配任意长度的 results;
- ✅ 高效可靠:单阶段完成,无 $unwind 导致的文档膨胀,性能稳定;
- ✅ 兼容 Mongoose:可直接在 .aggregate() 链中调用,返回结果与原 schema 字段名一致;
- ✅ 可扩展:后续可轻松叠加 $addFields 或 $group 进行全局统计(如总答题人数、最高选中率等)。
⚠️ 注意事项:
- 确保 results 字段始终为数组类型(可在 Schema 中定义 results: [[String]]);
- 若存在 null 或缺失 results 字段,建议前置 $ifNull 处理,例如:input: { $ifNull: ["$results", []] };
- 在生产环境聚合前,建议使用 .explain("executionStats") 验证性能,尤其当集合量级较大时。
该方案简洁、健壮且符合 MongoDB 的声明式设计哲学,是处理此类“数组维度降维统计”问题的标准实践。










