Skip to content

DOCS-13415 clarify wording for unwind #6180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 18, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 85 additions & 49 deletions source/reference/operator/aggregation/unwind.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ field name with a dollar sign ``$`` and enclose in quotes.
Document Operand with Options
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 3.2

You can pass a document to :pipeline:`$unwind` to specify various
behavior options.

Expand Down Expand Up @@ -170,75 +168,80 @@ 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.

``includeArrayIndex`` and ``preserveNullAndEmptyArrays``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. versionadded:: 3.2
Missing or Non-array Values
~~~~~~~~~~~~~~~~~~~~~~~~~~~

In :binary:`~bin.mongosh`, create a sample collection named
``inventory2`` with the following documents:
Consider the ``clothing`` collection:

.. code-block:: javascript

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 }
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 }
])

The following :pipeline:`$unwind` operations are equivalent and return
a document for each element in the ``sizes`` field. If the ``sizes``
field does not resolve to an array but is not missing, null, or an
empty array, :pipeline:`$unwind` treats the non-array operand as a
single element array.
:pipeline:`$unwind` treats the ``sizes`` field as a single element
array if:

- the field is present,
- the value is not null, and
- the value is not an empty array.

Expand the ``sizes`` arrays with :pipeline:`$unwind`:

.. code-block:: javascript

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

The operation returns the following documents:
The :pipeline:`$unwind` operation returns:

.. code-block:: javascript
:copyable: false

{ "_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" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" }
{ _id: 1, item: 'Shirt', sizes: 'S' },
{ _id: 1, item: 'Shirt', sizes: 'M' },
{ _id: 1, item: 'Shirt', sizes: 'L' },
{ _id: 3, item: 'Hat', sizes: 'M' }

``includeArrayIndex``
`````````````````````
- In document ``"_id": 1``, ``sizes`` is a populated array.
:pipeline:`$unwind` returns a document for each element in the
``sizes`` field.
- In document ``"_id": 3``, ``sizes`` resolves to a single element
array.
- Documents ``"_id": 2, "_id": 4``, and ``"_id": 5`` do not return
anything because the ``sizes`` field cannot be reduced to a single
element array.

The following :pipeline:`$unwind` operation uses the
:ref:`includeArrayIndex <unwind-includeArrayIndex>` option to include
the array index in the output.
.. note::

.. code-block:: javascript
The ``{ path: <FIELD> }`` syntax is optional. The following
:pipeline:`$unwind` operations are equivalent.

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

The operation unwinds the ``sizes`` array and includes the array index
of the array index in the new ``arrayIndex`` field. If the ``sizes``
field does not resolve to an array but is not missing, null, or an
empty array, the ``arrayIndex`` field is ``null``.
db.clothing.aggregate( [ { $unwind: "$sizes" } ] )
db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )

``preserveNullAndEmptyArrays`` and ``includeArrayIndex``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :ref:`ex-preservedNull` and :ref:`ex-includeArrayIndex` examples
use the following collection:

.. code-block:: javascript
:copyable: false

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

.. _ex-preservedNull:

``preserveNullAndEmptyArrays``
``````````````````````````````
Expand Down Expand Up @@ -269,6 +272,39 @@ null, missing, or an empty array:
{ "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") }
{ "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }

.. _ex-includeArrayIndex:

``includeArrayIndex``
`````````````````````

The following :pipeline:`$unwind` operation uses the
:ref:`includeArrayIndex <unwind-includeArrayIndex>` option to include
the array index in the output.

.. code-block:: javascript

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

The operation unwinds the ``sizes`` array and includes the array index
in the new ``arrayIndex`` field. 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``.

.. code-block:: javascript
:copyable: false

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

Expand Down