$unwind 干什么
$unwind 把数组字段拆开:数组里有几个元素,就输出几条文档,其余字段逐条复制。它是「文档内数组」通往「按元素分组统计」的桥:不展开就没法按 items.sku 这种元素级字段做 $group 汇总。
基本信息
| 适用版本 | MongoDB 4.x-8.x / mongosh |
|---|---|
| 简写语法 | { $unwind: "$items" } |
| 完整写法 | { path, includeArrayIndex, preserveNullAndEmptyArrays } |
| 阶段顺序 | 通常紧跟 $match,放在 $group 之前 |
| 嵌套数组 | 需要连续多次 $unwind |
典型使用场景
- 订单明细汇总:展开 items 后按 $items.sku 分组求销量与金额
- 标签/属性统计:把 tags 数组展开,得到每个标签的使用次数
- 明细与主表关联:展开后再 $lookup 补商品维度信息
- 按元素排序取 TopN:展开后 $sort + $limit,得到最贵/最多的明细行
- 去重与占比计算:展开数组后 $group 求 distinct 值数量
展开订单明细
展开订单明细
db.orders.aggregate([
{ $match: { ts: { $gte: ISODate("2026-10-01T00:00:00Z") } } }, # 先过滤,别等展开后再筛
{ $unwind: "$items" }, # 每个 items 元素输出一条文档
{ $group: { _id: "$items.sku", total: { $sum: "$items.qty" } } },
{ $sort: { total: -1 } },
{ $limit: 10 }
])
# 保留空数组/缺失字段的文档,否则这类订单会被整条丢掉
db.orders.aggregate([ { $unwind: { path: "$items", preserveNullAndEmptyArrays: true } } ])
# 把元素下标带出来,便于恢复原始顺序
db.orders.aggregate([ { $unwind: { path: "$items", includeArrayIndex: "idx" } } ])选项与行为
| 选项 | 作用 | 注意点 |
|---|---|---|
| path | 要展开的数组路径,必须带 $ 前缀 | 写成 "items" 会直接报错 |
| preserveNullAndEmptyArrays | true 时保留空数组与缺失字段的文档 | 默认 false,会让这些文档整体消失 |
| includeArrayIndex | 把元素下标写入新字段 | 下标从 0 开始,配合 $sort 可还原原顺序 |
| 嵌套数组 | 一次只展开一层 | 内层数组要再写一个 $unwind 阶段 |
不带 preserveNullAndEmptyArrays 时,数组为空或字段缺失的文档会被整条丢弃:统计总数时结果会比实际少,排查时很容易误判为数据丢失。
对象数组用点号访问元素字段,如 $items.sku、$items.price;展开后原文档的其余字段全部复制,所以 $group 里要显式选择需要的字段,避免结果膨胀。
$unwind 会把文档数放大到元素总数,位置越靠前代价越大;把 $match 放在 $unwind 之前先收窄数据,是这类管道的常规优化手段。