文档菜单
文档首页
/
MongoDB 手册
/ / /

$unwind (聚合)

本页内容

  • 定义
  • 兼容性
  • 语法
  • 行为
  • 示例
  • 其他资源
$unwind

将输入文档中的数组字段解构为输出文档,每个元素对应一个输出文档。每个输出文档都是输入文档,其中数组字段的值被元素替换。

您可以使用 $unwind 在以下环境中部署

  • MongoDB Atlas:云中MongoDB部署的完全托管服务

  • MongoDB Enterprise:基于订阅的、自我管理的MongoDB版本

  • MongoDB Community:源代码可用的、免费使用并可自我管理的MongoDB版本

您可以将字段路径操作数或文档操作数传递给展开数组字段。

您可以将数组字段路径传递给 $unwind。在使用此语法时,如果字段值为null、缺失或空数组,$unwind不会输出文档。

{ $unwind: <field path> }

指定字段路径时,请在字段名前加美元符号 $ 并用引号括起来。

您可以将文档传递给 $unwind 以指定各种行为选项。

{
$unwind:
{
path: <field path>,
includeArrayIndex: <string>,
preserveNullAndEmptyArrays: <boolean>
}
}
字段
类型
描述
字符串

数组字段的字段路径。要指定字段路径,请在字段名前加美元符号 $ 并用引号括起来。

字符串

可选。用于存储元素数组索引的新字段名称。名称不能以美元符号 $ 开头。

布尔值

可选。

  • 如果 true,如果 path 是 null、缺失或空数组,$unwind 输出文档。

  • 如果 false,如果 path 是 null、缺失或空数组,$unwind 不输出文档。

默认值是 false

  • 当操作数不是数组、null、或空数组时,$unwind 将操作数视为单个元素数组。

  • 当操作数是 null、缺失或空数组时,$unwind 遵循 preserveNullAndEmptyArrays 选项的行为。

如果您为输入文档中不存在的字段指定了路径,或者该字段是一个空数组,默认情况下,$unwind将忽略输入文档,不会为该输入文档输出文档。

要输出缺少数组字段的文档、null或空数组,请使用preserveNullAndEmptyArrays选项。

mongosh中,创建一个名为inventory的示例集合,并包含以下文档

db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })

以下聚合使用$unwind阶段输出sizes数组中的每个元素对应的文档

db.inventory.aggregate( [ { $unwind : "$sizes" } ] )

操作返回以下结果

{ "_id" : 1, "item" : "ABC1", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "L" }

每个文档与输入文档相同,只是sizes字段的值现在来自原始的sizes数组。

考虑clothing集合

db.clothing.insertMany([
{ "_id" : 1, "item" : "Shirt", "sizes": [ "S", "M", "L"] },
{ "_id" : 2, "item" : "Shorts", "sizes" : [ ] },
{ "_id" : 3, "item" : "Hat", "sizes": "M" },
{ "_id" : 4, "item" : "Gloves" },
{ "_id" : 5, "item" : "Scarf", "sizes" : null }
])

$unwindsizes字段视为一个元素数组,如果

  • 该字段存在,

  • 值不为空,并且

  • 值不是空数组。

使用$unwind扩展sizes数组:

db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )

$unwind操作返回

{ _id: 1, item: 'Shirt', sizes: 'S' },
{ _id: 1, item: 'Shirt', sizes: 'M' },
{ _id: 1, item: 'Shirt', sizes: 'L' },
{ _id: 3, item: 'Hat', sizes: 'M' }
  • 在文档"_id": 1中,sizes是一个填充的数组。$unwind返回每个sizes字段中的元素对应的文档。

  • 在文档"_id": 3中,sizes解析为单个元素数组。

  • 文档"_id": 2, "_id": 4"_id": 5没有任何返回,因为sizes字段无法减少到单个元素数组。

注意

{ path: <FIELD> }语法是可选的。以下$unwind操作是等效的。

db.clothing.aggregate( [ { $unwind: "$sizes" } ] )
db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )

preserveNullAndEmptyArraysincludeArrayIndex示例使用以下集合

