Skip to content

Commit 9b81e09

Browse files
committed
feat(firestore): Add ifNull and coalesce
1 parent c26967c commit 9b81e09

5 files changed

Lines changed: 499 additions & 15 deletions

File tree

handwritten/firestore/api-report/firestore.api.md

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,12 @@ function charLength(fieldName: string): FunctionExpression;
498498
// @beta
499499
function charLength(stringExpression: Expression): FunctionExpression;
500500

501+
// @beta
502+
function coalesce(first: Expression, second: Expression | unknown, ...others: Array<Expression | unknown>): Expression;
503+
504+
// @beta
505+
function coalesce(firstFieldName: string, second: Expression | unknown, ...others: Array<Expression | unknown>): Expression;
506+
501507
// Warning: (tsdoc-undefined-tag) The TSDoc tag "@class" is not defined in this configuration
502508
//
503509
// @public
@@ -1099,6 +1105,7 @@ abstract class Expression implements firestore.Pipelines.Expression, HasUserData
10991105
byteLength(): FunctionExpression;
11001106
ceil(): FunctionExpression;
11011107
charLength(): FunctionExpression;
1108+
coalesce(second: Expression | unknown, ...others: Array<Expression | unknown>): Expression;
11021109
collectionId(): FunctionExpression;
11031110
concat(second: Expression | unknown, ...others: Array<Expression | unknown>): FunctionExpression;
11041111
cosineDistance(vectorExpression: Expression): FunctionExpression;
@@ -1135,9 +1142,11 @@ abstract class Expression implements firestore.Pipelines.Expression, HasUserData
11351142
ifAbsent(elseExpression: unknown): Expression;
11361143
ifError(catchExpr: Expression): FunctionExpression;
11371144
ifError(catchValue: unknown): FunctionExpression;
1145+
ifNull(elseValue: unknown): Expression;
1146+
ifNull(elseExpression: unknown): Expression;
11381147
isAbsent(): BooleanExpression;
11391148
isError(): BooleanExpression;
1140-
isType(type: Type): BooleanExpression;
1149+
isType(type: string): BooleanExpression;
11411150
join(delimiterExpression: Expression): Expression;
11421151
join(delimiter: string): Expression;
11431152
last(): AggregateFunction;
@@ -1414,6 +1423,8 @@ class Firestore implements firestore.Firestore {
14141423
// Warning: (tsdoc-param-tag-with-invalid-name) The @param block should be followed by a valid parameter name: The identifier cannot non-word characters
14151424
// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}'
14161425
constructor(settings?: firestore.Settings);
1426+
// @internal
1427+
get alwaysUseImplicitOrderBy(): boolean;
14171428
// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag
14181429
// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@"
14191430
batch(): WriteBatch;
@@ -1636,6 +1647,18 @@ function ifError(tryExpr: Expression, catchExpr: Expression): FunctionExpression
16361647
// @beta
16371648
function ifError(tryExpr: Expression, catchValue: unknown): FunctionExpression;
16381649

1650+
// @beta
1651+
function ifNull(ifExpr: Expression, elseExpr: Expression): Expression;
1652+
1653+
// @beta
1654+
function ifNull(ifExpr: Expression, elseValue: unknown): Expression;
1655+
1656+
// @beta
1657+
function ifNull(ifFieldName: string, elseExpr: Expression): Expression;
1658+
1659+
// @beta
1660+
function ifNull(ifFieldName: string, elseValue: unknown): Expression;
1661+
16391662
// @beta
16401663
function isAbsent(value: Expression): BooleanExpression;
16411664

@@ -1646,10 +1669,10 @@ function isAbsent(field: string): BooleanExpression;
16461669
function isError(value: Expression): BooleanExpression;
16471670

16481671
// @beta
1649-
function isType(fieldName: string, type: Type): BooleanExpression;
1672+
function isType(fieldName: string, type: string): BooleanExpression;
16501673

16511674
// @beta
1652-
function isType(expression: Expression, type: Type): BooleanExpression;
1675+
function isType(expression: Expression, type: string): BooleanExpression;
16531676

16541677
// @beta
16551678
function join(arrayFieldName: string, delimiter: string): Expression;
@@ -1884,7 +1907,7 @@ class Ordering implements HasUserData {
18841907
// @beta
18851908
class Pipeline implements firestore.Pipelines.Pipeline {
18861909
// Warning: (ae-forgotten-export) The symbol "Stage" needs to be exported by the entry point index.d.ts
1887-
constructor(db: Firestore, stages: Stage[]);
1910+
constructor(db: Firestore | undefined, stages: Stage[]);
18881911
addFields(field: firestore.Pipelines.Selectable, ...additionalFields: firestore.Pipelines.Selectable[]): Pipeline;
18891912
// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag
18901913
addFields(options: firestore.Pipelines.AddFieldsStageOptions): Pipeline;
@@ -1933,8 +1956,8 @@ class Pipeline implements firestore.Pipelines.Pipeline {
19331956
union(options: firestore.Pipelines.UnionStageOptions): Pipeline;
19341957
unnest(selectable: firestore.Pipelines.Selectable, indexField?: string): Pipeline;
19351958
unnest(options: firestore.Pipelines.UnnestStageOptions): Pipeline;
1936-
// Warning: (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
1937-
_validateUserData<T extends Map<string, HasUserData> | HasUserData[] | HasUserData>(_: string, val: T): T;
1959+
// (undocumented)
1960+
_validateUserData(ignoreUndefinedProperties: boolean): void;
19381961
where(condition: firestore.Pipelines.BooleanExpression): Pipeline;
19391962
where(options: firestore.Pipelines.WhereStageOptions): Pipeline;
19401963
}
@@ -2114,7 +2137,6 @@ declare namespace Pipelines {
21142137
arrayConcat,
21152138
type,
21162139
isType,
2117-
Type,
21182140
timestampTruncate,
21192141
split,
21202142
ltrim,
@@ -2124,7 +2146,9 @@ declare namespace Pipelines {
21242146
stringReplaceAll,
21252147
stringReplaceOne,
21262148
nor,
2127-
switchOn
2149+
switchOn,
2150+
ifNull,
2151+
coalesce
21282152
}
21292153
}
21302154
export { Pipelines }
@@ -2354,7 +2378,7 @@ export class Query<AppModelType = firestore.DocumentData, DbModelType extends fi
23542378
// Warning: (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
23552379
//
23562380
// @internal
2357-
toProto(transactionOrReadTime?: Uint8Array | Timestamp | api.ITransactionOptions, explainOptions?: firestore.ExplainOptions): api.IRunQueryRequest;
2381+
toProto(transactionOrReadTime?: Uint8Array | Timestamp | api.ITransactionOptions, explainOptions?: firestore.ExplainOptions, forceImplicitOrderBy?: boolean): api.IRunQueryRequest;
23582382
// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}'
23592383
// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}'
23602384
// Warning: (tsdoc-param-tag-with-invalid-type) The @param block should not include a JSDoc-style '{type}'
@@ -2876,9 +2900,6 @@ function trunc(fieldName: string, decimalPlaces: number | Expression): FunctionE
28762900
// @beta
28772901
function trunc(expression: Expression, decimalPlaces: number | Expression): FunctionExpression;
28782902

2879-
// @beta
2880-
type Type = 'null' | 'array' | 'boolean' | 'bytes' | 'timestamp' | 'geo_point' | 'number' | 'int32' | 'int64' | 'float64' | 'decimal128' | 'map' | 'reference' | 'string' | 'vector' | 'max_key' | 'min_key' | 'object_id' | 'regex' | 'request_timestamp';
2881-
28822903
// Warning: (tsdoc-escape-right-brace) The "}" character should be escaped using a backslash to avoid confusion with a TSDoc inline tag
28832904
// Warning: (tsdoc-malformed-inline-tag) Expecting a TSDoc tag starting with "{@"
28842905
//
@@ -3118,9 +3139,9 @@ function xor(first: BooleanExpression, second: BooleanExpression, ...additionalC
31183139
// build/types/src/index.d.ts:371:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
31193140
// build/types/src/index.d.ts:378:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
31203141
// build/types/src/index.d.ts:387:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
3121-
// build/types/src/index.d.ts:881:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
3122-
// build/types/src/index.d.ts:900:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
3123-
// build/types/src/index.d.ts:915:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
3142+
// build/types/src/index.d.ts:886:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
3143+
// build/types/src/index.d.ts:905:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
3144+
// build/types/src/index.d.ts:920:8 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
31243145
// build/types/src/path.d.ts:29:4 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration
31253146
// build/types/src/path.d.ts:31:4 - (tsdoc-undefined-tag) The TSDoc tag "@class" is not defined in this configuration
31263147
// build/types/src/path.d.ts:146:4 - (tsdoc-undefined-tag) The TSDoc tag "@private" is not defined in this configuration

handwritten/firestore/dev/src/pipelines/expression.ts

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,6 +2876,74 @@ export abstract class Expression
28762876
]);
28772877
}
28782878

2879+
/**
2880+
* @beta
2881+
* Creates an expression that returns the `elseValue` argument if this expression results in a null value, else
2882+
* return the result of this expression evaluation.
2883+
*
2884+
* @example
2885+
* ```typescript
2886+
* // Returns the value of the 'optional_field', or returns 'default_value'
2887+
* // if the field is null.
2888+
* field("optional_field").ifNull("default_value")
2889+
* ```
2890+
*
2891+
* @param elseValue The value that will be returned if this Expression evaluates to a null value.
2892+
* @returns A new [Expression] representing the ifNull operation.
2893+
*/
2894+
ifNull(elseValue: unknown): Expression;
2895+
2896+
/**
2897+
* @beta
2898+
* Creates an expression that returns the `elseValue` argument if this expression results in a null value, else
2899+
* return the result of this expression evaluation.
2900+
*
2901+
* @example
2902+
* ```typescript
2903+
* // Returns the value of the 'optional_field', or if that is
2904+
* // null, then returns the value of the field `
2905+
* field("optional_field").ifNull(field('default_field'))
2906+
* ```
2907+
*
2908+
* @param elseExpression The Expression that will be evaluated if this Expression evaluates to a null value.
2909+
* @returns A new [Expression] representing the ifNull operation.
2910+
*/
2911+
ifNull(elseExpression: unknown): Expression;
2912+
ifNull(elseValueOrExpression: Expression | unknown): Expression {
2913+
return new FunctionExpression('if_null', [
2914+
this,
2915+
valueToDefaultExpr(elseValueOrExpression),
2916+
]);
2917+
}
2918+
2919+
/**
2920+
* @beta
2921+
* Creates an expression that Returns the first non-null, non-absent argument, without evaluating
2922+
* the rest of the arguments. When all arguments are null or absent, returns the last argument.
2923+
*
2924+
* @example
2925+
* ```typescript
2926+
* // Return "Anonymous" if the 'name' field is null.
2927+
* field("name").coalesce("Anonymous");
2928+
* // Return "val1" if "val1" is not null, otherwise "val2", or "default" if both are null.
2929+
* field("val1").coalesce(field("val2"), "default");
2930+
* ```
2931+
*
2932+
* @param replacement - The value to use if this expression evaluates to null.
2933+
* @param others - Optional additional values to check if previous values are null.
2934+
* @returns A new [Expression] representing the coalesce operation.
2935+
*/
2936+
coalesce(
2937+
second: Expression | unknown,
2938+
...others: Array<Expression | unknown>
2939+
): Expression {
2940+
const values = [second, ...others];
2941+
return new FunctionExpression('coalesce', [
2942+
this,
2943+
...values.map(valueToDefaultExpr),
2944+
]);
2945+
}
2946+
28792947
/**
28802948
* @beta
28812949
* Creates an expression that joins the elements of an array into a string.
@@ -9188,6 +9256,139 @@ export function ifAbsent(
91889256
);
91899257
}
91909258

9259+
/**
9260+
* @beta
9261+
* Creates an expression that returns the `elseExpr` argument if `ifExpr` is null, else
9262+
* return the result of the `ifExpr` argument evaluation.
9263+
*
9264+
* @example
9265+
* ```typescript
9266+
* // Returns the value of the 'optional_field', or returns value of the 'default_field'
9267+
* // if the 'optional_field' value is null.
9268+
* ifNull(field("optional_field"), field("default_field"))
9269+
* ```
9270+
*
9271+
* @param ifExpr The expression to check for null.
9272+
* @param elseExpr The value that will be returned if `ifExpr` evaluates to a null value.
9273+
* @returns A new `Expression` representing the ifNull operation.
9274+
*/
9275+
export function ifNull(ifExpr: Expression, elseExpr: Expression): Expression;
9276+
9277+
/**
9278+
* @beta
9279+
* Creates an expression that returns the `elseValue` argument if `ifExpr` is null, else
9280+
* return the result of the `ifExpr` argument evaluation.
9281+
*
9282+
* @example
9283+
* ```typescript
9284+
* // Returns the value of the 'optional_field', or returns 'default_value'
9285+
* // if the 'optional_field' value is null.
9286+
* ifNull(field("optional_field"), "default_value")
9287+
* ```
9288+
*
9289+
* @param ifExpr The expression to check for null.
9290+
* @param elseValue The value that will be returned if `ifExpr` evaluates to a null value.
9291+
* @returns A new `Expression` representing the ifNull operation.
9292+
*/
9293+
export function ifNull(ifExpr: Expression, elseValue: unknown): Expression;
9294+
9295+
/**
9296+
* @beta
9297+
* Creates an expression that returns the `elseExpr` argument if `ifFieldName` is null, else
9298+
* return the value of the field.
9299+
*
9300+
* @example
9301+
* ```typescript
9302+
* // Returns the value of the 'optional_field', or returns the value of
9303+
* // 'default_field' if 'optional_field' is null.
9304+
* ifNull("optional_field", field("default_field"))
9305+
* ```
9306+
*
9307+
* @param ifFieldName The field to check for null.
9308+
* @param elseExpr The expression that will be evaluated and returned if `ifFieldName` is
9309+
* null.
9310+
* @returns A new `Expression` representing the ifNull operation.
9311+
*/
9312+
export function ifNull(ifFieldName: string, elseExpr: Expression): Expression;
9313+
9314+
/**
9315+
* @beta
9316+
* Creates an expression that returns the `elseValue` argument if `ifFieldName` is null, else
9317+
* return the value of the field.
9318+
*
9319+
* @example
9320+
* ```typescript
9321+
* // Returns the value of the 'optional_field', or returns 'default_value'
9322+
* // if the field is null.
9323+
* ifNull("optional_field", "default_value")
9324+
* ```
9325+
*
9326+
* @param ifFieldName The field to check for null.
9327+
* @param elseValue The value that will be returned if `ifFieldName` is null.
9328+
* @returns A new `Expression` representing the ifNull operation.
9329+
*/
9330+
export function ifNull(ifFieldName: string, elseValue: unknown): Expression;
9331+
export function ifNull(
9332+
fieldNameOrExpression: string | Expression,
9333+
elseValue: Expression | unknown,
9334+
): Expression {
9335+
return fieldOrExpression(fieldNameOrExpression).ifNull(
9336+
valueToDefaultExpr(elseValue),
9337+
);
9338+
}
9339+
9340+
/**
9341+
* @beta
9342+
* Returns the first non-null value among the given expressions/values.
9343+
*
9344+
* @example
9345+
* ```typescript
9346+
* // Returns the value of 'optional_field', or if that is
9347+
* // null, then returns the value of 'default_field'
9348+
* coalesce("optional_field", field("default_field"))
9349+
* ```
9350+
*
9351+
* @param first The first expression to evaluate.
9352+
* @param second The next expression or literal to evaluate.
9353+
* @param others Additional expressions or literals to evaluate.
9354+
* @returns A new `Expression` representing the coalesce operation.
9355+
*/
9356+
export function coalesce(
9357+
first: Expression,
9358+
second: Expression | unknown,
9359+
...others: Array<Expression | unknown>
9360+
): Expression;
9361+
9362+
/**
9363+
* @beta
9364+
* Returns the first non-null value among the given expressions/values.
9365+
*
9366+
* @example
9367+
* ```typescript
9368+
* // Returns the value of 'optional_field', or if that is
9369+
* // null, then returns the value of 'default_field'
9370+
* coalesce("optional_field", field("default_field"))
9371+
* ```
9372+
*
9373+
* @param firstFieldName The first field name to evaluate.
9374+
* @param second The next expression or literal to evaluate.
9375+
* @param others Additional expressions or literals to evaluate.
9376+
* @returns A new `Expression` representing the coalesce operation.
9377+
*/
9378+
export function coalesce(
9379+
firstFieldName: string,
9380+
second: Expression | unknown,
9381+
...others: Array<Expression | unknown>
9382+
): Expression;
9383+
9384+
export function coalesce(
9385+
first: Expression | string,
9386+
second: Expression | unknown,
9387+
...others: Array<Expression | unknown>
9388+
): Expression {
9389+
return fieldOrExpression(first).coalesce(second, ...others);
9390+
}
9391+
91919392
/**
91929393
* @beta
91939394
* Creates an expression that joins the elements of an array into a string.

handwritten/firestore/dev/src/pipelines/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,7 @@ export {
156156
stringReplaceOne,
157157
nor,
158158
switchOn,
159+
ifNull,
160+
coalesce,
159161
// TODO(new-expression): Add new expression exports above this line
160162
} from './expression';

0 commit comments

Comments
 (0)