$unwind (aggregation)

On this page本页内容

Definition定义

$unwind

Deconstructs an array field from the input documents to output a document for each element. 从输入文档中解构数组字段,以输出每个元素的文档。Each output document is the input document with the value of the array field replaced by the element.每个输出文档都是输入文档,数组字段的值被元素替换。

Syntax语法

You can pass a field path operand or a document operand to unwind an array field.可以传递字段路径操作数或文档操作数以展开数组字段。

Field Path Operand字段路径操作数

You can pass the array field path to $unwind. 您可以将数组字段路径传递给$unwindWhen using this syntax, $unwind does not output a document if the field value is null, missing, or an empty array.使用此语法时,如果字段值为null、缺失或空数组,$unwind不会输出文档。

{ $unwind: <field path> }

When you specify the field path, prefix the field name with a dollar sign $ and enclose in quotes.指定字段路径时,在字段名称前面加上美元符号$,并用引号括起来。

Document Operand with Options带选项的文档操作数

You can pass a document to $unwind to specify various behavior options.您可以将文档传递给$unwind以指定各种行为选项。

{
  $unwind:
    {
      path: <field path>,
      includeArrayIndex: <string>,
      preserveNullAndEmptyArrays: <boolean>
    }
}
Field字段Type类型Description描述
pathstring

Field path to an array field. 数组字段的字段路径。To specify a field path, prefix the field name with a dollar sign $ and enclose in quotes.要指定字段路径,请在字段名称前面加上美元符号$,并用引号括起来。

includeArrayIndexstring

Optional. 可选。The name of a new field to hold the array index of the element. 用于保存元素数组索引的新字段的名称。The name cannot start with a dollar sign $.名称不能以美元符号$开头。

preserveNullAndEmptyArraysboolean

Optional.可选。

  • If true, if the path is null, missing, or an empty array, $unwind outputs the document.如果为true,则如果pathnull、缺失或空数组,$unwind将输出文档。
  • If false, if path is null, missing, or an empty array, $unwind does not output a document.如果为false,则如果pathnull、缺失或空数组,$unwind不会输出文档。

The default value is false.默认值为false

Behaviors行为

Non-Array Field Path非数组字段路径

Changed in version 3.2.在版本3.2中更改

$unwind stage no longer errors on non-array operands. 阶段不再对非数组操作数出错。If the operand does not resolve to an array but is not missing, null, or an empty array, $unwind treats the operand as a single element array. 如果操作数未解析为数组,但未丢失、空或空数组,$unwind将操作数视为单元素数组。If the operand is null, missing, or an empty array, the behavior of $unwind depends on the value of the preserveNullAndEmptyArrays option.如果操作数为null、缺失或空数组,$unwind的行为取决于preserveNullAndEmptyArrays选项的值。

Previously, if a value in the field specified by the field path is not an array, db.collection.aggregate() generates an error.以前,如果字段路径指定的字段中的值不是数组,则db.collection.aggregate()会生成错误。

Missing Field缺失字段

If you specify a path for a field that does not exist in an input document or the field is an empty array, $unwind, by default, ignores the input document and will not output documents for that input document.如果为输入文档中不存在的字段指定路径,或者该字段为空数组,则默认情况下,$unwind将忽略输入文档,并且不会输出该输入文档的文档。

To output documents where the array field is missing, null or an empty array, use the preserveNullAndEmptyArrays option.若要输出缺少数组字段、空或空数组的文档,请使用preserveNullAndEmptyArrays选项。

Examples示例

Unwind Array展开数组

In mongosh, create a sample collection named inventory with the following document:mongosh中,使用以下文档创建名为inventory的样本集合:

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

The following aggregation uses the $unwind stage to output a document for each element in the sizes array:以下聚合使用$unwind阶段为sizes数组中的每个元素输出文档:

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

The operation returns the following results:该操作返回以下结果:

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

Each document is identical to the input document except for the value of the sizes field which now holds a value from the original sizes array.每个文档都与输入文档相同,但sizes字段的值除外,该字段现在保存原始sizes数组中的值。

Missing or Non-array Values缺少或非数组值

Consider the clothing collection: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 }
])

$unwind treats the sizes field as a single element array if:如果出现以下情况,$unwind将大小字段视为单个元素数组:

  • the field is present,存在场,
  • the value is not null, and该值不为空,并且
  • the value is not an empty array.值不是空数组。

