Unnest (Transformation Stage)
Preview — Firestore in Native mode (with Pipeline Operations) for Enterprise Edition
This feature is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of theService Specific Terms. You can process personal data for this feature as outlined in theCloud Data Processing Addendum, subject to the obligations and restrictions described in the agreement under which you access Google Cloud. Pre-GA features are available "as is" and might have limited support. For more information, see thelaunch stage descriptions.
Description
Generates a new document for each element in an array.
The new documents contain all the fields from the input along with a differentelement from the array. The array element is stored to thealias given,potentially overwriting any pre-existing value with the same field name.
Optionally, theindex_field argument can be specified. When present,it includes the element's zero-based index from the source array in the outputdocuments.
This stage behaves similar toCROSS JOIN UNNEST(...) in many SQL systems.
Syntax
Node.js
constuserScore=awaitdb.pipeline().collection("/users").unnest(field('scores').as('userScore'),/* index_field= */'attempt').execute();Behavior
Alias and Index Field
Thealias and optionalindex_field will overwrite theoriginal fields if the fields already exist in the input document. If theindex_field is not provided, the output documents won't contain thisfield.
For example, for the following collection:
Node.js
awaitdb.collection('users').add({name:"foo",scores:[5,4],userScore:0});awaitdb.collection('users').add({name:"bar",scores:[1,3],attempt:5});Theunnest stage can be used to extract each individual score per user.
Node.js
constuserScore=awaitdb.pipeline().collection("/users").unnest(field('scores').as('userScore'),/* index_field= */'attempt').execute();In this case,userScore andattempt are both overwritten.
{name:"foo",scores:[5,4],userScore:5,attempt:0}{name:"foo",scores:[5,4],userScore:4,attempt:1}{name:"bar",scores:[1,3],userScore:1,attempt:0}{name:"bar",scores:[1,3],userScore:3,attempt:1}Additional examples
Swift
letresults=tryawaitdb.pipeline().database().unnest(Field("arrayField").as("unnestedArrayField"),indexField:"index").execute()
Kotlin
Android
valresults=db.pipeline().database().unnest(field("arrayField").alias("unnestedArrayField"),UnnestOptions().withIndexField("index")).execute()
Java
Android
Task<Pipeline.Snapshot>results=db.pipeline().database().unnest(field("arrayField").alias("unnestedArrayField"),newUnnestOptions().withIndexField("index")).execute();
Python
fromgoogle.cloud.firestore_v1.pipeline_expressionsimportFieldfromgoogle.cloud.firestore_v1.pipeline_stagesimportUnnestOptionsresults=(client.pipeline().database().unnest(Field.of("arrayField").as_("unnestedArrayField"),options=UnnestOptions(index_field="index"),).execute())
Java
Pipeline.Snapshotresults=firestore.pipeline().database().unnest("arrayField","unnestedArrayField",newUnnestOptions().withIndexField("index")).execute().get();
Non Array Values
If the input expression evaluates to a non-array value, then this stage willreturn the input document as is with theindex_field set toNULL,if specified.
For example, for the following collection:
Node.js
awaitdb.collection('users').add({name:"foo",scores:1});awaitdb.collection('users').add({name:"bar",scores:null});awaitdb.collection('users').add({name:"qux",scores:{backupScores:1}});Theunnest stage can be used to extract each individual score per user.
Node.js
constuserScore=awaitdb.pipeline().collection("/users").unnest(field('scores').as('userScore'),/* index_field= */'attempt').execute();This produces the following documents withattempt set toNULL.
{name:"foo",scores:1,attempt:null}{name:"bar",scores:null,attempt:null}{name:"qux",scores:{backupScores:1},attempt:null}Empty Array Values
If the input expression evaluates to an empty array, then no document will bereturned for that input document.
For example, for the following collection:
Node.js
awaitdb.collection('users').add({name:"foo",scores:[5,4]});awaitdb.collection('users').add({name:"bar",scores:[]});Theunnest stage can be used to extract each individual score per user.
Node.js
constuserScore=awaitdb.pipeline().collection("/users").unnest(field('scores').as('userScore'),/* index_field= */'attempt').execute();This produces the following documents with userbar missing from theoutput.
{name:"foo",scores:[5,4],userScore:5,attempt:0}{name:"foo",scores:[5,4],userScore:4,attempt:1}In order to return documents with empty arrays as well, you can wrap theunnested value in an array. For example:
Node.js
constuserScore=awaitdb.pipeline().collection("/users").unnest(conditional(equal(field('scores'),[]),array([field('scores')]),field('scores')).as("userScore"),/* index_field= */"attempt").execute();This will now return document with userbar.
{name:"foo",scores:[5,4],userScore:5,attempt:0}{name:"foo",scores:[5,4],userScore:4,attempt:1}{name:"bar",scores:[],userScore:[],attempt:0}Additional examples
Node.js
// Input// { identifier : 1, neighbors: [ "Alice", "Cathy" ] }// { identifier : 2, neighbors: [] }// { identifier : 3, neighbors: "Bob" }constresults=awaitdb.pipeline().database().unnest(Field.of('neighbors'),'unnestedNeighbors','index').execute();// Output// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 }// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 }// { identifier: 3, neighbors: "Bob", index: null}
Swift
// Input// { identifier : 1, neighbors: [ "Alice", "Cathy" ] }// { identifier : 2, neighbors: [] }// { identifier : 3, neighbors: "Bob" }letresults=tryawaitdb.pipeline().database().unnest(Field("neighbors").as("unnestedNeighbors"),indexField:"index").execute()// Output// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 }// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 }// { identifier: 3, neighbors: "Bob", index: null}
Kotlin
Android
// Input// { identifier : 1, neighbors: [ "Alice", "Cathy" ] }// { identifier : 2, neighbors: [] }// { identifier : 3, neighbors: "Bob" }valresults=db.pipeline().database().unnest(field("neighbors").alias("unnestedNeighbors"),UnnestOptions().withIndexField("index")).execute()// Output// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 }// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 }// { identifier: 3, neighbors: "Bob", index: null}
Java
Android
// Input// { identifier : 1, neighbors: [ "Alice", "Cathy" ] }// { identifier : 2, neighbors: [] }// { identifier : 3, neighbors: "Bob" }Task<Pipeline.Snapshot>results=db.pipeline().database().unnest(field("neighbors").alias("unnestedNeighbors"),newUnnestOptions().withIndexField("index")).execute();// Output// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Alice", index: 0 }// { identifier: 1, neighbors: [ "Alice", "Cathy" ], unnestedNeighbors: "Cathy", index: 1 }// { identifier: 3, neighbors: "Bob", index: null}
Python
fromgoogle.cloud.firestore_v1.pipeline_expressionsimportFieldfromgoogle.cloud.firestore_v1.pipeline_stagesimportUnnestOptions# Input# { "identifier" : 1, "neighbors": [ "Alice", "Cathy" ] }# { "identifier" : 2, "neighbors": [] }# { "identifier" : 3, "neighbors": "Bob" }results=(client.pipeline().database().unnest(Field.of("neighbors").as_("unnestedNeighbors"),options=UnnestOptions(index_field="index"),).execute())# Output# { "identifier": 1, "neighbors": [ "Alice", "Cathy" ],# "unnestedNeighbors": "Alice", "index": 0 }# { "identifier": 1, "neighbors": [ "Alice", "Cathy" ],# "unnestedNeighbors": "Cathy", "index": 1 }# { "identifier": 3, "neighbors": "Bob", "index": null}
Java
// Input// { "identifier" : 1, "neighbors": [ "Alice", "Cathy" ] }// { "identifier" : 2, "neighbors": [] }// { "identifier" : 3, "neighbors": "Bob" }Pipeline.Snapshotresults=firestore.pipeline().database().unnest("neighbors","unnestedNeighbors",newUnnestOptions().withIndexField("index")).execute().get();// Output// { "identifier": 1, "neighbors": [ "Alice", "Cathy" ],// "unnestedNeighbors": "Alice", "index": 0 }// { "identifier": 1, "neighbors": [ "Alice", "Cathy" ],// "unnestedNeighbors": "Cathy", "index": 1 }// { "identifier": 3, "neighbors": "Bob", "index": null}
Nested Unnest
In the case the expression evaluates to a nested array, multipleunnest stagesmust be used to flatten each nested level.
For example, for the following collection:
Node.js
awaitdb.collection('users').add({name:"foo",record:[{scores:[5,4],avg:4.5},{scores:[1,3],old_avg:2}]});Theunnest stage can be used sequentially to extract the innermost array.
Node.js
constuserScore=awaitdb.pipeline().collection("/users").unnest(field('record').as('record')).unnest(field('record.scores').as('userScore'),/* index_field= */'attempt').execute();This produces the following documents:
{name:"foo",record:[{scores:[5,4],avg:4.5}],userScore:5,attempt:0}{name:"foo",record:[{scores:[5,4],avg:4.5}],userScore:4,attempt:1}{name:"foo",record:[{scores:[1,3],avg:2}],userScore:1,attempt:0}{name:"foo",record:[{scores:[1,3],avg:2}],userScore:3,attempt:1}Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-18 UTC.