Skip to content

fix(firestore): read and write user data on pipeline execute#9715

Merged
dlarocque merged 2 commits intomainfrom
dl/pipeline-refactor
Mar 16, 2026
Merged

fix(firestore): read and write user data on pipeline execute#9715
dlarocque merged 2 commits intomainfrom
dl/pipeline-refactor

Conversation

@dlarocque
Copy link
Copy Markdown
Contributor

@dlarocque dlarocque commented Mar 11, 2026

To support standalone stage initializers (such as subcollection()), that can be constructed without an existing pipeline, we need to decouple stage initialization and user data validation. Standalone functions do not have access to Firestore or databaseId context when called, so validation must be deferred until the pipeline is executed with execute(pipeline).

Implementation Details:

  1. Removed user data parsing in stage methods (select, where, addFields, ...).
  2. Added a _readUserData() traversal to Pipeline class.
  3. Modified execute() to trigger pipeline._readUserData() before executing the pipeline.

Breaking Changes:

  1. Validation timing

Errors regarding invalid user data in stages will now be thrown when execute(pipeline) is called, rather than during the initialization of the stage (pipeline.collection().addFields(/* invalid data */)).
This aligns with the behaviour on both iOS, Android, and Java SDKs.

// Previously: error threw here
const ppl = firestore.pipeline().collection('books').select(field, undefined); // undefined is an unsupported field value

// Now: error throws here
await execute(ppl);
  1. Nested undefined on rawOptions throws

Inner object properties that are undefined inside rawOptions will now cause validation to fail, instead of being silently ignored. This is a side effect of execution parsing. Deferring validation allows the context to inherit the active setting for ignoreUndefinedProperties (defaulting to false) instead of forcing it to true as previously done during construction. The reason it only happens for nested properties is a quirk- probably something that should be fixed.

execute(pipeline, { 
  rawOptions: {
    custom: { foo: undefined } // now throws "Unsupported field value: undefined"
  }
});
  1. (Minor) Error message changes

Error message text will report the backend stage naming (snake_case) rather than the client naming (snakeCase).

Previous: Function addFields called with invalid data
New: Function add_fields called with invalid data

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 11, 2026

🦋 Changeset detected

Latest commit: b1944ac

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@firebase/firestore Patch
firebase Patch
@firebase/firestore-compat Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the Firestore pipeline to defer the reading and writing of user data until the pipeline's execute() method is invoked. This change centralizes user data processing, simplifying the pipeline's construction and stage management by removing UserDataReader and UserDataWriter dependencies from earlier phases. The primary benefit is a more controlled and explicit handling of user data, aligning its processing with the actual execution of the pipeline.

Highlights

  • Deferred User Data Processing: User data reading and writing operations within the pipeline are now deferred until the execute() method is explicitly called, rather than being performed during pipeline construction or stage addition.
  • Simplified Pipeline Constructors: The Pipeline and PipelineSource constructors, along with various stage-adding methods, have been simplified by removing direct dependencies on UserDataReader and AbstractUserDataWriter.
  • Centralized User Data Handling: The UserDataReader and UserDataWriter instances are now created and managed within the execute() function, ensuring that user data is processed at the appropriate time.
  • Pipeline as UserData: The Pipeline class now implements the UserData interface and includes a _readUserData method, allowing it to manage the reading of user data across its stages.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • packages/firestore/src/api/pipeline.ts
    • Removed imports for UserDataReader and AbstractUserDataWriter.
    • Updated the newPipeline method signature to remove userDataReader and userDataWriter parameters.
    • Modified the Pipeline class constructor to no longer accept userDataReader and _userDataWriter.
    • Added a _readUserData method to the Pipeline class to iterate and call _readUserData on its stages.
    • Removed userDataReader.createContext and stage._readUserData calls from various pipeline stage methods.
    • Adjusted _addStage to use the simplified newPipeline call.
  • packages/firestore/src/api/pipeline_impl.ts
    • Removed the newUserDataReader import.
    • Modified the execute function to call pipeline._readUserData(context) and instantiate ExpUserDataWriter locally.
    • Updated PipelineResult to use the locally created userDataWriter.
    • Simplified the Firestore.prototype.pipeline factory method by removing userDataReader and ExpUserDataWriter instantiation.
  • packages/firestore/src/lite-api/pipeline-source.ts
    • Removed imports for UserDataReader and UserDataSource.
    • Modified the PipelineSource constructor to remove the userDataReader parameter.
    • Removed userDataReader.createContext and stage._readUserData calls from source-related methods (collection, collectionGroup, database, documents).
  • packages/firestore/src/lite-api/pipeline.ts
    • Added an import for ParseContext.
    • Removed imports for UserDataReader, UserDataSource, and AbstractUserDataWriter.
    • Made the Pipeline class implement the UserData interface.
    • Modified the Pipeline class constructor to no longer accept userDataReader and _userDataWriter.
    • Added a _readUserData method to the Pipeline class.
    • Removed userDataReader.createContext and stage._readUserData calls from numerous pipeline stage methods.
    • Adjusted _addStage and newPipeline to use simplified parameter lists.
  • packages/firestore/src/lite-api/pipeline_impl.ts
    • Added an import for cast.
    • Removed the newUserDataReader import.
    • Modified the execute function to cast pipeline._db to Firestore, call pipeline._readUserData(context), and instantiate LiteUserDataWriter locally.
    • Updated PipelineResult to use the locally created userDataWriter.
    • Simplified the Firestore.prototype.pipeline factory method by removing userDataWriter and userDataReader instantiation.