db.inventory2.insertMany([
{ "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },
{ "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },
{ "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },
{ "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },
{ "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }
])

以下 $unwind 操作使用了 preserveNullAndEmptyArrays 选项,以包含那些 sizes 字段为 null、缺失或空数组的文档。

db.inventory2.aggregate( [
{ $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } }
] )

输出包括那些 sizes 字段为 null、缺失或空数组的文档

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
{ "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") }
{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
{ "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }
{ "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }

以下 $unwind 操作使用了 includeArrayIndex 选项,以包含输出中的数组索引。

db.inventory2.aggregate( [
{
$unwind:
{
path: "$sizes",
includeArrayIndex: "arrayIndex"
}
}])

该操作展开 sizes 数组,并在新的 arrayIndex 字段中包含数组索引。如果 sizes 字段未解析为填充的数组但不是缺失、null 或空数组,则 arrayIndex 字段为 null

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S", "arrayIndex" : NumberLong(0) }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M", "arrayIndex" : NumberLong(1) }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L", "arrayIndex" : NumberLong(2) }
{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M", "arrayIndex" : null }

mongosh中,创建一个名为inventory2的示例集合,包含以下文档

db.inventory2.insertMany([
{ "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },
{ "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },
{ "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },
{ "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },
{ "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }
])

以下管道展开sizes数组并将结果文档按展开的大小值分组

db.inventory2.aggregate( [
// First Stage
{
$unwind: { path: "$sizes", preserveNullAndEmptyArrays: true }
},
// Second Stage
{
$group:
{
_id: "$sizes",
averagePrice: { $avg: "$price" }
}
},
// Third Stage
{
$sort: { "averagePrice": -1 }
}
] )
第一阶段

$unwind阶段为sizes数组中的每个元素输出一个新文档。该阶段使用preserveNullAndEmptyArrays选项包括在输出中那些sizes字段不存在、为null或空数组的文档。此阶段将以下文档传递到下一阶段

{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" }
{ "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") }
{ "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
{ "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }
{ "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }
第二阶段

$group阶段按sizes分组文档并计算每个大小段的平均价格。此阶段将以下文档传递到下一阶段

{ "_id" : "S", "averagePrice" : NumberDecimal("80") }
{ "_id" : "L", "averagePrice" : NumberDecimal("80") }
{ "_id" : "M", "averagePrice" : NumberDecimal("120") }
{ "_id" : null, "averagePrice" : NumberDecimal("45.25") }
第三阶段

$sort阶段按averagePrice降序排序文档。该操作返回以下结果

{ "_id" : "M", "averagePrice" : NumberDecimal("120") }
{ "_id" : "L", "averagePrice" : NumberDecimal("80") }
{ "_id" : "S", "averagePrice" : NumberDecimal("80") }
{ "_id" : null, "averagePrice" : NumberDecimal("45.25") }

提示

另请参阅

mongosh中,创建一个名为sales的示例集合,包含以下文档

db.sales.insertMany([
{
_id: "1",
"items" : [
{
"name" : "pens",
"tags" : [ "writing", "office", "school", "stationary" ],
"price" : NumberDecimal("12.00"),
"quantity" : NumberInt("5")
},
{
"name" : "envelopes",
"tags" : [ "stationary", "office" ],
"price" : NumberDecimal("19.95"),
"quantity" : NumberInt("8")
}
]
},
{
_id: "2",
"items" : [
{
"name" : "laptop",
"tags" : [ "office", "electronics" ],
"price" : NumberDecimal("800.00"),
"quantity" : NumberInt("1")
},
{
"name" : "notepad",
"tags" : [ "stationary", "school" ],
"price" : NumberDecimal("14.95"),
"quantity" : NumberInt("3")
}
]
}
])

以下操作按标签将销售的项目分组,并计算每个标签的销售总额。

db.sales.aggregate([
// First Stage
{ $unwind: "$items" },
// Second Stage
{ $unwind: "$items.tags" },
// Third Stage
{
$group:
{
_id: "$items.tags",
totalSalesAmount:
{
$sum: { $multiply: [ "$items.price", "$items.quantity" ] }
}
}
}
])
第一阶段

第一个$unwind阶段为items数组中的每个元素输出一个新文档

{ "_id" : "1", "items" : { "name" : "pens", "tags" : [ "writing", "office", "school", "stationary" ], "price" : NumberDecimal("12.00"), "quantity" : 5 } }
{ "_id" : "1", "items" : { "name" : "envelopes", "tags" : [ "stationary", "office" ], "price" : NumberDecimal("19.95"), "quantity" : 8 } }
{ "_id" : "2", "items" : { "name" : "laptop", "tags" : [ "office", "electronics" ], "price" : NumberDecimal("800.00"), "quantity" : 1 } }
{ "_id" : "2", "items" : { "name" : "notepad", "tags" : [ "stationary", "school" ], "price" : NumberDecimal("14.95"), "quantity" : 3 } }
第二阶段

第二个$unwind阶段为items.tags数组中的每个元素输出一个新文档

{ "_id" : "1", "items" : { "name" : "pens", "tags" : "writing", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
{ "_id" : "1", "items" : { "name" : "pens", "tags" : "office", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
{ "_id" : "1", "items" : { "name" : "pens", "tags" : "school", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
{ "_id" : "1", "items" : { "name" : "pens", "tags" : "stationary", "price" : NumberDecimal("12.00"), "quantity" : 5 } }
{ "_id" : "1", "items" : { "name" : "envelopes", "tags" : "stationary", "price" : NumberDecimal("19.95"), "quantity" : 8 } }
{ "_id" : "1", "items" : { "name" : "envelopes", "tags" : "office", "price" : NumberDecimal("19.95"), "quantity" : 8 } }
{ "_id" : "2", "items" : { "name" : "laptop", "tags" : "office", "price" : NumberDecimal("800.00"), "quantity" : 1 } }
{ "_id" : "2", "items" : { "name" : "laptop", "tags" : "electronics", "price" : NumberDecimal("800.00"), "quantity" : 1 } }
{ "_id" : "2", "items" : { "name" : "notepad", "tags" : "stationary", "price" : NumberDecimal("14.95"), "quantity" : 3 } }
{ "_id" : "2", "items" : { "name" : "notepad", "tags" : "school", "price" : NumberDecimal("14.95"), "quantity" : 3 } }
第三阶段

$group阶段按标签对文档进行分组,并计算每个标签项目的销售总额

{ "_id" : "writing", "totalSalesAmount" : NumberDecimal("60.00") }
{ "_id" : "stationary", "totalSalesAmount" : NumberDecimal("264.45") }
{ "_id" : "electronics", "totalSalesAmount" : NumberDecimal("800.00") }
{ "_id" : "school", "totalSalesAmount" : NumberDecimal("104.85") }
{ "_id" : "office", "totalSalesAmount" : NumberDecimal("1019.60") }

提示

另请参阅

后退

$unset