
本文介绍如何使用 mongodb 聚合管道(特别是 `$map` 和 `$size`)将存储答题结果的二维数组(如 `results: [[“u1″,”u2”], [], [“u3″,”u4″,”u5”], [“u6”]]`)高效转换为对应子数组长度的一维数组(如 `[2, 0, 3, 1]`),完全适配动态题干选项数量(2–5 个)。
在基于 MongoDB 构建的在线测验系统中,常采用二维数组结构存储用户选择行为:外层数组每个位置对应一个预设答案选项,内层数组则存放选中该选项的用户 ID 列表。例如:
{ "results": [["10938381", "10938382"], [], ["10938383", "10938384", "10938385"], ["10938386"]] }
目标是将其聚合为统计维度——即每个选项被选择的次数,生成形如 { “results”: [2, 0, 3, 1] } 的结果。关键在于:不依赖固定长度,支持任意长度的外层数组(2–5 项)。
✅ 推荐方案:$map + $size
使用 $map 遍历 results 数组的每一项(即每个子数组),对每项应用 $size 运算符获取其元素个数,最终输出长度一致的新数组:
YourModel.aggregate([ { $project: { results: { $map: { input: "$results", in: { $size: "$$this" } } } } } ]);
- $map 是 mongodb 中专用于数组映射的表达式操作符;
- input: “$results” 指定待处理的源数组;
- in: { $size: “$$this” } 表示对当前遍历项(用 $$this 引用)计算其大小;
- 该操作天然支持变长数组,无论 results 包含 2、3、4 或 5 个子数组,均能正确输出等长的整数数组。
⚠️ 注意事项
- 确保 results 字段始终为数组类型(空数组 [] 也合法),若存在 NULL 或缺失字段,$size 将返回 0(MongoDB 行为),建议在写入时做数据校验;
- 若需同时保留原始 results 并新增统计字段(如 voteCounts),可改用别名投影:
{ $project: { voteCounts: { $map: { input: "$results", in: { $size: "$$this" } } } } } - 在 Mongoose 中调用时,确保模型已正确定义 results 的 Schema 类型(推荐 [[String]] 或 Array(Array(String)))以获得 typescript 支持与运行时校验。
该方案简洁、高效、无副作用,无需 $unwind/$group 等重量级阶段,适用于高并发实时统计场景,是处理此类“数组长度映射”需求的标准实践。