Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions handwritten/firestore/dev/src/pipelines/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from './pipeline-util';
import {HasUserData, Serializer, validateUserInput} from '../serializer';
import {cast} from '../util';
import {Pipeline} from './pipelines';

/**
* @beta
Expand Down Expand Up @@ -3071,6 +3072,23 @@ export abstract class Expression
]).asBoolean();
}

/**
* @beta
* Creates an expression that returns the value of a field from the document that results from the evaluation of this expression.
*
* @example
* ```typescript
* // Get the value of the "city" field in the "address" document.
* field("address").getField("city")
* ```
*
* @param key The field to access in the document.
* @returns A new `Expression` representing the value of the field in the document.
*/
getField(key: string | Expression): Expression {
return new FunctionExpression('field', [this, valueToDefaultExpr(key)]);
}

// TODO(new-expression): Add new expression method definitions above this line

/**
Expand Down Expand Up @@ -10237,6 +10255,190 @@ export function isType(
return fieldOrExpression(fieldNameOrExpression).isType(type);
}

/**
* @beta
* Creates an expression that returns the value of a field from a document that results from the evaluation of the expression.
*
* @example
* ```typescript
* // Get the value of the "city" field in the "address" document.
* getField(field("address"), "city")
* ```
*
* @param expression The expression representing the document.
* @param key The field to access in the document.
* @returns A new `Expression` representing the value of the field in the document.
*/
export function getField(expression: Expression, key: string): Expression;
/**
* @beta
* Creates an expression that returns the value of a field from a document that results from the evaluation of the expression.
*
* @example
* ```typescript
* // Get the value of the key resulting from the "addressField" variable in the "address" document.
* getField(field("address"), variable("addressField"))
* ```
*
* @param expression The expression representing the document.
* @param keyExpr The expression representing the key to access in the document.
* @returns A new `Expression` representing the value of the field in the document.
*/
export function getField(
expression: Expression,
keyExpr: Expression,
): Expression;
/**
* @beta
* Creates an expression that returns the value of a field from the document with the given field name.
*
* @example
* ```typescript
* // Get the value of the "city" field in the "address" document.
* getField("address", "city")
* ```
*
* @param fieldName The name of the field containing the map/document.
* @param key The key to access.
* @returns A new `Expression` representing the value of the field in the document.
*/
export function getField(fieldName: string, key: string): Expression;
/**
* @beta
* Creates an expression that returns the value of a field from the document with the given field name.
*
* @example
* ```typescript
* // Get the value of the "city" field in the "address" document.
* getField("address", variable("addressField"))
* ```
*
* @param fieldName The name of the field containing the map/document.
* @param keyExpr The key expression to access.
* @returns A new `Expression` representing the value of the field in the document.
*/
export function getField(fieldName: string, keyExpr: Expression): Expression;
export function getField(
fieldOrExpr: string | Expression,
keyOrExpr: string | Expression,
): Expression {
return fieldOrExpression(fieldOrExpr).getField(keyOrExpr);
}

/**
* @internal
* Expression representing a variable reference. This evaluates to the value of a variable
* defined in a pipeline.
*/
export class VariableExpression extends Expression {
expressionType: firestore.Pipelines.ExpressionType = 'Variable';

/**
* @hideconstructor
*/
constructor(private readonly name: string) {
super();
}

/**
* @internal
*/
_toProto(_serializer: Serializer): api.IValue {
return {
variableReferenceValue: this.name,
};
}

/**
* @internal
*/
_validateUserData(_ignoreUndefinedProperties: boolean): void {}
}

/**
* @beta
* Creates an expression that retrieves the value of a variable bound via `define()`.
*
* @example
* ```typescript
* db.pipeline().collection("products")
* .define(
* field("price").multiply(0.9).as("discountedPrice"),
* field("stock").add(10).as("newStock")
* )
* .where(variable("discountedPrice").lessThan(100))
* .select(field("name"), variable("newStock"));
* ```
*
* @param name - The name of the variable to retrieve.
* @returns An `Expression` representing the variable's value.
*/
export function variable(name: string): Expression {
return new VariableExpression(name);
}

/**
* @beta
* Creates an expression that represents the current document being processed.
*
* @example
* ```typescript
* // Define the current document as a variable "doc"
* firestore.pipeline().collection("books")
* .define(currentDocument().as("doc"))
* // Access a field from the defined document variable
* .select(variable("doc").mapGet("title"));
* ```
*
* @returns An `Expression` representing the current document.
*/
export function currentDocument(): Expression {
return new FunctionExpression('current_document', []);
}

/**
* @internal
*/
class PipelineValueExpression extends Expression {
expressionType: firestore.Pipelines.ExpressionType = 'PipelineValue';

/**
* @hideconstructor
*/
constructor(private readonly pipeline: firestore.Pipelines.Pipeline) {
super();
}

/**
* @internal
*/
_toProto(serializer: Serializer): api.IValue {
return {
// Casting to bypass type checking becuase _validateUserData does not exist in the public types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pipelineValue: (this.pipeline as Pipeline)._toProto(serializer),
};
}

/**
* @internal
*/
_validateUserData(_ignoreUndefinedProperties: boolean): void {
// Casting to bypass type checking becuase _validateUserData does not exist in the public types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.pipeline as any)._validateUserData('PipelineValueExpression');
}
}

/**
* @internal
*/
export function pipelineValue(
pipeline: firestore.Pipelines.Pipeline,
): Expression {
return new PipelineValueExpression(pipeline);
}

// TODO(new-expression): Add new top-level expression function definitions above this line

/**
Expand Down
4 changes: 4 additions & 0 deletions handwritten/firestore/dev/src/pipelines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export {
PipelineResult,
PipelineSnapshot,
PipelineSource,
subcollection,
} from './pipelines';

export {
Expand Down Expand Up @@ -156,5 +157,8 @@ export {
stringReplaceOne,
nor,
switchOn,
getField,
variable,
currentDocument,
// TODO(new-expression): Add new expression exports above this line
} from './expression';
12 changes: 11 additions & 1 deletion handwritten/firestore/dev/src/pipelines/pipeline-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import {
lessThan,
Field,
AggregateFunction,
pipelineValue,
AliasedExpression,
} from './expression';
import {Pipeline, PipelineResult, ExplainStats} from './pipelines';
import {StructuredPipeline} from './structured-pipeline';
Expand Down Expand Up @@ -603,6 +605,12 @@ export function isBooleanExpr(
return val instanceof BooleanExpression;
}

export function isAliasedExpr(
val: unknown,
): val is firestore.Pipelines.AliasedExpression {
return val instanceof AliasedExpression;
}

export function isField(val: unknown): val is firestore.Pipelines.Field {
return val instanceof Field;
}
Expand Down Expand Up @@ -630,6 +638,9 @@ export function valueToDefaultExpr(value: unknown): Expression {
if (isFirestoreValue(value)) {
return constant(value);
}
if (isPipeline(value)) {
return pipelineValue(value);
}
if (value instanceof Expression) {
return value;
} else if (isPlainObject(value)) {
Expand All @@ -640,7 +651,6 @@ export function valueToDefaultExpr(value: unknown): Expression {
result = constant(value);
}

// TODO(pipeline) is this still used?
result._createdFromLiteral = true;
return result;
}
Expand Down
Loading
Loading