Expand the sizes arrays with $unwind:使用$unwind展开sizes数组:

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

The $unwind operation returns:$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' }
  • In document "_id": 1, sizes is a populated array. 在文档"_id": 1中,sizes是一个填充数组$unwind returns a document for each element in the sizes field.$unwindsizes字段中的每个元素返回一个文档。
  • In document "_id": 3, sizes resolves to a single element array.在文档"_id": 3中,sizes解析为单个元素数组。
  • Documents "_id": 2, "_id": 4, and "_id": 5 do not return anything because the sizes field cannot be reduced to a single element array.文档"_id": 2, "_id": 4"_id": 5不返回任何内容,因为sizes字段不能缩减为单个元素数组。
Note注意

The { path: <FIELD> } syntax is optional. { path: <FIELD> }语法是可选的。The following $unwind operations are equivalent.以下$unwind操作相当。

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

preserveNullAndEmptyArrays and includeArrayIndex

The preserveNullAndEmptyArrays and includeArrayIndex examples use the following collection: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 }
])

preserveNullAndEmptyArrays

The following $unwind operation uses the preserveNullAndEmptyArrays option to include documents whose sizes field is null, missing, or an empty array.以下$unwind操作使用preserveNullAndEmptyArrays选项来包括sizes字段为null、缺失或空数组的文档。

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

The output includes those documents where the sizes field is null, missing, or an empty array:输出包括sizes字段为空、缺失或空数组的文档:

{ "_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 }

includeArrayIndex

The following $unwind operation uses the includeArrayIndex option to include the array index in the output.以下$unwind操作使用includeArrayIndex选项在输出中包含数组索引。

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

The operation unwinds the sizes array and includes the array index in the new arrayIndex field. 该操作将展开sizes数组,并将数组索引包含在新的arrayIndex字段中。If the sizes field does not resolve to a populated array but is not missing, null, or an empty array, the arrayIndex field is null.如果sizes字段未解析为填充的数组,但未丢失、空或空数组,则arrayIndex字段为空。

{ "_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 }

Group by Unwound Values按Unwound值分组

In mongosh, create a sample collection named inventory2 with the following documents: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 }
])

The following pipeline unwinds the sizes array and groups the resulting documents by the unwound size values:以下管道展开sizes数组,并根据展开的大小值对结果文档进行分组:

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

The $unwind stage outputs a new document for each element in the sizes array. $unwind阶段为sizes数组中的每个元素输出一个新文档。The stage uses the preserveNullAndEmptyArrays option to include in the output those documents where sizes field is missing, null or an empty array. 该阶段使用preserveNullAndEmptyArrays选项在输出中包括缺少sizes字段、null或空数组的文档。This stage passes the following documents to the next stage:此阶段将以下文件传递到下一阶段:

{ "_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 }
Second Stage:第二阶段:

The $group stage groups the documents by sizes and calculates the average price of each size. $group阶段按sizes对文档进行分组,并计算每个大小的平均价格。This stage passes the following documents to the next stage:此阶段将以下文档传递到下一阶段:

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

The $sort stage sorts the documents by averagePrice in descending order. The operation returns the following result:$sort阶段按averagePrice按降序对文档进行排序。该操作返回以下结果:

{ "_id" : "M", "averagePrice" : NumberDecimal("120") }
{ "_id" : "L", "averagePrice" : NumberDecimal("80") }
{ "_id" : "S", "averagePrice" : NumberDecimal("80") }
{ "_id" : null, "averagePrice" : NumberDecimal("45.25") }
Tip提示
See also: 参阅:

Unwind Embedded Arrays解开嵌入式数组

In mongosh, create a sample collection named sales with the following documents: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")
     }
    ]
  }
])

The following operation groups the items sold by their tags and calculates the total sales amount per each tag.以下操作将按标签销售的项目分组,并计算每个标签的总销售额。

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" ] }
          }
      }
  }
])
First Stage第一阶段

The first $unwind stage outputs a new document for each element in the items array:第一个$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 } }
Second Stage第二阶段

The second $unwind stage outputs a new document for each element in the items.tags arrays:第二个$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 } }
Third Stage第三阶段

The $group stage groups the documents by the tag and calculates the total sales amount of items with each tag:$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") }
Tip提示
See also: 参阅:

Additional Resources额外资源

←  $unset (aggregation)Aggregation Pipeline Operators →