Activity
  • No human activity has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request refactors the Firestore pipeline implementation by removing UserDataReader and UserDataWriter parameters from Pipeline and PipelineSource constructors and related methods. The Pipeline class now implements a UserData interface and includes a new _readUserData method, centralizing the data reading logic by iterating through its stages. Consequently, explicit userDataReader context creation and _readUserData calls are removed from individual pipeline stage creation methods. The execute functions in pipeline_impl.ts now handle userDataWriter instantiation and pipeline._readUserData calls locally, streamlining the pipeline's internal data handling.

Note: Security Review is unavailable for this PR.

@dlarocque dlarocque force-pushed the dl/pipeline-refactor branch from 7e91482 to 5907bba Compare March 11, 2026 20:17
@dlarocque dlarocque marked this pull request as ready for review March 11, 2026 20:18
@dlarocque dlarocque requested review from a team as code owners March 11, 2026 20:18
@dlarocque dlarocque force-pushed the dl/pipeline-refactor branch from 5907bba to 04328c7 Compare March 12, 2026 19:33
@dlarocque
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively refactors the Firestore pipeline to defer user data validation from stage initialization to the execute() call. The changes are consistently applied throughout the codebase, including necessary updates to public API definitions and test files. The implementation is clean and aligns with the described goals. I've identified a minor issue in the updated test cases where they don't fail if the execute() call unexpectedly succeeds, and I've provided suggestions to correct this.

const udr = new UserDataReader(
firestore._databaseId,
/* ignoreUndefinedProperties */ true
const userDataReader = newUserDataReader(firestore);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

note that this is now inheriting the ignoreUndefinedProperties config from the firestore instance, instead of setting it to true.

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.

good catch

@dlarocque dlarocque force-pushed the dl/pipeline-refactor branch from 04328c7 to b654f35 Compare March 12, 2026 20:34
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. Other than allowing subcollection, this feels like a nice cleanup

const udr = new UserDataReader(
firestore._databaseId,
/* ignoreUndefinedProperties */ true
const userDataReader = newUserDataReader(firestore);
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.

good catch

Copy link
Copy Markdown
Contributor

@DellaBitta DellaBitta left a comment

Choose a reason for hiding this comment

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

Nit: Ensure the changeset matches the release notes that you want on devsite.

@dlarocque dlarocque merged commit 54ff05e into main Mar 16, 2026
44 checks passed
@dlarocque dlarocque deleted the dl/pipeline-refactor branch March 16, 2026 15:41
@google-oss-bot google-oss-bot mentioned this pull request Mar 17, 2026
dlarocque added a commit that referenced this pull request Mar 25, 2026
Adds a step to `internalPipelineToExecutePipelineRequestProto` to read
user data before serialization.

This function was broken in
#9715, and caused the
`console support` tests to fail, which use this method to verify pipelines get serialized as expected.
These test failures weren't caught since
they weren't run in CI, and were skipped when ran locally with
`test:lite` and `test:browser`. I verified these tests now pass using
`test:node:prod`.
dlarocque added a commit that referenced this pull request Mar 25, 2026
Adds a step to `internalPipelineToExecutePipelineRequestProto` to read
user data before serialization.

This function was broken in
#9715, and caused the
`console support` tests to fail, which use this method to verify pipelines get serialized as expected.
These test failures weren't caught since
they weren't run in CI, and were skipped when ran locally with
`test:lite` and `test:browser`. I verified these tests now pass using
`test:node:prod`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants