Skip to main content

Aggregate.unwind

1. Interface Description

Function: Splits documents using each element in the specified array field. After splitting, a single document becomes one or more documents, each corresponding to an element in the array.

Declaration: Two forms

  • The parameter is a field name

unwind(<field name>)

  • The parameter is an object

unwind({ path: <field name>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> })

2. Input Parameters

ParameterTypeRequiredDescription
pathstringYesField name of the array to be split, must start with $.
includeArrayIndexstringNoOptional. Pass in a new field name to store the array index. The new field name must not start with $.
preserveNullAndEmptyArraysbooleanYesIf set to true, documents will still be output when the field specified in path is null, an empty array, or the field does not exist. If set to false, unwind will not output these documents. Default is false.

3. Response

ParameterTypeRequiredDescription
-AggregateYesAggregation object

4. Sample Code

Splitting Arrays

Assume we have a products collection containing the following data:

{ "_id": "1", "product": "tshirt", "size": ["S", "M", "L"] }
{ "_id": "2", "product": "pants", "size": [] }
{ "_id": "3", "product": "socks", "size": null }
{ "_id": "4", "product": "trousers", "size": ["S"] }
{ "_id": "5", "product": "sweater", "size": ["M", "L"] }

We split these documents based on the size field.

const tcb = require("@cloudbase/node-sdk");
const app = tcb.init({
env: "xxx",
});

const db = app.database();

exports.main = async (event, context) => {
const res = await db.collection("products").aggregate().unwind("$size").end();
console.log(res.data);
};

The output is as follows:

{ "_id": "1", "product": "tshirt", "size": "S" }
{ "_id": "1", "product": "tshirt", "size": "M" }
{ "_id": "1", "product": "tshirt", "size": "L" }
{ "_id": "4", "product": "trousers", "size": "S" }
{ "_id": "5", "product": "sweater", "size": "M" }
{ "_id": "5", "product": "sweater", "size": "L" }

Preserving the Original Array Index After Splitting

After splitting the documents based on the size field, we want to preserve the original array index in a new index field.

const tcb = require("@cloudbase/node-sdk");
const app = tcb.init({
env: "xxx",
});

const db = app.database();

exports.main = async (event, context) => {
const res = await db
.collection("products")
.aggregate()
.unwind({
path: "$size",
includeArrayIndex: "index",
})
.end();
console.log(res.data);
};

The output is as follows:

{ "_id": "1", "product": "tshirt", "size": "S", "index": 0 }
{ "_id": "1", "product": "tshirt", "size": "M", "index": 1 }
{ "_id": "1", "product": "tshirt", "size": "L", "index": 2 }
{ "_id": "4", "product": "trousers", "size": "S", "index": 0 }
{ "_id": "5", "product": "sweater", "size": "M", "index": 0 }
{ "_id": "5", "product": "sweater", "size": "L", "index": 1 }

Preserving Documents with Empty Fields

Note that there are two special rows of null value data in our collection:

...
{ "_id": "2", "product": "pants", "size": [] }
{ "_id": "3", "product": "socks", "size": null }
...

If you want to preserve documents where size is an empty array, null, or the size field does not exist in the output, you can use the preserveNullAndEmptyArrays parameter.

const tcb = require("@cloudbase/node-sdk");
const app = tcb.init({
env: "xxx",
});

const db = app.database();

exports.main = async (event, context) => {
const res = await db
.collection("products")
.aggregate()
.unwind({
path: "$size",
preserveNullAndEmptyArrays: true,
})
.end();
console.log(res.data);
};

The output is as follows:

{ "_id": "1", "product": "tshirt", "size": "S" }
{ "_id": "1", "product": "tshirt", "size": "M" }
{ "_id": "1", "product": "tshirt", "size": "L" }
{ "_id": "2", "product": "pants", "size": null }
{ "_id": "3", "product": "socks", "size": null }
{ "_id": "4", "product": "trousers", "size": "S" }
{ "_id": "5", "product": "sweater", "size": "M" }
{ "_id": "5", "product": "sweater", "size": "L" }