-
Notifications
You must be signed in to change notification settings - Fork 700
feat(firestore): add DML stages, literals source, and atomic execution option to Node SDK pipelines #9118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(firestore): add DML stages, literals source, and atomic execution option to Node SDK pipelines #9118
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,6 +108,12 @@ import { | |
| UpdateStage, | ||
| Search, | ||
| InternalSearchStageOptions, | ||
| InsertStage, | ||
| InternalInsertStageOptions, | ||
| UpsertStage, | ||
| InternalUpsertStageOptions, | ||
| LiteralsSource, | ||
| InternalLiteralsStageOptions, | ||
| } from './stage'; | ||
| import {StructuredPipeline} from './structured-pipeline'; | ||
| import Selectable = FirebaseFirestore.Pipelines.Selectable; | ||
|
|
@@ -125,6 +131,46 @@ import { | |
| export class PipelineSource implements firestore.Pipelines.PipelineSource { | ||
| constructor(private db: Firestore) {} | ||
|
|
||
| /** | ||
| * Set the pipeline's source to the in-memory documents specified by the given records. | ||
| * | ||
| * @param documents An array of objects/records specifying the in-memory documents. | ||
| * @param options Options defining how this LiteralsSource stage is evaluated. | ||
| */ | ||
| literals( | ||
| documents: Array<Record<string, unknown>>, | ||
| options?: firestore.Pipelines.LiteralsStageOptions, | ||
| ): Pipeline; | ||
| literals( | ||
| options: firestore.Pipelines.LiteralsStageOptions, | ||
| ): Pipeline; | ||
| literals( | ||
| docsOrOptions: | ||
| | Array<Record<string, unknown>> | ||
| | firestore.Pipelines.LiteralsStageOptions, | ||
| options?: firestore.Pipelines.LiteralsStageOptions, | ||
| ): Pipeline { | ||
| let documents: Array<Record<string, unknown>> = []; | ||
| let opts: InternalLiteralsStageOptions = {}; | ||
|
|
||
| if (Array.isArray(docsOrOptions)) { | ||
| documents = docsOrOptions; | ||
| opts = options ?? {}; | ||
| } else if ( | ||
| docsOrOptions && | ||
| Array.isArray( | ||
| (docsOrOptions as firestore.Pipelines.LiteralsStageOptions).documents, | ||
| ) | ||
| ) { | ||
| const {documents: docs, ...rest} = | ||
| docsOrOptions as firestore.Pipelines.LiteralsStageOptions; | ||
| documents = docs ?? []; | ||
| opts = rest; | ||
| } | ||
|
Comment on lines
+156
to
+169
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If if (Array.isArray(docsOrOptions)) {
documents = docsOrOptions;
opts = options ?? {};
} else if (docsOrOptions) {
const {documents: docs, ...rest} =
docsOrOptions as firestore.Pipelines.LiteralsStageOptions;
documents = docs ?? [];
opts = rest;
} |
||
|
|
||
| return new Pipeline(this.db, [new LiteralsSource(documents, opts)]); | ||
| } | ||
|
|
||
| /** | ||
| * Returns all documents from the entire collection. The collection can be nested. | ||
| * @param collection - Name or reference to the collection that will be used as the Pipeline source. | ||
|
|
@@ -1757,8 +1803,123 @@ export class Pipeline implements firestore.Pipelines.Pipeline { | |
| * @return A new {@code Pipeline} object with this stage appended to the stage list. | ||
| */ | ||
| update(transformedFields: AliasedExpression[]): Pipeline; | ||
| update(transformedFields?: AliasedExpression[]): Pipeline { | ||
| return this._addStage(new UpdateStage(transformedFields)); | ||
| /** | ||
| * @beta | ||
| * Performs an update operation using documents from previous stages. | ||
| * | ||
| * @param fieldsMap - Map of field transformations to apply. | ||
| * @return A new {@code Pipeline} object with this stage appended to the stage list. | ||
| */ | ||
| update( | ||
| fieldsMap: Record<string, Expression> | Map<string, Expression>, | ||
| ): Pipeline; | ||
| update( | ||
| transformedFieldsOrMap?: | ||
| | AliasedExpression[] | ||
| | Map<string, Expression> | ||
| | Record<string, Expression>, | ||
| ): Pipeline { | ||
| if ( | ||
| transformedFieldsOrMap instanceof Map || | ||
| Array.isArray(transformedFieldsOrMap) | ||
| ) { | ||
| return this._addStage(new UpdateStage(transformedFieldsOrMap)); | ||
| } else if (transformedFieldsOrMap && isPlainObject(transformedFieldsOrMap)) { | ||
| const map = new Map<string, Expression>( | ||
| Object.entries(transformedFieldsOrMap as Record<string, Expression>), | ||
| ); | ||
| return this._addStage(new UpdateStage(map)); | ||
| } | ||
| return this._addStage(new UpdateStage()); | ||
| } | ||
|
|
||
| /** | ||
| * @beta | ||
| * Performs an insert operation on documents from previous stages. | ||
| * | ||
| * @param options - Options defining how this Insert stage is evaluated. | ||
| * @return A new {@code Pipeline} object with this stage appended to the stage list. | ||
| */ | ||
| insert(options?: firestore.Pipelines.InsertStageOptions): Pipeline { | ||
| return this._addStage( | ||
| new InsertStage((options ?? {}) as InternalInsertStageOptions), | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * @beta | ||
| * Performs an upsert operation on documents from previous stages. | ||
| * | ||
| * @param transforms - Transformations to apply on upsert. | ||
| * @param options - Options defining how this Upsert stage is evaluated. | ||
| * @return A new {@code Pipeline} object with this stage appended to the stage list. | ||
| */ | ||
| upsert( | ||
| transforms?: AliasedExpression[], | ||
| options?: Omit<firestore.Pipelines.UpsertStageOptions, 'transforms'>, | ||
| ): Pipeline; | ||
| upsert(options?: firestore.Pipelines.UpsertStageOptions): Pipeline; | ||
| upsert( | ||
| transformsOrOptions?: | ||
| | AliasedExpression[] | ||
| | firestore.Pipelines.UpsertStageOptions, | ||
| options?: Omit<firestore.Pipelines.UpsertStageOptions, 'transforms'>, | ||
| ): Pipeline { | ||
| let transforms: AliasedExpression[] = []; | ||
| let opts: InternalUpsertStageOptions = {}; | ||
|
|
||
| if (Array.isArray(transformsOrOptions)) { | ||
| transforms = transformsOrOptions; | ||
| opts = (options ?? {}) as InternalUpsertStageOptions; | ||
| } else if (transformsOrOptions) { | ||
| const {transforms: t, ...rest} = | ||
| transformsOrOptions as firestore.Pipelines.UpsertStageOptions; | ||
| transforms = (t ?? []) as AliasedExpression[]; | ||
| opts = rest as InternalUpsertStageOptions; | ||
| } | ||
|
|
||
| return this._addStage(new UpsertStage(transforms, opts)); | ||
| } | ||
|
|
||
| /** | ||
| * Appends a literals stage to the pipeline. | ||
| * | ||
| * @param documents An array of objects/records specifying in-memory documents. | ||
| * @param options Options defining how this LiteralsSource stage is evaluated. | ||
| * @return A new {@code Pipeline} object with this stage appended to the stage list. | ||
| */ | ||
| literals( | ||
| documents: Array<Record<string, unknown>>, | ||
| options?: firestore.Pipelines.LiteralsStageOptions, | ||
| ): Pipeline; | ||
| literals( | ||
| options: firestore.Pipelines.LiteralsStageOptions, | ||
| ): Pipeline; | ||
| literals( | ||
| docsOrOptions: | ||
| | Array<Record<string, unknown>> | ||
| | firestore.Pipelines.LiteralsStageOptions, | ||
| options?: firestore.Pipelines.LiteralsStageOptions, | ||
| ): Pipeline { | ||
| let documents: Array<Record<string, unknown>> = []; | ||
| let opts: InternalLiteralsStageOptions = {}; | ||
|
|
||
| if (Array.isArray(docsOrOptions)) { | ||
| documents = docsOrOptions; | ||
| opts = options ?? {}; | ||
| } else if ( | ||
| docsOrOptions && | ||
| Array.isArray( | ||
| (docsOrOptions as firestore.Pipelines.LiteralsStageOptions).documents, | ||
| ) | ||
| ) { | ||
| const {documents: docs, ...rest} = | ||
| docsOrOptions as firestore.Pipelines.LiteralsStageOptions; | ||
| documents = docs ?? []; | ||
| opts = rest; | ||
| } | ||
|
Comment on lines
+1907
to
+1920
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If if (Array.isArray(docsOrOptions)) {
documents = docsOrOptions;
opts = options ?? {};
} else if (docsOrOptions) {
const {documents: docs, ...rest} =
docsOrOptions as firestore.Pipelines.LiteralsStageOptions;
documents = docs ?? [];
opts = rest;
} |
||
|
|
||
| return this._addStage(new LiteralsSource(documents, opts)); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When executing a pipeline within an active transaction runner, the transaction ID (
Uint8Array) must take precedence over theatomicoption. Ifatomicis checked first, any pipeline withatomic: truewill ignore the active transaction and execute as a separate, independent transaction, violating transaction isolation and atomicity. Reordering the checks ensures the active transaction is always respected.