Skip to content

feat(firestore): Add support for subqueries#7839

Open
dlarocque wants to merge 20 commits intomainfrom
dl/subq
Open

feat(firestore): Add support for subqueries#7839
dlarocque wants to merge 20 commits intomainfrom
dl/subq

Conversation

@dlarocque
Copy link
Copy Markdown
Contributor

@dlarocque dlarocque commented Mar 20, 2026

Adds support for Pipeline subqueries.

Ported from: firebase/firebase-android-sdk#7736

API Proposal: go/firestore-pipelines-subquery

@product-auto-label product-auto-label bot added the api: firestore Issues related to the Firestore API. label Mar 20, 2026
@dlarocque dlarocque force-pushed the dl/subq branch 2 times, most recently from 850ba24 to dd1a205 Compare March 23, 2026 20:07
Base automatically changed from dl/defer-validation to main March 24, 2026 14:12
@dlarocque dlarocque marked this pull request as ready for review March 26, 2026 20:01
@dlarocque dlarocque requested a review from a team as a code owner March 26, 2026 20:01
Copy link
Copy Markdown
Contributor

@cherylEnkidu cherylEnkidu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add this test as well: https://github.com/googleapis/java-firestore/pull/2323/changes#diff-566df7e9507530a3a2b0c886b9408ac028ed2f741506e4ed33a3b4075ea36672R986

Also implement the check when calling subcollection in Union stage

@dlarocque dlarocque requested a review from cherylEnkidu April 2, 2026 15:55
Copy link
Copy Markdown
Contributor

@MarkDuckworth MarkDuckworth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with suggestions for ref doc cleanup


/**
* @public
* Creates an expression that returns the value of a field from a document that results from the evaluation of the expression.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider something like

Creates an expression that gets a field from a map (object).

Firestore documentation uses "map" to refer to the data type, not document/sub-document.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* getField(field("address"), "city")
* ```
*
* @param expression The expression representing the document.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"an expression evaluating to the map from which the field will be extracted"

* firestore.pipeline().collection("books")
* .define(currentDocument().as("doc"))
* // Access a field from the defined document variable
* .select(variable("doc").mapGet("title"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use getField('title') instead of mapGet?


/**
* @public
* Defines one or more variables in the pipeline's scope. `define` is used to bind a value to a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think it would be valuable to state that it binds the variable to the pipeline's current document

For example considering the following

db.pipeline().collection("foo")
  .define(currentTimestamp().as("executionTime"))
  .addField(variable("executionTime").as("executionTime"))

If the customer reads this documentation as "define binds a variable to the pipeline", they they may assume that .define(currentTimestamp().as("executionTime")) is executed once for the pipeline.

But the reality is that .define(currentTimestamp().as("executionTime")) is executed for each document and binds a different value of executionTime for each document.


/**
* @public
* Converts this Pipeline into an expression that evaluates to an array of results.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider "array of map (objects)" for consistent data types.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Converts this Pipeline into an expression that evaluates to an array of maps (objects), where each result document of the pipeline is represented as a map in the returned array."

* .define(field("id").as("book_id"))
* .addFields(
* db.pipeline().collection("reviews")
* .where(field("book_id").equal(variable("book_id")))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might clarify things to choose a field name that does not match the variable name.

  ...
  .define(field("id").as("current_book_id"))
  ...
      .where(field("book_id").equal(variable("current_book_id")))

* // Calculate average rating for a restaurant
* db.pipeline().collection("restaurants").addFields(
* db.pipeline().collection("reviews")
* .where(field("restaurant_id").equal(variable("rid")))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is rid defined?

* // Calculate average rating AND count for a restaurant
* db.pipeline().collection("restaurants").addFields(
* db.pipeline().collection("reviews")
* .where(field("restaurant_id").equal(variable("rid")))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any changes you decide to make in the ref doc above would also need to be changed here

@MarkDuckworth MarkDuckworth added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 2, 2026
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: firestore Issues related to the Firestore API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants