Description
Generates new documents, either by referencing a subset of existing fields, or by assigning a field to the result of a given expression.
Examples
Web
const result = await execute(db.pipeline() .collection("books") .select(field("soldBooks").multiply(field("price")).round().as("partialRevenue")) .aggregate(field("partialRevenue").sum().as("totalRevenue")) );
Swift
let result = try await db.pipeline() .collection("books") .select([Field("soldBooks").multiply(Field("price")).round().as("partialRevenue")]) .aggregate([Field("partialRevenue").sum().as("totalRevenue")]) .execute()
Kotlin
val result = db.pipeline() .collection("books") .select(Expression.multiply(field("soldBooks"), field("price")).round().alias("partialRevenue")) .aggregate(AggregateFunction.sum("partialRevenue").alias("totalRevenue")) .execute()
Java
Task<Pipeline.Snapshot> result = db.pipeline() .collection("books") .select(Expression.multiply(field("soldBooks"), field("price")).round().alias("partialRevenue")) .aggregate(AggregateFunction.sum("partialRevenue").alias("totalRevenue")) .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field result = ( client.pipeline() .collection("books") .select( Field.of("soldBooks") .multiply(Field.of("price")) .round() .as_("partialRevenue") ) .aggregate(Field.of("partialRevenue").sum().as_("totalRevenue")) .execute() )
Java
Pipeline.Snapshot result = firestore .pipeline() .collection("books") .select(round(multiply(field("soldBooks"), field("price"))).as("partialRevenue")) .aggregate(sum("partialRevenue").as("totalRevenue")) .execute() .get();
Behavior
Position of a Select Stage
There are no restrictions on when a select stage can be used, but any fields not
included in a select stage won't be accessible to subsequent stages in a
pipeline. For example, to select only the name and location fields of all
cities in Canada from the following dataset:
Node.js
await db.collection("cities").doc("SF").set({
name: "San Francisco",
population: 800000,
location: {country: "USA", state: "California"}
});
await db.collection("cities").doc("TO").set({
name: "Toronto",
population: 3000000,
location: {country: "Canada", province: "Ontario"}
});
The following pipeline can be used:
Node.js
const names = await db.pipeline()
.collection("/cities")
.where(equal(field("location.country"), "Canada"))
.select(stringConcat(field("name"), ", ", field("location.country")).as("name"), "population")
.execute();
Which produces the following documents:
{ name: "Toronto, Canada", population: 3000000 },
However, if the select(...) stage is instead placed before the where(...)
stage, like:
Node.js
const names = await db.pipeline()
.collection("/cities")
.select(stringConcat(field("name"), ",", field("location.country")).as("name"), "population")
.where(equal(field("location.country"), "Canada"))
.execute();
No documents will be produced, because location.country has been removed from
the document before the execution of the where(...) stage.
Select Nested Fields
The select(...) stage can be used to select nested fields from both maps and
arrays. For example, to select the nested country field and first entry of the
landmarks array from the following documents:
Node.js
await db.collection("cities").doc("SF").set({
name: "San Francisco",
population: 800000,
location: { country: "USA", state: "California" },
landmarks: [ "Golden Gate Bridge", "Alcatraz" ]
});
await db.collection("cities").doc("TO").set({
name: "Toronto",
population: 3000000,
province: "ON",
location: { country: "Canada", province: "Ontario" },
landmarks: [ "CN Tower", "Casa Loma" ]
});
await db.collection("cities").doc("AT").set({
name: "Atlantis",
population: null
});
The following pipeline can be used:
Node.js
const locations = await db.pipeline()
.collection("/cities")
.select(
field("name").as("city"),
field("location.country").as("country"),
field("landmarks").offset(0).as("topLandmark"))
.execute();
Which produces the following documents:
{ city: "San Francisco", country: "USA", topLandmark: "Golden Gate Bridge" },
{ city: "Toronto", country: "Canada", topLandmark: "CN Tower" },
{ city: "Atlantis" }
If a nested map value or array value does not exist, it is not included in the
resulting document. Array and map access in the select stage behaves identically
to the offset(...) and get_field(...) functions, respectively.
Assign Nested Fields
The result of an expression can also be assigned to a nested field, making it
possible to have the select(...) stage return a subset of nested fields from
the previous stage. For example the following can be used to ensure only the
city & state information is returned while still preserving the document's
original shape:
Node.js
const results = await db.pipeline()
.collection("/users")
.addFields(
field("__name__"),
field("address.city").as("address.city"),
field("address.state").as("address.state"))
.execute();