diff --git a/common/api-review/firestore-lite.api.md b/common/api-review/firestore-lite.api.md index 603e2349505..d3edaf71247 100644 --- a/common/api-review/firestore-lite.api.md +++ b/common/api-review/firestore-lite.api.md @@ -9,14 +9,54 @@ import { FirebaseApp } from '@firebase/app'; import { FirebaseError } from '@firebase/util'; import { LogLevelString as LogLevel } from '@firebase/logger'; +// @beta +export interface Accumulator { + // (undocumented) + accumulator: true; +} + +// @beta +export type AccumulatorTarget = ExprWithAlias; + +// @beta (undocumented) +export class Add extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function add(left: Constant, right: Constant): Add; + +// @beta +export function add(left: Constant, right: any): Add; + +// @beta +export function add(left: string, right: Constant): Add; + +// @beta +export function add(left: string, right: any): Add; + // @public export function addDoc(reference: CollectionReference, data: WithFieldValue): Promise>; +// @beta (undocumented) +export class AddFields implements Stage { + constructor(fields: Map); + // (undocumented) + name: string; +} + // @public export type AddPrefixToKeys> = { [K in keyof T & string as `${Prefix}.${K}`]+?: string extends K ? any : T[K]; }; +// @beta (undocumented) +export class Aggregate implements Stage { + constructor(accumulators: Map, groups: Map); + // (undocumented) + name: string; +} + // @public export class AggregateField { readonly aggregateType: AggregateType; @@ -53,18 +93,138 @@ export type AggregateSpecData = { // @public export type AggregateType = 'count' | 'avg' | 'sum'; +// @beta (undocumented) +export class And extends FirestoreFunction implements FilterCondition { + constructor(conditions: FilterExpr[]); + // (undocumented) + filterable: true; +} + // @public export function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; +// @beta (undocumented) +export class ArrayConcat extends FirestoreFunction { + constructor(array: Constant, elements: Constant[]); + } + +// @beta +export function arrayConcat(array: Constant, elements: Constant[]): ArrayConcat; + +// @beta +export function arrayConcat(array: Constant, elements: any[]): ArrayConcat; + +// @beta +export function arrayConcat(array: string, elements: Constant[]): ArrayConcat; + +// @beta +export function arrayConcat(array: string, elements: any[]): ArrayConcat; + +// @beta (undocumented) +export class ArrayContains extends FirestoreFunction implements FilterCondition { + constructor(array: Constant, element: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function arrayContains(array: Constant, element: Constant): ArrayContains; + +// @beta +export function arrayContains(array: Constant, element: any): ArrayContains; + +// @beta +export function arrayContains(array: string, element: Constant): ArrayContains; + +// @beta +export function arrayContains(array: string, element: any): ArrayContains; + +// @beta (undocumented) +export class ArrayContainsAll extends FirestoreFunction implements FilterCondition { + constructor(array: Constant, values: Constant[]); + // (undocumented) + filterable: true; + } + +// @beta +export function arrayContainsAll(array: Constant, values: Constant[]): ArrayContainsAll; + +// @beta +export function arrayContainsAll(array: Constant, values: any[]): ArrayContainsAll; + +// @beta +export function arrayContainsAll(array: string, values: Constant[]): ArrayContainsAll; + +// @beta +export function arrayContainsAll(array: string, values: any[]): ArrayContainsAll; + +// @beta (undocumented) +export class ArrayContainsAny extends FirestoreFunction implements FilterCondition { + constructor(array: Constant, values: Constant[]); + // (undocumented) + filterable: true; + } + +// @beta +export function arrayContainsAny(array: Constant, values: Constant[]): ArrayContainsAny; + +// @beta +export function arrayContainsAny(array: Constant, values: any[]): ArrayContainsAny; + +// @beta +export function arrayContainsAny(array: string, values: Constant[]): ArrayContainsAny; + +// @beta +export function arrayContainsAny(array: string, values: any[]): ArrayContainsAny; + +// @beta (undocumented) +export class ArrayElement extends FirestoreFunction { + constructor(); +} + +// @beta (undocumented) +export class ArrayLength extends FirestoreFunction { + constructor(array: Constant); + } + +// @beta +export function arrayLength(array: Constant): ArrayLength; + // @public export function arrayRemove(...elements: unknown[]): FieldValue; +// @beta (undocumented) +export class ArrayReverse extends FirestoreFunction { + constructor(array: Constant); + } + // @public export function arrayUnion(...elements: unknown[]): FieldValue; +// @beta +export function ascending(expr: Constant): Ordering; + // @public export function average(field: string | FieldPath): AggregateField; +// @beta (undocumented) +export class Avg extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + +// @beta (undocumented) +export class ByteLength extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function byteLength(expr: Constant): ByteLength; + +// @beta +export function byteLength(field: string): ByteLength; + // @public export class Bytes { static fromBase64String(base64: string): Bytes; @@ -75,6 +235,17 @@ export class Bytes { toUint8Array(): Uint8Array; } +// @beta (undocumented) +export class CharLength extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function charLength(field: string): CharLength; + +// @beta +export function charLength(expr: Constant): CharLength; + // @public export type ChildUpdateFields = V extends Record ? AddPrefixToKeys> : never; @@ -90,6 +261,13 @@ export function collection(refer // @public export function collectionGroup(firestore: Firestore, collectionId: string): Query; +// @beta (undocumented) +export class CollectionGroupSource implements Stage { + constructor(collectionId: string); + // (undocumented) + name: string; +} + // @public export class CollectionReference extends Query { get id(): string; @@ -100,20 +278,208 @@ export class CollectionReference; } +// @beta (undocumented) +export class CollectionSource implements Stage { + constructor(collectionPath: string); + // (undocumented) + name: string; +} + // @public export function connectFirestoreEmulator(firestore: Firestore, host: string, port: number, options?: { mockUserToken?: EmulatorMockTokenOptions | string; }): void; +// @beta +export class Constant { + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + static of(value: number): Constant; + static of(value: string): Constant; + static of(value: boolean): Constant; + static of(value: null): Constant; + static of(value: undefined): Constant; + static of(value: GeoPoint): Constant; + static of(value: Timestamp): Constant; + static of(value: Date): Constant; + static of(value: Uint8Array): Constant; + static of(value: DocumentReference): Constant; + static of(value: any[]): Constant; + static of(value: Map): Constant; + static of(value: VectorValue): Constant; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + static vector(value: number[] | VectorValue): Constant; + vectorLength(): VectorLength; +} + +// @beta (undocumented) +export class CosineDistance extends FirestoreFunction { + constructor(vector1: Constant, vector2: Constant); + } + +// @beta +export function cosineDistance(expr: string, other: number[]): CosineDistance; + +// @beta +export function cosineDistance(expr: string, other: VectorValue): CosineDistance; + +// @beta +export function cosineDistance(expr: string, other: Constant): CosineDistance; + +// @beta +export function cosineDistance(expr: Constant, other: number[]): CosineDistance; + +// @beta +export function cosineDistance(expr: Constant, other: VectorValue): CosineDistance; + +// @beta +export function cosineDistance(expr: Constant, other: Constant): CosineDistance; + +// @beta (undocumented) +export class Count extends FirestoreFunction implements Accumulator { + constructor(value: Constant | undefined, distinct: boolean); + // (undocumented) + accumulator: true; + } + // @public export function count(): AggregateField; +// @beta +export function countAll(): Count; + +// @beta (undocumented) +export class DatabaseSource implements Stage { + // (undocumented) + name: string; +} + // @public export function deleteDoc(reference: DocumentReference): Promise; // @public export function deleteField(): FieldValue; +// @beta +export function descending(expr: Constant): Ordering; + +// @beta (undocumented) +export class Distinct implements Stage { + constructor(groups: Map); + // (undocumented) + name: string; +} + +// @beta (undocumented) +export class Divide extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function divide(left: Constant, right: Constant): Divide; + +// @beta +export function divide(left: Constant, right: any): Divide; + +// @beta +export function divide(left: string, right: Constant): Divide; + +// @beta +export function divide(left: string, right: any): Divide; + // @public export function doc(firestore: Firestore, path: string, ...pathSegments: string[]): DocumentReference; @@ -153,6 +519,38 @@ export class DocumentSnapshot; } +// @beta (undocumented) +export class DocumentsSource implements Stage { + constructor(docPaths: string[]); + // (undocumented) + name: string; + // (undocumented) + static of(refs: DocumentReference[]): DocumentsSource; +} + +// @beta (undocumented) +export class DotProduct extends FirestoreFunction { + constructor(vector1: Constant, vector2: Constant); + } + +// @beta +export function dotProduct(expr: string, other: number[]): DotProduct; + +// @beta +export function dotProduct(expr: string, other: VectorValue): DotProduct; + +// @beta +export function dotProduct(expr: string, other: Constant): DotProduct; + +// @beta +export function dotProduct(expr: Constant, other: number[]): DotProduct; + +// @beta +export function dotProduct(expr: Constant, other: VectorValue): DotProduct; + +// @beta +export function dotProduct(expr: Constant, other: Constant): DotProduct; + export { EmulatorMockTokenOptions } // @public @@ -167,20 +565,458 @@ export function endBefore(snapsh // @public export function endBefore(...fieldValues: unknown[]): QueryEndAtConstraint; +// @beta (undocumented) +export class EndsWith extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, suffix: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function endsWith(expr: string, suffix: string): EndsWith; + +// @beta +export function endsWith(expr: string, suffix: Constant): EndsWith; + +// @beta +export function endsWith(expr: Constant, suffix: string): EndsWith; + +// @beta +export function endsWith(expr: Constant, suffix: Constant): EndsWith; + +// @beta (undocumented) +export class Eq extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function eq(left: Constant, right: Constant): Eq; + +// @beta +export function eq(left: Constant, right: any): Eq; + +// @beta +export function eq(left: string, right: Constant): Eq; + +// @beta +export function eq(left: string, right: any): Eq; + +// @beta (undocumented) +export class EuclideanDistance extends FirestoreFunction { + constructor(vector1: Constant, vector2: Constant); + } + +// @beta +export function euclideanDistance(expr: string, other: number[]): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: string, other: VectorValue): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: string, other: Constant): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: Constant, other: number[]): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: Constant, other: VectorValue): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: Constant, other: Constant): EuclideanDistance; + +// @beta +export function execute(pipeline: Pipeline): Promise>>; + +// @beta (undocumented) +export class Exists extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function exists(value: Constant): Exists; + +// @beta +export function exists(field: string): Exists; + +// @beta +export type ExprType = 'Field' | 'Constant' | 'Function' | 'ListOfExprs' | 'ExprWithAlias'; + +// @beta (undocumented) +export class ExprWithAlias implements Selectable { + constructor(expr: T, alias: string); + add(other: Constant): Add; + add(other: any): Add; + // (undocumented) + alias: string; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + expr: T; + // (undocumented) + exprType: ExprType; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + // (undocumented) + selectable: true; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + +// @beta +export class Field implements Selectable { + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + // (undocumented) + fieldName(): string; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + static of(name: string): Field; + // (undocumented) + static of(path: FieldPath): Field; + // (undocumented) + static of(pipeline: Pipeline, name: string): Field; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + // (undocumented) + selectable: true; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + // @public export class FieldPath { constructor(...fieldNames: string[]); isEqual(other: FieldPath): boolean; } +// @beta (undocumented) +export class Fields implements Selectable { + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + // (undocumented) + fieldList(): Field[]; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + // (undocumented) + static of(name: string, ...others: string[]): Fields; + // (undocumented) + static ofAll(): Fields; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + // (undocumented) + selectable: true; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + // @public export abstract class FieldValue { abstract isEqual(other: FieldValue): boolean; } +// @beta +export interface FilterCondition { + // (undocumented) + filterable: true; +} + +// @beta +export type FilterExpr = Constant & FilterCondition; + +// @beta (undocumented) +export class FindNearest implements Stage { + // (undocumented) + name: string; +} + +// @beta (undocumented) +export interface FindNearestOptions { + // (undocumented) + distanceField?: string; + // (undocumented) + distanceMeasure: 'euclidean' | 'cosine' | 'dot_product'; + // (undocumented) + field: Field; + // (undocumented) + limit?: number; + // (undocumented) + vectorValue: VectorValue | number[]; +} + // @public export class Firestore { get app(): FirebaseApp; + // Warning: (ae-incompatible-release-tags) The symbol "pipeline" is marked as @public, but its signature references "PipelineSource" which is marked as @beta + pipeline(): PipelineSource; toJSON(): object; type: 'firestore-lite' | 'firestore'; } @@ -199,69 +1035,488 @@ export class FirestoreError extends FirebaseError { readonly stack?: string; } -// @public -export type FirestoreErrorCode = 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated'; +// @public +export type FirestoreErrorCode = 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated'; + +// @beta +export class FirestoreFunction { + constructor(name: string, params: Constant[]); + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + +// @beta +export function genericFunction(name: string, params: Constant[]): FirestoreFunction; + +// @beta (undocumented) +export class GenericStage implements Stage { + constructor(name: string, params: unknown[]); + // (undocumented) + name: string; +} + +// @public +export class GeoPoint { + constructor(latitude: number, longitude: number); + isEqual(other: GeoPoint): boolean; + get latitude(): number; + get longitude(): number; + toJSON(): { + latitude: number; + longitude: number; + }; +} + +// @public +export function getAggregate(query: Query, aggregateSpec: AggregateSpecType): Promise>; + +// @public +export function getCount(query: Query): Promise; +}, AppModelType, DbModelType>>; + +// @public +export function getDoc(reference: DocumentReference): Promise>; + +// @public +export function getDocs(query: Query): Promise>; + +// @public +export function getFirestore(): Firestore; + +// @public +export function getFirestore(app: FirebaseApp): Firestore; + +// @beta +export function getFirestore(databaseId: string): Firestore; + +// @beta +export function getFirestore(app: FirebaseApp, databaseId: string): Firestore; + +// @beta (undocumented) +export class Gt extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function gt(left: Constant, right: Constant): Gt; + +// @beta +export function gt(left: Constant, right: any): Gt; + +// @beta +export function gt(left: string, right: Constant): Gt; + +// @beta +export function gt(left: string, right: any): Gt; + +// @beta (undocumented) +export class Gte extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function gte(left: Constant, right: Constant): Gte; + +// @beta +export function gte(left: Constant, right: any): Gte; + +// @beta +export function gte(left: string, right: Constant): Gte; + +// @beta +export function gte(left: string, right: any): Gte; + +// @beta (undocumented) +export class If extends FirestoreFunction implements FilterCondition { + constructor(condition: FilterExpr, thenExpr: Constant, elseExpr: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function ifFunction(condition: FilterExpr, thenExpr: Constant, elseExpr: Constant): If; + +// @beta (undocumented) +export class In extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, others: Constant[]); + // (undocumented) + filterable: true; + } + +// @beta +export function inAny(element: Constant, others: Constant[]): In; + +// @beta +export function inAny(element: Constant, others: any[]): In; + +// @beta +export function inAny(element: string, others: Constant[]): In; + +// @beta +export function inAny(element: string, others: any[]): In; + +// @public +export function increment(n: number): FieldValue; + +// @public +export function initializeFirestore(app: FirebaseApp, settings: Settings): Firestore; + +// @beta +export function initializeFirestore(app: FirebaseApp, settings: Settings, databaseId?: string): Firestore; + +// @beta (undocumented) +export class IsNan extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function isNan(value: Constant): IsNan; + +// @beta +export function isNan(value: string): IsNan; + +// @beta (undocumented) +export class Like extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, pattern: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function like(left: string, pattern: string): Like; + +// @beta +export function like(left: string, pattern: Constant): Like; + +// @beta +export function like(left: Constant, pattern: string): Like; + +// @beta +export function like(left: Constant, pattern: Constant): Like; + +// @beta (undocumented) +export class Limit implements Stage { + constructor(limit: number); + // (undocumented) + name: string; +} + +// @public +export function limit(limit: number): QueryLimitConstraint; + +// @public +export function limitToLast(limit: number): QueryLimitConstraint; + +// @beta (undocumented) +export class LogicalMax extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function logicalMax(left: Constant, right: Constant): LogicalMax; + +// @beta +export function logicalMax(left: Constant, right: any): LogicalMax; + +// @beta +export function logicalMax(left: string, right: Constant): LogicalMax; + +// @beta +export function logicalMax(left: string, right: any): LogicalMax; + +// @beta (undocumented) +export class LogicalMin extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function logicalMin(left: Constant, right: Constant): LogicalMin; + +// @beta +export function logicalMin(left: Constant, right: any): LogicalMin; + +// @beta +export function logicalMin(left: string, right: Constant): LogicalMin; + +// @beta +export function logicalMin(left: string, right: any): LogicalMin; + +export { LogLevel } + +// @beta (undocumented) +export class Lt extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function lt(left: Constant, right: Constant): Lt; + +// @beta +export function lt(left: Constant, right: any): Lt; + +// @beta +export function lt(left: string, right: Constant): Lt; + +// @beta +export function lt(left: string, right: any): Lt; + +// @beta (undocumented) +export class Lte extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function lte(left: Constant, right: Constant): Lte; + +// @beta +export function lte(left: Constant, right: any): Lte; + +// Warning: (ae-incompatible-release-tags) The symbol "lte" is marked as @public, but its signature references "Constant" which is marked as @beta +// Warning: (ae-incompatible-release-tags) The symbol "lte" is marked as @public, but its signature references "Lte" which is marked as @beta +// +// @public +export function lte(left: string, right: Constant): Lte; + +// @beta +export function lte(left: string, right: any): Lte; + +// @beta (undocumented) +export class MapGet extends FirestoreFunction { + constructor(map: Constant, name: string); +} + +// @beta +export function mapGet(mapField: string, subField: string): MapGet; + +// @beta +export function mapGet(mapExpr: Constant, subField: string): MapGet; + +// @beta (undocumented) +export class Max extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + +// @beta +export function max(value: Constant): Max; + +// @beta +export function max(value: string): Max; + +// @beta (undocumented) +export class Min extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + +// @beta +export function min(value: Constant): Min; + +// @beta +export function min(value: string): Min; -// @public -export class GeoPoint { - constructor(latitude: number, longitude: number); - isEqual(other: GeoPoint): boolean; - get latitude(): number; - get longitude(): number; - toJSON(): { - latitude: number; - longitude: number; - }; -} +// @beta (undocumented) +export class Mod extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } -// @public -export function getAggregate(query: Query, aggregateSpec: AggregateSpecType): Promise>; +// @beta +export function mod(left: Constant, right: Constant): Mod; -// @public -export function getCount(query: Query): Promise; -}, AppModelType, DbModelType>>; +// @beta +export function mod(left: Constant, right: any): Mod; -// @public -export function getDoc(reference: DocumentReference): Promise>; +// @beta +export function mod(left: string, right: Constant): Mod; -// @public -export function getDocs(query: Query): Promise>; +// @beta +export function mod(left: string, right: any): Mod; -// @public -export function getFirestore(): Firestore; +// @beta (undocumented) +export class Multiply extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } -// @public -export function getFirestore(app: FirebaseApp): Firestore; +// @beta +export function multiply(left: Constant, right: Constant): Multiply; // @beta -export function getFirestore(databaseId: string): Firestore; +export function multiply(left: Constant, right: any): Multiply; // @beta -export function getFirestore(app: FirebaseApp, databaseId: string): Firestore; +export function multiply(left: string, right: Constant): Multiply; -// @public -export function increment(n: number): FieldValue; +// @beta +export function multiply(left: string, right: any): Multiply; -// @public -export function initializeFirestore(app: FirebaseApp, settings: Settings): Firestore; +// @beta (undocumented) +export class Neq extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } // @beta -export function initializeFirestore(app: FirebaseApp, settings: Settings, databaseId?: string): Firestore; +export function neq(left: Constant, right: Constant): Neq; -// @public -export function limit(limit: number): QueryLimitConstraint; +// @beta +export function neq(left: Constant, right: any): Neq; -// @public -export function limitToLast(limit: number): QueryLimitConstraint; +// @beta +export function neq(left: string, right: Constant): Neq; -export { LogLevel } +// @beta +export function neq(left: string, right: any): Neq; // @public export type NestedUpdateFields> = UnionToIntersection<{ [K in keyof T & string]: ChildUpdateFields; }[keyof T & string]>; +// @beta (undocumented) +export class Not extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function not(filter: FilterExpr): Not; + +// @beta +export function notInAny(element: Constant, others: Constant[]): Not; + +// @beta +export function notInAny(element: Constant, others: any[]): Not; + +// @beta +export function notInAny(element: string, others: Constant[]): Not; + +// @beta +export function notInAny(element: string, others: any[]): Not; + +// @beta (undocumented) +export class Offset implements Stage { + constructor(offset: number); + // (undocumented) + name: string; + } + +// @beta (undocumented) +export class Or extends FirestoreFunction implements FilterCondition { + constructor(conditions: FilterExpr[]); + // (undocumented) + filterable: true; +} + // @public export function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; @@ -271,11 +1526,109 @@ export function orderBy(fieldPath: string | FieldPath, directionStr?: OrderByDir // @public export type OrderByDirection = 'desc' | 'asc'; +// @beta +export class Ordering { + constructor(expr: Constant, direction: 'ascending' | 'descending'); + } + // @public export type PartialWithFieldValue = Partial | (T extends Primitive ? T : T extends {} ? { [K in keyof T]?: PartialWithFieldValue | FieldValue; } : never); +// @public +export class Pipeline { + /* Excluded from this release type: _db */ + // Warning: (ae-incompatible-release-tags) The symbol "addFields" is marked as @public, but its signature references "Selectable" which is marked as @beta + addFields(...fields: Selectable[]): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "aggregate" is marked as @public, but its signature references "AccumulatorTarget" which is marked as @beta + aggregate(...accumulators: AccumulatorTarget[]): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + aggregate(options: { + accumulators: AccumulatorTarget[]; + groups?: Array; + }): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "distinct" is marked as @public, but its signature references "Selectable" which is marked as @beta + distinct(...groups: Array): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "execute" is marked as @public, but its signature references "PipelineResult" which is marked as @beta + execute(): Promise>>; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "findNearest" is marked as @public, but its signature references "FindNearestOptions" which is marked as @beta + // + // (undocumented) + findNearest(options: FindNearestOptions): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + genericStage(name: string, params: any[]): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + limit(limit: number): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + offset(offset: number): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "select" is marked as @public, but its signature references "Selectable" which is marked as @beta + select(...selections: Array): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "sort" is marked as @public, but its signature references "Ordering" which is marked as @beta + sort(...orderings: Ordering[]): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // (undocumented) + sort(options: { + orderings: Ordering[]; + }): Pipeline; + /* Excluded from this release type: userDataWriter */ + /* Excluded from this release type: documentReferenceFactory */ + // Warning: (ae-incompatible-release-tags) The symbol "where" is marked as @public, but its signature references "FilterCondition" which is marked as @beta + // Warning: (ae-incompatible-release-tags) The symbol "where" is marked as @public, but its signature references "Constant" which is marked as @beta + where(condition: FilterCondition & Constant): Pipeline; +} + +// Warning: (ae-incompatible-release-tags) The symbol "pipeline" is marked as @public, but its signature references "PipelineSource" which is marked as @beta +// +// @public +export function pipeline(firestore: Firestore): PipelineSource; + +// @public +export function pipeline(query: Query): Pipeline; + +// @beta +export class PipelineResult { + /* Excluded from this release type: _ref */ + /* Excluded from this release type: _fields */ + /* Excluded from this release type: __constructor */ + get createTime(): Timestamp | undefined; + data(): AppModelType | undefined; + get executionTime(): Timestamp; + get(fieldPath: string | FieldPath): any; + get id(): string | undefined; + get ref(): DocumentReference | undefined; + get updateTime(): Timestamp | undefined; +} + +// @beta +export class PipelineSource { + // (undocumented) + collection(collectionPath: string): Pipeline; + // (undocumented) + collectionGroup(collectionId: string): Pipeline; + // (undocumented) + database(): Pipeline; + // (undocumented) + documents(docs: DocumentReference[]): Pipeline; + } + // @public export type Primitive = string | number | boolean | undefined | null; @@ -284,6 +1637,7 @@ export class Query | null; readonly firestore: Firestore; + pipeline(): Pipeline; readonly type: 'query' | 'collection'; withConverter(converter: null): Query; withConverter(converter: FirestoreDataConverter): Query; @@ -360,9 +1714,102 @@ export class QueryStartAtConstraint extends QueryConstraint { // @public export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; +// @beta (undocumented) +export class RegexContains extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, pattern: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function regexContains(left: string, pattern: string): RegexContains; + +// @beta +export function regexContains(left: string, pattern: Constant): RegexContains; + +// @beta +export function regexContains(left: Constant, pattern: string): RegexContains; + +// @beta +export function regexContains(left: Constant, pattern: Constant): RegexContains; + +// @beta (undocumented) +export class RegexMatch extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, pattern: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function regexMatch(left: string, pattern: string): RegexMatch; + +// @beta +export function regexMatch(left: string, pattern: Constant): RegexMatch; + +// @beta +export function regexMatch(left: Constant, pattern: string): RegexMatch; + +// @beta +export function regexMatch(left: Constant, pattern: Constant): RegexMatch; + +// @beta (undocumented) +export class ReplaceAll extends FirestoreFunction { + constructor(value: Constant, find: Constant, replace: Constant); + } + +// @beta +export function replaceAll(value: Constant, find: string, replace: string): ReplaceAll; + +// @beta +export function replaceAll(value: Constant, find: Constant, replace: Constant): ReplaceAll; + +// @beta +export function replaceAll(field: string, find: string, replace: string): ReplaceAll; + +// @beta (undocumented) +export class ReplaceFirst extends FirestoreFunction { + constructor(value: Constant, find: Constant, replace: Constant); + } + +// @beta +export function replaceFirst(value: Constant, find: string, replace: string): ReplaceFirst; + +// @beta +export function replaceFirst(value: Constant, find: Constant, replace: Constant): ReplaceFirst; + +// @beta +export function replaceFirst(field: string, find: string, replace: string): ReplaceFirst; + +// @beta (undocumented) +export class Reverse extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function reverse(expr: Constant): Reverse; + +// @beta +export function reverse(field: string): Reverse; + // @public export function runTransaction(firestore: Firestore, updateFunction: (transaction: Transaction) => Promise, options?: TransactionOptions): Promise; +// @beta (undocumented) +export class Select implements Stage { + constructor(projections: Map); + // (undocumented) + name: string; + } + +// @beta +export interface Selectable { + // (undocumented) + selectable: true; +} + +// @beta +export type SelectableExpr = Constant & Selectable; + // @public export function serverTimestamp(): FieldValue; @@ -392,6 +1839,19 @@ export interface Settings { // @public export function snapshotEqual(left: DocumentSnapshot | QuerySnapshot, right: DocumentSnapshot | QuerySnapshot): boolean; +// @beta (undocumented) +export class Sort implements Stage { + constructor(orders: Ordering[]); + // (undocumented) + name: string; + } + +// @beta (undocumented) +export interface Stage { + // (undocumented) + name: string; +} + // @public export function startAfter(snapshot: DocumentSnapshot): QueryStartAtConstraint; @@ -404,6 +1864,79 @@ export function startAt(snapshot // @public export function startAt(...fieldValues: unknown[]): QueryStartAtConstraint; +// @beta (undocumented) +export class StartsWith extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, prefix: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function startsWith(expr: string, prefix: string): StartsWith; + +// @beta +export function startsWith(expr: string, prefix: Constant): StartsWith; + +// @beta +export function startsWith(expr: Constant, prefix: string): StartsWith; + +// @beta +export function startsWith(expr: Constant, prefix: Constant): StartsWith; + +// @beta (undocumented) +export class StrConcat extends FirestoreFunction { + constructor(first: Constant, rest: Constant[]); + } + +// @beta +export function strConcat(first: string, ...elements: Array): StrConcat; + +// @beta +export function strConcat(first: Constant, ...elements: Array): StrConcat; + +// @beta (undocumented) +export class StrContains extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, substring: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function strContains(left: string, substring: string): StrContains; + +// @beta +export function strContains(left: string, substring: Constant): StrContains; + +// @beta +export function strContains(left: Constant, substring: string): StrContains; + +// @beta +export function strContains(left: Constant, substring: Constant): StrContains; + +// @beta (undocumented) +export class Subtract extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function subtract(left: Constant, right: Constant): Subtract; + +// @beta +export function subtract(left: Constant, right: any): Subtract; + +// @beta +export function subtract(left: string, right: Constant): Subtract; + +// @beta +export function subtract(left: string, right: any): Subtract; + +// @beta (undocumented) +export class Sum extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + // @public export function sum(field: string | FieldPath): AggregateField; @@ -431,6 +1964,89 @@ export class Timestamp { valueOf(): string; } +// @beta (undocumented) +export class TimestampAdd extends FirestoreFunction { + constructor(timestamp: Constant, unit: Constant, amount: Constant); + } + +// @beta +export function timestampAdd(timestamp: Constant, unit: Constant, amount: Constant): TimestampAdd; + +// @beta +export function timestampAdd(timestamp: Constant, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + +// @beta +export function timestampAdd(field: string, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + +// @beta (undocumented) +export class TimestampSub extends FirestoreFunction { + constructor(timestamp: Constant, unit: Constant, amount: Constant); + } + +// @beta +export function timestampSub(timestamp: Constant, unit: Constant, amount: Constant): TimestampSub; + +// @beta +export function timestampSub(timestamp: Constant, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + +// @beta +export function timestampSub(field: string, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + +// @beta (undocumented) +export class TimestampToUnixMicros extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function timestampToUnixMicros(expr: Constant): TimestampToUnixMicros; + +// @beta +export function timestampToUnixMicros(field: string): TimestampToUnixMicros; + +// @beta (undocumented) +export class TimestampToUnixMillis extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function timestampToUnixMillis(expr: Constant): TimestampToUnixMillis; + +// @beta +export function timestampToUnixMillis(field: string): TimestampToUnixMillis; + +// @beta (undocumented) +export class TimestampToUnixSeconds extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function timestampToUnixSeconds(expr: Constant): TimestampToUnixSeconds; + +// @beta +export function timestampToUnixSeconds(field: string): TimestampToUnixSeconds; + +// @beta (undocumented) +export class ToLower extends FirestoreFunction { + constructor(expr: Constant); + } + +// @beta +export function toLower(expr: string): ToLower; + +// @beta +export function toLower(expr: Constant): ToLower; + +// @beta (undocumented) +export class ToUpper extends FirestoreFunction { + constructor(expr: Constant); + } + +// @beta +export function toUpper(expr: string): ToUpper; + +// @beta +export function toUpper(expr: Constant): ToUpper; + // @public export class Transaction { delete(documentRef: DocumentReference): this; @@ -446,9 +2062,53 @@ export interface TransactionOptions { readonly maxAttempts?: number; } +// @beta (undocumented) +export class Trim extends FirestoreFunction { + constructor(expr: Constant); + } + +// @beta +export function trim(expr: string): Trim; + +// @beta +export function trim(expr: Constant): Trim; + // @public export type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; +// @beta (undocumented) +export class UnixMicrosToTimestamp extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function unixMicrosToTimestamp(expr: Constant): UnixMicrosToTimestamp; + +// @beta +export function unixMicrosToTimestamp(field: string): UnixMicrosToTimestamp; + +// @beta (undocumented) +export class UnixMillisToTimestamp extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function unixMillisToTimestamp(expr: Constant): UnixMillisToTimestamp; + +// @beta +export function unixMillisToTimestamp(field: string): UnixMillisToTimestamp; + +// @beta (undocumented) +export class UnixSecondsToTimestamp extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function unixSecondsToTimestamp(expr: Constant): UnixSecondsToTimestamp; + +// @beta +export function unixSecondsToTimestamp(field: string): UnixSecondsToTimestamp; + // @public export type UpdateData = T extends Primitive ? T : T extends {} ? { [K in keyof T]?: UpdateData | FieldValue; @@ -460,9 +2120,23 @@ export function updateDoc(refere // @public export function updateDoc(reference: DocumentReference, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): Promise; +// @public +export function useFirestorePipelines(): void; + // @public export function vector(values?: number[]): VectorValue; +// @beta (undocumented) +export class VectorLength extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function vectorLength(expr: Constant): VectorLength; + +// @beta +export function vectorLength(field: string): VectorLength; + // @public export class VectorValue { /* Excluded from this release type: __constructor */ @@ -470,6 +2144,13 @@ export class VectorValue { toArray(): number[]; } +// @beta (undocumented) +export class Where implements Stage { + constructor(condition: FilterCondition & Constant); + // (undocumented) + name: string; +} + // @public export function where(fieldPath: string | FieldPath, opStr: WhereFilterOp, value: unknown): QueryFieldFilterConstraint; @@ -494,4 +2175,21 @@ export class WriteBatch { // @public export function writeBatch(firestore: Firestore): WriteBatch; +// @beta (undocumented) +export class Xor extends FirestoreFunction implements FilterCondition { + constructor(conditions: FilterExpr[]); + // (undocumented) + filterable: true; +} + +// @beta +export function xor(left: FilterExpr, ...right: FilterExpr[]): Xor; + + +// Warnings were encountered during analysis: +// +// /home/runner/work/firebase-js-sdk/firebase-js-sdk/packages/firestore/dist/lite/index.d.ts:9177:9 - (ae-incompatible-release-tags) The symbol "accumulators" is marked as @public, but its signature references "AccumulatorTarget" which is marked as @beta +// /home/runner/work/firebase-js-sdk/firebase-js-sdk/packages/firestore/dist/lite/index.d.ts:9178:9 - (ae-incompatible-release-tags) The symbol "groups" is marked as @public, but its signature references "Selectable" which is marked as @beta +// /home/runner/work/firebase-js-sdk/firebase-js-sdk/packages/firestore/dist/lite/index.d.ts:9207:9 - (ae-incompatible-release-tags) The symbol "orderings" is marked as @public, but its signature references "Ordering" which is marked as @beta + ``` diff --git a/common/api-review/firestore.api.md b/common/api-review/firestore.api.md index 34b56b97f21..212d93972b2 100644 --- a/common/api-review/firestore.api.md +++ b/common/api-review/firestore.api.md @@ -9,14 +9,54 @@ import { FirebaseApp } from '@firebase/app'; import { FirebaseError } from '@firebase/util'; import { LogLevelString as LogLevel } from '@firebase/logger'; +// @beta +export interface Accumulator { + // (undocumented) + accumulator: true; +} + +// @beta +export type AccumulatorTarget = ExprWithAlias; + +// @beta (undocumented) +export class Add extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function add(left: Constant, right: Constant): Add; + +// @beta +export function add(left: Constant, right: any): Add; + +// @beta +export function add(left: string, right: Constant): Add; + +// @beta +export function add(left: string, right: any): Add; + // @public export function addDoc(reference: CollectionReference, data: WithFieldValue): Promise>; +// @beta (undocumented) +export class AddFields implements Stage { + constructor(fields: Map); + // (undocumented) + name: string; +} + // @public export type AddPrefixToKeys> = { [K in keyof T & string as `${Prefix}.${K}`]+?: string extends K ? any : T[K]; }; +// @beta (undocumented) +export class Aggregate implements Stage { + constructor(accumulators: Map, groups: Map); + // (undocumented) + name: string; +} + // @public export class AggregateField { readonly aggregateType: AggregateType; @@ -53,18 +93,147 @@ export type AggregateSpecData = { // @public export type AggregateType = 'count' | 'avg' | 'sum'; +// @beta (undocumented) +export class And extends FirestoreFunction implements FilterCondition { + constructor(conditions: FilterExpr[]); + // (undocumented) + filterable: true; +} + // @public export function and(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; +// @beta +export function andFunction(left: FilterExpr, ...right: FilterExpr[]): And; + +// @beta (undocumented) +export class ArrayConcat extends FirestoreFunction { + constructor(array: Constant, elements: Constant[]); + } + +// @beta +export function arrayConcat(array: Constant, elements: Constant[]): ArrayConcat; + +// @beta +export function arrayConcat(array: Constant, elements: any[]): ArrayConcat; + +// @beta +export function arrayConcat(array: string, elements: Constant[]): ArrayConcat; + +// @beta +export function arrayConcat(array: string, elements: any[]): ArrayConcat; + +// @beta (undocumented) +export class ArrayContains extends FirestoreFunction implements FilterCondition { + constructor(array: Constant, element: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function arrayContains(array: Constant, element: Constant): ArrayContains; + +// @beta +export function arrayContains(array: Constant, element: any): ArrayContains; + +// @beta +export function arrayContains(array: string, element: Constant): ArrayContains; + +// @beta +export function arrayContains(array: string, element: any): ArrayContains; + +// @beta (undocumented) +export class ArrayContainsAll extends FirestoreFunction implements FilterCondition { + constructor(array: Constant, values: Constant[]); + // (undocumented) + filterable: true; + } + +// @beta +export function arrayContainsAll(array: Constant, values: Constant[]): ArrayContainsAll; + +// @beta +export function arrayContainsAll(array: Constant, values: any[]): ArrayContainsAll; + +// @beta +export function arrayContainsAll(array: string, values: Constant[]): ArrayContainsAll; + +// @beta +export function arrayContainsAll(array: string, values: any[]): ArrayContainsAll; + +// @beta (undocumented) +export class ArrayContainsAny extends FirestoreFunction implements FilterCondition { + constructor(array: Constant, values: Constant[]); + // (undocumented) + filterable: true; + } + +// @beta +export function arrayContainsAny(array: Constant, values: Constant[]): ArrayContainsAny; + +// @beta +export function arrayContainsAny(array: Constant, values: any[]): ArrayContainsAny; + +// @beta +export function arrayContainsAny(array: string, values: Constant[]): ArrayContainsAny; + +// @beta +export function arrayContainsAny(array: string, values: any[]): ArrayContainsAny; + +// @beta (undocumented) +export class ArrayElement extends FirestoreFunction { + constructor(); +} + +// @beta (undocumented) +export class ArrayLength extends FirestoreFunction { + constructor(array: Constant); + } + +// @beta +export function arrayLength(array: Constant): ArrayLength; + // @public export function arrayRemove(...elements: unknown[]): FieldValue; +// @beta (undocumented) +export class ArrayReverse extends FirestoreFunction { + constructor(array: Constant); + } + // @public export function arrayUnion(...elements: unknown[]): FieldValue; +// @beta +export function ascending(expr: Constant): Ordering; + // @public export function average(field: string | FieldPath): AggregateField; +// @beta (undocumented) +export class Avg extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + +// @beta +export function avgFunction(value: Constant): Avg; + +// @beta +export function avgFunction(value: string): Avg; + +// @beta (undocumented) +export class ByteLength extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function byteLength(expr: Constant): ByteLength; + +// @beta +export function byteLength(field: string): ByteLength; + // @public export class Bytes { static fromBase64String(base64: string): Bytes; @@ -78,6 +247,17 @@ export class Bytes { // @public export const CACHE_SIZE_UNLIMITED = -1; +// @beta (undocumented) +export class CharLength extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function charLength(field: string): CharLength; + +// @beta +export function charLength(expr: Constant): CharLength; + // @public export type ChildUpdateFields = V extends Record ? AddPrefixToKeys> : never; @@ -96,6 +276,13 @@ export function collection(refer // @public export function collectionGroup(firestore: Firestore, collectionId: string): Query; +// @beta (undocumented) +export class CollectionGroupSource implements Stage { + constructor(collectionId: string); + // (undocumented) + name: string; +} + // @public export class CollectionReference extends Query { get id(): string; @@ -106,14 +293,183 @@ export class CollectionReference; } +// @beta (undocumented) +export class CollectionSource implements Stage { + constructor(collectionPath: string); + // (undocumented) + name: string; +} + // @public export function connectFirestoreEmulator(firestore: Firestore, host: string, port: number, options?: { mockUserToken?: EmulatorMockTokenOptions | string; }): void; +// @beta +export class Constant { + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + static of(value: number): Constant; + static of(value: string): Constant; + static of(value: boolean): Constant; + static of(value: null): Constant; + static of(value: undefined): Constant; + static of(value: GeoPoint): Constant; + static of(value: Timestamp): Constant; + static of(value: Date): Constant; + static of(value: Uint8Array): Constant; + static of(value: DocumentReference): Constant; + static of(value: any[]): Constant; + static of(value: Map): Constant; + static of(value: VectorValue): Constant; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + static vector(value: number[] | VectorValue): Constant; + vectorLength(): VectorLength; +} + +// @beta (undocumented) +export class CosineDistance extends FirestoreFunction { + constructor(vector1: Constant, vector2: Constant); + } + +// @beta +export function cosineDistance(expr: string, other: number[]): CosineDistance; + +// @beta +export function cosineDistance(expr: string, other: VectorValue): CosineDistance; + +// @beta +export function cosineDistance(expr: string, other: Constant): CosineDistance; + +// @beta +export function cosineDistance(expr: Constant, other: number[]): CosineDistance; + +// @beta +export function cosineDistance(expr: Constant, other: VectorValue): CosineDistance; + +// @beta +export function cosineDistance(expr: Constant, other: Constant): CosineDistance; + +// @beta (undocumented) +export class Count extends FirestoreFunction implements Accumulator { + constructor(value: Constant | undefined, distinct: boolean); + // (undocumented) + accumulator: true; + } + // @public export function count(): AggregateField; +// @beta +export function countAll(): Count; + +// @beta +export function countFunction(value: Constant): Count; + +// Warning: (ae-incompatible-release-tags) The symbol "countFunction" is marked as @public, but its signature references "Count" which is marked as @beta +// +// @public +export function countFunction(value: string): Count; + +// @beta (undocumented) +export class DatabaseSource implements Stage { + // (undocumented) + name: string; +} + // @public export function deleteAllPersistentCacheIndexes(indexManager: PersistentCacheIndexManager): void; @@ -123,12 +479,39 @@ export function deleteDoc(refere // @public export function deleteField(): FieldValue; +// @beta +export function descending(expr: Constant): Ordering; + // @public export function disableNetwork(firestore: Firestore): Promise; // @public export function disablePersistentCacheIndexAutoCreation(indexManager: PersistentCacheIndexManager): void; +// @beta (undocumented) +export class Distinct implements Stage { + constructor(groups: Map); + // (undocumented) + name: string; +} + +// @beta (undocumented) +export class Divide extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function divide(left: Constant, right: Constant): Divide; + +// @beta +export function divide(left: Constant, right: any): Divide; + +// @beta +export function divide(left: string, right: Constant): Divide; + +// @beta +export function divide(left: string, right: any): Divide; + // @public export function doc(firestore: Firestore, path: string, ...pathSegments: string[]): DocumentReference; @@ -180,6 +563,38 @@ export class DocumentSnapshot; } +// @beta (undocumented) +export class DocumentsSource implements Stage { + constructor(docPaths: string[]); + // (undocumented) + name: string; + // (undocumented) + static of(refs: DocumentReference[]): DocumentsSource; +} + +// @beta (undocumented) +export class DotProduct extends FirestoreFunction { + constructor(vector1: Constant, vector2: Constant); + } + +// @beta +export function dotProduct(expr: string, other: number[]): DotProduct; + +// @beta +export function dotProduct(expr: string, other: VectorValue): DotProduct; + +// @beta +export function dotProduct(expr: string, other: Constant): DotProduct; + +// @beta +export function dotProduct(expr: Constant, other: number[]): DotProduct; + +// @beta +export function dotProduct(expr: Constant, other: VectorValue): DotProduct; + +// @beta +export function dotProduct(expr: Constant, other: Constant): DotProduct; + export { EmulatorMockTokenOptions } // @public @deprecated @@ -206,25 +621,463 @@ export function endBefore(snapsh // @public export function endBefore(...fieldValues: unknown[]): QueryEndAtConstraint; +// @beta (undocumented) +export class EndsWith extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, suffix: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function endsWith(expr: string, suffix: string): EndsWith; + +// @beta +export function endsWith(expr: string, suffix: Constant): EndsWith; + +// @beta +export function endsWith(expr: Constant, suffix: string): EndsWith; + +// @beta +export function endsWith(expr: Constant, suffix: Constant): EndsWith; + +// @beta (undocumented) +export class Eq extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function eq(left: Constant, right: Constant): Eq; + +// @beta +export function eq(left: Constant, right: any): Eq; + +// @beta +export function eq(left: string, right: Constant): Eq; + +// @beta +export function eq(left: string, right: any): Eq; + +// @beta (undocumented) +export class EuclideanDistance extends FirestoreFunction { + constructor(vector1: Constant, vector2: Constant); + } + +// @beta +export function euclideanDistance(expr: string, other: number[]): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: string, other: VectorValue): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: string, other: Constant): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: Constant, other: number[]): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: Constant, other: VectorValue): EuclideanDistance; + +// @beta +export function euclideanDistance(expr: Constant, other: Constant): EuclideanDistance; + +// @beta +export function execute(pipeline: Pipeline): Promise>>; + +// @beta (undocumented) +export class Exists extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function exists(value: Constant): Exists; + +// @beta +export function exists(field: string): Exists; + // @public export interface ExperimentalLongPollingOptions { timeoutSeconds?: number; } +// @beta +export type ExprType = 'Field' | 'Constant' | 'Function' | 'ListOfExprs' | 'ExprWithAlias'; + +// @beta (undocumented) +export class ExprWithAlias implements Selectable { + constructor(expr: T, alias: string); + add(other: Constant): Add; + add(other: any): Add; + // (undocumented) + alias: string; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + expr: T; + // (undocumented) + exprType: ExprType; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + // (undocumented) + selectable: true; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + +// @beta +export class Field implements Selectable { + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + // (undocumented) + fieldName(): string; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + static of(name: string): Field; + // (undocumented) + static of(path: FieldPath): Field; + // (undocumented) + static of(pipeline: Pipeline, name: string): Field; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + // (undocumented) + selectable: true; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + // @public export class FieldPath { constructor(...fieldNames: string[]); isEqual(other: FieldPath): boolean; } +// @beta (undocumented) +export class Fields implements Selectable { + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + // (undocumented) + fieldList(): Field[]; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + // (undocumented) + static of(name: string, ...others: string[]): Fields; + // (undocumented) + static ofAll(): Fields; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + // (undocumented) + selectable: true; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + // @public export abstract class FieldValue { abstract isEqual(other: FieldValue): boolean; } +// @beta +export interface FilterCondition { + // (undocumented) + filterable: true; +} + +// @beta +export type FilterExpr = Constant & FilterCondition; + +// @beta (undocumented) +export class FindNearest implements Stage { + // (undocumented) + name: string; +} + +// @beta (undocumented) +export interface FindNearestOptions { + // (undocumented) + distanceField?: string; + // (undocumented) + distanceMeasure: 'euclidean' | 'cosine' | 'dot_product'; + // (undocumented) + field: Field; + // (undocumented) + limit?: number; + // (undocumented) + vectorValue: VectorValue | number[]; +} + // @public export class Firestore { get app(): FirebaseApp; + // Warning: (ae-incompatible-release-tags) The symbol "pipeline" is marked as @public, but its signature references "PipelineSource" which is marked as @beta + pipeline(): PipelineSource; toJSON(): object; type: 'firestore-lite' | 'firestore'; } @@ -246,6 +1099,108 @@ export class FirestoreError extends FirebaseError { // @public export type FirestoreErrorCode = 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated'; +// @beta +export class FirestoreFunction { + constructor(name: string, params: Constant[]); + add(other: Constant): Add; + add(other: any): Add; + arrayConcat(arrays: Constant[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat; + arrayContains(element: Constant): ArrayContains; + arrayContains(element: any): ArrayContains; + arrayContainsAll(...values: Constant[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAny(...values: Constant[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayLength(): ArrayLength; + as(name: string): ExprWithAlias; + ascending(): Ordering; + avg(): Avg; + byteLength(): ByteLength; + charLength(): CharLength; + cosineDistance(other: Constant): CosineDistance; + cosineDistance(other: VectorValue): CosineDistance; + cosineDistance(other: number[]): CosineDistance; + count(): Count; + descending(): Ordering; + divide(other: Constant): Divide; + divide(other: any): Divide; + dotProduct(other: Constant): DotProduct; + dotProduct(other: VectorValue): DotProduct; + // (undocumented) + dotProduct(other: number[]): DotProduct; + endsWith(suffix: string): EndsWith; + endsWith(suffix: Constant): EndsWith; + eq(other: Constant): Eq; + eq(other: any): Eq; + euclideanDistance(other: Constant): EuclideanDistance; + euclideanDistance(other: VectorValue): EuclideanDistance; + // (undocumented) + euclideanDistance(other: number[]): EuclideanDistance; + exists(): Exists; + // (undocumented) + exprType: ExprType; + gt(other: Constant): Gt; + gt(other: any): Gt; + gte(other: Constant): Gte; + gte(other: any): Gte; + in(...others: Constant[]): In; + // (undocumented) + in(...others: any[]): In; + isNaN(): IsNan; + like(pattern: string): Like; + // (undocumented) + like(pattern: Constant): Like; + logicalMax(other: Constant): LogicalMax; + logicalMax(other: any): LogicalMax; + logicalMin(other: Constant): LogicalMin; + logicalMin(other: any): LogicalMin; + lt(other: Constant): Lt; + lt(other: any): Lt; + lte(other: Constant): Lte; + lte(other: any): Lte; + mapGet(subfield: string): MapGet; + max(): Max; + min(): Min; + mod(other: Constant): Mod; + mod(other: any): Mod; + multiply(other: Constant): Multiply; + multiply(other: any): Multiply; + neq(other: Constant): Neq; + neq(other: any): Neq; + regexContains(pattern: string): RegexContains; + regexContains(pattern: Constant): RegexContains; + regexMatch(pattern: string): RegexMatch; + regexMatch(pattern: Constant): RegexMatch; + replaceAll(find: string, replace: string): ReplaceAll; + replaceAll(find: Constant, replace: Constant): ReplaceAll; + replaceFirst(find: string, replace: string): ReplaceFirst; + replaceFirst(find: Constant, replace: Constant): ReplaceFirst; + reverse(): Reverse; + startsWith(prefix: string): StartsWith; + startsWith(prefix: Constant): StartsWith; + strConcat(...elements: Array): StrConcat; + strContains(substring: string): StrContains; + strContains(expr: Constant): StrContains; + subtract(other: Constant): Subtract; + subtract(other: any): Subtract; + sum(): Sum; + timestampAdd(unit: Constant, amount: Constant): TimestampAdd; + timestampAdd(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + timestampSub(unit: Constant, amount: Constant): TimestampSub; + timestampSub(unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + timestampToUnixMicros(): TimestampToUnixMicros; + timestampToUnixMillis(): TimestampToUnixMillis; + timestampToUnixSeconds(): TimestampToUnixSeconds; + toLower(): ToLower; + toUpper(): ToUpper; + trim(): Trim; + unixMicrosToTimestamp(): UnixMicrosToTimestamp; + unixMillisToTimestamp(): UnixMillisToTimestamp; + unixSecondsToTimestamp(): UnixSecondsToTimestamp; + vectorLength(): VectorLength; +} + // @public export type FirestoreLocalCache = MemoryLocalCache | PersistentLocalCache; @@ -261,6 +1216,16 @@ export interface FirestoreSettings { ssl?: boolean; } +// @beta +export function genericFunction(name: string, params: Constant[]): FirestoreFunction; + +// @beta (undocumented) +export class GenericStage implements Stage { + constructor(name: string, params: unknown[]); + // (undocumented) + name: string; +} + // @public export class GeoPoint { constructor(latitude: number, longitude: number); @@ -314,6 +1279,73 @@ export function getFirestore(app: FirebaseApp, databaseId: string): Firestore; // @public export function getPersistentCacheIndexManager(firestore: Firestore): PersistentCacheIndexManager | null; +// @beta (undocumented) +export class Gt extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function gt(left: Constant, right: Constant): Gt; + +// @beta +export function gt(left: Constant, right: any): Gt; + +// @beta +export function gt(left: string, right: Constant): Gt; + +// @beta +export function gt(left: string, right: any): Gt; + +// @beta (undocumented) +export class Gte extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function gte(left: Constant, right: Constant): Gte; + +// @beta +export function gte(left: Constant, right: any): Gte; + +// @beta +export function gte(left: string, right: Constant): Gte; + +// @beta +export function gte(left: string, right: any): Gte; + +// @beta (undocumented) +export class If extends FirestoreFunction implements FilterCondition { + constructor(condition: FilterExpr, thenExpr: Constant, elseExpr: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function ifFunction(condition: FilterExpr, thenExpr: Constant, elseExpr: Constant): If; + +// @beta (undocumented) +export class In extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, others: Constant[]); + // (undocumented) + filterable: true; + } + +// @beta +export function inAny(element: Constant, others: Constant[]): In; + +// @beta +export function inAny(element: Constant, others: any[]): In; + +// @beta +export function inAny(element: string, others: Constant[]): In; + +// @beta +export function inAny(element: string, others: any[]): In; + // @public export function increment(n: number): FieldValue; @@ -344,6 +1376,45 @@ export interface IndexField { // @public export function initializeFirestore(app: FirebaseApp, settings: FirestoreSettings, databaseId?: string): Firestore; +// @beta (undocumented) +export class IsNan extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function isNan(value: Constant): IsNan; + +// @beta +export function isNan(value: string): IsNan; + +// @beta (undocumented) +export class Like extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, pattern: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function like(left: string, pattern: string): Like; + +// @beta +export function like(left: string, pattern: Constant): Like; + +// @beta +export function like(left: Constant, pattern: string): Like; + +// @beta +export function like(left: Constant, pattern: Constant): Like; + +// @beta (undocumented) +export class Limit implements Stage { + constructor(limit: number); + // (undocumented) + name: string; +} + // @public export function limit(limit: number): QueryLimitConstraint; @@ -372,7 +1443,106 @@ export interface LoadBundleTaskProgress { totalDocuments: number; } -export { LogLevel } +// @beta (undocumented) +export class LogicalMax extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function logicalMax(left: Constant, right: Constant): LogicalMax; + +// @beta +export function logicalMax(left: Constant, right: any): LogicalMax; + +// @beta +export function logicalMax(left: string, right: Constant): LogicalMax; + +// @beta +export function logicalMax(left: string, right: any): LogicalMax; + +// @beta (undocumented) +export class LogicalMin extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function logicalMin(left: Constant, right: Constant): LogicalMin; + +// @beta +export function logicalMin(left: Constant, right: any): LogicalMin; + +// @beta +export function logicalMin(left: string, right: Constant): LogicalMin; + +// @beta +export function logicalMin(left: string, right: any): LogicalMin; + +export { LogLevel } + +// @beta (undocumented) +export class Lt extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function lt(left: Constant, right: Constant): Lt; + +// @beta +export function lt(left: Constant, right: any): Lt; + +// @beta +export function lt(left: string, right: Constant): Lt; + +// @beta +export function lt(left: string, right: any): Lt; + +// @beta (undocumented) +export class Lte extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function lte(left: Constant, right: Constant): Lte; + +// @beta +export function lte(left: Constant, right: any): Lte; + +// Warning: (ae-incompatible-release-tags) The symbol "lte" is marked as @public, but its signature references "Constant" which is marked as @beta +// Warning: (ae-incompatible-release-tags) The symbol "lte" is marked as @public, but its signature references "Lte" which is marked as @beta +// +// @public +export function lte(left: string, right: Constant): Lte; + +// @beta +export function lte(left: string, right: any): Lte; + +// @beta (undocumented) +export class MapGet extends FirestoreFunction { + constructor(map: Constant, name: string); +} + +// @beta +export function mapGet(mapField: string, subField: string): MapGet; + +// @beta +export function mapGet(mapExpr: Constant, subField: string): MapGet; + +// @beta (undocumented) +export class Max extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + +// @beta +export function max(value: Constant): Max; + +// @beta +export function max(value: string): Max; // @public export interface MemoryCacheSettings { @@ -411,14 +1581,109 @@ export function memoryLruGarbageCollector(settings?: { cacheSizeBytes?: number; }): MemoryLruGarbageCollector; +// @beta (undocumented) +export class Min extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + +// @beta +export function min(value: Constant): Min; + +// @beta +export function min(value: string): Min; + +// @beta (undocumented) +export class Mod extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function mod(left: Constant, right: Constant): Mod; + +// @beta +export function mod(left: Constant, right: any): Mod; + +// @beta +export function mod(left: string, right: Constant): Mod; + +// @beta +export function mod(left: string, right: any): Mod; + +// @beta (undocumented) +export class Multiply extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function multiply(left: Constant, right: Constant): Multiply; + +// @beta +export function multiply(left: Constant, right: any): Multiply; + +// @beta +export function multiply(left: string, right: Constant): Multiply; + +// @beta +export function multiply(left: string, right: any): Multiply; + // @public export function namedQuery(firestore: Firestore, name: string): Promise; +// @beta (undocumented) +export class Neq extends FirestoreFunction implements FilterCondition { + constructor(left: Constant, right: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function neq(left: Constant, right: Constant): Neq; + +// @beta +export function neq(left: Constant, right: any): Neq; + +// @beta +export function neq(left: string, right: Constant): Neq; + +// @beta +export function neq(left: string, right: any): Neq; + // @public export type NestedUpdateFields> = UnionToIntersection<{ [K in keyof T & string]: ChildUpdateFields; }[keyof T & string]>; +// @beta (undocumented) +export class Not extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant); + // (undocumented) + filterable: true; +} + +// @beta +export function not(filter: FilterExpr): Not; + +// @beta +export function notInAny(element: Constant, others: Constant[]): Not; + +// @beta +export function notInAny(element: Constant, others: any[]): Not; + +// @beta +export function notInAny(element: string, others: Constant[]): Not; + +// @beta +export function notInAny(element: string, others: any[]): Not; + +// @beta (undocumented) +export class Offset implements Stage { + constructor(offset: number); + // (undocumented) + name: string; + } + // @public export function onSnapshot(reference: DocumentReference, observer: { next?: (snapshot: DocumentSnapshot) => void; @@ -469,6 +1734,13 @@ export function onSnapshotsInSync(firestore: Firestore, observer: { // @public export function onSnapshotsInSync(firestore: Firestore, onSync: () => void): Unsubscribe; +// @beta (undocumented) +export class Or extends FirestoreFunction implements FilterCondition { + constructor(conditions: FilterExpr[]); + // (undocumented) + filterable: true; +} + // @public export function or(...queryConstraints: QueryFilterConstraint[]): QueryCompositeFilterConstraint; @@ -478,6 +1750,14 @@ export function orderBy(fieldPath: string | FieldPath, directionStr?: OrderByDir // @public export type OrderByDirection = 'desc' | 'asc'; +// @beta +export class Ordering { + constructor(expr: Constant, direction: 'ascending' | 'descending'); + } + +// @beta +export function orFunction(left: FilterExpr, ...right: FilterExpr[]): Or; + // @public export type PartialWithFieldValue = Partial | (T extends Primitive ? T : T extends {} ? { [K in keyof T]?: PartialWithFieldValue | FieldValue; @@ -534,6 +1814,79 @@ export interface PersistentSingleTabManagerSettings { // @public export type PersistentTabManager = PersistentSingleTabManager | PersistentMultipleTabManager; +// @public (undocumented) +export class Pipeline { + /* Excluded from this release type: __constructor */ + // Warning: (ae-incompatible-release-tags) The symbol "addFields" is marked as @public, but its signature references "Selectable" which is marked as @beta + addFields(...fields: Selectable[]): Pipeline; + // Warning: (ae-incompatible-release-tags) The symbol "aggregate" is marked as @public, but its signature references "AccumulatorTarget" which is marked as @beta + aggregate(...accumulators: AccumulatorTarget[]): Pipeline; + aggregate(options: { accumulators: AccumulatorTarget[]; groups?: Array; }): Pipeline; + // (undocumented) + converter: any; + // Warning: (ae-incompatible-release-tags) The symbol "distinct" is marked as @public, but its signature references "Selectable" which is marked as @beta + distinct(...groups: Array): Pipeline; + // Warning: (ae-incompatible-release-tags) The symbol "execute" is marked as @public, but its signature references "PipelineResult" which is marked as @beta + execute(): Promise>>; + // Warning: (ae-incompatible-release-tags) The symbol "findNearest" is marked as @public, but its signature references "FindNearestOptions" which is marked as @beta + // + // (undocumented) + findNearest(options: FindNearestOptions): Pipeline; + genericStage(name: string, params: any[]): Pipeline; + limit(limit: number): Pipeline; + offset(offset: number): Pipeline; + readUserData: any; + // Warning: (ae-incompatible-release-tags) The symbol "select" is marked as @public, but its signature references "Selectable" which is marked as @beta + select(...selections: Array): Pipeline; + // (undocumented) + selectablesToMap: any; + // Warning: (ae-incompatible-release-tags) The symbol "sort" is marked as @public, but its signature references "Ordering" which is marked as @beta + sort(...orderings: Ordering[]): Pipeline; + // (undocumented) + sort(options: { orderings: Ordering[]; }): Pipeline; + // (undocumented) + stages: any; + // (undocumented) + userDataReader: any; + // Warning: (ae-incompatible-release-tags) The symbol "where" is marked as @public, but its signature references "FilterCondition" which is marked as @beta + // Warning: (ae-incompatible-release-tags) The symbol "where" is marked as @public, but its signature references "Constant" which is marked as @beta + where(condition: FilterCondition & Constant): Pipeline; +} + +// Warning: (ae-incompatible-release-tags) The symbol "pipeline" is marked as @public, but its signature references "PipelineSource" which is marked as @beta +// +// @public +export function pipeline(firestore: Firestore): PipelineSource; + +// @public +export function pipeline(query: Query): Pipeline; + +// @beta +export class PipelineResult { + /* Excluded from this release type: _ref */ + /* Excluded from this release type: _fields */ + /* Excluded from this release type: __constructor */ + get createTime(): Timestamp | undefined; + data(): AppModelType | undefined; + get executionTime(): Timestamp; + get(fieldPath: string | FieldPath): any; + get id(): string | undefined; + get ref(): DocumentReference | undefined; + get updateTime(): Timestamp | undefined; +} + +// @beta +export class PipelineSource { + // (undocumented) + collection(collectionPath: string): Pipeline; + // (undocumented) + collectionGroup(collectionId: string): Pipeline; + // (undocumented) + database(): Pipeline; + // (undocumented) + documents(docs: DocumentReference[]): Pipeline; + } + // @public export type Primitive = string | number | boolean | undefined | null; @@ -542,6 +1895,7 @@ export class Query | null; readonly firestore: Firestore; + pipeline(): Pipeline; readonly type: 'query' | 'collection'; withConverter(converter: null): Query; withConverter(converter: FirestoreDataConverter): Query; @@ -620,9 +1974,102 @@ export class QueryStartAtConstraint extends QueryConstraint { // @public export function refEqual(left: DocumentReference | CollectionReference, right: DocumentReference | CollectionReference): boolean; +// @beta (undocumented) +export class RegexContains extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, pattern: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function regexContains(left: string, pattern: string): RegexContains; + +// @beta +export function regexContains(left: string, pattern: Constant): RegexContains; + +// @beta +export function regexContains(left: Constant, pattern: string): RegexContains; + +// @beta +export function regexContains(left: Constant, pattern: Constant): RegexContains; + +// @beta (undocumented) +export class RegexMatch extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, pattern: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function regexMatch(left: string, pattern: string): RegexMatch; + +// @beta +export function regexMatch(left: string, pattern: Constant): RegexMatch; + +// @beta +export function regexMatch(left: Constant, pattern: string): RegexMatch; + +// @beta +export function regexMatch(left: Constant, pattern: Constant): RegexMatch; + +// @beta (undocumented) +export class ReplaceAll extends FirestoreFunction { + constructor(value: Constant, find: Constant, replace: Constant); + } + +// @beta +export function replaceAll(value: Constant, find: string, replace: string): ReplaceAll; + +// @beta +export function replaceAll(value: Constant, find: Constant, replace: Constant): ReplaceAll; + +// @beta +export function replaceAll(field: string, find: string, replace: string): ReplaceAll; + +// @beta (undocumented) +export class ReplaceFirst extends FirestoreFunction { + constructor(value: Constant, find: Constant, replace: Constant); + } + +// @beta +export function replaceFirst(value: Constant, find: string, replace: string): ReplaceFirst; + +// @beta +export function replaceFirst(value: Constant, find: Constant, replace: Constant): ReplaceFirst; + +// @beta +export function replaceFirst(field: string, find: string, replace: string): ReplaceFirst; + +// @beta (undocumented) +export class Reverse extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function reverse(expr: Constant): Reverse; + +// @beta +export function reverse(field: string): Reverse; + // @public export function runTransaction(firestore: Firestore, updateFunction: (transaction: Transaction) => Promise, options?: TransactionOptions): Promise; +// @beta (undocumented) +export class Select implements Stage { + constructor(projections: Map); + // (undocumented) + name: string; + } + +// @beta +export interface Selectable { + // (undocumented) + selectable: true; +} + +// @beta +export type SelectableExpr = Constant & Selectable; + // @public export function serverTimestamp(): FieldValue; @@ -669,6 +2116,19 @@ export interface SnapshotOptions { readonly serverTimestamps?: 'estimate' | 'previous' | 'none'; } +// @beta (undocumented) +export class Sort implements Stage { + constructor(orders: Ordering[]); + // (undocumented) + name: string; + } + +// @beta (undocumented) +export interface Stage { + // (undocumented) + name: string; +} + // @public export function startAfter(snapshot: DocumentSnapshot): QueryStartAtConstraint; @@ -681,9 +2141,88 @@ export function startAt(snapshot // @public export function startAt(...fieldValues: unknown[]): QueryStartAtConstraint; +// @beta (undocumented) +export class StartsWith extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, prefix: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function startsWith(expr: string, prefix: string): StartsWith; + +// @beta +export function startsWith(expr: string, prefix: Constant): StartsWith; + +// @beta +export function startsWith(expr: Constant, prefix: string): StartsWith; + +// @beta +export function startsWith(expr: Constant, prefix: Constant): StartsWith; + +// @beta (undocumented) +export class StrConcat extends FirestoreFunction { + constructor(first: Constant, rest: Constant[]); + } + +// @beta +export function strConcat(first: string, ...elements: Array): StrConcat; + +// @beta +export function strConcat(first: Constant, ...elements: Array): StrConcat; + +// @beta (undocumented) +export class StrContains extends FirestoreFunction implements FilterCondition { + constructor(expr: Constant, substring: Constant); + // (undocumented) + filterable: true; + } + +// @beta +export function strContains(left: string, substring: string): StrContains; + +// @beta +export function strContains(left: string, substring: Constant): StrContains; + +// @beta +export function strContains(left: Constant, substring: string): StrContains; + +// @beta +export function strContains(left: Constant, substring: Constant): StrContains; + +// @beta (undocumented) +export class Subtract extends FirestoreFunction { + constructor(left: Constant, right: Constant); + } + +// @beta +export function subtract(left: Constant, right: Constant): Subtract; + +// @beta +export function subtract(left: Constant, right: any): Subtract; + +// @beta +export function subtract(left: string, right: Constant): Subtract; + +// @beta +export function subtract(left: string, right: any): Subtract; + +// @beta (undocumented) +export class Sum extends FirestoreFunction implements Accumulator { + constructor(value: Constant, distinct: boolean); + // (undocumented) + accumulator: true; + } + // @public export function sum(field: string | FieldPath): AggregateField; +// @beta +export function sumFunction(value: Constant): Sum; + +// @beta +export function sumFunction(value: string): Sum; + // @public export type TaskState = 'Error' | 'Running' | 'Success'; @@ -711,6 +2250,89 @@ export class Timestamp { valueOf(): string; } +// @beta (undocumented) +export class TimestampAdd extends FirestoreFunction { + constructor(timestamp: Constant, unit: Constant, amount: Constant); + } + +// @beta +export function timestampAdd(timestamp: Constant, unit: Constant, amount: Constant): TimestampAdd; + +// @beta +export function timestampAdd(timestamp: Constant, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + +// @beta +export function timestampAdd(field: string, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampAdd; + +// @beta (undocumented) +export class TimestampSub extends FirestoreFunction { + constructor(timestamp: Constant, unit: Constant, amount: Constant); + } + +// @beta +export function timestampSub(timestamp: Constant, unit: Constant, amount: Constant): TimestampSub; + +// @beta +export function timestampSub(timestamp: Constant, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + +// @beta +export function timestampSub(field: string, unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', amount: number): TimestampSub; + +// @beta (undocumented) +export class TimestampToUnixMicros extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function timestampToUnixMicros(expr: Constant): TimestampToUnixMicros; + +// @beta +export function timestampToUnixMicros(field: string): TimestampToUnixMicros; + +// @beta (undocumented) +export class TimestampToUnixMillis extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function timestampToUnixMillis(expr: Constant): TimestampToUnixMillis; + +// @beta +export function timestampToUnixMillis(field: string): TimestampToUnixMillis; + +// @beta (undocumented) +export class TimestampToUnixSeconds extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function timestampToUnixSeconds(expr: Constant): TimestampToUnixSeconds; + +// @beta +export function timestampToUnixSeconds(field: string): TimestampToUnixSeconds; + +// @beta (undocumented) +export class ToLower extends FirestoreFunction { + constructor(expr: Constant); + } + +// @beta +export function toLower(expr: string): ToLower; + +// @beta +export function toLower(expr: Constant): ToLower; + +// @beta (undocumented) +export class ToUpper extends FirestoreFunction { + constructor(expr: Constant); + } + +// @beta +export function toUpper(expr: string): ToUpper; + +// @beta +export function toUpper(expr: Constant): ToUpper; + // @public export class Transaction { delete(documentRef: DocumentReference): this; @@ -726,9 +2348,53 @@ export interface TransactionOptions { readonly maxAttempts?: number; } +// @beta (undocumented) +export class Trim extends FirestoreFunction { + constructor(expr: Constant); + } + +// @beta +export function trim(expr: string): Trim; + +// @beta +export function trim(expr: Constant): Trim; + // @public export type UnionToIntersection = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never; +// @beta (undocumented) +export class UnixMicrosToTimestamp extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function unixMicrosToTimestamp(expr: Constant): UnixMicrosToTimestamp; + +// @beta +export function unixMicrosToTimestamp(field: string): UnixMicrosToTimestamp; + +// @beta (undocumented) +export class UnixMillisToTimestamp extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function unixMillisToTimestamp(expr: Constant): UnixMillisToTimestamp; + +// @beta +export function unixMillisToTimestamp(field: string): UnixMillisToTimestamp; + +// @beta (undocumented) +export class UnixSecondsToTimestamp extends FirestoreFunction { + constructor(input: Constant); + } + +// @beta +export function unixSecondsToTimestamp(expr: Constant): UnixSecondsToTimestamp; + +// @beta +export function unixSecondsToTimestamp(field: string): UnixSecondsToTimestamp; + // @public export interface Unsubscribe { (): void; @@ -745,9 +2411,23 @@ export function updateDoc(refere // @public export function updateDoc(reference: DocumentReference, field: string | FieldPath, value: unknown, ...moreFieldsAndValues: unknown[]): Promise; +// @public +export function useFirestorePipelines(): void; + // @public export function vector(values?: number[]): VectorValue; +// @beta (undocumented) +export class VectorLength extends FirestoreFunction { + constructor(value: Constant); + } + +// @beta +export function vectorLength(expr: Constant): VectorLength; + +// @beta +export function vectorLength(field: string): VectorLength; + // @public export class VectorValue { /* Excluded from this release type: __constructor */ @@ -758,6 +2438,13 @@ export class VectorValue { // @public export function waitForPendingWrites(firestore: Firestore): Promise; +// @beta (undocumented) +export class Where implements Stage { + constructor(condition: FilterCondition & Constant); + // (undocumented) + name: string; +} + // @public export function where(fieldPath: string | FieldPath, opStr: WhereFilterOp, value: unknown): QueryFieldFilterConstraint; @@ -782,5 +2469,21 @@ export class WriteBatch { // @public export function writeBatch(firestore: Firestore): WriteBatch; +// @beta (undocumented) +export class Xor extends FirestoreFunction implements FilterCondition { + constructor(conditions: FilterExpr[]); + // (undocumented) + filterable: true; +} + +// @beta +export function xor(left: FilterExpr, ...right: FilterExpr[]): Xor; + + +// Warnings were encountered during analysis: +// +// /home/runner/work/firebase-js-sdk/firebase-js-sdk/packages/firestore/dist/index.d.ts:10102:26 - (ae-incompatible-release-tags) The symbol "accumulators" is marked as @public, but its signature references "AccumulatorTarget" which is marked as @beta +// /home/runner/work/firebase-js-sdk/firebase-js-sdk/packages/firestore/dist/index.d.ts:10102:61 - (ae-incompatible-release-tags) The symbol "groups" is marked as @public, but its signature references "Selectable" which is marked as @beta +// /home/runner/work/firebase-js-sdk/firebase-js-sdk/packages/firestore/dist/index.d.ts:10129:21 - (ae-incompatible-release-tags) The symbol "orderings" is marked as @public, but its signature references "Ordering" which is marked as @beta ``` diff --git a/package.json b/package.json index 62fe45a3256..d6fbc47e019 100644 --- a/package.json +++ b/package.json @@ -143,6 +143,7 @@ "postinstall-postinstall": "2.1.0", "prettier": "2.8.7", "protractor": "5.4.2", + "protobufjs-cli": "^1.1.3", "request": "2.88.2", "semver": "7.5.3", "simple-git": "3.24.0", diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts index b751f0a8254..636eb4c6709 100644 --- a/packages/firestore/lite/index.ts +++ b/packages/firestore/lite/index.ts @@ -27,6 +27,171 @@ import { registerFirestore } from './register'; registerFirestore(); +export { PipelineSource } from '../src/lite-api/pipeline-source'; + +export { PipelineResult } from '../src/lite-api/pipeline-result'; + +export { Pipeline, pipeline } from '../src/lite-api/pipeline'; + +export { useFirestorePipelines } from '../src/lite-api/database_augmentation'; + +export { execute } from '../src/lite-api/pipeline_impl'; + +export { + Stage, + FindNearestOptions, + AddFields, + Aggregate, + Distinct, + CollectionSource, + CollectionGroupSource, + DatabaseSource, + DocumentsSource, + Where, + FindNearest, + Limit, + Offset, + Select, + Sort, + GenericStage +} from '../src/lite-api/stage'; + +export { + add, + subtract, + multiply, + divide, + mod, + eq, + neq, + lt, + lte, + gt, + gte, + arrayConcat, + arrayContains, + arrayContainsAny, + arrayContainsAll, + arrayLength, + inAny, + notInAny, + xor, + ifFunction, + not, + logicalMax, + logicalMin, + exists, + isNan, + reverse, + replaceFirst, + replaceAll, + byteLength, + charLength, + like, + regexContains, + regexMatch, + strContains, + startsWith, + endsWith, + toLower, + toUpper, + trim, + strConcat, + mapGet, + countAll, + min, + max, + cosineDistance, + dotProduct, + euclideanDistance, + vectorLength, + unixMicrosToTimestamp, + timestampToUnixMicros, + unixMillisToTimestamp, + timestampToUnixMillis, + unixSecondsToTimestamp, + timestampToUnixSeconds, + timestampAdd, + timestampSub, + genericFunction, + ascending, + descending, + ExprWithAlias, + Field, + Fields, + Constant, + FirestoreFunction, + Add, + Subtract, + Multiply, + Divide, + Mod, + Eq, + Neq, + Lt, + Lte, + Gt, + Gte, + ArrayConcat, + ArrayReverse, + ArrayContains, + ArrayContainsAll, + ArrayContainsAny, + ArrayLength, + ArrayElement, + In, + IsNan, + Exists, + Not, + And, + Or, + Xor, + If, + LogicalMax, + LogicalMin, + Reverse, + ReplaceFirst, + ReplaceAll, + CharLength, + ByteLength, + Like, + RegexContains, + RegexMatch, + StrContains, + StartsWith, + EndsWith, + ToLower, + ToUpper, + Trim, + StrConcat, + MapGet, + Count, + Sum, + Avg, + Min, + Max, + CosineDistance, + DotProduct, + EuclideanDistance, + VectorLength, + UnixMicrosToTimestamp, + TimestampToUnixMicros, + UnixMillisToTimestamp, + TimestampToUnixMillis, + UnixSecondsToTimestamp, + TimestampToUnixSeconds, + TimestampAdd, + TimestampSub, + Ordering, + ExprType, + AccumulatorTarget, + FilterExpr, + SelectableExpr, + Selectable, + FilterCondition, + Accumulator +} from '../src/lite-api/expressions'; + export { aggregateQuerySnapshotEqual, getCount, diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts index ea969c6b94c..4d33b925706 100644 --- a/packages/firestore/src/api.ts +++ b/packages/firestore/src/api.ts @@ -15,6 +15,176 @@ * limitations under the License. */ +export { PipelineSource } from './lite-api/pipeline-source'; + +export { PipelineResult } from './lite-api/pipeline-result'; + +export { Pipeline, pipeline } from './api/pipeline'; + +export { useFirestorePipelines } from './api/database_augmentation'; + +export { execute } from './lite-api/pipeline_impl'; + +export { + Stage, + FindNearestOptions, + AddFields, + Aggregate, + Distinct, + CollectionSource, + CollectionGroupSource, + DatabaseSource, + DocumentsSource, + Where, + FindNearest, + Limit, + Offset, + Select, + Sort, + GenericStage +} from './lite-api/stage'; + +export { + add, + subtract, + multiply, + divide, + mod, + eq, + neq, + lt, + lte, + gt, + gte, + arrayConcat, + arrayContains, + arrayContainsAny, + arrayContainsAll, + arrayLength, + inAny, + notInAny, + xor, + ifFunction, + not, + logicalMax, + logicalMin, + exists, + isNan, + reverse, + replaceFirst, + replaceAll, + byteLength, + charLength, + like, + regexContains, + regexMatch, + strContains, + startsWith, + endsWith, + toLower, + toUpper, + trim, + strConcat, + mapGet, + countAll, + countFunction, + sumFunction, + avgFunction, + andFunction, + orFunction, + min, + max, + cosineDistance, + dotProduct, + euclideanDistance, + vectorLength, + unixMicrosToTimestamp, + timestampToUnixMicros, + unixMillisToTimestamp, + timestampToUnixMillis, + unixSecondsToTimestamp, + timestampToUnixSeconds, + timestampAdd, + timestampSub, + genericFunction, + ascending, + descending, + ExprWithAlias, + Field, + Fields, + Constant, + FirestoreFunction, + Add, + Subtract, + Multiply, + Divide, + Mod, + Eq, + Neq, + Lt, + Lte, + Gt, + Gte, + ArrayConcat, + ArrayReverse, + ArrayContains, + ArrayContainsAll, + ArrayContainsAny, + ArrayLength, + ArrayElement, + In, + IsNan, + Exists, + Not, + And, + Or, + Xor, + If, + LogicalMax, + LogicalMin, + Reverse, + ReplaceFirst, + ReplaceAll, + CharLength, + ByteLength, + Like, + RegexContains, + RegexMatch, + StrContains, + StartsWith, + EndsWith, + ToLower, + ToUpper, + Trim, + StrConcat, + MapGet, + Count, + Sum, + Avg, + Min, + Max, + CosineDistance, + DotProduct, + EuclideanDistance, + VectorLength, + UnixMicrosToTimestamp, + TimestampToUnixMicros, + UnixMillisToTimestamp, + TimestampToUnixMillis, + UnixSecondsToTimestamp, + TimestampToUnixSeconds, + TimestampAdd, + TimestampSub, + Ordering, + ExprType, + AccumulatorTarget, + FilterExpr, + SelectableExpr, + Selectable, + FilterCondition, + Accumulator +} from './lite-api/expressions'; + export { aggregateFieldEqual, aggregateQuerySnapshotEqual, @@ -224,7 +394,8 @@ export { isBase64Available as _isBase64Available } from './platform/base64'; export { DatabaseId as _DatabaseId } from './core/database_info'; export { _internalQueryToProtoQueryTarget, - _internalAggregationQueryToProtoRunAggregationQueryRequest + _internalAggregationQueryToProtoRunAggregationQueryRequest, + _internalPipelineToExecutePipelineRequestProto } from './remote/internal_serializer'; export { cast as _cast, diff --git a/packages/firestore/src/api/aggregate.ts b/packages/firestore/src/api/aggregate.ts index f0e2c1e1dc0..453f9e0a841 100644 --- a/packages/firestore/src/api/aggregate.ts +++ b/packages/firestore/src/api/aggregate.ts @@ -15,17 +15,21 @@ * limitations under the License. */ -import { AggregateField, AggregateSpec, DocumentData, Query } from '../api'; import { AggregateImpl } from '../core/aggregate'; import { firestoreClientRunAggregateQuery } from '../core/firestore_client'; import { count } from '../lite-api/aggregate'; -import { AggregateQuerySnapshot } from '../lite-api/aggregate_types'; +import { + AggregateField, + AggregateQuerySnapshot, + AggregateSpec +} from '../lite-api/aggregate_types'; +import { DocumentData, Query } from '../lite-api/reference'; import { ApiClientObjectMap, Value } from '../protos/firestore_proto_api'; import { cast } from '../util/input_validation'; import { mapToArray } from '../util/obj'; import { ensureFirestoreConfigured, Firestore } from './database'; -import { ExpUserDataWriter } from './reference_impl'; +import { ExpUserDataWriter } from './user_data_writer'; export { aggregateQuerySnapshotEqual, diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index 0757378a74c..cc1fab0c86a 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -46,6 +46,7 @@ import { connectFirestoreEmulator, Firestore as LiteFirestore } from '../lite-api/database'; +import type { PipelineSource } from '../lite-api/pipeline-source'; import { Query } from '../lite-api/reference'; import { indexedDbClearPersistence, @@ -128,6 +129,15 @@ export class Firestore extends LiteFirestore { await terminate; } } + + /** + * Pipeline query. + */ + pipeline(): PipelineSource { + throw new Error( + 'Pipelines not initialized. Your application must call `useFirestorePipelines()` before using Firestore Pipeline features.' + ); + } } /** diff --git a/packages/firestore/src/api/database_augmentation.ts b/packages/firestore/src/api/database_augmentation.ts new file mode 100644 index 00000000000..0eb8c91a034 --- /dev/null +++ b/packages/firestore/src/api/database_augmentation.ts @@ -0,0 +1,57 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Pipeline } from '../lite-api/pipeline'; +import { PipelineSource } from '../lite-api/pipeline-source'; +import { newUserDataReader } from '../lite-api/user_data_reader'; +import { DocumentKey } from '../model/document_key'; + +import { Firestore } from './database'; +import { DocumentReference, Query } from './reference'; +import { ExpUserDataWriter } from './user_data_writer'; + +export function useFirestorePipelines(): void { + Firestore.prototype.pipeline = function (): PipelineSource { + const firestore = this; + return new PipelineSource( + this, + newUserDataReader(firestore), + new ExpUserDataWriter(firestore), + (key: DocumentKey) => { + return new DocumentReference(firestore, null, key); + } + ); + }; + + Query.prototype.pipeline = function (): Pipeline { + let pipeline; + if (this._query.collectionGroup) { + pipeline = this.firestore + .pipeline() + .collectionGroup(this._query.collectionGroup); + } else { + pipeline = this.firestore + .pipeline() + .collection(this._query.path.canonicalString()); + } + + // TODO(pipeline) convert existing query filters, limits, etc into + // pipeline stages + + return pipeline; + }; +} diff --git a/packages/firestore/src/api/pipeline.ts b/packages/firestore/src/api/pipeline.ts new file mode 100644 index 00000000000..14532ba85c0 --- /dev/null +++ b/packages/firestore/src/api/pipeline.ts @@ -0,0 +1,135 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { firestoreClientExecutePipeline } from '../core/firestore_client'; +import { Pipeline as LitePipeline } from '../lite-api/pipeline'; +import { PipelineResult } from '../lite-api/pipeline-result'; +import { PipelineSource } from '../lite-api/pipeline-source'; +import { DocumentData, DocumentReference, Query } from '../lite-api/reference'; +import { Stage } from '../lite-api/stage'; +import { UserDataReader } from '../lite-api/user_data_reader'; +import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; +import { DocumentKey } from '../model/document_key'; +import { cast } from '../util/input_validation'; + +import { ensureFirestoreConfigured, Firestore } from './database'; + +export class Pipeline< + AppModelType = DocumentData +> extends LitePipeline { + /** + * @internal + * @private + * @param db + * @param userDataReader + * @param userDataWriter + * @param documentReferenceFactory + * @param stages + * @param converter + */ + constructor( + db: Firestore, + userDataReader: UserDataReader, + userDataWriter: AbstractUserDataWriter, + documentReferenceFactory: (id: DocumentKey) => DocumentReference, + stages: Stage[], + // TODO(pipeline) support converter + //private converter: FirestorePipelineConverter = defaultPipelineConverter() + converter: unknown = {} + ) { + super( + db, + userDataReader, + userDataWriter, + documentReferenceFactory, + stages, + converter + ); + } + + /** + * Executes this pipeline and returns a Promise to represent the asynchronous operation. + * + *

The returned Promise can be used to track the progress of the pipeline execution + * and retrieve the results (or handle any errors) asynchronously. + * + *

The pipeline results are returned as a list of {@link PipelineResult} objects. Each {@link + * PipelineResult} typically represents a single key/value map that has passed through all the + * stages of the pipeline, however this might differ depending on the stages involved in the + * pipeline. For example: + * + *

    + *
  • If there are no stages or only transformation stages, each {@link PipelineResult} + * represents a single document.
  • + *
  • If there is an aggregation, only a single {@link PipelineResult} is returned, + * representing the aggregated results over the entire dataset .
  • + *
  • If there is an aggregation stage with grouping, each {@link PipelineResult} represents a + * distinct group and its associated aggregated values.
  • + *
+ * + *

Example: + * + * ```typescript + * const futureResults = await firestore.pipeline().collection("books") + * .where(gt(Field.of("rating"), 4.5)) + * .select("title", "author", "rating") + * .execute(); + * ``` + * + * @return A Promise representing the asynchronous pipeline execution. + */ + execute(): Promise>> { + const firestore = cast(this._db, Firestore); + const client = ensureFirestoreConfigured(firestore); + return firestoreClientExecutePipeline(client, this).then(result => { + const docs = result.map( + element => + new PipelineResult( + this.userDataWriter, + element.key?.path + ? this.documentReferenceFactory(element.key) + : undefined, + element.fields, + element.executionTime?.toTimestamp(), + element.createTime?.toTimestamp(), + element.updateTime?.toTimestamp() + //this.converter + ) + ); + + return docs; + }); + } +} + +/** + * Experimental Modular API for console testing. + * @param firestore + */ +export function pipeline(firestore: Firestore): PipelineSource; + +/** + * Experimental Modular API for console testing. + * @param query + */ +export function pipeline(query: Query): Pipeline; + +export function pipeline( + firestoreOrQuery: Firestore | Query +): PipelineSource | Pipeline { + return firestoreOrQuery.pipeline(); +} diff --git a/packages/firestore/src/api/reference_impl.ts b/packages/firestore/src/api/reference_impl.ts index e730fb40da7..86956a52785 100644 --- a/packages/firestore/src/api/reference_impl.ts +++ b/packages/firestore/src/api/reference_impl.ts @@ -36,7 +36,6 @@ import { } from '../core/firestore_client'; import { newQueryForPath, Query as InternalQuery } from '../core/query'; import { ViewSnapshot } from '../core/view_snapshot'; -import { Bytes } from '../lite-api/bytes'; import { FieldPath } from '../lite-api/field_path'; import { validateHasExplicitOrderByForLimitToLast } from '../lite-api/query'; import { @@ -58,15 +57,14 @@ import { parseUpdateData, parseUpdateVarargs } from '../lite-api/user_data_reader'; -import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; import { DeleteMutation, Mutation, Precondition } from '../model/mutation'; import { debugAssert } from '../util/assert'; -import { ByteString } from '../util/byte_string'; import { FirestoreError } from '../util/error'; import { cast } from '../util/input_validation'; import { ensureFirestoreConfigured, Firestore } from './database'; import { DocumentSnapshot, QuerySnapshot, SnapshotMetadata } from './snapshot'; +import { ExpUserDataWriter } from './user_data_writer'; /** * An options object that can be passed to {@link (onSnapshot:1)} and {@link @@ -123,21 +121,6 @@ export function getDoc( ).then(snapshot => convertToDocSnapshot(firestore, reference, snapshot)); } -export class ExpUserDataWriter extends AbstractUserDataWriter { - constructor(protected firestore: Firestore) { - super(); - } - - protected convertBytes(bytes: ByteString): Bytes { - return new Bytes(bytes); - } - - protected convertReference(name: string): DocumentReference { - const key = this.convertDocumentKey(name, this.firestore._databaseId); - return new DocumentReference(this.firestore, /* converter= */ null, key); - } -} - /** * Reads the document referred to by this `DocumentReference` from cache. * Returns an error if the document is not currently cached. diff --git a/packages/firestore/src/api/transaction.ts b/packages/firestore/src/api/transaction.ts index 955866f19b4..8f83f527182 100644 --- a/packages/firestore/src/api/transaction.ts +++ b/packages/firestore/src/api/transaction.ts @@ -28,9 +28,9 @@ import { validateReference } from '../lite-api/write_batch'; import { cast } from '../util/input_validation'; import { ensureFirestoreConfigured, Firestore } from './database'; -import { ExpUserDataWriter } from './reference_impl'; import { DocumentSnapshot, SnapshotMetadata } from './snapshot'; import { TransactionOptions } from './transaction_options'; +import { ExpUserDataWriter } from './user_data_writer'; /** * A reference to a transaction. diff --git a/packages/firestore/src/api/user_data_writer.ts b/packages/firestore/src/api/user_data_writer.ts new file mode 100644 index 00000000000..3567f72cd93 --- /dev/null +++ b/packages/firestore/src/api/user_data_writer.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Bytes } from '../lite-api/bytes'; +import { DocumentReference } from '../lite-api/reference'; +import { AbstractUserDataWriter } from '../lite-api/user_data_writer'; +import { ByteString } from '../util/byte_string'; + +import { Firestore } from './database'; + +export class ExpUserDataWriter extends AbstractUserDataWriter { + constructor(protected firestore: Firestore) { + super(); + } + + protected convertBytes(bytes: ByteString): Bytes { + return new Bytes(bytes); + } + + protected convertReference(name: string): DocumentReference { + const key = this.convertDocumentKey(name, this.firestore._databaseId); + return new DocumentReference(this.firestore, /* converter= */ null, key); + } +} diff --git a/packages/firestore/src/core/firestore_client.ts b/packages/firestore/src/core/firestore_client.ts index e2aa19aaba8..ad485c7f77a 100644 --- a/packages/firestore/src/core/firestore_client.ts +++ b/packages/firestore/src/core/firestore_client.ts @@ -23,6 +23,7 @@ import { CredentialsProvider } from '../api/credentials'; import { User } from '../auth/user'; +import { Pipeline } from '../lite-api/pipeline'; import { LocalStore } from '../local/local_store'; import { localStoreConfigureFieldIndexes, @@ -38,11 +39,16 @@ import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; import { FieldIndex } from '../model/field_index'; import { Mutation } from '../model/mutation'; +import { PipelineStreamElement } from '../model/pipeline_stream_element'; import { toByteStreamReader } from '../platform/byte_stream_reader'; import { newSerializer } from '../platform/serializer'; import { newTextEncoder } from '../platform/text_serializer'; import { ApiClientObjectMap, Value } from '../protos/firestore_proto_api'; -import { Datastore, invokeRunAggregationQueryRpc } from '../remote/datastore'; +import { + Datastore, + invokeExecutePipeline, + invokeRunAggregationQueryRpc +} from '../remote/datastore'; import { RemoteStore, remoteStoreDisableNetwork, @@ -549,6 +555,23 @@ export function firestoreClientRunAggregateQuery( return deferred.promise; } +export function firestoreClientExecutePipeline( + client: FirestoreClient, + pipeline: Pipeline +): Promise { + const deferred = new Deferred(); + + client.asyncQueue.enqueueAndForget(async () => { + try { + const datastore = await getDatastore(client); + deferred.resolve(invokeExecutePipeline(datastore, pipeline)); + } catch (e) { + deferred.reject(e as Error); + } + }); + return deferred.promise; +} + export function firestoreClientWrite( client: FirestoreClient, mutations: Mutation[] diff --git a/packages/firestore/src/core/pipeline-util.ts b/packages/firestore/src/core/pipeline-util.ts new file mode 100644 index 00000000000..732d2963fcf --- /dev/null +++ b/packages/firestore/src/core/pipeline-util.ts @@ -0,0 +1,252 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Constant, + Expr, + Field, + FilterCondition, + not, + andFunction, + orFunction +} from '../lite-api/expressions'; +import { isNanValue, isNullValue } from '../model/values'; +import { + ArrayValue as ProtoArrayValue, + Function as ProtoFunction, + LatLng as ProtoLatLng, + MapValue as ProtoMapValue, + Pipeline as ProtoPipeline, + Timestamp as ProtoTimestamp, + Value as ProtoValue +} from '../protos/firestore_proto_api'; +import { fail } from '../util/assert'; +import { isPlainObject } from '../util/input_validation'; + +import { + CompositeFilter as CompositeFilterInternal, + CompositeOperator, + FieldFilter as FieldFilterInternal, + Filter as FilterInternal, + Operator +} from './filter'; + +/* eslint @typescript-eslint/no-explicit-any: 0 */ + +function isITimestamp(obj: any): obj is ProtoTimestamp { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + if ( + 'seconds' in obj && + (obj.seconds === null || + typeof obj.seconds === 'number' || + typeof obj.seconds === 'string') && + 'nanos' in obj && + (obj.nanos === null || typeof obj.nanos === 'number') + ) { + return true; + } + + return false; +} +function isILatLng(obj: any): obj is ProtoLatLng { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + if ( + 'latitude' in obj && + (obj.latitude === null || typeof obj.latitude === 'number') && + 'longitude' in obj && + (obj.longitude === null || typeof obj.longitude === 'number') + ) { + return true; + } + + return false; +} +function isIArrayValue(obj: any): obj is ProtoArrayValue { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + if ('values' in obj && (obj.values === null || Array.isArray(obj.values))) { + return true; + } + + return false; +} +function isIMapValue(obj: any): obj is ProtoMapValue { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + if ('fields' in obj && (obj.fields === null || isPlainObject(obj.fields))) { + return true; + } + + return false; +} +function isIFunction(obj: any): obj is ProtoFunction { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + if ( + 'name' in obj && + (obj.name === null || typeof obj.name === 'string') && + 'args' in obj && + (obj.args === null || Array.isArray(obj.args)) + ) { + return true; + } + + return false; +} + +function isIPipeline(obj: any): obj is ProtoPipeline { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + if ('stages' in obj && (obj.stages === null || Array.isArray(obj.stages))) { + return true; + } + + return false; +} + +export function isFirestoreValue(obj: any): obj is ProtoValue { + if (typeof obj !== 'object' || obj === null) { + return false; // Must be a non-null object + } + + // Check optional properties and their types + if ( + ('nullValue' in obj && + (obj.nullValue === null || obj.nullValue === 'NULL_VALUE')) || + ('booleanValue' in obj && + (obj.booleanValue === null || typeof obj.booleanValue === 'boolean')) || + ('integerValue' in obj && + (obj.integerValue === null || + typeof obj.integerValue === 'number' || + typeof obj.integerValue === 'string')) || + ('doubleValue' in obj && + (obj.doubleValue === null || typeof obj.doubleValue === 'number')) || + ('timestampValue' in obj && + (obj.timestampValue === null || isITimestamp(obj.timestampValue))) || + ('stringValue' in obj && + (obj.stringValue === null || typeof obj.stringValue === 'string')) || + ('bytesValue' in obj && + (obj.bytesValue === null || obj.bytesValue instanceof Uint8Array)) || + ('referenceValue' in obj && + (obj.referenceValue === null || + typeof obj.referenceValue === 'string')) || + ('geoPointValue' in obj && + (obj.geoPointValue === null || isILatLng(obj.geoPointValue))) || + ('arrayValue' in obj && + (obj.arrayValue === null || isIArrayValue(obj.arrayValue))) || + ('mapValue' in obj && + (obj.mapValue === null || isIMapValue(obj.mapValue))) || + ('fieldReferenceValue' in obj && + (obj.fieldReferenceValue === null || + typeof obj.fieldReferenceValue === 'string')) || + ('functionValue' in obj && + (obj.functionValue === null || isIFunction(obj.functionValue))) || + ('pipelineValue' in obj && + (obj.pipelineValue === null || isIPipeline(obj.pipelineValue))) + ) { + return true; + } + + return false; +} + +export function toPipelineFilterCondition( + f: FilterInternal +): FilterCondition & Expr { + if (f instanceof FieldFilterInternal) { + const field = Field.of(f.field.toString()); + if (isNanValue(f.value)) { + if (f.op === Operator.EQUAL) { + return andFunction(field.exists(), field.isNaN()); + } else { + return andFunction(field.exists(), not(field.isNaN())); + } + } else if (isNullValue(f.value)) { + if (f.op === Operator.EQUAL) { + return andFunction(field.exists(), field.eq(null)); + } else { + return andFunction(field.exists(), not(field.eq(null))); + } + } else { + // Comparison filters + const value = f.value; + switch (f.op) { + case Operator.LESS_THAN: + return andFunction(field.exists(), field.lt(value)); + case Operator.LESS_THAN_OR_EQUAL: + return andFunction(field.exists(), field.lte(value)); + case Operator.GREATER_THAN: + return andFunction(field.exists(), field.gt(value)); + case Operator.GREATER_THAN_OR_EQUAL: + return andFunction(field.exists(), field.gte(value)); + case Operator.EQUAL: + return andFunction(field.exists(), field.eq(value)); + case Operator.NOT_EQUAL: + return andFunction(field.exists(), field.neq(value)); + case Operator.ARRAY_CONTAINS: + return andFunction(field.exists(), field.arrayContains(value)); + case Operator.IN: { + const values = value?.arrayValue?.values?.map((val: any) => + Constant.of(val) + ); + return andFunction(field.exists(), field.in(...values!)); + } + case Operator.ARRAY_CONTAINS_ANY: { + const values = value?.arrayValue?.values?.map((val: any) => + Constant.of(val) + ); + return andFunction(field.exists(), field.arrayContainsAny(values!)); + } + case Operator.NOT_IN: { + const values = value?.arrayValue?.values?.map((val: any) => + Constant.of(val) + ); + return andFunction(field.exists(), not(field.in(...values!))); + } + default: + fail('Unexpected operator'); + } + } + } else if (f instanceof CompositeFilterInternal) { + switch (f.op) { + case CompositeOperator.AND: { + const conditions = f + .getFilters() + .map(f => toPipelineFilterCondition(f)); + return andFunction(conditions[0], ...conditions.slice(1)); + } + case CompositeOperator.OR: { + const conditions = f + .getFilters() + .map(f => toPipelineFilterCondition(f)); + return orFunction(conditions[0], ...conditions.slice(1)); + } + default: + fail('Unexpected operator'); + } + } + + throw new Error(`Failed to convert filter to pipeline conditions: ${f}`); +} diff --git a/packages/firestore/src/lite-api/database.ts b/packages/firestore/src/lite-api/database.ts index 9ea4d4ec52e..6ef01bb5172 100644 --- a/packages/firestore/src/lite-api/database.ts +++ b/packages/firestore/src/lite-api/database.ts @@ -41,6 +41,9 @@ import { cast } from '../util/input_validation'; import { logWarn } from '../util/log'; import { FirestoreService, removeComponents } from './components'; +// `import type` to avoid bundling the source for +// pipelines if `useFirestorePipelines()` is not called +import type { PipelineSource } from './pipeline-source'; import { DEFAULT_HOST, FirestoreSettingsImpl, @@ -173,6 +176,15 @@ export class Firestore implements FirestoreService { removeComponents(this); return Promise.resolve(); } + + /** + * Pipeline query. + */ + pipeline(): PipelineSource { + throw new Error( + 'Pipelines not initialized. Your application must call `useFirestorePipelines()` before using Firestore Pipeline features.' + ); + } } /** diff --git a/packages/firestore/src/lite-api/database_augmentation.ts b/packages/firestore/src/lite-api/database_augmentation.ts new file mode 100644 index 00000000000..14b9cf101c5 --- /dev/null +++ b/packages/firestore/src/lite-api/database_augmentation.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DocumentKey } from '../model/document_key'; + +import { Firestore } from './database'; +import { Pipeline } from './pipeline'; +import { PipelineSource } from './pipeline-source'; +import { DocumentReference, Query } from './reference'; +import { LiteUserDataWriter } from './reference_impl'; +import { newUserDataReader } from './user_data_reader'; + +export function useFirestorePipelines(): void { + Firestore.prototype.pipeline = function (): PipelineSource { + const userDataWriter = new LiteUserDataWriter(this); + const userDataReader = newUserDataReader(this); + return new PipelineSource( + this, + userDataReader, + userDataWriter, + (key: DocumentKey) => { + return new DocumentReference(this, null, key); + } + ); + }; + + Query.prototype.pipeline = function (): Pipeline { + let pipeline; + if (this._query.collectionGroup) { + pipeline = this.firestore + .pipeline() + .collectionGroup(this._query.collectionGroup); + } else { + pipeline = this.firestore + .pipeline() + .collection(this._query.path.canonicalString()); + } + + // TODO(pipeline) convert existing query filters, limits, etc into + // pipeline stages + + return pipeline; + }; +} diff --git a/packages/firestore/src/lite-api/expressions.ts b/packages/firestore/src/lite-api/expressions.ts new file mode 100644 index 00000000000..26a2f7c617c --- /dev/null +++ b/packages/firestore/src/lite-api/expressions.ts @@ -0,0 +1,6738 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint @typescript-eslint/no-explicit-any: 0 */ + +import { + DOCUMENT_KEY_NAME, + FieldPath as InternalFieldPath +} from '../model/path'; +import { Value as ProtoValue } from '../protos/firestore_proto_api'; +import { + JsonProtoSerializer, + ProtoSerializable, + toStringValue, + UserData +} from '../remote/serializer'; +import { hardAssert } from '../util/assert'; + +import { documentId, FieldPath } from './field_path'; +import { GeoPoint } from './geo_point'; +import { Pipeline } from './pipeline'; +import { DocumentReference } from './reference'; +import { Timestamp } from './timestamp'; +import { + fieldPathFromArgument, + parseData, + UserDataReader, + UserDataSource +} from './user_data_reader'; +import { VectorValue } from './vector_value'; + +/** + * @beta + * + * An interface that represents a selectable expression. + */ +export interface Selectable { + selectable: true; +} + +/** + * @beta + * + * An interface that represents a filter condition. + */ +export interface FilterCondition { + filterable: true; +} + +/** + * @beta + * + * An interface that represents an accumulator. + */ +export interface Accumulator { + accumulator: true; + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue; +} + +/** + * @beta + * + * An accumulator target, which is an expression with an alias that also implements the Accumulator interface. + */ +export type AccumulatorTarget = ExprWithAlias; + +/** + * @beta + * + * A filter expression, which is an expression that also implements the FilterCondition interface. + */ +export type FilterExpr = Expr & FilterCondition; + +/** + * @beta + * + * A selectable expression, which is an expression that also implements the Selectable interface. + */ +export type SelectableExpr = Expr & Selectable; + +/** + * @beta + * + * An enumeration of the different types of expressions. + */ +export type ExprType = + | 'Field' + | 'Constant' + | 'Function' + | 'ListOfExprs' + | 'ExprWithAlias'; + +/** + * @beta + * + * Represents an expression that can be evaluated to a value within the execution of a {@link + * Pipeline}. + * + * Expressions are the building blocks for creating complex queries and transformations in + * Firestore pipelines. They can represent: + * + * - **Field references:** Access values from document fields. + * - **Literals:** Represent constant values (strings, numbers, booleans). + * - **Function calls:** Apply functions to one or more expressions. + * - **Aggregations:** Calculate aggregate values (e.g., sum, average) over a set of documents. + * + * The `Expr` class provides a fluent API for building expressions. You can chain together + * method calls to create complex expressions. + */ +export abstract class Expr implements ProtoSerializable, UserData { + /** + * Creates an expression that adds this expression to another expression. + * + * ```typescript + * // Add the value of the 'quantity' field and the 'reserve' field. + * Field.of("quantity").add(Field.of("reserve")); + * ``` + * + * @param other The expression to add to this expression. + * @return A new `Expr` representing the addition operation. + */ + add(other: Expr): Add; + + /** + * Creates an expression that adds this expression to a constant value. + * + * ```typescript + * // Add 5 to the value of the 'age' field + * Field.of("age").add(5); + * ``` + * + * @param other The constant value to add. + * @return A new `Expr` representing the addition operation. + */ + add(other: any): Add; + add(other: any): Add { + if (other instanceof Expr) { + return new Add(this, other); + } + return new Add(this, Constant.of(other)); + } + + /** + * Creates an expression that subtracts another expression from this expression. + * + * ```typescript + * // Subtract the 'discount' field from the 'price' field + * Field.of("price").subtract(Field.of("discount")); + * ``` + * + * @param other The expression to subtract from this expression. + * @return A new `Expr` representing the subtraction operation. + */ + subtract(other: Expr): Subtract; + + /** + * Creates an expression that subtracts a constant value from this expression. + * + * ```typescript + * // Subtract 20 from the value of the 'total' field + * Field.of("total").subtract(20); + * ``` + * + * @param other The constant value to subtract. + * @return A new `Expr` representing the subtraction operation. + */ + subtract(other: any): Subtract; + subtract(other: any): Subtract { + if (other instanceof Expr) { + return new Subtract(this, other); + } + return new Subtract(this, Constant.of(other)); + } + + /** + * Creates an expression that multiplies this expression by another expression. + * + * ```typescript + * // Multiply the 'quantity' field by the 'price' field + * Field.of("quantity").multiply(Field.of("price")); + * ``` + * + * @param other The expression to multiply by. + * @return A new `Expr` representing the multiplication operation. + */ + multiply(other: Expr): Multiply; + + /** + * Creates an expression that multiplies this expression by a constant value. + * + * ```typescript + * // Multiply the 'value' field by 2 + * Field.of("value").multiply(2); + * ``` + * + * @param other The constant value to multiply by. + * @return A new `Expr` representing the multiplication operation. + */ + multiply(other: any): Multiply; + multiply(other: any): Multiply { + if (other instanceof Expr) { + return new Multiply(this, other); + } + return new Multiply(this, Constant.of(other)); + } + + /** + * Creates an expression that divides this expression by another expression. + * + * ```typescript + * // Divide the 'total' field by the 'count' field + * Field.of("total").divide(Field.of("count")); + * ``` + * + * @param other The expression to divide by. + * @return A new `Expr` representing the division operation. + */ + divide(other: Expr): Divide; + + /** + * Creates an expression that divides this expression by a constant value. + * + * ```typescript + * // Divide the 'value' field by 10 + * Field.of("value").divide(10); + * ``` + * + * @param other The constant value to divide by. + * @return A new `Expr` representing the division operation. + */ + divide(other: any): Divide; + divide(other: any): Divide { + if (other instanceof Expr) { + return new Divide(this, other); + } + return new Divide(this, Constant.of(other)); + } + + /** + * Creates an expression that calculates the modulo (remainder) of dividing this expression by another expression. + * + * ```typescript + * // Calculate the remainder of dividing the 'value' field by the 'divisor' field + * Field.of("value").mod(Field.of("divisor")); + * ``` + * + * @param other The expression to divide by. + * @return A new `Expr` representing the modulo operation. + */ + mod(other: Expr): Mod; + + /** + * Creates an expression that calculates the modulo (remainder) of dividing this expression by a constant value. + * + * ```typescript + * // Calculate the remainder of dividing the 'value' field by 10 + * Field.of("value").mod(10); + * ``` + * + * @param other The constant value to divide by. + * @return A new `Expr` representing the modulo operation. + */ + mod(other: any): Mod; + mod(other: any): Mod { + if (other instanceof Expr) { + return new Mod(this, other); + } + return new Mod(this, Constant.of(other)); + } + + // /** + // * Creates an expression that applies a bitwise AND operation between this expression and another expression. + // * + // * ```typescript + // * // Calculate the bitwise AND of 'field1' and 'field2'. + // * Field.of("field1").bitAnd(Field.of("field2")); + // * ``` + // * + // * @param other The right operand expression. + // * @return A new {@code Expr} representing the bitwise AND operation. + // */ + // bitAnd(other: Expr): BitAnd; + // + // /** + // * Creates an expression that applies a bitwise AND operation between this expression and a constant value. + // * + // * ```typescript + // * // Calculate the bitwise AND of 'field1' and 0xFF. + // * Field.of("field1").bitAnd(0xFF); + // * ``` + // * + // * @param other The right operand constant. + // * @return A new {@code Expr} representing the bitwise AND operation. + // */ + // bitAnd(other: any): BitAnd; + // bitAnd(other: any): BitAnd { + // if (other instanceof Expr) { + // return new BitAnd(this, other); + // } + // return new BitAnd(this, Constant.of(other)); + // } + // + // /** + // * Creates an expression that applies a bitwise OR operation between this expression and another expression. + // * + // * ```typescript + // * // Calculate the bitwise OR of 'field1' and 'field2'. + // * Field.of("field1").bitOr(Field.of("field2")); + // * ``` + // * + // * @param other The right operand expression. + // * @return A new {@code Expr} representing the bitwise OR operation. + // */ + // bitOr(other: Expr): BitOr; + // + // /** + // * Creates an expression that applies a bitwise OR operation between this expression and a constant value. + // * + // * ```typescript + // * // Calculate the bitwise OR of 'field1' and 0xFF. + // * Field.of("field1").bitOr(0xFF); + // * ``` + // * + // * @param other The right operand constant. + // * @return A new {@code Expr} representing the bitwise OR operation. + // */ + // bitOr(other: any): BitOr; + // bitOr(other: any): BitOr { + // if (other instanceof Expr) { + // return new BitOr(this, other); + // } + // return new BitOr(this, Constant.of(other)); + // } + // + // /** + // * Creates an expression that applies a bitwise XOR operation between this expression and another expression. + // * + // * ```typescript + // * // Calculate the bitwise XOR of 'field1' and 'field2'. + // * Field.of("field1").bitXor(Field.of("field2")); + // * ``` + // * + // * @param other The right operand expression. + // * @return A new {@code Expr} representing the bitwise XOR operation. + // */ + // bitXor(other: Expr): BitXor; + // + // /** + // * Creates an expression that applies a bitwise XOR operation between this expression and a constant value. + // * + // * ```typescript + // * // Calculate the bitwise XOR of 'field1' and 0xFF. + // * Field.of("field1").bitXor(0xFF); + // * ``` + // * + // * @param other The right operand constant. + // * @return A new {@code Expr} representing the bitwise XOR operation. + // */ + // bitXor(other: any): BitXor; + // bitXor(other: any): BitXor { + // if (other instanceof Expr) { + // return new BitXor(this, other); + // } + // return new BitXor(this, Constant.of(other)); + // } + // + // /** + // * Creates an expression that applies a bitwise NOT operation to this expression. + // * + // * ```typescript + // * // Calculate the bitwise NOT of 'field1'. + // * Field.of("field1").bitNot(); + // * ``` + // * + // * @return A new {@code Expr} representing the bitwise NOT operation. + // */ + // bitNot(): BitNot { + // return new BitNot(this); + // } + // + // /** + // * Creates an expression that applies a bitwise left shift operation between this expression and another expression. + // * + // * ```typescript + // * // Calculate the bitwise left shift of 'field1' by 'field2' bits. + // * Field.of("field1").bitLeftShift(Field.of("field2")); + // * ``` + // * + // * @param other The right operand expression representing the number of bits to shift. + // * @return A new {@code Expr} representing the bitwise left shift operation. + // */ + // bitLeftShift(other: Expr): BitLeftShift; + // + // /** + // * Creates an expression that applies a bitwise left shift operation between this expression and a constant value. + // * + // * ```typescript + // * // Calculate the bitwise left shift of 'field1' by 2 bits. + // * Field.of("field1").bitLeftShift(2); + // * ``` + // * + // * @param other The right operand constant representing the number of bits to shift. + // * @return A new {@code Expr} representing the bitwise left shift operation. + // */ + // bitLeftShift(other: number): BitLeftShift; + // bitLeftShift(other: Expr | number): BitLeftShift { + // if (typeof other === 'number') { + // return new BitLeftShift(this, Constant.of(other)); + // } + // return new BitLeftShift(this, other as Expr); + // } + // + // /** + // * Creates an expression that applies a bitwise right shift operation between this expression and another expression. + // * + // * ```typescript + // * // Calculate the bitwise right shift of 'field1' by 'field2' bits. + // * Field.of("field1").bitRightShift(Field.of("field2")); + // * ``` + // * + // * @param other The right operand expression representing the number of bits to shift. + // * @return A new {@code Expr} representing the bitwise right shift operation. + // */ + // bitRightShift(other: Expr): BitRightShift; + // + // /** + // * Creates an expression that applies a bitwise right shift operation between this expression and a constant value. + // * + // * ```typescript + // * // Calculate the bitwise right shift of 'field1' by 2 bits. + // * Field.of("field1").bitRightShift(2); + // * ``` + // * + // * @param other The right operand constant representing the number of bits to shift. + // * @return A new {@code Expr} representing the bitwise right shift operation. + // */ + // bitRightShift(other: number): BitRightShift; + // bitRightShift(other: Expr | number): BitRightShift { + // if (typeof other === 'number') { + // return new BitRightShift(this, Constant.of(other)); + // } + // return new BitRightShift(this, other as Expr); + // } + + /** + * Creates an expression that checks if this expression is equal to another expression. + * + * ```typescript + * // Check if the 'age' field is equal to 21 + * Field.of("age").eq(21); + * ``` + * + * @param other The expression to compare for equality. + * @return A new `Expr` representing the equality comparison. + */ + eq(other: Expr): Eq; + + /** + * Creates an expression that checks if this expression is equal to a constant value. + * + * ```typescript + * // Check if the 'city' field is equal to "London" + * Field.of("city").eq("London"); + * ``` + * + * @param other The constant value to compare for equality. + * @return A new `Expr` representing the equality comparison. + */ + eq(other: any): Eq; + eq(other: any): Eq { + if (other instanceof Expr) { + return new Eq(this, other); + } + return new Eq(this, Constant.of(other)); + } + + /** + * Creates an expression that checks if this expression is not equal to another expression. + * + * ```typescript + * // Check if the 'status' field is not equal to "completed" + * Field.of("status").neq("completed"); + * ``` + * + * @param other The expression to compare for inequality. + * @return A new `Expr` representing the inequality comparison. + */ + neq(other: Expr): Neq; + + /** + * Creates an expression that checks if this expression is not equal to a constant value. + * + * ```typescript + * // Check if the 'country' field is not equal to "USA" + * Field.of("country").neq("USA"); + * ``` + * + * @param other The constant value to compare for inequality. + * @return A new `Expr` representing the inequality comparison. + */ + neq(other: any): Neq; + neq(other: any): Neq { + if (other instanceof Expr) { + return new Neq(this, other); + } + return new Neq(this, Constant.of(other)); + } + + /** + * Creates an expression that checks if this expression is less than another expression. + * + * ```typescript + * // Check if the 'age' field is less than 'limit' + * Field.of("age").lt(Field.of('limit')); + * ``` + * + * @param other The expression to compare for less than. + * @return A new `Expr` representing the less than comparison. + */ + lt(other: Expr): Lt; + + /** + * Creates an expression that checks if this expression is less than a constant value. + * + * ```typescript + * // Check if the 'price' field is less than 50 + * Field.of("price").lt(50); + * ``` + * + * @param other The constant value to compare for less than. + * @return A new `Expr` representing the less than comparison. + */ + lt(other: any): Lt; + lt(other: any): Lt { + if (other instanceof Expr) { + return new Lt(this, other); + } + return new Lt(this, Constant.of(other)); + } + + /** + * Creates an expression that checks if this expression is less than or equal to another + * expression. + * + * ```typescript + * // Check if the 'quantity' field is less than or equal to 20 + * Field.of("quantity").lte(Constant.of(20)); + * ``` + * + * @param other The expression to compare for less than or equal to. + * @return A new `Expr` representing the less than or equal to comparison. + */ + lte(other: Expr): Lte; + + /** + * Creates an expression that checks if this expression is less than or equal to a constant value. + * + * ```typescript + * // Check if the 'score' field is less than or equal to 70 + * Field.of("score").lte(70); + * ``` + * + * @param other The constant value to compare for less than or equal to. + * @return A new `Expr` representing the less than or equal to comparison. + */ + lte(other: any): Lte; + lte(other: any): Lte { + if (other instanceof Expr) { + return new Lte(this, other); + } + return new Lte(this, Constant.of(other)); + } + + /** + * Creates an expression that checks if this expression is greater than another expression. + * + * ```typescript + * // Check if the 'age' field is greater than the 'limit' field + * Field.of("age").gt(Field.of("limit")); + * ``` + * + * @param other The expression to compare for greater than. + * @return A new `Expr` representing the greater than comparison. + */ + gt(other: Expr): Gt; + + /** + * Creates an expression that checks if this expression is greater than a constant value. + * + * ```typescript + * // Check if the 'price' field is greater than 100 + * Field.of("price").gt(100); + * ``` + * + * @param other The constant value to compare for greater than. + * @return A new `Expr` representing the greater than comparison. + */ + gt(other: any): Gt; + gt(other: any): Gt { + if (other instanceof Expr) { + return new Gt(this, other); + } + return new Gt(this, Constant.of(other)); + } + + /** + * Creates an expression that checks if this expression is greater than or equal to another + * expression. + * + * ```typescript + * // Check if the 'quantity' field is greater than or equal to field 'requirement' plus 1 + * Field.of("quantity").gte(Field.of('requirement').add(1)); + * ``` + * + * @param other The expression to compare for greater than or equal to. + * @return A new `Expr` representing the greater than or equal to comparison. + */ + gte(other: Expr): Gte; + + /** + * Creates an expression that checks if this expression is greater than or equal to a constant + * value. + * + * ```typescript + * // Check if the 'score' field is greater than or equal to 80 + * Field.of("score").gte(80); + * ``` + * + * @param other The constant value to compare for greater than or equal to. + * @return A new `Expr` representing the greater than or equal to comparison. + */ + gte(other: any): Gte; + gte(other: any): Gte { + if (other instanceof Expr) { + return new Gte(this, other); + } + return new Gte(this, Constant.of(other)); + } + + /** + * Creates an expression that concatenates an array expression with one or more other arrays. + * + * ```typescript + * // Combine the 'items' array with another array field. + * Field.of("items").arrayConcat(Field.of("otherItems")); + * ``` + * + * @param arrays The array expressions to concatenate. + * @return A new `Expr` representing the concatenated array. + */ + arrayConcat(arrays: Expr[]): ArrayConcat; + + /** + * Creates an expression that concatenates an array expression with one or more other arrays. + * + * ```typescript + * // Combine the 'tags' array with a new array and an array field + * Field.of("tags").arrayConcat(Arrays.asList("newTag1", "newTag2"), Field.of("otherTag")); + * ``` + * + * @param arrays The array expressions or values to concatenate. + * @return A new `Expr` representing the concatenated array. + */ + arrayConcat(arrays: any[]): ArrayConcat; + arrayConcat(arrays: any[]): ArrayConcat { + const exprValues = arrays.map(value => + value instanceof Expr ? value : Constant.of(value) + ); + return new ArrayConcat(this, exprValues); + } + + /** + * Creates an expression that checks if an array contains a specific element. + * + * ```typescript + * // Check if the 'sizes' array contains the value from the 'selectedSize' field + * Field.of("sizes").arrayContains(Field.of("selectedSize")); + * ``` + * + * @param element The element to search for in the array. + * @return A new `Expr` representing the 'array_contains' comparison. + */ + arrayContains(element: Expr): ArrayContains; + + /** + * Creates an expression that checks if an array contains a specific value. + * + * ```typescript + * // Check if the 'colors' array contains "red" + * Field.of("colors").arrayContains("red"); + * ``` + * + * @param element The element to search for in the array. + * @return A new `Expr` representing the 'array_contains' comparison. + */ + arrayContains(element: any): ArrayContains; + arrayContains(element: any): ArrayContains { + if (element instanceof Expr) { + return new ArrayContains(this, element); + } + return new ArrayContains(this, Constant.of(element)); + } + + /** + * Creates an expression that checks if an array contains all the specified elements. + * + * ```typescript + * // Check if the 'tags' array contains both "news" and "sports" + * Field.of("tags").arrayContainsAll(Field.of("tag1"), Field.of("tag2")); + * ``` + * + * @param values The elements to check for in the array. + * @return A new `Expr` representing the 'array_contains_all' comparison. + */ + arrayContainsAll(...values: Expr[]): ArrayContainsAll; + + /** + * Creates an expression that checks if an array contains all the specified elements. + * + * ```typescript + * // Check if the 'tags' array contains both of the values from field 'tag1' and "tag2" + * Field.of("tags").arrayContainsAll(Field.of("tag1"), Field.of("tag2")); + * ``` + * + * @param values The elements to check for in the array. + * @return A new `Expr` representing the 'array_contains_all' comparison. + */ + arrayContainsAll(...values: any[]): ArrayContainsAll; + arrayContainsAll(...values: any[]): ArrayContainsAll { + const exprValues = values.map(value => + value instanceof Expr ? value : Constant.of(value) + ); + return new ArrayContainsAll(this, exprValues); + } + + /** + * Creates an expression that checks if an array contains any of the specified elements. + * + * ```typescript + * // Check if the 'categories' array contains either values from field "cate1" or "cate2" + * Field.of("categories").arrayContainsAny(Field.of("cate1"), Field.of("cate2")); + * ``` + * + * @param values The elements to check for in the array. + * @return A new `Expr` representing the 'array_contains_any' comparison. + */ + arrayContainsAny(...values: Expr[]): ArrayContainsAny; + + /** + * Creates an expression that checks if an array contains any of the specified elements. + * + * ```typescript + * // Check if the 'groups' array contains either the value from the 'userGroup' field + * // or the value "guest" + * Field.of("groups").arrayContainsAny(Field.of("userGroup"), "guest"); + * ``` + * + * @param values The elements to check for in the array. + * @return A new `Expr` representing the 'array_contains_any' comparison. + */ + arrayContainsAny(...values: any[]): ArrayContainsAny; + arrayContainsAny(...values: any[]): ArrayContainsAny { + const exprValues = values.map(value => + value instanceof Expr ? value : Constant.of(value) + ); + return new ArrayContainsAny(this, exprValues); + } + + /** + * Creates an expression that calculates the length of an array. + * + * ```typescript + * // Get the number of items in the 'cart' array + * Field.of("cart").arrayLength(); + * ``` + * + * @return A new `Expr` representing the length of the array. + */ + arrayLength(): ArrayLength { + return new ArrayLength(this); + } + + /** + * Creates an expression that checks if this expression is equal to any of the provided values or + * expressions. + * + * ```typescript + * // Check if the 'category' field is either "Electronics" or value of field 'primaryType' + * Field.of("category").in("Electronics", Field.of("primaryType")); + * ``` + * + * @param others The values or expressions to check against. + * @return A new `Expr` representing the 'IN' comparison. + */ + in(...others: Expr[]): In; + + /** + * Creates an expression that checks if this expression is equal to any of the provided values or + * expressions. + * + * ```typescript + * // Check if the 'category' field is either "Electronics" or value of field 'primaryType' + * Field.of("category").in("Electronics", Field.of("primaryType")); + * ``` + * + * @param others The values or expressions to check against. + * @return A new `Expr` representing the 'IN' comparison. + */ + in(...others: any[]): In; + in(...others: any[]): In { + const exprOthers = others.map(other => + other instanceof Expr ? other : Constant.of(other) + ); + return new In(this, exprOthers); + } + + /** + * Creates an expression that checks if this expression evaluates to 'NaN' (Not a Number). + * + * ```typescript + * // Check if the result of a calculation is NaN + * Field.of("value").divide(0).isNaN(); + * ``` + * + * @return A new `Expr` representing the 'isNaN' check. + */ + isNaN(): IsNan { + return new IsNan(this); + } + + /** + * Creates an expression that checks if a field exists in the document. + * + * ```typescript + * // Check if the document has a field named "phoneNumber" + * Field.of("phoneNumber").exists(); + * ``` + * + * @return A new `Expr` representing the 'exists' check. + */ + exists(): Exists { + return new Exists(this); + } + + /** + * Creates an expression that calculates the character length of a string in UTF-8. + * + * ```typescript + * // Get the character length of the 'name' field in its UTF-8 form. + * Field.of("name").charLength(); + * ``` + * + * @return A new `Expr` representing the length of the string. + */ + charLength(): CharLength { + return new CharLength(this); + } + + /** + * Creates an expression that performs a case-sensitive string comparison. + * + * ```typescript + * // Check if the 'title' field contains the word "guide" (case-sensitive) + * Field.of("title").like("%guide%"); + * ``` + * + * @param pattern The pattern to search for. You can use "%" as a wildcard character. + * @return A new `Expr` representing the 'like' comparison. + */ + like(pattern: string): Like; + + /** + * Creates an expression that performs a case-sensitive string comparison. + * + * ```typescript + * // Check if the 'title' field contains the word "guide" (case-sensitive) + * Field.of("title").like("%guide%"); + * ``` + * + * @param pattern The pattern to search for. You can use "%" as a wildcard character. + * @return A new `Expr` representing the 'like' comparison. + */ + like(pattern: Expr): Like; + like(stringOrExpr: string | Expr): Like { + if (typeof stringOrExpr === 'string') { + return new Like(this, Constant.of(stringOrExpr)); + } + return new Like(this, stringOrExpr as Expr); + } + + /** + * Creates an expression that checks if a string contains a specified regular expression as a + * substring. + * + * ```typescript + * // Check if the 'description' field contains "example" (case-insensitive) + * Field.of("description").regexContains("(?i)example"); + * ``` + * + * @param pattern The regular expression to use for the search. + * @return A new `Expr` representing the 'contains' comparison. + */ + regexContains(pattern: string): RegexContains; + + /** + * Creates an expression that checks if a string contains a specified regular expression as a + * substring. + * + * ```typescript + * // Check if the 'description' field contains the regular expression stored in field 'regex' + * Field.of("description").regexContains(Field.of("regex")); + * ``` + * + * @param pattern The regular expression to use for the search. + * @return A new `Expr` representing the 'contains' comparison. + */ + regexContains(pattern: Expr): RegexContains; + regexContains(stringOrExpr: string | Expr): RegexContains { + if (typeof stringOrExpr === 'string') { + return new RegexContains(this, Constant.of(stringOrExpr)); + } + return new RegexContains(this, stringOrExpr as Expr); + } + + /** + * Creates an expression that checks if a string matches a specified regular expression. + * + * ```typescript + * // Check if the 'email' field matches a valid email pattern + * Field.of("email").regexMatch("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"); + * ``` + * + * @param pattern The regular expression to use for the match. + * @return A new `Expr` representing the regular expression match. + */ + regexMatch(pattern: string): RegexMatch; + + /** + * Creates an expression that checks if a string matches a specified regular expression. + * + * ```typescript + * // Check if the 'email' field matches a regular expression stored in field 'regex' + * Field.of("email").regexMatch(Field.of("regex")); + * ``` + * + * @param pattern The regular expression to use for the match. + * @return A new `Expr` representing the regular expression match. + */ + regexMatch(pattern: Expr): RegexMatch; + regexMatch(stringOrExpr: string | Expr): RegexMatch { + if (typeof stringOrExpr === 'string') { + return new RegexMatch(this, Constant.of(stringOrExpr)); + } + return new RegexMatch(this, stringOrExpr as Expr); + } + + /** + * Creates an expression that checks if a string contains a specified substring. + * + * ```typescript + * // Check if the 'description' field contains "example". + * Field.of("description").strContains("example"); + * ``` + * + * @param substring The substring to search for. + * @return A new `Expr` representing the 'contains' comparison. + */ + strContains(substring: string): StrContains; + + /** + * Creates an expression that checks if a string contains the string represented by another expression. + * + * ```typescript + * // Check if the 'description' field contains the value of the 'keyword' field. + * Field.of("description").strContains(Field.of("keyword")); + * ``` + * + * @param expr The expression representing the substring to search for. + * @return A new `Expr` representing the 'contains' comparison. + */ + strContains(expr: Expr): StrContains; + strContains(stringOrExpr: string | Expr): StrContains { + if (typeof stringOrExpr === 'string') { + return new StrContains(this, Constant.of(stringOrExpr)); + } + return new StrContains(this, stringOrExpr as Expr); + } + + /** + * Creates an expression that checks if a string starts with a given prefix. + * + * ```typescript + * // Check if the 'name' field starts with "Mr." + * Field.of("name").startsWith("Mr."); + * ``` + * + * @param prefix The prefix to check for. + * @return A new `Expr` representing the 'starts with' comparison. + */ + startsWith(prefix: string): StartsWith; + + /** + * Creates an expression that checks if a string starts with a given prefix (represented as an + * expression). + * + * ```typescript + * // Check if the 'fullName' field starts with the value of the 'firstName' field + * Field.of("fullName").startsWith(Field.of("firstName")); + * ``` + * + * @param prefix The prefix expression to check for. + * @return A new `Expr` representing the 'starts with' comparison. + */ + startsWith(prefix: Expr): StartsWith; + startsWith(stringOrExpr: string | Expr): StartsWith { + if (typeof stringOrExpr === 'string') { + return new StartsWith(this, Constant.of(stringOrExpr)); + } + return new StartsWith(this, stringOrExpr as Expr); + } + + /** + * Creates an expression that checks if a string ends with a given postfix. + * + * ```typescript + * // Check if the 'filename' field ends with ".txt" + * Field.of("filename").endsWith(".txt"); + * ``` + * + * @param suffix The postfix to check for. + * @return A new `Expr` representing the 'ends with' comparison. + */ + endsWith(suffix: string): EndsWith; + + /** + * Creates an expression that checks if a string ends with a given postfix (represented as an + * expression). + * + * ```typescript + * // Check if the 'url' field ends with the value of the 'extension' field + * Field.of("url").endsWith(Field.of("extension")); + * ``` + * + * @param suffix The postfix expression to check for. + * @return A new `Expr` representing the 'ends with' comparison. + */ + endsWith(suffix: Expr): EndsWith; + endsWith(stringOrExpr: string | Expr): EndsWith { + if (typeof stringOrExpr === 'string') { + return new EndsWith(this, Constant.of(stringOrExpr)); + } + return new EndsWith(this, stringOrExpr as Expr); + } + + /** + * Creates an expression that converts a string to lowercase. + * + * ```typescript + * // Convert the 'name' field to lowercase + * Field.of("name").toLower(); + * ``` + * + * @return A new `Expr` representing the lowercase string. + */ + toLower(): ToLower { + return new ToLower(this); + } + + /** + * Creates an expression that converts a string to uppercase. + * + * ```typescript + * // Convert the 'title' field to uppercase + * Field.of("title").toUpper(); + * ``` + * + * @return A new `Expr` representing the uppercase string. + */ + toUpper(): ToUpper { + return new ToUpper(this); + } + + /** + * Creates an expression that removes leading and trailing whitespace from a string. + * + * ```typescript + * // Trim whitespace from the 'userInput' field + * Field.of("userInput").trim(); + * ``` + * + * @return A new `Expr` representing the trimmed string. + */ + trim(): Trim { + return new Trim(this); + } + + /** + * Creates an expression that concatenates string expressions together. + * + * ```typescript + * // Combine the 'firstName', " ", and 'lastName' fields into a single string + * Field.of("firstName").strConcat(Constant.of(" "), Field.of("lastName")); + * ``` + * + * @param elements The expressions (typically strings) to concatenate. + * @return A new `Expr` representing the concatenated string. + */ + strConcat(...elements: Array): StrConcat { + const exprs = elements.map(e => + typeof e === 'string' ? Constant.of(e) : (e as Expr) + ); + return new StrConcat(this, exprs); + } + + /** + * Creates an expression that reverses this string expression. + * + * ```typescript + * // Reverse the value of the 'myString' field. + * Field.of("myString").reverse(); + * ``` + * + * @return A new {@code Expr} representing the reversed string. + */ + reverse(): Reverse { + return new Reverse(this); + } + + /** + * Creates an expression that replaces the first occurrence of a substring within this string expression with another substring. + * + * ```typescript + * // Replace the first occurrence of "hello" with "hi" in the 'message' field + * Field.of("message").replaceFirst("hello", "hi"); + * ``` + * + * @param find The substring to search for. + * @param replace The substring to replace the first occurrence of 'find' with. + * @return A new {@code Expr} representing the string with the first occurrence replaced. + */ + replaceFirst(find: string, replace: string): ReplaceFirst; + + /** + * Creates an expression that replaces the first occurrence of a substring within this string expression with another substring, + * where the substring to find and the replacement substring are specified by expressions. + * + * ```typescript + * // Replace the first occurrence of the value in 'findField' with the value in 'replaceField' in the 'message' field + * Field.of("message").replaceFirst(Field.of("findField"), Field.of("replaceField")); + * ``` + * + * @param find The expression representing the substring to search for. + * @param replace The expression representing the substring to replace the first occurrence of 'find' with. + * @return A new {@code Expr} representing the string with the first occurrence replaced. + */ + replaceFirst(find: Expr, replace: Expr): ReplaceFirst; + replaceFirst(find: Expr | string, replace: Expr | string): ReplaceFirst { + const normalizedFind = typeof find === 'string' ? Constant.of(find) : find; + const normalizedReplace = + typeof replace === 'string' ? Constant.of(replace) : replace; + return new ReplaceFirst( + this, + normalizedFind as Expr, + normalizedReplace as Expr + ); + } + + /** + * Creates an expression that replaces all occurrences of a substring within this string expression with another substring. + * + * ```typescript + * // Replace all occurrences of "hello" with "hi" in the 'message' field + * Field.of("message").replaceAll("hello", "hi"); + * ``` + * + * @param find The substring to search for. + * @param replace The substring to replace all occurrences of 'find' with. + * @return A new {@code Expr} representing the string with all occurrences replaced. + */ + replaceAll(find: string, replace: string): ReplaceAll; + + /** + * Creates an expression that replaces all occurrences of a substring within this string expression with another substring, + * where the substring to find and the replacement substring are specified by expressions. + * + * ```typescript + * // Replace all occurrences of the value in 'findField' with the value in 'replaceField' in the 'message' field + * Field.of("message").replaceAll(Field.of("findField"), Field.of("replaceField")); + * ``` + * + * @param find The expression representing the substring to search for. + * @param replace The expression representing the substring to replace all occurrences of 'find' with. + * @return A new {@code Expr} representing the string with all occurrences replaced. + */ + replaceAll(find: Expr, replace: Expr): ReplaceAll; + replaceAll(find: Expr | string, replace: Expr | string): ReplaceAll { + const normalizedFind = typeof find === 'string' ? Constant.of(find) : find; + const normalizedReplace = + typeof replace === 'string' ? Constant.of(replace) : replace; + return new ReplaceAll( + this, + normalizedFind as Expr, + normalizedReplace as Expr + ); + } + + /** + * Creates an expression that calculates the length of this string expression in bytes. + * + * ```typescript + * // Calculate the length of the 'myString' field in bytes. + * Field.of("myString").byteLength(); + * ``` + * + * @return A new {@code Expr} representing the length of the string in bytes. + */ + byteLength(): ByteLength { + return new ByteLength(this); + } + + /** + * Accesses a value from a map (object) field using the provided key. + * + * ```typescript + * // Get the 'city' value from the 'address' map field + * Field.of("address").mapGet("city"); + * ``` + * + * @param subfield The key to access in the map. + * @return A new `Expr` representing the value associated with the given key in the map. + */ + mapGet(subfield: string): MapGet { + return new MapGet(this, subfield); + } + + /** + * Creates an aggregation that counts the number of stage inputs with valid evaluations of the + * expression or field. + * + * ```typescript + * // Count the total number of products + * Field.of("productId").count().as("totalProducts"); + * ``` + * + * @return A new `Accumulator` representing the 'count' aggregation. + */ + count(): Count { + return new Count(this, false); + } + + /** + * Creates an aggregation that calculates the sum of a numeric field across multiple stage inputs. + * + * ```typescript + * // Calculate the total revenue from a set of orders + * Field.of("orderAmount").sum().as("totalRevenue"); + * ``` + * + * @return A new `Accumulator` representing the 'sum' aggregation. + */ + sum(): Sum { + return new Sum(this, false); + } + + /** + * Creates an aggregation that calculates the average (mean) of a numeric field across multiple + * stage inputs. + * + * ```typescript + * // Calculate the average age of users + * Field.of("age").avg().as("averageAge"); + * ``` + * + * @return A new `Accumulator` representing the 'avg' aggregation. + */ + avg(): Avg { + return new Avg(this, false); + } + + /** + * Creates an aggregation that finds the minimum value of a field across multiple stage inputs. + * + * ```typescript + * // Find the lowest price of all products + * Field.of("price").min().as("lowestPrice"); + * ``` + * + * @return A new `Accumulator` representing the 'min' aggregation. + */ + min(): Min { + return new Min(this, false); + } + + /** + * Creates an aggregation that finds the maximum value of a field across multiple stage inputs. + * + * ```typescript + * // Find the highest score in a leaderboard + * Field.of("score").max().as("highestScore"); + * ``` + * + * @return A new `Accumulator` representing the 'max' aggregation. + */ + max(): Max { + return new Max(this, false); + } + + /** + * Creates an expression that returns the larger value between this expression and another expression, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the larger value between the 'timestamp' field and the current timestamp. + * Field.of("timestamp").logicalMax(Function.currentTimestamp()); + * ``` + * + * @param other The expression to compare with. + * @return A new {@code Expr} representing the logical max operation. + */ + logicalMax(other: Expr): LogicalMax; + + /** + * Creates an expression that returns the larger value between this expression and a constant value, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the larger value between the 'value' field and 10. + * Field.of("value").logicalMax(10); + * ``` + * + * @param other The constant value to compare with. + * @return A new {@code Expr} representing the logical max operation. + */ + logicalMax(other: any): LogicalMax; + logicalMax(other: any): LogicalMax { + if (other instanceof Expr) { + return new LogicalMax(this, other as Expr); + } + return new LogicalMax(this, Constant.of(other)); + } + + /** + * Creates an expression that returns the smaller value between this expression and another expression, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the smaller value between the 'timestamp' field and the current timestamp. + * Field.of("timestamp").logicalMin(Function.currentTimestamp()); + * ``` + * + * @param other The expression to compare with. + * @return A new {@code Expr} representing the logical min operation. + */ + logicalMin(other: Expr): LogicalMin; + + /** + * Creates an expression that returns the smaller value between this expression and a constant value, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the smaller value between the 'value' field and 10. + * Field.of("value").logicalMin(10); + * ``` + * + * @param other The constant value to compare with. + * @return A new {@code Expr} representing the logical min operation. + */ + logicalMin(other: any): LogicalMin; + logicalMin(other: any): LogicalMin { + if (other instanceof Expr) { + return new LogicalMin(this, other as Expr); + } + return new LogicalMin(this, Constant.of(other)); + } + + /** + * Creates an expression that calculates the length (number of dimensions) of this Firestore Vector expression. + * + * ```typescript + * // Get the vector length (dimension) of the field 'embedding'. + * Field.of("embedding").vectorLength(); + * ``` + * + * @return A new {@code Expr} representing the length of the vector. + */ + vectorLength(): VectorLength { + return new VectorLength(this); + } + + /** + * Calculates the cosine distance between two vectors. + * + * ```typescript + * // Calculate the cosine distance between the 'userVector' field and the 'itemVector' field + * Field.of("userVector").cosineDistance(Field.of("itemVector")); + * ``` + * + * @param other The other vector (represented as an Expr) to compare against. + * @return A new `Expr` representing the cosine distance between the two vectors. + */ + cosineDistance(other: Expr): CosineDistance; + /** + * Calculates the Cosine distance between two vectors. + * + * ```typescript + * // Calculate the Cosine distance between the 'location' field and a target location + * Field.of("location").cosineDistance(new VectorValue([37.7749, -122.4194])); + * ``` + * + * @param other The other vector (as a VectorValue) to compare against. + * @return A new `Expr` representing the Cosine* distance between the two vectors. + */ + cosineDistance(other: VectorValue): CosineDistance; + /** + * Calculates the Cosine distance between two vectors. + * + * ```typescript + * // Calculate the Cosine distance between the 'location' field and a target location + * Field.of("location").cosineDistance([37.7749, -122.4194]); + * ``` + * + * @param other The other vector (as an array of numbers) to compare against. + * @return A new `Expr` representing the Cosine distance between the two vectors. + */ + cosineDistance(other: number[]): CosineDistance; + cosineDistance(other: Expr | VectorValue | number[]): CosineDistance { + if (other instanceof Expr) { + return new CosineDistance(this, other as Expr); + } else { + return new CosineDistance( + this, + Constant.vector(other as VectorValue | number[]) + ); + } + } + + /** + * Calculates the dot product between two vectors. + * + * ```typescript + * // Calculate the dot product between a feature vector and a target vector + * Field.of("features").dotProduct([0.5, 0.8, 0.2]); + * ``` + * + * @param other The other vector (as an array of numbers) to calculate with. + * @return A new `Expr` representing the dot product between the two vectors. + */ + dotProduct(other: Expr): DotProduct; + + /** + * Calculates the dot product between two vectors. + * + * ```typescript + * // Calculate the dot product between a feature vector and a target vector + * Field.of("features").dotProduct(new VectorValue([0.5, 0.8, 0.2])); + * ``` + * + * @param other The other vector (as an array of numbers) to calculate with. + * @return A new `Expr` representing the dot product between the two vectors. + */ + dotProduct(other: VectorValue): DotProduct; + + /** + * Calculates the dot product between two vectors. + * + * ```typescript + * // Calculate the dot product between a feature vector and a target vector + * Field.of("features").dotProduct([0.5, 0.8, 0.2]); + * ``` + * + * @param other The other vector (as an array of numbers) to calculate with. + * @return A new `Expr` representing the dot product between the two vectors. + */ + dotProduct(other: number[]): DotProduct; + dotProduct(other: Expr | VectorValue | number[]): DotProduct { + if (other instanceof Expr) { + return new DotProduct(this, other as Expr); + } else { + return new DotProduct( + this, + Constant.vector(other as VectorValue | number[]) + ); + } + } + + /** + * Calculates the Euclidean distance between two vectors. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * Field.of("location").euclideanDistance([37.7749, -122.4194]); + * ``` + * + * @param other The other vector (as an array of numbers) to calculate with. + * @return A new `Expr` representing the Euclidean distance between the two vectors. + */ + euclideanDistance(other: Expr): EuclideanDistance; + + /** + * Calculates the Euclidean distance between two vectors. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * Field.of("location").euclideanDistance(new VectorValue([37.7749, -122.4194])); + * ``` + * + * @param other The other vector (as a VectorValue) to compare against. + * @return A new `Expr` representing the Euclidean distance between the two vectors. + */ + euclideanDistance(other: VectorValue): EuclideanDistance; + + /** + * Calculates the Euclidean distance between two vectors. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * Field.of("location").euclideanDistance([37.7749, -122.4194]); + * ``` + * + * @param other The other vector (as an array of numbers) to compare against. + * @return A new `Expr` representing the Euclidean distance between the two vectors. + */ + euclideanDistance(other: number[]): EuclideanDistance; + euclideanDistance(other: Expr | VectorValue | number[]): EuclideanDistance { + if (other instanceof Expr) { + return new EuclideanDistance(this, other as Expr); + } else { + return new EuclideanDistance( + this, + Constant.vector(other as VectorValue | number[]) + ); + } + } + + /** + * Creates an expression that interprets this expression as the number of microseconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'microseconds' field as microseconds since epoch. + * Field.of("microseconds").unixMicrosToTimestamp(); + * ``` + * + * @return A new {@code Expr} representing the timestamp. + */ + unixMicrosToTimestamp(): UnixMicrosToTimestamp { + return new UnixMicrosToTimestamp(this); + } + + /** + * Creates an expression that converts this timestamp expression to the number of microseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to microseconds since epoch. + * Field.of("timestamp").timestampToUnixMicros(); + * ``` + * + * @return A new {@code Expr} representing the number of microseconds since epoch. + */ + timestampToUnixMicros(): TimestampToUnixMicros { + return new TimestampToUnixMicros(this); + } + + /** + * Creates an expression that interprets this expression as the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'milliseconds' field as milliseconds since epoch. + * Field.of("milliseconds").unixMillisToTimestamp(); + * ``` + * + * @return A new {@code Expr} representing the timestamp. + */ + unixMillisToTimestamp(): UnixMillisToTimestamp { + return new UnixMillisToTimestamp(this); + } + + /** + * Creates an expression that converts this timestamp expression to the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to milliseconds since epoch. + * Field.of("timestamp").timestampToUnixMillis(); + * ``` + * + * @return A new {@code Expr} representing the number of milliseconds since epoch. + */ + timestampToUnixMillis(): TimestampToUnixMillis { + return new TimestampToUnixMillis(this); + } + + /** + * Creates an expression that interprets this expression as the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'seconds' field as seconds since epoch. + * Field.of("seconds").unixSecondsToTimestamp(); + * ``` + * + * @return A new {@code Expr} representing the timestamp. + */ + unixSecondsToTimestamp(): UnixSecondsToTimestamp { + return new UnixSecondsToTimestamp(this); + } + + /** + * Creates an expression that converts this timestamp expression to the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to seconds since epoch. + * Field.of("timestamp").timestampToUnixSeconds(); + * ``` + * + * @return A new {@code Expr} representing the number of seconds since epoch. + */ + timestampToUnixSeconds(): TimestampToUnixSeconds { + return new TimestampToUnixSeconds(this); + } + + /** + * Creates an expression that adds a specified amount of time to this timestamp expression. + * + * ```typescript + * // Add some duration determined by field 'unit' and 'amount' to the 'timestamp' field. + * Field.of("timestamp").timestampAdd(Field.of("unit"), Field.of("amount")); + * ``` + * + * @param unit The expression evaluates to unit of time, must be one of 'microsecond', 'millisecond', 'second', 'minute', 'hour', 'day'. + * @param amount The expression evaluates to amount of the unit. + * @return A new {@code Expr} representing the resulting timestamp. + */ + timestampAdd(unit: Expr, amount: Expr): TimestampAdd; + + /** + * Creates an expression that adds a specified amount of time to this timestamp expression. + * + * ```typescript + * // Add 1 day to the 'timestamp' field. + * Field.of("timestamp").timestampAdd("day", 1); + * ``` + * + * @param unit The unit of time to add (e.g., "day", "hour"). + * @param amount The amount of time to add. + * @return A new {@code Expr} representing the resulting timestamp. + */ + timestampAdd( + unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', + amount: number + ): TimestampAdd; + timestampAdd( + unit: + | Expr + | 'microsecond' + | 'millisecond' + | 'second' + | 'minute' + | 'hour' + | 'day', + amount: Expr | number + ): TimestampAdd { + const normalizedUnit = typeof unit === 'string' ? Constant.of(unit) : unit; + const normalizedAmount = + typeof amount === 'number' ? Constant.of(amount) : amount; + return new TimestampAdd( + this, + normalizedUnit as Expr, + normalizedAmount as Expr + ); + } + + /** + * Creates an expression that subtracts a specified amount of time from this timestamp expression. + * + * ```typescript + * // Subtract some duration determined by field 'unit' and 'amount' from the 'timestamp' field. + * Field.of("timestamp").timestampSub(Field.of("unit"), Field.of("amount")); + * ``` + * + * @param unit The expression evaluates to unit of time, must be one of 'microsecond', 'millisecond', 'second', 'minute', 'hour', 'day'. + * @param amount The expression evaluates to amount of the unit. + * @return A new {@code Expr} representing the resulting timestamp. + */ + timestampSub(unit: Expr, amount: Expr): TimestampSub; + + /** + * Creates an expression that subtracts a specified amount of time from this timestamp expression. + * + * ```typescript + * // Subtract 1 day from the 'timestamp' field. + * Field.of("timestamp").timestampSub("day", 1); + * ``` + * + * @param unit The unit of time to subtract (e.g., "day", "hour"). + * @param amount The amount of time to subtract. + * @return A new {@code Expr} representing the resulting timestamp. + */ + timestampSub( + unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', + amount: number + ): TimestampSub; + timestampSub( + unit: + | Expr + | 'microsecond' + | 'millisecond' + | 'second' + | 'minute' + | 'hour' + | 'day', + amount: Expr | number + ): TimestampSub { + const normalizedUnit = typeof unit === 'string' ? Constant.of(unit) : unit; + const normalizedAmount = + typeof amount === 'number' ? Constant.of(amount) : amount; + return new TimestampSub( + this, + normalizedUnit as Expr, + normalizedAmount as Expr + ); + } + + /** + * Creates an {@link Ordering} that sorts documents in ascending order based on this expression. + * + * ```typescript + * // Sort documents by the 'name' field in ascending order + * pipeline().collection("users") + * .sort(Field.of("name").ascending()); + * ``` + * + * @return A new `Ordering` for ascending sorting. + */ + ascending(): Ordering { + return ascending(this); + } + + /** + * Creates an {@link Ordering} that sorts documents in descending order based on this expression. + * + * ```typescript + * // Sort documents by the 'createdAt' field in descending order + * firestore.pipeline().collection("users") + * .sort(Field.of("createdAt").descending()); + * ``` + * + * @return A new `Ordering` for descending sorting. + */ + descending(): Ordering { + return descending(this); + } + + /** + * Assigns an alias to this expression. + * + * Aliases are useful for renaming fields in the output of a stage or for giving meaningful + * names to calculated values. + * + * ```typescript + * // Calculate the total price and assign it the alias "totalPrice" and add it to the output. + * firestore.pipeline().collection("items") + * .addFields(Field.of("price").multiply(Field.of("quantity")).as("totalPrice")); + * ``` + * + * @param name The alias to assign to this expression. + * @return A new {@link ExprWithAlias} that wraps this + * expression and associates it with the provided alias. + */ + as(name: string): ExprWithAlias { + return new ExprWithAlias(this, name); + } + + /** + * @private + * @internal + */ + abstract _toProto(serializer: JsonProtoSerializer): ProtoValue; + + /** + * @private + * @internal + */ + abstract _readUserData(dataReader: UserDataReader): void; +} + +/** + * @beta + */ +export class ExprWithAlias extends Expr implements Selectable { + exprType: ExprType = 'ExprWithAlias'; + selectable = true as const; + + constructor(public expr: T, public alias: string) { + super(); + } + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + throw new Error('ExprWithAlias should not be serialized directly.'); + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void { + this.expr._readUserData(dataReader); + } +} + +/** + * @internal + */ +class ListOfExprs extends Expr { + exprType: ExprType = 'ListOfExprs'; + constructor(private exprs: Expr[]) { + super(); + } + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + return { + arrayValue: { + values: this.exprs.map(p => p._toProto(serializer)!) + } + }; + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void { + this.exprs.forEach((expr: Expr) => expr._readUserData(dataReader)); + } +} + +/** + * @beta + * + * Represents a reference to a field in a Firestore document, or outputs of a {@link Pipeline} stage. + * + *

Field references are used to access document field values in expressions and to specify fields + * for sorting, filtering, and projecting data in Firestore pipelines. + * + *

You can create a `Field` instance using the static {@link #of} method: + * + * ```typescript + * // Create a Field instance for the 'name' field + * const nameField = Field.of("name"); + * + * // Create a Field instance for a nested field 'address.city' + * const cityField = Field.of("address.city"); + * ``` + */ +export class Field extends Expr implements Selectable { + exprType: ExprType = 'Field'; + selectable = true as const; + + private constructor( + private fieldPath: InternalFieldPath, + private pipeline: Pipeline | null = null + ) { + super(); + } + + /** + * Creates a {@code Field} instance representing the field at the given path. + * + * The path can be a simple field name (e.g., "name") or a dot-separated path to a nested field + * (e.g., "address.city"). + * + * ```typescript + * // Create a Field instance for the 'title' field + * const titleField = Field.of("title"); + * + * // Create a Field instance for a nested field 'author.firstName' + * const authorFirstNameField = Field.of("author.firstName"); + * ``` + * + * @param name The path to the field. + * @return A new {@code Field} instance representing the specified field. + */ + static of(name: string): Field; + static of(path: FieldPath): Field; + static of(pipeline: Pipeline, name: string): Field; + static of( + pipelineOrName: Pipeline | string | FieldPath, + name?: string + ): Field { + if (typeof pipelineOrName === 'string') { + if (DOCUMENT_KEY_NAME === pipelineOrName) { + return new Field(documentId()._internalPath); + } + return new Field(fieldPathFromArgument('of', pipelineOrName)); + } else if (pipelineOrName instanceof FieldPath) { + if (documentId().isEqual(pipelineOrName)) { + return new Field(documentId()._internalPath); + } + return new Field(pipelineOrName._internalPath); + } else { + return new Field( + fieldPathFromArgument('of', name!), + pipelineOrName as Pipeline + ); + } + } + + fieldName(): string { + return this.fieldPath.canonicalString(); + } + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + return { + fieldReferenceValue: this.fieldPath.canonicalString() + }; + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void {} +} + +/** + * @beta + */ +export class Fields extends Expr implements Selectable { + exprType: ExprType = 'Field'; + selectable = true as const; + + private constructor(private fields: Field[]) { + super(); + } + + static of(name: string, ...others: string[]): Fields { + return new Fields([Field.of(name), ...others.map(Field.of)]); + } + + static ofAll(): Fields { + return new Fields([]); + } + + fieldList(): Field[] { + return this.fields.map(f => f); + } + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + return { + arrayValue: { + values: this.fields.map(f => f._toProto(serializer)) + } + }; + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void { + this.fields.forEach(expr => expr._readUserData(dataReader)); + } +} + +/** + * @beta + * + * Represents a constant value that can be used in a Firestore pipeline expression. + * + * You can create a `Constant` instance using the static {@link #of} method: + * + * ```typescript + * // Create a Constant instance for the number 10 + * const ten = Constant.of(10); + * + * // Create a Constant instance for the string "hello" + * const hello = Constant.of("hello"); + * ``` + */ +export class Constant extends Expr { + exprType: ExprType = 'Constant'; + + private _protoValue?: ProtoValue; + + private constructor(private value: any) { + super(); + } + + /** + * Creates a `Constant` instance for a number value. + * + * @param value The number value. + * @return A new `Constant` instance. + */ + static of(value: number): Constant; + + /** + * Creates a `Constant` instance for a string value. + * + * @param value The string value. + * @return A new `Constant` instance. + */ + static of(value: string): Constant; + + /** + * Creates a `Constant` instance for a boolean value. + * + * @param value The boolean value. + * @return A new `Constant` instance. + */ + static of(value: boolean): Constant; + + /** + * Creates a `Constant` instance for a null value. + * + * @param value The null value. + * @return A new `Constant` instance. + */ + static of(value: null): Constant; + + /** + * Creates a `Constant` instance for an undefined value. + * + * @param value The undefined value. + * @return A new `Constant` instance. + */ + static of(value: undefined): Constant; + + /** + * Creates a `Constant` instance for a GeoPoint value. + * + * @param value The GeoPoint value. + * @return A new `Constant` instance. + */ + static of(value: GeoPoint): Constant; + + /** + * Creates a `Constant` instance for a Timestamp value. + * + * @param value The Timestamp value. + * @return A new `Constant` instance. + */ + static of(value: Timestamp): Constant; + + /** + * Creates a `Constant` instance for a Date value. + * + * @param value The Date value. + * @return A new `Constant` instance. + */ + static of(value: Date): Constant; + + /** + * Creates a `Constant` instance for a Uint8Array value. + * + * @param value The Uint8Array value. + * @return A new `Constant` instance. + */ + static of(value: Uint8Array): Constant; + + /** + * Creates a `Constant` instance for a DocumentReference value. + * + * @param value The DocumentReference value. + * @return A new `Constant` instance. + */ + static of(value: DocumentReference): Constant; + + // TODO(pipeline) if we make this public, then the Proto types should also be documented + /** + * Creates a `Constant` instance for a Firestore proto value. + * @private + * @internal + * @param value The Firestore proto value. + * @return A new `Constant` instance. + */ + static of(value: ProtoValue): Constant; + + /** + * Creates a `Constant` instance for an array value. + * + * @param value The array value. + * @return A new `Constant` instance. + */ + static of(value: any[]): Constant; + + /** + * Creates a `Constant` instance for a map value. + * + * @param value The map value. + * @return A new `Constant` instance. + */ + static of(value: Map): Constant; + + /** + * Creates a `Constant` instance for a VectorValue value. + * + * @param value The VectorValue value. + * @return A new `Constant` instance. + */ + static of(value: VectorValue): Constant; + + static of(value: any): Constant { + return new Constant(value); + } + + /** + * Creates a `Constant` instance for a VectorValue value. + * + * ```typescript + * // Create a Constant instance for a vector value + * const vectorConstant = Constant.ofVector([1, 2, 3]); + * ``` + * + * @param value The VectorValue value. + * @return A new `Constant` instance. + */ + static vector(value: number[] | VectorValue): Constant { + if (value instanceof VectorValue) { + return new Constant(value); + } else { + return new Constant(new VectorValue(value as number[])); + } + } + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + hardAssert( + this._protoValue !== undefined, + 'Value of this constant has not been serialized to proto value' + ); + return this._protoValue; + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void { + const context = dataReader.createContext( + UserDataSource.Argument, + 'Constant.of' + ); + if (this.value === undefined) { + // TODO how should we treat the value of `undefined`? + this._protoValue = parseData(null, context)!; + } else { + this._protoValue = parseData(this.value, context)!; + } + } +} + +/** + * @beta + * + * This class defines the base class for Firestore {@link Pipeline} functions, which can be evaluated within pipeline + * execution. + * + * Typically, you would not use this class or its children directly. Use either the functions like {@link and}, {@link eq}, + * or the methods on {@link Expr} ({@link Expr#eq}, {@link Expr#lt}, etc) to construct new Function instances. + */ +export class FirestoreFunction extends Expr { + exprType: ExprType = 'Function'; + constructor(private name: string, private params: Expr[]) { + super(); + } + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + return { + functionValue: { + name: this.name, + args: this.params.map(p => p._toProto(serializer)) + } + }; + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void { + this.params.forEach(expr => expr._readUserData(dataReader)); + } +} + +/** + * @beta + */ +export class Add extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('add', [left, right]); + } +} + +/** + * @beta + */ +export class Subtract extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('subtract', [left, right]); + } +} + +/** + * @beta + */ +export class Multiply extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('multiply', [left, right]); + } +} + +/** + * @beta + */ +export class Divide extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('divide', [left, right]); + } +} + +/** + * @beta + */ +export class Mod extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('mod', [left, right]); + } +} + +// /** +// * @beta +// */ +// export class BitAnd extends FirestoreFunction { +// constructor( +// private left: Expr, +// private right: Expr +// ) { +// super('bit_and', [left, right]); +// } +// } +// +// /** +// * @beta +// */ +// export class BitOr extends FirestoreFunction { +// constructor( +// private left: Expr, +// private right: Expr +// ) { +// super('bit_or', [left, right]); +// } +// } +// +// /** +// * @beta +// */ +// export class BitXor extends FirestoreFunction { +// constructor( +// private left: Expr, +// private right: Expr +// ) { +// super('bit_xor', [left, right]); +// } +// } +// +// /** +// * @beta +// */ +// export class BitNot extends FirestoreFunction { +// constructor(private operand: Expr) { +// super('bit_not', [operand]); +// } +// } +// +// /** +// * @beta +// */ +// export class BitLeftShift extends FirestoreFunction { +// constructor( +// private left: Expr, +// private right: Expr +// ) { +// super('bit_left_shift', [left, right]); +// } +// } +// +// /** +// * @beta +// */ +// export class BitRightShift extends FirestoreFunction { +// constructor( +// private left: Expr, +// private right: Expr +// ) { +// super('bit_right_shift', [left, right]); +// } +// } + +/** + * @beta + */ +export class Eq extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private right: Expr) { + super('eq', [left, right]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Neq extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private right: Expr) { + super('neq', [left, right]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Lt extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private right: Expr) { + super('lt', [left, right]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Lte extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private right: Expr) { + super('lte', [left, right]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Gt extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private right: Expr) { + super('gt', [left, right]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Gte extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private right: Expr) { + super('gte', [left, right]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class ArrayConcat extends FirestoreFunction { + constructor(private array: Expr, private elements: Expr[]) { + super('array_concat', [array, ...elements]); + } +} + +/** + * @beta + */ +export class ArrayReverse extends FirestoreFunction { + constructor(private array: Expr) { + super('array_reverse', [array]); + } +} + +/** + * @beta + */ +export class ArrayContains + extends FirestoreFunction + implements FilterCondition +{ + constructor(private array: Expr, private element: Expr) { + super('array_contains', [array, element]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class ArrayContainsAll + extends FirestoreFunction + implements FilterCondition +{ + constructor(private array: Expr, private values: Expr[]) { + super('array_contains_all', [array, new ListOfExprs(values)]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class ArrayContainsAny + extends FirestoreFunction + implements FilterCondition +{ + constructor(private array: Expr, private values: Expr[]) { + super('array_contains_any', [array, new ListOfExprs(values)]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class ArrayLength extends FirestoreFunction { + constructor(private array: Expr) { + super('array_length', [array]); + } +} + +/** + * @beta + */ +export class ArrayElement extends FirestoreFunction { + constructor() { + super('array_element', []); + } +} + +/** + * @beta + */ +export class In extends FirestoreFunction implements FilterCondition { + constructor(private left: Expr, private others: Expr[]) { + super('in', [left, new ListOfExprs(others)]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class IsNan extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr) { + super('is_nan', [expr]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Exists extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr) { + super('exists', [expr]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Not extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr) { + super('not', [expr]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class And extends FirestoreFunction implements FilterCondition { + constructor(private conditions: FilterExpr[]) { + super('and', conditions); + } + + filterable = true as const; +} + +/** + * @beta + */ +export class Or extends FirestoreFunction implements FilterCondition { + constructor(private conditions: FilterExpr[]) { + super('or', conditions); + } + filterable = true as const; +} + +/** + * @beta + */ +export class Xor extends FirestoreFunction implements FilterCondition { + constructor(private conditions: FilterExpr[]) { + super('xor', conditions); + } + filterable = true as const; +} + +/** + * @beta + */ +export class If extends FirestoreFunction implements FilterCondition { + constructor( + private condition: FilterExpr, + private thenExpr: Expr, + private elseExpr: Expr + ) { + super('if', [condition, thenExpr, elseExpr]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class LogicalMax extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('logical_max', [left, right]); + } +} + +/** + * @beta + */ +export class LogicalMin extends FirestoreFunction { + constructor(private left: Expr, private right: Expr) { + super('logical_min', [left, right]); + } +} + +/** + * @beta + */ +export class Reverse extends FirestoreFunction { + constructor(private value: Expr) { + super('reverse', [value]); + } +} + +/** + * @beta + */ +export class ReplaceFirst extends FirestoreFunction { + constructor(private value: Expr, private find: Expr, private replace: Expr) { + super('replace_first', [value, find, replace]); + } +} + +/** + * @beta + */ +export class ReplaceAll extends FirestoreFunction { + constructor(private value: Expr, private find: Expr, private replace: Expr) { + super('replace_all', [value, find, replace]); + } +} + +/** + * @beta + */ +export class CharLength extends FirestoreFunction { + constructor(private value: Expr) { + super('char_length', [value]); + } +} + +/** + * @beta + */ +export class ByteLength extends FirestoreFunction { + constructor(private value: Expr) { + super('byte_length', [value]); + } +} + +/** + * @beta + */ +export class Like extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr, private pattern: Expr) { + super('like', [expr, pattern]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class RegexContains + extends FirestoreFunction + implements FilterCondition +{ + constructor(private expr: Expr, private pattern: Expr) { + super('regex_contains', [expr, pattern]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class RegexMatch extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr, private pattern: Expr) { + super('regex_match', [expr, pattern]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class StrContains extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr, private substring: Expr) { + super('str_contains', [expr, substring]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class StartsWith extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr, private prefix: Expr) { + super('starts_with', [expr, prefix]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class EndsWith extends FirestoreFunction implements FilterCondition { + constructor(private expr: Expr, private suffix: Expr) { + super('ends_with', [expr, suffix]); + } + filterable = true as const; +} + +/** + * @beta + */ +export class ToLower extends FirestoreFunction { + constructor(private expr: Expr) { + super('to_lower', [expr]); + } +} + +/** + * @beta + */ +export class ToUpper extends FirestoreFunction { + constructor(private expr: Expr) { + super('to_upper', [expr]); + } +} + +/** + * @beta + */ +export class Trim extends FirestoreFunction { + constructor(private expr: Expr) { + super('trim', [expr]); + } +} + +/** + * @beta + */ +export class StrConcat extends FirestoreFunction { + constructor(private first: Expr, private rest: Expr[]) { + super('str_concat', [first, ...rest]); + } +} + +/** + * @beta + */ +export class MapGet extends FirestoreFunction { + constructor(map: Expr, name: string) { + super('map_get', [map, Constant.of(name)]); + } +} + +/** + * @beta + */ +export class Count extends FirestoreFunction implements Accumulator { + accumulator = true as const; + constructor(private value: Expr | undefined, private distinct: boolean) { + super('count', value === undefined ? [] : [value]); + } +} + +/** + * @beta + */ +export class Sum extends FirestoreFunction implements Accumulator { + accumulator = true as const; + constructor(private value: Expr, private distinct: boolean) { + super('sum', [value]); + } +} + +/** + * @beta + */ +export class Avg extends FirestoreFunction implements Accumulator { + accumulator = true as const; + constructor(private value: Expr, private distinct: boolean) { + super('avg', [value]); + } +} + +/** + * @beta + */ +export class Min extends FirestoreFunction implements Accumulator { + accumulator = true as const; + constructor(private value: Expr, private distinct: boolean) { + super('min', [value]); + } +} + +/** + * @beta + */ +export class Max extends FirestoreFunction implements Accumulator { + accumulator = true as const; + constructor(private value: Expr, private distinct: boolean) { + super('max', [value]); + } +} + +/** + * @beta + */ +export class CosineDistance extends FirestoreFunction { + constructor(private vector1: Expr, private vector2: Expr) { + super('cosine_distance', [vector1, vector2]); + } +} + +/** + * @beta + */ +export class DotProduct extends FirestoreFunction { + constructor(private vector1: Expr, private vector2: Expr) { + super('dot_product', [vector1, vector2]); + } +} + +/** + * @beta + */ +export class EuclideanDistance extends FirestoreFunction { + constructor(private vector1: Expr, private vector2: Expr) { + super('euclidean_distance', [vector1, vector2]); + } +} + +/** + * @beta + */ +export class VectorLength extends FirestoreFunction { + constructor(private value: Expr) { + super('vector_length', [value]); + } +} + +/** + * @beta + */ +export class UnixMicrosToTimestamp extends FirestoreFunction { + constructor(private input: Expr) { + super('unix_micros_to_timestamp', [input]); + } +} + +/** + * @beta + */ +export class TimestampToUnixMicros extends FirestoreFunction { + constructor(private input: Expr) { + super('timestamp_to_unix_micros', [input]); + } +} + +/** + * @beta + */ +export class UnixMillisToTimestamp extends FirestoreFunction { + constructor(private input: Expr) { + super('unix_millis_to_timestamp', [input]); + } +} + +/** + * @beta + */ +export class TimestampToUnixMillis extends FirestoreFunction { + constructor(private input: Expr) { + super('timestamp_to_unix_millis', [input]); + } +} + +/** + * @beta + */ +export class UnixSecondsToTimestamp extends FirestoreFunction { + constructor(private input: Expr) { + super('unix_seconds_to_timestamp', [input]); + } +} + +/** + * @beta + */ +export class TimestampToUnixSeconds extends FirestoreFunction { + constructor(private input: Expr) { + super('timestamp_to_unix_seconds', [input]); + } +} + +/** + * @beta + */ +export class TimestampAdd extends FirestoreFunction { + constructor( + private timestamp: Expr, + private unit: Expr, + private amount: Expr + ) { + super('timestamp_add', [timestamp, unit, amount]); + } +} + +/** + * @beta + */ +export class TimestampSub extends FirestoreFunction { + constructor( + private timestamp: Expr, + private unit: Expr, + private amount: Expr + ) { + super('timestamp_sub', [timestamp, unit, amount]); + } +} + +/** + * @beta + * + * Creates an expression that adds two expressions together. + * + * ```typescript + * // Add the value of the 'quantity' field and the 'reserve' field. + * add(Field.of("quantity"), Field.of("reserve")); + * ``` + * + * @param left The first expression to add. + * @param right The second expression to add. + * @return A new {@code Expr} representing the addition operation. + */ +export function add(left: Expr, right: Expr): Add; + +/** + * @beta + * + * Creates an expression that adds an expression to a constant value. + * + * ```typescript + * // Add 5 to the value of the 'age' field + * add(Field.of("age"), 5); + * ``` + * + * @param left The expression to add to. + * @param right The constant value to add. + * @return A new {@code Expr} representing the addition operation. + */ +export function add(left: Expr, right: any): Add; + +/** + * @beta + * + * Creates an expression that adds a field's value to an expression. + * + * ```typescript + * // Add the value of the 'quantity' field and the 'reserve' field. + * add("quantity", Field.of("reserve")); + * ``` + * + * @param left The field name to add to. + * @param right The expression to add. + * @return A new {@code Expr} representing the addition operation. + */ +export function add(left: string, right: Expr): Add; + +/** + * @beta + * + * Creates an expression that adds a field's value to a constant value. + * + * ```typescript + * // Add 5 to the value of the 'age' field + * add("age", 5); + * ``` + * + * @param left The field name to add to. + * @param right The constant value to add. + * @return A new {@code Expr} representing the addition operation. + */ +export function add(left: string, right: any): Add; +export function add(left: Expr | string, right: Expr | any): Add { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new Add(normalizedLeft, normalizedRight); +} + +/** + * @beta + * + * Creates an expression that subtracts two expressions. + * + * ```typescript + * // Subtract the 'discount' field from the 'price' field + * subtract(Field.of("price"), Field.of("discount")); + * ``` + * + * @param left The expression to subtract from. + * @param right The expression to subtract. + * @return A new {@code Expr} representing the subtraction operation. + */ +export function subtract(left: Expr, right: Expr): Subtract; + +/** + * @beta + * + * Creates an expression that subtracts a constant value from an expression. + * + * ```typescript + * // Subtract the constant value 2 from the 'value' field + * subtract(Field.of("value"), 2); + * ``` + * + * @param left The expression to subtract from. + * @param right The constant value to subtract. + * @return A new {@code Expr} representing the subtraction operation. + */ +export function subtract(left: Expr, right: any): Subtract; + +/** + * @beta + * + * Creates an expression that subtracts an expression from a field's value. + * + * ```typescript + * // Subtract the 'discount' field from the 'price' field + * subtract("price", Field.of("discount")); + * ``` + * + * @param left The field name to subtract from. + * @param right The expression to subtract. + * @return A new {@code Expr} representing the subtraction operation. + */ +export function subtract(left: string, right: Expr): Subtract; + +/** + * @beta + * + * Creates an expression that subtracts a constant value from a field's value. + * + * ```typescript + * // Subtract 20 from the value of the 'total' field + * subtract("total", 20); + * ``` + * + * @param left The field name to subtract from. + * @param right The constant value to subtract. + * @return A new {@code Expr} representing the subtraction operation. + */ +export function subtract(left: string, right: any): Subtract; +export function subtract(left: Expr | string, right: Expr | any): Subtract { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new Subtract(normalizedLeft, normalizedRight); +} + +/** + * @beta + * + * Creates an expression that multiplies two expressions together. + * + * ```typescript + * // Multiply the 'quantity' field by the 'price' field + * multiply(Field.of("quantity"), Field.of("price")); + * ``` + * + * @param left The first expression to multiply. + * @param right The second expression to multiply. + * @return A new {@code Expr} representing the multiplication operation. + */ +export function multiply(left: Expr, right: Expr): Multiply; + +/** + * @beta + * + * Creates an expression that multiplies an expression by a constant value. + * + * ```typescript + * // Multiply the value of the 'price' field by 2 + * multiply(Field.of("price"), 2); + * ``` + * + * @param left The expression to multiply. + * @param right The constant value to multiply by. + * @return A new {@code Expr} representing the multiplication operation. + */ +export function multiply(left: Expr, right: any): Multiply; + +/** + * @beta + * + * Creates an expression that multiplies a field's value by an expression. + * + * ```typescript + * // Multiply the 'quantity' field by the 'price' field + * multiply("quantity", Field.of("price")); + * ``` + * + * @param left The field name to multiply. + * @param right The expression to multiply by. + * @return A new {@code Expr} representing the multiplication operation. + */ +export function multiply(left: string, right: Expr): Multiply; + +/** + * @beta + * + * Creates an expression that multiplies a field's value by a constant value. + * + * ```typescript + * // Multiply the 'value' field by 2 + * multiply("value", 2); + * ``` + * + * @param left The field name to multiply. + * @param right The constant value to multiply by. + * @return A new {@code Expr} representing the multiplication operation. + */ +export function multiply(left: string, right: any): Multiply; +export function multiply(left: Expr | string, right: Expr | any): Multiply { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new Multiply(normalizedLeft, normalizedRight); +} + +/** + * @beta + * + * Creates an expression that divides two expressions. + * + * ```typescript + * // Divide the 'total' field by the 'count' field + * divide(Field.of("total"), Field.of("count")); + * ``` + * + * @param left The expression to be divided. + * @param right The expression to divide by. + * @return A new {@code Expr} representing the division operation. + */ +export function divide(left: Expr, right: Expr): Divide; + +/** + * @beta + * + * Creates an expression that divides an expression by a constant value. + * + * ```typescript + * // Divide the 'value' field by 10 + * divide(Field.of("value"), 10); + * ``` + * + * @param left The expression to be divided. + * @param right The constant value to divide by. + * @return A new {@code Expr} representing the division operation. + */ +export function divide(left: Expr, right: any): Divide; + +/** + * @beta + * + * Creates an expression that divides a field's value by an expression. + * + * ```typescript + * // Divide the 'total' field by the 'count' field + * divide("total", Field.of("count")); + * ``` + * + * @param left The field name to be divided. + * @param right The expression to divide by. + * @return A new {@code Expr} representing the division operation. + */ +export function divide(left: string, right: Expr): Divide; + +/** + * @beta + * + * Creates an expression that divides a field's value by a constant value. + * + * ```typescript + * // Divide the 'value' field by 10 + * divide("value", 10); + * ``` + * + * @param left The field name to be divided. + * @param right The constant value to divide by. + * @return A new {@code Expr} representing the division operation. + */ +export function divide(left: string, right: any): Divide; +export function divide(left: Expr | string, right: Expr | any): Divide { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new Divide(normalizedLeft, normalizedRight); +} + +/** + * @beta + * + * Creates an expression that calculates the modulo (remainder) of dividing two expressions. + * + * ```typescript + * // Calculate the remainder of dividing 'field1' by 'field2'. + * mod(Field.of("field1"), Field.of("field2")); + * ``` + * + * @param left The dividend expression. + * @param right The divisor expression. + * @return A new {@code Expr} representing the modulo operation. + */ +export function mod(left: Expr, right: Expr): Mod; + +/** + * @beta + * + * Creates an expression that calculates the modulo (remainder) of dividing an expression by a constant. + * + * ```typescript + * // Calculate the remainder of dividing 'field1' by 5. + * mod(Field.of("field1"), 5); + * ``` + * + * @param left The dividend expression. + * @param right The divisor constant. + * @return A new {@code Expr} representing the modulo operation. + */ +export function mod(left: Expr, right: any): Mod; + +/** + * @beta + * + * Creates an expression that calculates the modulo (remainder) of dividing a field's value by an expression. + * + * ```typescript + * // Calculate the remainder of dividing 'field1' by 'field2'. + * mod("field1", Field.of("field2")); + * ``` + * + * @param left The dividend field name. + * @param right The divisor expression. + * @return A new {@code Expr} representing the modulo operation. + */ +export function mod(left: string, right: Expr): Mod; + +/** + * @beta + * + * Creates an expression that calculates the modulo (remainder) of dividing a field's value by a constant. + * + * ```typescript + * // Calculate the remainder of dividing 'field1' by 5. + * mod("field1", 5); + * ``` + * + * @param left The dividend field name. + * @param right The divisor constant. + * @return A new {@code Expr} representing the modulo operation. + */ +export function mod(left: string, right: any): Mod; +export function mod(left: Expr | string, right: Expr | any): Mod { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new Mod(normalizedLeft, normalizedRight); +} + +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise AND operation between two expressions. +// * +// * ```typescript +// * // Calculate the bitwise AND of 'field1' and 'field2'. +// * bitAnd(Field.of("field1"), Field.of("field2")); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand expression. +// * @return A new {@code Expr} representing the bitwise AND operation. +// */ +// export function bitAnd(left: Expr, right: Expr): BitAnd; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise AND operation between an expression and a constant. +// * +// * ```typescript +// * // Calculate the bitwise AND of 'field1' and 0xFF. +// * bitAnd(Field.of("field1"), 0xFF); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand constant. +// * @return A new {@code Expr} representing the bitwise AND operation. +// */ +// export function bitAnd(left: Expr, right: any): BitAnd; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise AND operation between a field and an expression. +// * +// * ```typescript +// * // Calculate the bitwise AND of 'field1' and 'field2'. +// * bitAnd("field1", Field.of("field2")); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand expression. +// * @return A new {@code Expr} representing the bitwise AND operation. +// */ +// export function bitAnd(left: string, right: Expr): BitAnd; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise AND operation between a field and a constant. +// * +// * ```typescript +// * // Calculate the bitwise AND of 'field1' and 0xFF. +// * bitAnd("field1", 0xFF); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand constant. +// * @return A new {@code Expr} representing the bitwise AND operation. +// */ +// export function bitAnd(left: string, right: any): BitAnd; +// export function bitAnd(left: Expr | string, right: Expr | any): BitAnd { +// const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; +// const normalizedRight = right instanceof Expr ? right : Constant.of(right); +// return new BitAnd(normalizedLeft, normalizedRight); +// } +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise OR operation between two expressions. +// * +// * ```typescript +// * // Calculate the bitwise OR of 'field1' and 'field2'. +// * bitOr(Field.of("field1"), Field.of("field2")); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand expression. +// * @return A new {@code Expr} representing the bitwise OR operation. +// */ +// export function bitOr(left: Expr, right: Expr): BitOr; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise OR operation between an expression and a constant. +// * +// * ```typescript +// * // Calculate the bitwise OR of 'field1' and 0xFF. +// * bitOr(Field.of("field1"), 0xFF); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand constant. +// * @return A new {@code Expr} representing the bitwise OR operation. +// */ +// export function bitOr(left: Expr, right: any): BitOr; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise OR operation between a field and an expression. +// * +// * ```typescript +// * // Calculate the bitwise OR of 'field1' and 'field2'. +// * bitOr("field1", Field.of("field2")); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand expression. +// * @return A new {@code Expr} representing the bitwise OR operation. +// */ +// export function bitOr(left: string, right: Expr): BitOr; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise OR operation between a field and a constant. +// * +// * ```typescript +// * // Calculate the bitwise OR of 'field1' and 0xFF. +// * bitOr("field1", 0xFF); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand constant. +// * @return A new {@code Expr} representing the bitwise OR operation. +// */ +// export function bitOr(left: string, right: any): BitOr; +// export function bitOr(left: Expr | string, right: Expr | any): BitOr { +// const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; +// const normalizedRight = right instanceof Expr ? right : Constant.of(right); +// return new BitOr(normalizedLeft, normalizedRight); +// } +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise XOR operation between two expressions. +// * +// * ```typescript +// * // Calculate the bitwise XOR of 'field1' and 'field2'. +// * bitXor(Field.of("field1"), Field.of("field2")); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand expression. +// * @return A new {@code Expr} representing the bitwise XOR operation. +// */ +// export function bitXor(left: Expr, right: Expr): BitXor; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise XOR operation between an expression and a constant. +// * +// * ```typescript +// * // Calculate the bitwise XOR of 'field1' and 0xFF. +// * bitXor(Field.of("field1"), 0xFF); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand constant. +// * @return A new {@code Expr} representing the bitwise XOR operation. +// */ +// export function bitXor(left: Expr, right: any): BitXor; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise XOR operation between a field and an expression. +// * +// * ```typescript +// * // Calculate the bitwise XOR of 'field1' and 'field2'. +// * bitXor("field1", Field.of("field2")); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand expression. +// * @return A new {@code Expr} representing the bitwise XOR operation. +// */ +// export function bitXor(left: string, right: Expr): BitXor; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise XOR operation between a field and a constant. +// * +// * ```typescript +// * // Calculate the bitwise XOR of 'field1' and 0xFF. +// * bitXor("field1", 0xFF); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand constant. +// * @return A new {@code Expr} representing the bitwise XOR operation. +// */ +// export function bitXor(left: string, right: any): BitXor; +// export function bitXor(left: Expr | string, right: Expr | any): BitXor { +// const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; +// const normalizedRight = right instanceof Expr ? right : Constant.of(right); +// return new BitXor(normalizedLeft, normalizedRight); +// } +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise NOT operation to an expression. +// * +// * ```typescript +// * // Calculate the bitwise NOT of 'field1'. +// * bitNot(Field.of("field1")); +// * ``` +// * +// * @param operand The operand expression. +// * @return A new {@code Expr} representing the bitwise NOT operation. +// */ +// export function bitNot(operand: Expr): BitNot; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise NOT operation to a field. +// * +// * ```typescript +// * // Calculate the bitwise NOT of 'field1'. +// * bitNot("field1"); +// * ``` +// * +// * @param operand The operand field name. +// * @return A new {@code Expr} representing the bitwise NOT operation. +// */ +// export function bitNot(operand: string): BitNot; +// export function bitNot(operand: Expr | string): BitNot { +// const normalizedOperand = +// typeof operand === 'string' ? Field.of(operand) : operand; +// return new BitNot(normalizedOperand); +// } +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise left shift operation between two expressions. +// * +// * ```typescript +// * // Calculate the bitwise left shift of 'field1' by 'field2' bits. +// * bitLeftShift(Field.of("field1"), Field.of("field2")); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand expression representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise left shift operation. +// */ +// export function bitLeftShift(left: Expr, right: Expr): BitLeftShift; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise left shift operation between an expression and a constant. +// * +// * ```typescript +// * // Calculate the bitwise left shift of 'field1' by 2 bits. +// * bitLeftShift(Field.of("field1"), 2); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand constant representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise left shift operation. +// */ +// export function bitLeftShift(left: Expr, right: any): BitLeftShift; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise left shift operation between a field and an expression. +// * +// * ```typescript +// * // Calculate the bitwise left shift of 'field1' by 'field2' bits. +// * bitLeftShift("field1", Field.of("field2")); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand expression representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise left shift operation. +// */ +// export function bitLeftShift(left: string, right: Expr): BitLeftShift; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise left shift operation between a field and a constant. +// * +// * ```typescript +// * // Calculate the bitwise left shift of 'field1' by 2 bits. +// * bitLeftShift("field1", 2); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand constant representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise left shift operation. +// */ +// export function bitLeftShift(left: string, right: any): BitLeftShift; +// export function bitLeftShift( +// left: Expr | string, +// right: Expr | any +// ): BitLeftShift { +// const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; +// const normalizedRight = right instanceof Expr ? right : Constant.of(right); +// return new BitLeftShift(normalizedLeft, normalizedRight); +// } +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise right shift operation between two expressions. +// * +// * ```typescript +// * // Calculate the bitwise right shift of 'field1' by 'field2' bits. +// * bitRightShift(Field.of("field1"), Field.of("field2")); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand expression representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise right shift operation. +// */ +// export function bitRightShift(left: Expr, right: Expr): BitRightShift; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise right shift operation between an expression and a constant. +// * +// * ```typescript +// * // Calculate the bitwise right shift of 'field1' by 2 bits. +// * bitRightShift(Field.of("field1"), 2); +// * ``` +// * +// * @param left The left operand expression. +// * @param right The right operand constant representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise right shift operation. +// */ +// export function bitRightShift(left: Expr, right: any): BitRightShift; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise right shift operation between a field and an expression. +// * +// * ```typescript +// * // Calculate the bitwise right shift of 'field1' by 'field2' bits. +// * bitRightShift("field1", Field.of("field2")); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand expression representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise right shift operation. +// */ +// export function bitRightShift(left: string, right: Expr): BitRightShift; +// +// /** +// * @beta +// * +// * Creates an expression that applies a bitwise right shift operation between a field and a constant. +// * +// * ```typescript +// * // Calculate the bitwise right shift of 'field1' by 2 bits. +// * bitRightShift("field1", 2); +// * ``` +// * +// * @param left The left operand field name. +// * @param right The right operand constant representing the number of bits to shift. +// * @return A new {@code Expr} representing the bitwise right shift operation. +// */ +// export function bitRightShift(left: string, right: any): BitRightShift; +// export function bitRightShift( +// left: Expr | string, +// right: Expr | any +// ): BitRightShift { +// const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; +// const normalizedRight = right instanceof Expr ? right : Constant.of(right); +// return new BitRightShift(normalizedLeft, normalizedRight); +// } + +/** + * @beta + * + * Creates an expression that checks if two expressions are equal. + * + * ```typescript + * // Check if the 'age' field is equal to an expression + * eq(Field.of("age"), Field.of("minAge").add(10)); + * ``` + * + * @param left The first expression to compare. + * @param right The second expression to compare. + * @return A new `Expr` representing the equality comparison. + */ +export function eq(left: Expr, right: Expr): Eq; + +/** + * @beta + * + * Creates an expression that checks if an expression is equal to a constant value. + * + * ```typescript + * // Check if the 'age' field is equal to 21 + * eq(Field.of("age"), 21); + * ``` + * + * @param left The expression to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the equality comparison. + */ +export function eq(left: Expr, right: any): Eq; + +/** + * @beta + * + * Creates an expression that checks if a field's value is equal to an expression. + * + * ```typescript + * // Check if the 'age' field is equal to the 'limit' field + * eq("age", Field.of("limit")); + * ``` + * + * @param left The field name to compare. + * @param right The expression to compare to. + * @return A new `Expr` representing the equality comparison. + */ +export function eq(left: string, right: Expr): Eq; + +/** + * @beta + * + * Creates an expression that checks if a field's value is equal to a constant value. + * + * ```typescript + * // Check if the 'city' field is equal to string constant "London" + * eq("city", "London"); + * ``` + * + * @param left The field name to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the equality comparison. + */ +export function eq(left: string, right: any): Eq; +export function eq(left: Expr | string, right: any): Eq { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const rightExpr = right instanceof Expr ? right : Constant.of(right); + return new Eq(leftExpr, rightExpr); +} + +/** + * @beta + * + * Creates an expression that checks if two expressions are not equal. + * + * ```typescript + * // Check if the 'status' field is not equal to field 'finalState' + * neq(Field.of("status"), Field.of("finalState")); + * ``` + * + * @param left The first expression to compare. + * @param right The second expression to compare. + * @return A new `Expr` representing the inequality comparison. + */ +export function neq(left: Expr, right: Expr): Neq; + +/** + * @beta + * + * Creates an expression that checks if an expression is not equal to a constant value. + * + * ```typescript + * // Check if the 'status' field is not equal to "completed" + * neq(Field.of("status"), "completed"); + * ``` + * + * @param left The expression to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the inequality comparison. + */ +export function neq(left: Expr, right: any): Neq; + +/** + * @beta + * + * Creates an expression that checks if a field's value is not equal to an expression. + * + * ```typescript + * // Check if the 'status' field is not equal to the value of 'expectedStatus' + * neq("status", Field.of("expectedStatus")); + * ``` + * + * @param left The field name to compare. + * @param right The expression to compare to. + * @return A new `Expr` representing the inequality comparison. + */ +export function neq(left: string, right: Expr): Neq; + +/** + * @beta + * + * Creates an expression that checks if a field's value is not equal to a constant value. + * + * ```typescript + * // Check if the 'country' field is not equal to "USA" + * neq("country", "USA"); + * ``` + * + * @param left The field name to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the inequality comparison. + */ +export function neq(left: string, right: any): Neq; +export function neq(left: Expr | string, right: any): Neq { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const rightExpr = right instanceof Expr ? right : Constant.of(right); + return new Neq(leftExpr, rightExpr); +} + +/** + * @beta + * + * Creates an expression that checks if the first expression is less than the second expression. + * + * ```typescript + * // Check if the 'age' field is less than 30 + * lt(Field.of("age"), Field.of("limit")); + * ``` + * + * @param left The first expression to compare. + * @param right The second expression to compare. + * @return A new `Expr` representing the less than comparison. + */ +export function lt(left: Expr, right: Expr): Lt; + +/** + * @beta + * + * Creates an expression that checks if an expression is less than a constant value. + * + * ```typescript + * // Check if the 'age' field is less than 30 + * lt(Field.of("age"), 30); + * ``` + * + * @param left The expression to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the less than comparison. + */ +export function lt(left: Expr, right: any): Lt; + +/** + * @beta + * + * Creates an expression that checks if a field's value is less than an expression. + * + * ```typescript + * // Check if the 'age' field is less than the 'limit' field + * lt("age", Field.of("limit")); + * ``` + * + * @param left The field name to compare. + * @param right The expression to compare to. + * @return A new `Expr` representing the less than comparison. + */ +export function lt(left: string, right: Expr): Lt; + +/** + * @beta + * + * Creates an expression that checks if a field's value is less than a constant value. + * + * ```typescript + * // Check if the 'price' field is less than 50 + * lt("price", 50); + * ``` + * + * @param left The field name to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the less than comparison. + */ +export function lt(left: string, right: any): Lt; +export function lt(left: Expr | string, right: any): Lt { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const rightExpr = right instanceof Expr ? right : Constant.of(right); + return new Lt(leftExpr, rightExpr); +} + +/** + * @beta + * + * Creates an expression that checks if the first expression is less than or equal to the second + * expression. + * + * ```typescript + * // Check if the 'quantity' field is less than or equal to 20 + * lte(Field.of("quantity"), Field.of("limit")); + * ``` + * + * @param left The first expression to compare. + * @param right The second expression to compare. + * @return A new `Expr` representing the less than or equal to comparison. + */ +export function lte(left: Expr, right: Expr): Lte; + +/** + * @beta + * + * Creates an expression that checks if an expression is less than or equal to a constant value. + * + * ```typescript + * // Check if the 'quantity' field is less than or equal to 20 + * lte(Field.of("quantity"), 20); + * ``` + * + * @param left The expression to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the less than or equal to comparison. + */ +export function lte(left: Expr, right: any): Lte; + +/** + * Creates an expression that checks if a field's value is less than or equal to an expression. + * + * ```typescript + * // Check if the 'quantity' field is less than or equal to the 'limit' field + * lte("quantity", Field.of("limit")); + * ``` + * + * @param left The field name to compare. + * @param right The expression to compare to. + * @return A new `Expr` representing the less than or equal to comparison. + */ +export function lte(left: string, right: Expr): Lte; + +/** + * @beta + * + * Creates an expression that checks if a field's value is less than or equal to a constant value. + * + * ```typescript + * // Check if the 'score' field is less than or equal to 70 + * lte("score", 70); + * ``` + * + * @param left The field name to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the less than or equal to comparison. + */ +export function lte(left: string, right: any): Lte; +export function lte(left: Expr | string, right: any): Lte { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const rightExpr = right instanceof Expr ? right : Constant.of(right); + return new Lte(leftExpr, rightExpr); +} + +/** + * @beta + * + * Creates an expression that checks if the first expression is greater than the second + * expression. + * + * ```typescript + * // Check if the 'age' field is greater than 18 + * gt(Field.of("age"), Constant(9).add(9)); + * ``` + * + * @param left The first expression to compare. + * @param right The second expression to compare. + * @return A new `Expr` representing the greater than comparison. + */ +export function gt(left: Expr, right: Expr): Gt; + +/** + * @beta + * + * Creates an expression that checks if an expression is greater than a constant value. + * + * ```typescript + * // Check if the 'age' field is greater than 18 + * gt(Field.of("age"), 18); + * ``` + * + * @param left The expression to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the greater than comparison. + */ +export function gt(left: Expr, right: any): Gt; + +/** + * @beta + * + * Creates an expression that checks if a field's value is greater than an expression. + * + * ```typescript + * // Check if the value of field 'age' is greater than the value of field 'limit' + * gt("age", Field.of("limit")); + * ``` + * + * @param left The field name to compare. + * @param right The expression to compare to. + * @return A new `Expr` representing the greater than comparison. + */ +export function gt(left: string, right: Expr): Gt; + +/** + * @beta + * + * Creates an expression that checks if a field's value is greater than a constant value. + * + * ```typescript + * // Check if the 'price' field is greater than 100 + * gt("price", 100); + * ``` + * + * @param left The field name to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the greater than comparison. + */ +export function gt(left: string, right: any): Gt; +export function gt(left: Expr | string, right: any): Gt { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const rightExpr = right instanceof Expr ? right : Constant.of(right); + return new Gt(leftExpr, rightExpr); +} + +/** + * @beta + * + * Creates an expression that checks if the first expression is greater than or equal to the + * second expression. + * + * ```typescript + * // Check if the 'quantity' field is greater than or equal to the field "threshold" + * gte(Field.of("quantity"), Field.of("threshold")); + * ``` + * + * @param left The first expression to compare. + * @param right The second expression to compare. + * @return A new `Expr` representing the greater than or equal to comparison. + */ +export function gte(left: Expr, right: Expr): Gte; + +/** + * @beta + * + * Creates an expression that checks if an expression is greater than or equal to a constant + * value. + * + * ```typescript + * // Check if the 'quantity' field is greater than or equal to 10 + * gte(Field.of("quantity"), 10); + * ``` + * + * @param left The expression to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the greater than or equal to comparison. + */ +export function gte(left: Expr, right: any): Gte; + +/** + * @beta + * + * Creates an expression that checks if a field's value is greater than or equal to an expression. + * + * ```typescript + * // Check if the value of field 'age' is greater than or equal to the value of field 'limit' + * gte("age", Field.of("limit")); + * ``` + * + * @param left The field name to compare. + * @param right The expression to compare to. + * @return A new `Expr` representing the greater than or equal to comparison. + */ +export function gte(left: string, right: Expr): Gte; + +/** + * @beta + * + * Creates an expression that checks if a field's value is greater than or equal to a constant + * value. + * + * ```typescript + * // Check if the 'score' field is greater than or equal to 80 + * gte("score", 80); + * ``` + * + * @param left The field name to compare. + * @param right The constant value to compare to. + * @return A new `Expr` representing the greater than or equal to comparison. + */ +export function gte(left: string, right: any): Gte; +export function gte(left: Expr | string, right: any): Gte { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const rightExpr = right instanceof Expr ? right : Constant.of(right); + return new Gte(leftExpr, rightExpr); +} + +/** + * @beta + * + * Creates an expression that concatenates an array expression with other arrays. + * + * ```typescript + * // Combine the 'items' array with two new item arrays + * arrayConcat(Field.of("items"), [Field.of("newItems"), Field.of("otherItems")]); + * ``` + * + * @param array The array expression to concatenate to. + * @param elements The array expressions to concatenate. + * @return A new {@code Expr} representing the concatenated array. + */ +export function arrayConcat(array: Expr, elements: Expr[]): ArrayConcat; + +/** + * @beta + * + * Creates an expression that concatenates an array expression with other arrays and/or values. + * + * ```typescript + * // Combine the 'tags' array with a new array + * arrayConcat(Field.of("tags"), ["newTag1", "newTag2"]); + * ``` + * + * @param array The array expression to concatenate to. + * @param elements The array expressions or single values to concatenate. + * @return A new {@code Expr} representing the concatenated array. + */ +export function arrayConcat(array: Expr, elements: any[]): ArrayConcat; + +/** + * @beta + * + * Creates an expression that concatenates a field's array value with other arrays. + * + * ```typescript + * // Combine the 'items' array with two new item arrays + * arrayConcat("items", [Field.of("newItems"), Field.of("otherItems")]); + * ``` + * + * @param array The field name containing array values. + * @param elements The array expressions to concatenate. + * @return A new {@code Expr} representing the concatenated array. + */ +export function arrayConcat(array: string, elements: Expr[]): ArrayConcat; + +/** + * @beta + * + * Creates an expression that concatenates a field's array value with other arrays and/or values. + * + * ```typescript + * // Combine the 'tags' array with a new array + * arrayConcat("tags", ["newTag1", "newTag2"]); + * ``` + * + * @param array The field name containing array values. + * @param elements The array expressions or single values to concatenate. + * @return A new {@code Expr} representing the concatenated array. + */ +export function arrayConcat(array: string, elements: any[]): ArrayConcat; +export function arrayConcat( + array: Expr | string, + elements: any[] +): ArrayConcat { + const arrayExpr = array instanceof Expr ? array : Field.of(array); + const exprValues = elements.map(element => + element instanceof Expr ? element : Constant.of(element) + ); + return new ArrayConcat(arrayExpr, exprValues); +} + +/** + * @beta + * + * Creates an expression that checks if an array expression contains a specific element. + * + * ```typescript + * // Check if the 'colors' array contains the value of field 'selectedColor' + * arrayContains(Field.of("colors"), Field.of("selectedColor")); + * ``` + * + * @param array The array expression to check. + * @param element The element to search for in the array. + * @return A new {@code Expr} representing the 'array_contains' comparison. + */ +export function arrayContains(array: Expr, element: Expr): ArrayContains; + +/** + * @beta + * + * Creates an expression that checks if an array expression contains a specific element. + * + * ```typescript + * // Check if the 'colors' array contains "red" + * arrayContains(Field.of("colors"), "red"); + * ``` + * + * @param array The array expression to check. + * @param element The element to search for in the array. + * @return A new {@code Expr} representing the 'array_contains' comparison. + */ +export function arrayContains(array: Expr, element: any): ArrayContains; + +/** + * @beta + * + * Creates an expression that checks if a field's array value contains a specific element. + * + * ```typescript + * // Check if the 'colors' array contains the value of field 'selectedColor' + * arrayContains("colors", Field.of("selectedColor")); + * ``` + * + * @param array The field name to check. + * @param element The element to search for in the array. + * @return A new {@code Expr} representing the 'array_contains' comparison. + */ +export function arrayContains(array: string, element: Expr): ArrayContains; + +/** + * @beta + * + * Creates an expression that checks if a field's array value contains a specific value. + * + * ```typescript + * // Check if the 'colors' array contains "red" + * arrayContains("colors", "red"); + * ``` + * + * @param array The field name to check. + * @param element The element to search for in the array. + * @return A new {@code Expr} representing the 'array_contains' comparison. + */ +export function arrayContains(array: string, element: any): ArrayContains; +export function arrayContains( + array: Expr | string, + element: any +): ArrayContains { + const arrayExpr = array instanceof Expr ? array : Field.of(array); + const elementExpr = element instanceof Expr ? element : Constant.of(element); + return new ArrayContains(arrayExpr, elementExpr); +} + +/** + * @beta + * + * Creates an expression that checks if an array expression contains any of the specified + * elements. + * + * ```typescript + * // Check if the 'categories' array contains either values from field "cate1" or "Science" + * arrayContainsAny(Field.of("categories"), [Field.of("cate1"), "Science"]); + * ``` + * + * @param array The array expression to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_any' comparison. + */ +export function arrayContainsAny(array: Expr, values: Expr[]): ArrayContainsAny; + +/** + * @beta + * + * Creates an expression that checks if an array expression contains any of the specified + * elements. + * + * ```typescript + * // Check if the 'categories' array contains either values from field "cate1" or "Science" + * arrayContainsAny(Field.of("categories"), [Field.of("cate1"), "Science"]); + * ``` + * + * @param array The array expression to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_any' comparison. + */ +export function arrayContainsAny(array: Expr, values: any[]): ArrayContainsAny; + +/** + * @beta + * + * Creates an expression that checks if a field's array value contains any of the specified + * elements. + * + * ```typescript + * // Check if the 'groups' array contains either the value from the 'userGroup' field + * // or the value "guest" + * arrayContainsAny("categories", [Field.of("cate1"), "Science"]); + * ``` + * + * @param array The field name to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_any' comparison. + */ +export function arrayContainsAny( + array: string, + values: Expr[] +): ArrayContainsAny; + +/** + * @beta + * + * Creates an expression that checks if a field's array value contains any of the specified + * elements. + * + * ```typescript + * // Check if the 'groups' array contains either the value from the 'userGroup' field + * // or the value "guest" + * arrayContainsAny("categories", [Field.of("cate1"), "Science"]); + * ``` + * + * @param array The field name to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_any' comparison. + */ +export function arrayContainsAny( + array: string, + values: any[] +): ArrayContainsAny; +export function arrayContainsAny( + array: Expr | string, + values: any[] +): ArrayContainsAny { + const arrayExpr = array instanceof Expr ? array : Field.of(array); + const exprValues = values.map(value => + value instanceof Expr ? value : Constant.of(value) + ); + return new ArrayContainsAny(arrayExpr, exprValues); +} + +/** + * @beta + * + * Creates an expression that checks if an array expression contains all the specified elements. + * + * ```typescript + * // Check if the 'tags' array contains both of the values from field 'tag1', 'tag2' and "tag3" + * arrayContainsAll(Field.of("tags"), [Field.of("tag1"), "SciFi", "Adventure"]); + * ``` + * + * @param array The array expression to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_all' comparison. + */ +export function arrayContainsAll(array: Expr, values: Expr[]): ArrayContainsAll; + +/** + * @beta + * + * Creates an expression that checks if an array expression contains all the specified elements. + * + * ```typescript + * // Check if the 'tags' array contains both of the values from field 'tag1', 'tag2' and "tag3" + * arrayContainsAll(Field.of("tags"), [Field.of("tag1"), "SciFi", "Adventure"]); + * ``` + * + * @param array The array expression to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_all' comparison. + */ +export function arrayContainsAll(array: Expr, values: any[]): ArrayContainsAll; + +/** + * @beta + * + * Creates an expression that checks if a field's array value contains all the specified values or + * expressions. + * + * ```typescript + * // Check if the 'tags' array contains both of the values from field 'tag1' and "tag2" + * arrayContainsAll("tags", [Field.of("tag1"), "SciFi", "Adventure"]); + * ``` + * + * @param array The field name to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_all' comparison. + */ +export function arrayContainsAll( + array: string, + values: Expr[] +): ArrayContainsAll; + +/** + * @beta + * + * Creates an expression that checks if a field's array value contains all the specified values or + * expressions. + * + * ```typescript + * // Check if the 'tags' array contains both of the values from field 'tag1' and "tag2" + * arrayContainsAll("tags", [Field.of("tag1"), "SciFi", "Adventure"]); + * ``` + * + * @param array The field name to check. + * @param values The elements to check for in the array. + * @return A new {@code Expr} representing the 'array_contains_all' comparison. + */ +export function arrayContainsAll( + array: string, + values: any[] +): ArrayContainsAll; +export function arrayContainsAll( + array: Expr | string, + values: any[] +): ArrayContainsAll { + const arrayExpr = array instanceof Expr ? array : Field.of(array); + const exprValues = values.map(value => + value instanceof Expr ? value : Constant.of(value) + ); + return new ArrayContainsAll(arrayExpr, exprValues); +} + +/** + * @beta + * + * Creates an expression that calculates the length of an array expression. + * + * ```typescript + * // Get the number of items in the 'cart' array + * arrayLength(Field.of("cart")); + * ``` + * + * @param array The array expression to calculate the length of. + * @return A new {@code Expr} representing the length of the array. + */ +export function arrayLength(array: Expr): ArrayLength { + return new ArrayLength(array); +} + +/** + * @beta + * + * Creates an expression that checks if an expression is equal to any of the provided values or + * expressions. + * + * ```typescript + * // Check if the 'category' field is either "Electronics" or value of field 'primaryType' + * inAny(Field.of("category"), [Constant.of("Electronics"), Field.of("primaryType")]); + * ``` + * + * @param element The expression to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'IN' comparison. + */ +export function inAny(element: Expr, others: Expr[]): In; + +/** + * @beta + * + * Creates an expression that checks if an expression is equal to any of the provided values or + * expressions. + * + * ```typescript + * // Check if the 'category' field is either "Electronics" or value of field 'primaryType' + * inAny(Field.of("category"), ["Electronics", Field.of("primaryType")]); + * ``` + * + * @param element The expression to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'IN' comparison. + */ +export function inAny(element: Expr, others: any[]): In; + +/** + * @beta + * + * Creates an expression that checks if a field's value is equal to any of the provided values or + * expressions. + * + * ```typescript + * // Check if the 'category' field is either "Electronics" or value of field 'primaryType' + * inAny("category", [Constant.of("Electronics"), Field.of("primaryType")]); + * ``` + * + * @param element The field to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'IN' comparison. + */ +export function inAny(element: string, others: Expr[]): In; + +/** + * @beta + * + * Creates an expression that checks if a field's value is equal to any of the provided values or + * expressions. + * + * ```typescript + * // Check if the 'category' field is either "Electronics" or value of field 'primaryType' + * inAny("category", ["Electronics", Field.of("primaryType")]); + * ``` + * + * @param element The field to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'IN' comparison. + */ +export function inAny(element: string, others: any[]): In; +export function inAny(element: Expr | string, others: any[]): In { + const elementExpr = element instanceof Expr ? element : Field.of(element); + const exprOthers = others.map(other => + other instanceof Expr ? other : Constant.of(other) + ); + return new In(elementExpr, exprOthers); +} + +/** + * @beta + * + * Creates an expression that checks if an expression is not equal to any of the provided values + * or expressions. + * + * ```typescript + * // Check if the 'status' field is neither "pending" nor the value of 'rejectedStatus' + * notInAny(Field.of("status"), [Constant.of("pending"), Field.of("rejectedStatus")]); + * ``` + * + * @param element The expression to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'NOT IN' comparison. + */ +export function notInAny(element: Expr, others: Expr[]): Not; + +/** + * @beta + * + * Creates an expression that checks if an expression is not equal to any of the provided values + * or expressions. + * + * ```typescript + * // Check if the 'status' field is neither "pending" nor the value of 'rejectedStatus' + * notInAny(Field.of("status"), ["pending", Field.of("rejectedStatus")]); + * ``` + * + * @param element The expression to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'NOT IN' comparison. + */ +export function notInAny(element: Expr, others: any[]): Not; + +/** + * @beta + * + * Creates an expression that checks if a field's value is not equal to any of the provided values + * or expressions. + * + * ```typescript + * // Check if the 'status' field is neither "pending" nor the value of 'rejectedStatus' + * notInAny("status", [Constant.of("pending"), Field.of("rejectedStatus")]); + * ``` + * + * @param element The field name to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'NOT IN' comparison. + */ +export function notInAny(element: string, others: Expr[]): Not; + +/** + * @beta + * + * Creates an expression that checks if a field's value is not equal to any of the provided values + * or expressions. + * + * ```typescript + * // Check if the 'status' field is neither "pending" nor the value of 'rejectedStatus' + * notInAny("status", ["pending", Field.of("rejectedStatus")]); + * ``` + * + * @param element The field name to compare. + * @param others The values to check against. + * @return A new {@code Expr} representing the 'NOT IN' comparison. + */ +export function notInAny(element: string, others: any[]): Not; +export function notInAny(element: Expr | string, others: any[]): Not { + const elementExpr = element instanceof Expr ? element : Field.of(element); + const exprOthers = others.map(other => + other instanceof Expr ? other : Constant.of(other) + ); + return new Not(new In(elementExpr, exprOthers)); +} + +/** + * @beta + * + * Creates an expression that performs a logical 'XOR' (exclusive OR) operation on multiple filter + * conditions. + * + * ```typescript + * // Check if only one of the conditions is true: 'age' greater than 18, 'city' is "London", + * // or 'status' is "active". + * const condition = xor( + * gt("age", 18), + * eq("city", "London"), + * eq("status", "active")); + * ``` + * + * @param left The first filter condition. + * @param right Additional filter conditions to 'XOR' together. + * @return A new {@code Expr} representing the logical 'XOR' operation. + */ +export function xor(left: FilterExpr, ...right: FilterExpr[]): Xor { + return new Xor([left, ...right]); +} + +/** + * @beta + * + * Creates a conditional expression that evaluates to a 'then' expression if a condition is true + * and an 'else' expression if the condition is false. + * + * ```typescript + * // If 'age' is greater than 18, return "Adult"; otherwise, return "Minor". + * ifFunction( + * gt("age", 18), Constant.of("Adult"), Constant.of("Minor")); + * ``` + * + * @param condition The condition to evaluate. + * @param thenExpr The expression to evaluate if the condition is true. + * @param elseExpr The expression to evaluate if the condition is false. + * @return A new {@code Expr} representing the conditional expression. + */ +export function ifFunction( + condition: FilterExpr, + thenExpr: Expr, + elseExpr: Expr +): If { + return new If(condition, thenExpr, elseExpr); +} + +/** + * @beta + * + * Creates an expression that negates a filter condition. + * + * ```typescript + * // Find documents where the 'completed' field is NOT true + * not(eq("completed", true)); + * ``` + * + * @param filter The filter condition to negate. + * @return A new {@code Expr} representing the negated filter condition. + */ +export function not(filter: FilterExpr): Not { + return new Not(filter); +} + +/** + * @beta + * + * Creates an expression that returns the larger value between two expressions, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the larger value between the 'field1' field and the 'field2' field. + * logicalMax(Field.of("field1"), Field.of("field2")); + * ``` + * + * @param left The left operand expression. + * @param right The right operand expression. + * @return A new {@code Expr} representing the logical max operation. + */ +export function logicalMax(left: Expr, right: Expr): LogicalMax; + +/** + * @beta + * + * Creates an expression that returns the larger value between an expression and a constant value, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the larger value between the 'value' field and 10. + * logicalMax(Field.of("value"), 10); + * ``` + * + * @param left The left operand expression. + * @param right The right operand constant. + * @return A new {@code Expr} representing the logical max operation. + */ +export function logicalMax(left: Expr, right: any): LogicalMax; + +/** + * @beta + * + * Creates an expression that returns the larger value between a field and an expression, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the larger value between the 'field1' field and the 'field2' field. + * logicalMax("field1", Field.of('field2')); + * ``` + * + * @param left The left operand field name. + * @param right The right operand expression. + * @return A new {@code Expr} representing the logical max operation. + */ +export function logicalMax(left: string, right: Expr): LogicalMax; + +/** + * @beta + * + * Creates an expression that returns the larger value between a field and a constant value, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the larger value between the 'value' field and 10. + * logicalMax("value", 10); + * ``` + * + * @param left The left operand field name. + * @param right The right operand constant. + * @return A new {@code Expr} representing the logical max operation. + */ +export function logicalMax(left: string, right: any): LogicalMax; +export function logicalMax(left: Expr | string, right: Expr | any): LogicalMax { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new LogicalMax(normalizedLeft, normalizedRight); +} + +/** + * @beta + * + * Creates an expression that returns the smaller value between two expressions, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the smaller value between the 'field1' field and the 'field2' field. + * logicalMin(Field.of("field1"), Field.of("field2")); + * ``` + * + * @param left The left operand expression. + * @param right The right operand expression. + * @return A new {@code Expr} representing the logical min operation. + */ +export function logicalMin(left: Expr, right: Expr): LogicalMin; + +/** + * @beta + * + * Creates an expression that returns the smaller value between an expression and a constant value, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the smaller value between the 'value' field and 10. + * logicalMin(Field.of("value"), 10); + * ``` + * + * @param left The left operand expression. + * @param right The right operand constant. + * @return A new {@code Expr} representing the logical min operation. + */ +export function logicalMin(left: Expr, right: any): LogicalMin; + +/** + * @beta + * + * Creates an expression that returns the smaller value between a field and an expression, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the smaller value between the 'field1' field and the 'field2' field. + * logicalMin("field1", Field.of("field2")); + * ``` + * + * @param left The left operand field name. + * @param right The right operand expression. + * @return A new {@code Expr} representing the logical min operation. + */ +export function logicalMin(left: string, right: Expr): LogicalMin; + +/** + * @beta + * + * Creates an expression that returns the smaller value between a field and a constant value, based on Firestore's value type ordering. + * + * ```typescript + * // Returns the smaller value between the 'value' field and 10. + * logicalMin("value", 10); + * ``` + * + * @param left The left operand field name. + * @param right The right operand constant. + * @return A new {@code Expr} representing the logical min operation. + */ +export function logicalMin(left: string, right: any): LogicalMin; +export function logicalMin(left: Expr | string, right: Expr | any): LogicalMin { + const normalizedLeft = typeof left === 'string' ? Field.of(left) : left; + const normalizedRight = right instanceof Expr ? right : Constant.of(right); + return new LogicalMin(normalizedLeft, normalizedRight); +} + +/** + * @beta + * + * Creates an expression that checks if a field exists. + * + * ```typescript + * // Check if the document has a field named "phoneNumber" + * exists(Field.of("phoneNumber")); + * ``` + * + * @param value An expression evaluates to the name of the field to check. + * @return A new {@code Expr} representing the 'exists' check. + */ +export function exists(value: Expr): Exists; + +/** + * @beta + * + * Creates an expression that checks if a field exists. + * + * ```typescript + * // Check if the document has a field named "phoneNumber" + * exists("phoneNumber"); + * ``` + * + * @param field The field name to check. + * @return A new {@code Expr} representing the 'exists' check. + */ +export function exists(field: string): Exists; +export function exists(valueOrField: Expr | string): Exists { + const valueExpr = + valueOrField instanceof Expr ? valueOrField : Field.of(valueOrField); + return new Exists(valueExpr); +} + +/** + * @beta + * + * Creates an expression that checks if an expression evaluates to 'NaN' (Not a Number). + * + * ```typescript + * // Check if the result of a calculation is NaN + * isNaN(Field.of("value").divide(0)); + * ``` + * + * @param value The expression to check. + * @return A new {@code Expr} representing the 'isNaN' check. + */ +export function isNan(value: Expr): IsNan; + +/** + * @beta + * + * Creates an expression that checks if a field's value evaluates to 'NaN' (Not a Number). + * + * ```typescript + * // Check if the result of a calculation is NaN + * isNaN("value"); + * ``` + * + * @param value The name of the field to check. + * @return A new {@code Expr} representing the 'isNaN' check. + */ +export function isNan(value: string): IsNan; +export function isNan(value: Expr | string): IsNan { + const valueExpr = value instanceof Expr ? value : Field.of(value); + return new IsNan(valueExpr); +} + +/** + * @beta + * + * Creates an expression that reverses a string. + * + * ```typescript + * // Reverse the value of the 'myString' field. + * reverse(Field.of("myString")); + * ``` + * + * @param expr The expression representing the string to reverse. + * @return A new {@code Expr} representing the reversed string. + */ +export function reverse(expr: Expr): Reverse; + +/** + * @beta + * + * Creates an expression that reverses a string represented by a field. + * + * ```typescript + * // Reverse the value of the 'myString' field. + * reverse("myString"); + * ``` + * + * @param field The name of the field representing the string to reverse. + * @return A new {@code Expr} representing the reversed string. + */ +export function reverse(field: string): Reverse; +export function reverse(expr: Expr | string): Reverse { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new Reverse(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that replaces the first occurrence of a substring within a string with another substring. + * + * ```typescript + * // Replace the first occurrence of "hello" with "hi" in the 'message' field. + * replaceFirst(Field.of("message"), "hello", "hi"); + * ``` + * + * @param value The expression representing the string to perform the replacement on. + * @param find The substring to search for. + * @param replace The substring to replace the first occurrence of 'find' with. + * @return A new {@code Expr} representing the string with the first occurrence replaced. + */ +export function replaceFirst( + value: Expr, + find: string, + replace: string +): ReplaceFirst; + +/** + * @beta + * + * Creates an expression that replaces the first occurrence of a substring within a string with another substring, + * where the substring to find and the replacement substring are specified by expressions. + * + * ```typescript + * // Replace the first occurrence of the value in 'findField' with the value in 'replaceField' in the 'message' field. + * replaceFirst(Field.of("message"), Field.of("findField"), Field.of("replaceField")); + * ``` + * + * @param value The expression representing the string to perform the replacement on. + * @param find The expression representing the substring to search for. + * @param replace The expression representing the substring to replace the first occurrence of 'find' with. + * @return A new {@code Expr} representing the string with the first occurrence replaced. + */ +export function replaceFirst( + value: Expr, + find: Expr, + replace: Expr +): ReplaceFirst; + +/** + * @beta + * + * Creates an expression that replaces the first occurrence of a substring within a string represented by a field with another substring. + * + * ```typescript + * // Replace the first occurrence of "hello" with "hi" in the 'message' field. + * replaceFirst("message", "hello", "hi"); + * ``` + * + * @param field The name of the field representing the string to perform the replacement on. + * @param find The substring to search for. + * @param replace The substring to replace the first occurrence of 'find' with. + * @return A new {@code Expr} representing the string with the first occurrence replaced. + */ +export function replaceFirst( + field: string, + find: string, + replace: string +): ReplaceFirst; +export function replaceFirst( + value: Expr | string, + find: Expr | string, + replace: Expr | string +): ReplaceFirst { + const normalizedValue = typeof value === 'string' ? Field.of(value) : value; + const normalizedFind = typeof find === 'string' ? Constant.of(find) : find; + const normalizedReplace = + typeof replace === 'string' ? Constant.of(replace) : replace; + return new ReplaceFirst(normalizedValue, normalizedFind, normalizedReplace); +} + +/** + * @beta + * + * Creates an expression that replaces all occurrences of a substring within a string with another substring. + * + * ```typescript + * // Replace all occurrences of "hello" with "hi" in the 'message' field. + * replaceAll(Field.of("message"), "hello", "hi"); + * ``` + * + * @param value The expression representing the string to perform the replacement on. + * @param find The substring to search for. + * @param replace The substring to replace all occurrences of 'find' with. + * @return A new {@code Expr} representing the string with all occurrences replaced. + */ +export function replaceAll( + value: Expr, + find: string, + replace: string +): ReplaceAll; + +/** + * @beta + * + * Creates an expression that replaces all occurrences of a substring within a string with another substring, + * where the substring to find and the replacement substring are specified by expressions. + * + * ```typescript + * // Replace all occurrences of the value in 'findField' with the value in 'replaceField' in the 'message' field. + * replaceAll(Field.of("message"), Field.of("findField"), Field.of("replaceField")); + * ``` + * + * @param value The expression representing the string to perform the replacement on. + * @param find The expression representing the substring to search for. + * @param replace The expression representing the substring to replace all occurrences of 'find' with. + * @return A new {@code Expr} representing the string with all occurrences replaced. + */ +export function replaceAll(value: Expr, find: Expr, replace: Expr): ReplaceAll; + +/** + * @beta + * + * Creates an expression that replaces all occurrences of a substring within a string represented by a field with another substring. + * + * ```typescript + * // Replace all occurrences of "hello" with "hi" in the 'message' field. + * replaceAll("message", "hello", "hi"); + * ``` + * + * @param field The name of the field representing the string to perform the replacement on. + * @param find The substring to search for. + * @param replace The substring to replace all occurrences of 'find' with. + * @return A new {@code Expr} representing the string with all occurrences replaced. + */ +export function replaceAll( + field: string, + find: string, + replace: string +): ReplaceAll; +export function replaceAll( + value: Expr | string, + find: Expr | string, + replace: Expr | string +): ReplaceAll { + const normalizedValue = typeof value === 'string' ? Field.of(value) : value; + const normalizedFind = typeof find === 'string' ? Constant.of(find) : find; + const normalizedReplace = + typeof replace === 'string' ? Constant.of(replace) : replace; + return new ReplaceAll(normalizedValue, normalizedFind, normalizedReplace); +} + +/** + * @beta + * + * Creates an expression that calculates the byte length of a string in UTF-8, or just the length of a Blob. + * + * ```typescript + * // Calculate the length of the 'myString' field in bytes. + * byteLength(Field.of("myString")); + * ``` + * + * @param expr The expression representing the string. + * @return A new {@code Expr} representing the length of the string in bytes. + */ +export function byteLength(expr: Expr): ByteLength; + +/** + * @beta + * + * Creates an expression that calculates the length of a string represented by a field in UTF-8 bytes, or just the length of a Blob. + * + * ```typescript + * // Calculate the length of the 'myString' field in bytes. + * byteLength("myString"); + * ``` + * + * @param field The name of the field representing the string. + * @return A new {@code Expr} representing the length of the string in bytes. + */ +export function byteLength(field: string): ByteLength; +export function byteLength(expr: Expr | string): ByteLength { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new ByteLength(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that calculates the character length of a string field in UTF8. + * + * ```typescript + * // Get the character length of the 'name' field in UTF-8. + * strLength("name"); + * ``` + * + * @param field The name of the field containing the string. + * @return A new {@code Expr} representing the length of the string. + */ +export function charLength(field: string): CharLength; + +/** + * @beta + * + * Creates an expression that calculates the character length of a string expression in UTF-8. + * + * ```typescript + * // Get the character length of the 'name' field in UTF-8. + * strLength(Field.of("name")); + * ``` + * + * @param expr The expression representing the string to calculate the length of. + * @return A new {@code Expr} representing the length of the string. + */ +export function charLength(expr: Expr): CharLength; +export function charLength(value: Expr | string): CharLength { + const valueExpr = value instanceof Expr ? value : Field.of(value); + return new CharLength(valueExpr); +} + +/** + * @beta + * + * Creates an expression that performs a case-sensitive wildcard string comparison against a + * field. + * + * ```typescript + * // Check if the 'title' field contains the string "guide" + * like("title", "%guide%"); + * ``` + * + * @param left The name of the field containing the string. + * @param pattern The pattern to search for. You can use "%" as a wildcard character. + * @return A new {@code Expr} representing the 'like' comparison. + */ +export function like(left: string, pattern: string): Like; + +/** + * @beta + * + * Creates an expression that performs a case-sensitive wildcard string comparison against a + * field. + * + * ```typescript + * // Check if the 'title' field contains the string "guide" + * like("title", Field.of("pattern")); + * ``` + * + * @param left The name of the field containing the string. + * @param pattern The pattern to search for. You can use "%" as a wildcard character. + * @return A new {@code Expr} representing the 'like' comparison. + */ +export function like(left: string, pattern: Expr): Like; + +/** + * @beta + * + * Creates an expression that performs a case-sensitive wildcard string comparison. + * + * ```typescript + * // Check if the 'title' field contains the string "guide" + * like(Field.of("title"), "%guide%"); + * ``` + * + * @param left The expression representing the string to perform the comparison on. + * @param pattern The pattern to search for. You can use "%" as a wildcard character. + * @return A new {@code Expr} representing the 'like' comparison. + */ +export function like(left: Expr, pattern: string): Like; + +/** + * @beta + * + * Creates an expression that performs a case-sensitive wildcard string comparison. + * + * ```typescript + * // Check if the 'title' field contains the string "guide" + * like(Field.of("title"), Field.of("pattern")); + * ``` + * + * @param left The expression representing the string to perform the comparison on. + * @param pattern The pattern to search for. You can use "%" as a wildcard character. + * @return A new {@code Expr} representing the 'like' comparison. + */ +export function like(left: Expr, pattern: Expr): Like; +export function like(left: Expr | string, pattern: Expr | string): Like { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const patternExpr = pattern instanceof Expr ? pattern : Constant.of(pattern); + return new Like(leftExpr, patternExpr); +} + +/** + * @beta + * + * Creates an expression that checks if a string field contains a specified regular expression as + * a substring. + * + * ```typescript + * // Check if the 'description' field contains "example" (case-insensitive) + * regexContains("description", "(?i)example"); + * ``` + * + * @param left The name of the field containing the string. + * @param pattern The regular expression to use for the search. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function regexContains(left: string, pattern: string): RegexContains; + +/** + * @beta + * + * Creates an expression that checks if a string field contains a specified regular expression as + * a substring. + * + * ```typescript + * // Check if the 'description' field contains "example" (case-insensitive) + * regexContains("description", Field.of("pattern")); + * ``` + * + * @param left The name of the field containing the string. + * @param pattern The regular expression to use for the search. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function regexContains(left: string, pattern: Expr): RegexContains; + +/** + * @beta + * + * Creates an expression that checks if a string expression contains a specified regular + * expression as a substring. + * + * ```typescript + * // Check if the 'description' field contains "example" (case-insensitive) + * regexContains(Field.of("description"), "(?i)example"); + * ``` + * + * @param left The expression representing the string to perform the comparison on. + * @param pattern The regular expression to use for the search. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function regexContains(left: Expr, pattern: string): RegexContains; + +/** + * @beta + * + * Creates an expression that checks if a string expression contains a specified regular + * expression as a substring. + * + * ```typescript + * // Check if the 'description' field contains "example" (case-insensitive) + * regexContains(Field.of("description"), Field.of("pattern")); + * ``` + * + * @param left The expression representing the string to perform the comparison on. + * @param pattern The regular expression to use for the search. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function regexContains(left: Expr, pattern: Expr): RegexContains; +export function regexContains( + left: Expr | string, + pattern: Expr | string +): RegexContains { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const patternExpr = pattern instanceof Expr ? pattern : Constant.of(pattern); + return new RegexContains(leftExpr, patternExpr); +} + +/** + * @beta + * + * Creates an expression that checks if a string field matches a specified regular expression. + * + * ```typescript + * // Check if the 'email' field matches a valid email pattern + * regexMatch("email", "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"); + * ``` + * + * @param left The name of the field containing the string. + * @param pattern The regular expression to use for the match. + * @return A new {@code Expr} representing the regular expression match. + */ +export function regexMatch(left: string, pattern: string): RegexMatch; + +/** + * @beta + * + * Creates an expression that checks if a string field matches a specified regular expression. + * + * ```typescript + * // Check if the 'email' field matches a valid email pattern + * regexMatch("email", Field.of("pattern")); + * ``` + * + * @param left The name of the field containing the string. + * @param pattern The regular expression to use for the match. + * @return A new {@code Expr} representing the regular expression match. + */ +export function regexMatch(left: string, pattern: Expr): RegexMatch; + +/** + * @beta + * + * Creates an expression that checks if a string expression matches a specified regular + * expression. + * + * ```typescript + * // Check if the 'email' field matches a valid email pattern + * regexMatch(Field.of("email"), "[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"); + * ``` + * + * @param left The expression representing the string to match against. + * @param pattern The regular expression to use for the match. + * @return A new {@code Expr} representing the regular expression match. + */ +export function regexMatch(left: Expr, pattern: string): RegexMatch; + +/** + * @beta + * + * Creates an expression that checks if a string expression matches a specified regular + * expression. + * + * ```typescript + * // Check if the 'email' field matches a valid email pattern + * regexMatch(Field.of("email"), Field.of("pattern")); + * ``` + * + * @param left The expression representing the string to match against. + * @param pattern The regular expression to use for the match. + * @return A new {@code Expr} representing the regular expression match. + */ +export function regexMatch(left: Expr, pattern: Expr): RegexMatch; +export function regexMatch( + left: Expr | string, + pattern: Expr | string +): RegexMatch { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const patternExpr = pattern instanceof Expr ? pattern : Constant.of(pattern); + return new RegexMatch(leftExpr, patternExpr); +} + +/** + * @beta + * + * Creates an expression that checks if a string field contains a specified substring. + * + * ```typescript + * // Check if the 'description' field contains "example". + * strContains("description", "example"); + * ``` + * + * @param left The name of the field containing the string. + * @param substring The substring to search for. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function strContains(left: string, substring: string): StrContains; + +/** + * @beta + * + * Creates an expression that checks if a string field contains a substring specified by an expression. + * + * ```typescript + * // Check if the 'description' field contains the value of the 'keyword' field. + * strContains("description", Field.of("keyword")); + * ``` + * + * @param left The name of the field containing the string. + * @param substring The expression representing the substring to search for. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function strContains(left: string, substring: Expr): StrContains; + +/** + * @beta + * + * Creates an expression that checks if a string expression contains a specified substring. + * + * ```typescript + * // Check if the 'description' field contains "example". + * strContains(Field.of("description"), "example"); + * ``` + * + * @param left The expression representing the string to perform the comparison on. + * @param substring The substring to search for. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function strContains(left: Expr, substring: string): StrContains; + +/** + * @beta + * + * Creates an expression that checks if a string expression contains a substring specified by another expression. + * + * ```typescript + * // Check if the 'description' field contains the value of the 'keyword' field. + * strContains(Field.of("description"), Field.of("keyword")); + * ``` + * + * @param left The expression representing the string to perform the comparison on. + * @param substring The expression representing the substring to search for. + * @return A new {@code Expr} representing the 'contains' comparison. + */ +export function strContains(left: Expr, substring: Expr): StrContains; +export function strContains( + left: Expr | string, + substring: Expr | string +): StrContains { + const leftExpr = left instanceof Expr ? left : Field.of(left); + const substringExpr = + substring instanceof Expr ? substring : Constant.of(substring); + return new StrContains(leftExpr, substringExpr); +} + +/** + * @beta + * + * Creates an expression that checks if a field's value starts with a given prefix. + * + * ```typescript + * // Check if the 'name' field starts with "Mr." + * startsWith("name", "Mr."); + * ``` + * + * @param expr The field name to check. + * @param prefix The prefix to check for. + * @return A new {@code Expr} representing the 'starts with' comparison. + */ +export function startsWith(expr: string, prefix: string): StartsWith; + +/** + * @beta + * + * Creates an expression that checks if a field's value starts with a given prefix. + * + * ```typescript + * // Check if the 'fullName' field starts with the value of the 'firstName' field + * startsWith("fullName", Field.of("firstName")); + * ``` + * + * @param expr The field name to check. + * @param prefix The expression representing the prefix. + * @return A new {@code Expr} representing the 'starts with' comparison. + */ +export function startsWith(expr: string, prefix: Expr): StartsWith; + +/** + * @beta + * + * Creates an expression that checks if a string expression starts with a given prefix. + * + * ```typescript + * // Check if the result of concatenating 'firstName' and 'lastName' fields starts with "Mr." + * startsWith(Field.of("fullName"), "Mr."); + * ``` + * + * @param expr The expression to check. + * @param prefix The prefix to check for. + * @return A new {@code Expr} representing the 'starts with' comparison. + */ +export function startsWith(expr: Expr, prefix: string): StartsWith; + +/** + * @beta + * + * Creates an expression that checks if a string expression starts with a given prefix. + * + * ```typescript + * // Check if the result of concatenating 'firstName' and 'lastName' fields starts with "Mr." + * startsWith(Field.of("fullName"), Field.of("prefix")); + * ``` + * + * @param expr The expression to check. + * @param prefix The prefix to check for. + * @return A new {@code Expr} representing the 'starts with' comparison. + */ +export function startsWith(expr: Expr, prefix: Expr): StartsWith; +export function startsWith( + expr: Expr | string, + prefix: Expr | string +): StartsWith { + const exprLeft = expr instanceof Expr ? expr : Field.of(expr); + const prefixExpr = prefix instanceof Expr ? prefix : Constant.of(prefix); + return new StartsWith(exprLeft, prefixExpr); +} + +/** + * @beta + * + * Creates an expression that checks if a field's value ends with a given postfix. + * + * ```typescript + * // Check if the 'filename' field ends with ".txt" + * endsWith("filename", ".txt"); + * ``` + * + * @param expr The field name to check. + * @param suffix The postfix to check for. + * @return A new {@code Expr} representing the 'ends with' comparison. + */ +export function endsWith(expr: string, suffix: string): EndsWith; + +/** + * @beta + * + * Creates an expression that checks if a field's value ends with a given postfix. + * + * ```typescript + * // Check if the 'url' field ends with the value of the 'extension' field + * endsWith("url", Field.of("extension")); + * ``` + * + * @param expr The field name to check. + * @param suffix The expression representing the postfix. + * @return A new {@code Expr} representing the 'ends with' comparison. + */ +export function endsWith(expr: string, suffix: Expr): EndsWith; + +/** + * @beta + * + * Creates an expression that checks if a string expression ends with a given postfix. + * + * ```typescript + * // Check if the result of concatenating 'firstName' and 'lastName' fields ends with "Jr." + * endsWith(Field.of("fullName"), "Jr."); + * ``` + * + * @param expr The expression to check. + * @param suffix The postfix to check for. + * @return A new {@code Expr} representing the 'ends with' comparison. + */ +export function endsWith(expr: Expr, suffix: string): EndsWith; + +/** + * @beta + * + * Creates an expression that checks if a string expression ends with a given postfix. + * + * ```typescript + * // Check if the result of concatenating 'firstName' and 'lastName' fields ends with "Jr." + * endsWith(Field.of("fullName"), Constant.of("Jr.")); + * ``` + * + * @param expr The expression to check. + * @param suffix The postfix to check for. + * @return A new {@code Expr} representing the 'ends with' comparison. + */ +export function endsWith(expr: Expr, suffix: Expr): EndsWith; +export function endsWith(expr: Expr | string, suffix: Expr | string): EndsWith { + const exprLeft = expr instanceof Expr ? expr : Field.of(expr); + const suffixExpr = suffix instanceof Expr ? suffix : Constant.of(suffix); + return new EndsWith(exprLeft, suffixExpr); +} + +/** + * @beta + * + * Creates an expression that converts a string field to lowercase. + * + * ```typescript + * // Convert the 'name' field to lowercase + * toLower("name"); + * ``` + * + * @param expr The name of the field containing the string. + * @return A new {@code Expr} representing the lowercase string. + */ +export function toLower(expr: string): ToLower; + +/** + * @beta + * + * Creates an expression that converts a string expression to lowercase. + * + * ```typescript + * // Convert the 'name' field to lowercase + * toLower(Field.of("name")); + * ``` + * + * @param expr The expression representing the string to convert to lowercase. + * @return A new {@code Expr} representing the lowercase string. + */ +export function toLower(expr: Expr): ToLower; +export function toLower(expr: Expr | string): ToLower { + return new ToLower(expr instanceof Expr ? expr : Field.of(expr)); +} + +/** + * @beta + * + * Creates an expression that converts a string field to uppercase. + * + * ```typescript + * // Convert the 'title' field to uppercase + * toUpper("title"); + * ``` + * + * @param expr The name of the field containing the string. + * @return A new {@code Expr} representing the uppercase string. + */ +export function toUpper(expr: string): ToUpper; + +/** + * @beta + * + * Creates an expression that converts a string expression to uppercase. + * + * ```typescript + * // Convert the 'title' field to uppercase + * toUppercase(Field.of("title")); + * ``` + * + * @param expr The expression representing the string to convert to uppercase. + * @return A new {@code Expr} representing the uppercase string. + */ +export function toUpper(expr: Expr): ToUpper; +export function toUpper(expr: Expr | string): ToUpper { + return new ToUpper(expr instanceof Expr ? expr : Field.of(expr)); +} + +/** + * @beta + * + * Creates an expression that removes leading and trailing whitespace from a string field. + * + * ```typescript + * // Trim whitespace from the 'userInput' field + * trim("userInput"); + * ``` + * + * @param expr The name of the field containing the string. + * @return A new {@code Expr} representing the trimmed string. + */ +export function trim(expr: string): Trim; + +/** + * @beta + * + * Creates an expression that removes leading and trailing whitespace from a string expression. + * + * ```typescript + * // Trim whitespace from the 'userInput' field + * trim(Field.of("userInput")); + * ``` + * + * @param expr The expression representing the string to trim. + * @return A new {@code Expr} representing the trimmed string. + */ +export function trim(expr: Expr): Trim; +export function trim(expr: Expr | string): Trim { + return new Trim(expr instanceof Expr ? expr : Field.of(expr)); +} + +/** + * @beta + * + * Creates an expression that concatenates string functions, fields or constants together. + * + * ```typescript + * // Combine the 'firstName', " ", and 'lastName' fields into a single string + * strConcat("firstName", " ", Field.of("lastName")); + * ``` + * + * @param first The field name containing the initial string value. + * @param elements The expressions (typically strings) to concatenate. + * @return A new {@code Expr} representing the concatenated string. + */ +export function strConcat( + first: string, + ...elements: Array +): StrConcat; + +/** + * @beta + * Creates an expression that concatenates string expressions together. + * + * ```typescript + * // Combine the 'firstName', " ", and 'lastName' fields into a single string + * strConcat(Field.of("firstName"), " ", Field.of("lastName")); + * ``` + * + * @param first The initial string expression to concatenate to. + * @param elements The expressions (typically strings) to concatenate. + * @return A new {@code Expr} representing the concatenated string. + */ +export function strConcat( + first: Expr, + ...elements: Array +): StrConcat; +export function strConcat( + first: string | Expr, + ...elements: Array +): StrConcat { + const exprs = elements.map(e => (e instanceof Expr ? e : Constant.of(e))); + return new StrConcat(first instanceof Expr ? first : Field.of(first), exprs); +} + +/** + * @beta + * + * Accesses a value from a map (object) field using the provided key. + * + * ```typescript + * // Get the 'city' value from the 'address' map field + * mapGet("address", "city"); + * ``` + * + * @param mapField The field name of the map field. + * @param subField The key to access in the map. + * @return A new {@code Expr} representing the value associated with the given key in the map. + */ +export function mapGet(mapField: string, subField: string): MapGet; + +/** + * @beta + * + * Accesses a value from a map (object) expression using the provided key. + * + * ```typescript + * // Get the 'city' value from the 'address' map field + * mapGet(Field.of("address"), "city"); + * ``` + * + * @param mapExpr The expression representing the map. + * @param subField The key to access in the map. + * @return A new {@code Expr} representing the value associated with the given key in the map. + */ +export function mapGet(mapExpr: Expr, subField: string): MapGet; +export function mapGet(fieldOrExpr: string | Expr, subField: string): MapGet { + return new MapGet( + typeof fieldOrExpr === 'string' ? Field.of(fieldOrExpr) : fieldOrExpr, + subField + ); +} + +/** + * @beta + * + * Creates an aggregation that counts the total number of stage inputs. + * + * ```typescript + * // Count the total number of users + * countAll().as("totalUsers"); + * ``` + * + * @return A new {@code Accumulator} representing the 'countAll' aggregation. + */ +export function countAll(): Count { + return new Count(undefined, false); +} + +/** + * @beta + * + * Creates an aggregation that counts the number of stage inputs with valid evaluations of the + * provided expression. + * + * ```typescript + * // Count the number of items where the price is greater than 10 + * count(Field.of("price").gt(10)).as("expensiveItemCount"); + * ``` + * + * @param value The expression to count. + * @return A new {@code Accumulator} representing the 'count' aggregation. + */ +export function countFunction(value: Expr): Count; + +/** + * Creates an aggregation that counts the number of stage inputs with valid evaluations of the + * provided field. + * + * ```typescript + * // Count the total number of products + * count("productId").as("totalProducts"); + * ``` + * + * @param value The name of the field to count. + * @return A new {@code Accumulator} representing the 'count' aggregation. + */ +export function countFunction(value: string): Count; +export function countFunction(value: Expr | string): Count { + const exprValue = value instanceof Expr ? value : Field.of(value); + return new Count(exprValue, false); +} + +/** + * @beta + * + * Creates an aggregation that calculates the sum of values from an expression across multiple + * stage inputs. + * + * ```typescript + * // Calculate the total revenue from a set of orders + * sum(Field.of("orderAmount")).as("totalRevenue"); + * ``` + * + * @param value The expression to sum up. + * @return A new {@code Accumulator} representing the 'sum' aggregation. + */ +export function sumFunction(value: Expr): Sum; + +/** + * @beta + * + * Creates an aggregation that calculates the sum of a field's values across multiple stage + * inputs. + * + * ```typescript + * // Calculate the total revenue from a set of orders + * sum("orderAmount").as("totalRevenue"); + * ``` + * + * @param value The name of the field containing numeric values to sum up. + * @return A new {@code Accumulator} representing the 'sum' aggregation. + */ +export function sumFunction(value: string): Sum; +export function sumFunction(value: Expr | string): Sum { + const exprValue = value instanceof Expr ? value : Field.of(value); + return new Sum(exprValue, false); +} + +/** + * @beta + * + * Creates an aggregation that calculates the average (mean) of values from an expression across + * multiple stage inputs. + * + * ```typescript + * // Calculate the average age of users + * avg(Field.of("age")).as("averageAge"); + * ``` + * + * @param value The expression representing the values to average. + * @return A new {@code Accumulator} representing the 'avg' aggregation. + */ +export function avgFunction(value: Expr): Avg; + +/** + * @beta + * + * Creates an aggregation that calculates the average (mean) of a field's values across multiple + * stage inputs. + * + * ```typescript + * // Calculate the average age of users + * avg("age").as("averageAge"); + * ``` + * + * @param value The name of the field containing numeric values to average. + * @return A new {@code Accumulator} representing the 'avg' aggregation. + */ +export function avgFunction(value: string): Avg; +export function avgFunction(value: Expr | string): Avg { + const exprValue = value instanceof Expr ? value : Field.of(value); + return new Avg(exprValue, false); +} + +/** + * @beta + * + * Creates an aggregation that finds the minimum value of an expression across multiple stage + * inputs. + * + * ```typescript + * // Find the lowest price of all products + * min(Field.of("price")).as("lowestPrice"); + * ``` + * + * @param value The expression to find the minimum value of. + * @return A new {@code Accumulator} representing the 'min' aggregation. + */ +export function min(value: Expr): Min; + +/** + * @beta + * + * Creates an aggregation that finds the minimum value of a field across multiple stage inputs. + * + * ```typescript + * // Find the lowest price of all products + * min("price").as("lowestPrice"); + * ``` + * + * @param value The name of the field to find the minimum value of. + * @return A new {@code Accumulator} representing the 'min' aggregation. + */ +export function min(value: string): Min; +export function min(value: Expr | string): Min { + const exprValue = value instanceof Expr ? value : Field.of(value); + return new Min(exprValue, false); +} + +/** + * @beta + * + * Creates an aggregation that finds the maximum value of an expression across multiple stage + * inputs. + * + * ```typescript + * // Find the highest score in a leaderboard + * max(Field.of("score")).as("highestScore"); + * ``` + * + * @param value The expression to find the maximum value of. + * @return A new {@code Accumulator} representing the 'max' aggregation. + */ +export function max(value: Expr): Max; + +/** + * @beta + * + * Creates an aggregation that finds the maximum value of a field across multiple stage inputs. + * + * ```typescript + * // Find the highest score in a leaderboard + * max("score").as("highestScore"); + * ``` + * + * @param value The name of the field to find the maximum value of. + * @return A new {@code Accumulator} representing the 'max' aggregation. + */ +export function max(value: string): Max; +export function max(value: Expr | string): Max { + const exprValue = value instanceof Expr ? value : Field.of(value); + return new Max(exprValue, false); +} + +/** + * @beta + * + * Calculates the Cosine distance between a field's vector value and a double array. + * + * ```typescript + * // Calculate the Cosine distance between the 'location' field and a target location + * cosineDistance("location", [37.7749, -122.4194]); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (as an array of doubles) to compare against. + * @return A new {@code Expr} representing the Cosine distance between the two vectors. + */ +export function cosineDistance(expr: string, other: number[]): CosineDistance; + +/** + * @beta + * + * Calculates the Cosine distance between a field's vector value and a VectorValue. + * + * ```typescript + * // Calculate the Cosine distance between the 'location' field and a target location + * cosineDistance("location", new VectorValue([37.7749, -122.4194])); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (as a VectorValue) to compare against. + * @return A new {@code Expr} representing the Cosine distance between the two vectors. + */ +export function cosineDistance( + expr: string, + other: VectorValue +): CosineDistance; + +/** + * @beta + * + * Calculates the Cosine distance between a field's vector value and a vector expression. + * + * ```typescript + * // Calculate the cosine distance between the 'userVector' field and the 'itemVector' field + * cosineDistance("userVector", Field.of("itemVector")); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (represented as an Expr) to compare against. + * @return A new {@code Expr} representing the cosine distance between the two vectors. + */ +export function cosineDistance(expr: string, other: Expr): CosineDistance; + +/** + * @beta + * + * Calculates the Cosine distance between a vector expression and a double array. + * + * ```typescript + * // Calculate the cosine distance between the 'location' field and a target location + * cosineDistance(Field.of("location"), [37.7749, -122.4194]); + * ``` + * + * @param expr The first vector (represented as an Expr) to compare against. + * @param other The other vector (as an array of doubles) to compare against. + * @return A new {@code Expr} representing the cosine distance between the two vectors. + */ +export function cosineDistance(expr: Expr, other: number[]): CosineDistance; + +/** + * @beta + * + * Calculates the Cosine distance between a vector expression and a VectorValue. + * + * ```typescript + * // Calculate the cosine distance between the 'location' field and a target location + * cosineDistance(Field.of("location"), new VectorValue([37.7749, -122.4194])); + * ``` + * + * @param expr The first vector (represented as an Expr) to compare against. + * @param other The other vector (as a VectorValue) to compare against. + * @return A new {@code Expr} representing the cosine distance between the two vectors. + */ +export function cosineDistance(expr: Expr, other: VectorValue): CosineDistance; + +/** + * @beta + * + * Calculates the Cosine distance between two vector expressions. + * + * ```typescript + * // Calculate the cosine distance between the 'userVector' field and the 'itemVector' field + * cosineDistance(Field.of("userVector"), Field.of("itemVector")); + * ``` + * + * @param expr The first vector (represented as an Expr) to compare against. + * @param other The other vector (represented as an Expr) to compare against. + * @return A new {@code Expr} representing the cosine distance between the two vectors. + */ +export function cosineDistance(expr: Expr, other: Expr): CosineDistance; +export function cosineDistance( + expr: Expr | string, + other: Expr | number[] | VectorValue +): CosineDistance { + const expr1 = expr instanceof Expr ? expr : Field.of(expr); + const expr2 = other instanceof Expr ? other : Constant.vector(other); + return new CosineDistance(expr1, expr2); +} + +/** + * @beta + * + * Calculates the dot product between a field's vector value and a double array. + * + * ```typescript + * // Calculate the dot product distance between a feature vector and a target vector + * dotProduct("features", [0.5, 0.8, 0.2]); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (as an array of doubles) to calculate with. + * @return A new {@code Expr} representing the dot product between the two vectors. + */ +export function dotProduct(expr: string, other: number[]): DotProduct; + +/** + * @beta + * + * Calculates the dot product between a field's vector value and a VectorValue. + * + * ```typescript + * // Calculate the dot product distance between a feature vector and a target vector + * dotProduct("features", new VectorValue([0.5, 0.8, 0.2])); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (as a VectorValue) to calculate with. + * @return A new {@code Expr} representing the dot product between the two vectors. + */ +export function dotProduct(expr: string, other: VectorValue): DotProduct; + +/** + * @beta + * + * Calculates the dot product between a field's vector value and a vector expression. + * + * ```typescript + * // Calculate the dot product distance between two document vectors: 'docVector1' and 'docVector2' + * dotProduct("docVector1", Field.of("docVector2")); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (represented as an Expr) to calculate with. + * @return A new {@code Expr} representing the dot product between the two vectors. + */ +export function dotProduct(expr: string, other: Expr): DotProduct; + +/** + * @beta + * + * Calculates the dot product between a vector expression and a double array. + * + * ```typescript + * // Calculate the dot product between a feature vector and a target vector + * dotProduct(Field.of("features"), [0.5, 0.8, 0.2]); + * ``` + * + * @param expr The first vector (represented as an Expr) to calculate with. + * @param other The other vector (as an array of doubles) to calculate with. + * @return A new {@code Expr} representing the dot product between the two vectors. + */ +export function dotProduct(expr: Expr, other: number[]): DotProduct; + +/** + * @beta + * + * Calculates the dot product between a vector expression and a VectorValue. + * + * ```typescript + * // Calculate the dot product between a feature vector and a target vector + * dotProduct(Field.of("features"), new VectorValue([0.5, 0.8, 0.2])); + * ``` + * + * @param expr The first vector (represented as an Expr) to calculate with. + * @param other The other vector (as a VectorValue) to calculate with. + * @return A new {@code Expr} representing the dot product between the two vectors. + */ +export function dotProduct(expr: Expr, other: VectorValue): DotProduct; + +/** + * @beta + * + * Calculates the dot product between two vector expressions. + * + * ```typescript + * // Calculate the dot product between two document vectors: 'docVector1' and 'docVector2' + * dotProduct(Field.of("docVector1"), Field.of("docVector2")); + * ``` + * + * @param expr The first vector (represented as an Expr) to calculate with. + * @param other The other vector (represented as an Expr) to calculate with. + * @return A new {@code Expr} representing the dot product between the two vectors. + */ +export function dotProduct(expr: Expr, other: Expr): DotProduct; +export function dotProduct( + expr: Expr | string, + other: Expr | number[] | VectorValue +): DotProduct { + const expr1 = expr instanceof Expr ? expr : Field.of(expr); + const expr2 = other instanceof Expr ? other : Constant.vector(other); + return new DotProduct(expr1, expr2); +} + +/** + * @beta + * + * Calculates the Euclidean distance between a field's vector value and a double array. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * euclideanDistance("location", [37.7749, -122.4194]); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (as an array of doubles) to compare against. + * @return A new {@code Expr} representing the Euclidean distance between the two vectors. + */ +export function euclideanDistance( + expr: string, + other: number[] +): EuclideanDistance; + +/** + * @beta + * + * Calculates the Euclidean distance between a field's vector value and a VectorValue. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * euclideanDistance("location", new VectorValue([37.7749, -122.4194])); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (as a VectorValue) to compare against. + * @return A new {@code Expr} representing the Euclidean distance between the two vectors. + */ +export function euclideanDistance( + expr: string, + other: VectorValue +): EuclideanDistance; + +/** + * @beta + * + * Calculates the Euclidean distance between a field's vector value and a vector expression. + * + * ```typescript + * // Calculate the Euclidean distance between two vector fields: 'pointA' and 'pointB' + * euclideanDistance("pointA", Field.of("pointB")); + * ``` + * + * @param expr The name of the field containing the first vector. + * @param other The other vector (represented as an Expr) to compare against. + * @return A new {@code Expr} representing the Euclidean distance between the two vectors. + */ +export function euclideanDistance(expr: string, other: Expr): EuclideanDistance; + +/** + * @beta + * + * Calculates the Euclidean distance between a vector expression and a double array. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * + * euclideanDistance(Field.of("location"), [37.7749, -122.4194]); + * ``` + * + * @param expr The first vector (represented as an Expr) to compare against. + * @param other The other vector (as an array of doubles) to compare against. + * @return A new {@code Expr} representing the Euclidean distance between the two vectors. + */ +export function euclideanDistance( + expr: Expr, + other: number[] +): EuclideanDistance; + +/** + * @beta + * + * Calculates the Euclidean distance between a vector expression and a VectorValue. + * + * ```typescript + * // Calculate the Euclidean distance between the 'location' field and a target location + * euclideanDistance(Field.of("location"), new VectorValue([37.7749, -122.4194])); + * ``` + * + * @param expr The first vector (represented as an Expr) to compare against. + * @param other The other vector (as a VectorValue) to compare against. + * @return A new {@code Expr} representing the Euclidean distance between the two vectors. + */ +export function euclideanDistance( + expr: Expr, + other: VectorValue +): EuclideanDistance; + +/** + * @beta + * + * Calculates the Euclidean distance between two vector expressions. + * + * ```typescript + * // Calculate the Euclidean distance between two vector fields: 'pointA' and 'pointB' + * euclideanDistance(Field.of("pointA"), Field.of("pointB")); + * ``` + * + * @param expr The first vector (represented as an Expr) to compare against. + * @param other The other vector (represented as an Expr) to compare against. + * @return A new {@code Expr} representing the Euclidean distance between the two vectors. + */ +export function euclideanDistance(expr: Expr, other: Expr): EuclideanDistance; +export function euclideanDistance( + expr: Expr | string, + other: Expr | number[] | VectorValue +): EuclideanDistance { + const expr1 = expr instanceof Expr ? expr : Field.of(expr); + const expr2 = other instanceof Expr ? other : Constant.vector(other); + return new EuclideanDistance(expr1, expr2); +} + +/** + * @beta + * + * Creates an expression that calculates the length of a Firestore Vector. + * + * ```typescript + * // Get the vector length (dimension) of the field 'embedding'. + * vectorLength(Field.of("embedding")); + * ``` + * + * @param expr The expression representing the Firestore Vector. + * @return A new {@code Expr} representing the length of the array. + */ +export function vectorLength(expr: Expr): VectorLength; + +/** + * @beta + * + * Creates an expression that calculates the length of a Firestore Vector represented by a field. + * + * ```typescript + * // Get the vector length (dimension) of the field 'embedding'. + * vectorLength("embedding"); + * ``` + * + * @param field The name of the field representing the Firestore Vector. + * @return A new {@code Expr} representing the length of the array. + */ +export function vectorLength(field: string): VectorLength; +export function vectorLength(expr: Expr | string): VectorLength { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new VectorLength(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that interprets an expression as the number of microseconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'microseconds' field as microseconds since epoch. + * unixMicrosToTimestamp(Field.of("microseconds")); + * ``` + * + * @param expr The expression representing the number of microseconds since epoch. + * @return A new {@code Expr} representing the timestamp. + */ +export function unixMicrosToTimestamp(expr: Expr): UnixMicrosToTimestamp; + +/** + * @beta + * + * Creates an expression that interprets a field's value as the number of microseconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'microseconds' field as microseconds since epoch. + * unixMicrosToTimestamp("microseconds"); + * ``` + * + * @param field The name of the field representing the number of microseconds since epoch. + * @return A new {@code Expr} representing the timestamp. + */ +export function unixMicrosToTimestamp(field: string): UnixMicrosToTimestamp; +export function unixMicrosToTimestamp( + expr: Expr | string +): UnixMicrosToTimestamp { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new UnixMicrosToTimestamp(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that converts a timestamp expression to the number of microseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to microseconds since epoch. + * timestampToUnixMicros(Field.of("timestamp")); + * ``` + * + * @param expr The expression representing the timestamp. + * @return A new {@code Expr} representing the number of microseconds since epoch. + */ +export function timestampToUnixMicros(expr: Expr): TimestampToUnixMicros; + +/** + * @beta + * + * Creates an expression that converts a timestamp field to the number of microseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to microseconds since epoch. + * timestampToUnixMicros("timestamp"); + * ``` + * + * @param field The name of the field representing the timestamp. + * @return A new {@code Expr} representing the number of microseconds since epoch. + */ +export function timestampToUnixMicros(field: string): TimestampToUnixMicros; +export function timestampToUnixMicros( + expr: Expr | string +): TimestampToUnixMicros { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new TimestampToUnixMicros(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that interprets an expression as the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'milliseconds' field as milliseconds since epoch. + * unixMillisToTimestamp(Field.of("milliseconds")); + * ``` + * + * @param expr The expression representing the number of milliseconds since epoch. + * @return A new {@code Expr} representing the timestamp. + */ +export function unixMillisToTimestamp(expr: Expr): UnixMillisToTimestamp; + +/** + * @beta + * + * Creates an expression that interprets a field's value as the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'milliseconds' field as milliseconds since epoch. + * unixMillisToTimestamp("milliseconds"); + * ``` + * + * @param field The name of the field representing the number of milliseconds since epoch. + * @return A new {@code Expr} representing the timestamp. + */ +export function unixMillisToTimestamp(field: string): UnixMillisToTimestamp; +export function unixMillisToTimestamp( + expr: Expr | string +): UnixMillisToTimestamp { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new UnixMillisToTimestamp(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that converts a timestamp expression to the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to milliseconds since epoch. + * timestampToUnixMillis(Field.of("timestamp")); + * ``` + * + * @param expr The expression representing the timestamp. + * @return A new {@code Expr} representing the number of milliseconds since epoch. + */ +export function timestampToUnixMillis(expr: Expr): TimestampToUnixMillis; + +/** + * @beta + * + * Creates an expression that converts a timestamp field to the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to milliseconds since epoch. + * timestampToUnixMillis("timestamp"); + * ``` + * + * @param field The name of the field representing the timestamp. + * @return A new {@code Expr} representing the number of milliseconds since epoch. + */ +export function timestampToUnixMillis(field: string): TimestampToUnixMillis; +export function timestampToUnixMillis( + expr: Expr | string +): TimestampToUnixMillis { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new TimestampToUnixMillis(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that interprets an expression as the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'seconds' field as seconds since epoch. + * unixSecondsToTimestamp(Field.of("seconds")); + * ``` + * + * @param expr The expression representing the number of seconds since epoch. + * @return A new {@code Expr} representing the timestamp. + */ +export function unixSecondsToTimestamp(expr: Expr): UnixSecondsToTimestamp; + +/** + * @beta + * + * Creates an expression that interprets a field's value as the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC) + * and returns a timestamp. + * + * ```typescript + * // Interpret the 'seconds' field as seconds since epoch. + * unixSecondsToTimestamp("seconds"); + * ``` + * + * @param field The name of the field representing the number of seconds since epoch. + * @return A new {@code Expr} representing the timestamp. + */ +export function unixSecondsToTimestamp(field: string): UnixSecondsToTimestamp; +export function unixSecondsToTimestamp( + expr: Expr | string +): UnixSecondsToTimestamp { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new UnixSecondsToTimestamp(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that converts a timestamp expression to the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to seconds since epoch. + * timestampToUnixSeconds(Field.of("timestamp")); + * ``` + * + * @param expr The expression representing the timestamp. + * @return A new {@code Expr} representing the number of seconds since epoch. + */ +export function timestampToUnixSeconds(expr: Expr): TimestampToUnixSeconds; + +/** + * @beta + * + * Creates an expression that converts a timestamp field to the number of seconds since the Unix epoch (1970-01-01 00:00:00 UTC). + * + * ```typescript + * // Convert the 'timestamp' field to seconds since epoch. + * timestampToUnixSeconds("timestamp"); + * ``` + * + * @param field The name of the field representing the timestamp. + * @return A new {@code Expr} representing the number of seconds since epoch. + */ +export function timestampToUnixSeconds(field: string): TimestampToUnixSeconds; +export function timestampToUnixSeconds( + expr: Expr | string +): TimestampToUnixSeconds { + const normalizedExpr = typeof expr === 'string' ? Field.of(expr) : expr; + return new TimestampToUnixSeconds(normalizedExpr); +} + +/** + * @beta + * + * Creates an expression that adds a specified amount of time to a timestamp. + * + * ```typescript + * // Add some duration determined by field 'unit' and 'amount' to the 'timestamp' field. + * timestampAdd(Field.of("timestamp"), Field.of("unit"), Field.of("amount")); + * ``` + * + * @param timestamp The expression representing the timestamp. + * @param unit The expression evaluates to unit of time, must be one of 'microsecond', 'millisecond', 'second', 'minute', 'hour', 'day'. + * @param amount The expression evaluates to amount of the unit. + * @return A new {@code Expr} representing the resulting timestamp. + */ +export function timestampAdd( + timestamp: Expr, + unit: Expr, + amount: Expr +): TimestampAdd; + +/** + * @beta + * + * Creates an expression that adds a specified amount of time to a timestamp. + * + * ```typescript + * // Add 1 day to the 'timestamp' field. + * timestampAdd(Field.of("timestamp"), "day", 1); + * ``` + * + * @param timestamp The expression representing the timestamp. + * @param unit The unit of time to add (e.g., "day", "hour"). + * @param amount The amount of time to add. + * @return A new {@code Expr} representing the resulting timestamp. + */ +export function timestampAdd( + timestamp: Expr, + unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', + amount: number +): TimestampAdd; + +/** + * @beta + * + * Creates an expression that adds a specified amount of time to a timestamp represented by a field. + * + * ```typescript + * // Add 1 day to the 'timestamp' field. + * timestampAdd("timestamp", "day", 1); + * ``` + * + * @param field The name of the field representing the timestamp. + * @param unit The unit of time to add (e.g., "day", "hour"). + * @param amount The amount of time to add. + * @return A new {@code Expr} representing the resulting timestamp. + */ +export function timestampAdd( + field: string, + unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', + amount: number +): TimestampAdd; +export function timestampAdd( + timestamp: Expr | string, + unit: + | Expr + | 'microsecond' + | 'millisecond' + | 'second' + | 'minute' + | 'hour' + | 'day', + amount: Expr | number +): TimestampAdd { + const normalizedTimestamp = + typeof timestamp === 'string' ? Field.of(timestamp) : timestamp; + const normalizedUnit = unit instanceof Expr ? unit : Constant.of(unit); + const normalizedAmount = + typeof amount === 'number' ? Constant.of(amount) : amount; + return new TimestampAdd( + normalizedTimestamp, + normalizedUnit, + normalizedAmount + ); +} + +/** + * @beta + * + * Creates an expression that subtracts a specified amount of time from a timestamp. + * + * ```typescript + * // Subtract some duration determined by field 'unit' and 'amount' from the 'timestamp' field. + * timestampSub(Field.of("timestamp"), Field.of("unit"), Field.of("amount")); + * ``` + * + * @param timestamp The expression representing the timestamp. + * @param unit The expression evaluates to unit of time, must be one of 'microsecond', 'millisecond', 'second', 'minute', 'hour', 'day'. + * @param amount The expression evaluates to amount of the unit. + * @return A new {@code Expr} representing the resulting timestamp. + */ +export function timestampSub( + timestamp: Expr, + unit: Expr, + amount: Expr +): TimestampSub; + +/** + * @beta + * + * Creates an expression that subtracts a specified amount of time from a timestamp. + * + * ```typescript + * // Subtract 1 day from the 'timestamp' field. + * timestampSub(Field.of("timestamp"), "day", 1); + * ``` + * + * @param timestamp The expression representing the timestamp. + * @param unit The unit of time to subtract (e.g., "day", "hour"). + * @param amount The amount of time to subtract. + * @return A new {@code Expr} representing the resulting timestamp. + */ +export function timestampSub( + timestamp: Expr, + unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', + amount: number +): TimestampSub; + +/** + * @beta + * + * Creates an expression that subtracts a specified amount of time from a timestamp represented by a field. + * + * ```typescript + * // Subtract 1 day from the 'timestamp' field. + * timestampSub("timestamp", "day", 1); + * ``` + * + * @param field The name of the field representing the timestamp. + * @param unit The unit of time to subtract (e.g., "day", "hour"). + * @param amount The amount of time to subtract. + * @return A new {@code Expr} representing the resulting timestamp. + */ +export function timestampSub( + field: string, + unit: 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day', + amount: number +): TimestampSub; +export function timestampSub( + timestamp: Expr | string, + unit: + | Expr + | 'microsecond' + | 'millisecond' + | 'second' + | 'minute' + | 'hour' + | 'day', + amount: Expr | number +): TimestampSub { + const normalizedTimestamp = + typeof timestamp === 'string' ? Field.of(timestamp) : timestamp; + const normalizedUnit = unit instanceof Expr ? unit : Constant.of(unit); + const normalizedAmount = + typeof amount === 'number' ? Constant.of(amount) : amount; + return new TimestampSub( + normalizedTimestamp, + normalizedUnit, + normalizedAmount + ); +} + +/** + * @beta + * + * Creates functions that work on the backend but do not exist in the SDK yet. + * + * ```typescript + * // Call a user defined function named "myFunc" with the arguments 10 and 20 + * // This is the same of the 'sum(Field.of("price"))', if it did not exist + * genericFunction("sum", [Field.of("price")]); + * ``` + * + * @param name The name of the user defined function. + * @param params The arguments to pass to the function. + * @return A new {@code Function} representing the function call. + */ +export function genericFunction( + name: string, + params: Expr[] +): FirestoreFunction { + return new FirestoreFunction(name, params); +} + +/** + * @beta + * + * Creates an expression that performs a logical 'AND' operation on multiple filter conditions. + * + * ```typescript + * // Check if the 'age' field is greater than 18 AND the 'city' field is "London" AND + * // the 'status' field is "active" + * const condition = and(gt("age", 18), eq("city", "London"), eq("status", "active")); + * ``` + * + * @param left The first filter condition. + * @param right Additional filter conditions to 'AND' together. + * @return A new {@code Expr} representing the logical 'AND' operation. + */ +export function andFunction(left: FilterExpr, ...right: FilterExpr[]): And { + return new And([left, ...right]); +} + +/** + * @beta + * + * Creates an expression that performs a logical 'OR' operation on multiple filter conditions. + * + * ```typescript + * // Check if the 'age' field is greater than 18 OR the 'city' field is "London" OR + * // the 'status' field is "active" + * const condition = or(gt("age", 18), eq("city", "London"), eq("status", "active")); + * ``` + * + * @param left The first filter condition. + * @param right Additional filter conditions to 'OR' together. + * @return A new {@code Expr} representing the logical 'OR' operation. + */ +export function orFunction(left: FilterExpr, ...right: FilterExpr[]): Or { + return new Or([left, ...right]); +} + +/** + * @beta + * + * Creates an {@link Ordering} that sorts documents in ascending order based on this expression. + * + * ```typescript + * // Sort documents by the 'name' field in ascending order + * firestore.pipeline().collection("users") + * .sort(ascending(Field.of("name"))); + * ``` + * + * @param expr The expression to create an ascending ordering for. + * @return A new `Ordering` for ascending sorting. + */ +export function ascending(expr: Expr): Ordering { + return new Ordering(expr, 'ascending'); +} + +/** + * @beta + * + * Creates an {@link Ordering} that sorts documents in descending order based on this expression. + * + * ```typescript + * // Sort documents by the 'createdAt' field in descending order + * firestore.pipeline().collection("users") + * .sort(descending(Field.of("createdAt"))); + * ``` + * + * @param expr The expression to create a descending ordering for. + * @return A new `Ordering` for descending sorting. + */ +export function descending(expr: Expr): Ordering { + return new Ordering(expr, 'descending'); +} + +/** + * @beta + * + * Represents an ordering criterion for sorting documents in a Firestore pipeline. + * + * You create `Ordering` instances using the `ascending` and `descending` helper functions. + */ +export class Ordering { + constructor( + private expr: Expr, + private direction: 'ascending' | 'descending' + ) {} + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoValue { + return { + mapValue: { + fields: { + direction: toStringValue(this.direction), + expression: this.expr._toProto(serializer) + } + } + }; + } + + /** + * @private + * @internal + */ + _readUserData(dataReader: UserDataReader): void { + this.expr._readUserData(dataReader); + } +} diff --git a/packages/firestore/src/lite-api/pipeline-result.ts b/packages/firestore/src/lite-api/pipeline-result.ts new file mode 100644 index 00000000000..f3951099ba2 --- /dev/null +++ b/packages/firestore/src/lite-api/pipeline-result.ts @@ -0,0 +1,232 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObjectValue } from '../model/object_value'; +import { isOptionalEqual } from '../util/misc'; + +import { FieldPath } from './field_path'; +import { DocumentData, DocumentReference, refEqual } from './reference'; +import { fieldPathFromArgument } from './snapshot'; +import { Timestamp } from './timestamp'; +import { AbstractUserDataWriter } from './user_data_writer'; + +/** + * @beta + * + * A PipelineResult contains data read from a Firestore Pipeline. The data can be extracted with the + * {@link #data()} or {@link #get(String)} methods. + * + *

If the PipelineResult represents a non-document result, `ref` will return a undefined + * value. + */ +export class PipelineResult { + private readonly _userDataWriter: AbstractUserDataWriter; + + private readonly _executionTime: Timestamp | undefined; + private readonly _createTime: Timestamp | undefined; + private readonly _updateTime: Timestamp | undefined; + + /** + * @internal + * @private + */ + readonly _ref: DocumentReference | undefined; + + /** + * @internal + * @private + */ + readonly _fields: ObjectValue | undefined; + + /** + * @private + * @internal + * + * @param userDataWriter The serializer used to encode/decode protobuf. + * @param ref The reference to the document. + * @param _fieldsProto The fields of the Firestore `Document` Protobuf backing + * this document (or undefined if the document does not exist). + * @param readTime The time when this result was read (or undefined if + * the document exists only locally). + * @param createTime The time when the document was created if the result is a document, undefined otherwise. + * @param updateTime The time when the document was last updated if the result is a document, undefined otherwise. + */ + constructor( + userDataWriter: AbstractUserDataWriter, + ref?: DocumentReference, + fields?: ObjectValue, + executionTime?: Timestamp, + createTime?: Timestamp, + updateTime?: Timestamp + // TODO converter + //readonly converter: FirestorePipelineConverter = defaultPipelineConverter() + ) { + this._ref = ref; + this._userDataWriter = userDataWriter; + this._executionTime = executionTime; + this._createTime = createTime; + this._updateTime = updateTime; + this._fields = fields; + } + + /** + * The reference of the document, if it is a document; otherwise `undefined`. + */ + get ref(): DocumentReference | undefined { + return this._ref; + } + + /** + * The ID of the document for which this PipelineResult contains data, if it is a document; otherwise `undefined`. + * + * @type {string} + * @readonly + * + */ + get id(): string | undefined { + return this._ref?.id; + } + + /** + * The time the document was created. Undefined if this result is not a document. + * + * @type {Timestamp|undefined} + * @readonly + */ + get createTime(): Timestamp | undefined { + return this._createTime; + } + + /** + * The time the document was last updated (at the time the snapshot was + * generated). Undefined if this result is not a document. + * + * @type {Timestamp|undefined} + * @readonly + */ + get updateTime(): Timestamp | undefined { + return this._updateTime; + } + + /** + * The time at which the pipeline producing this result is executed. + * + * @type {Timestamp} + * @readonly + * + */ + get executionTime(): Timestamp { + if (this._executionTime === undefined) { + throw new Error( + "'executionTime' is expected to exist, but it is undefined" + ); + } + return this._executionTime; + } + + /** + * Retrieves all fields in the result as an object. Returns 'undefined' if + * the document doesn't exist. + * + * @returns {T|undefined} An object containing all fields in the document or + * 'undefined' if the document doesn't exist. + * + * @example + * ``` + * let p = firestore.pipeline().collection('col'); + * + * p.execute().then(results => { + * let data = results[0].data(); + * console.log(`Retrieved data: ${JSON.stringify(data)}`); + * }); + * ``` + */ + data(): AppModelType | undefined { + if (this._fields === undefined) { + return undefined; + } + + // TODO(pipelines) + // We only want to use the converter and create a new QueryDocumentSnapshot + // if a converter has been provided. + // if (!!this.converter && this.converter !== defaultPipelineConverter()) { + // return this.converter.fromFirestore( + // new PipelineResult< DocumentData>( + // this._serializer, + // this.ref, + // this._fieldsProto, + // this._executionTime, + // this.createTime, + // this.updateTime, + // defaultPipelineConverter() + // ) + // ); + // } else {{ + return this._userDataWriter.convertValue( + this._fields.value + ) as AppModelType; + //} + } + + /** + * Retrieves the field specified by `field`. + * + * @param {string|FieldPath} field The field path + * (e.g. 'foo' or 'foo.bar') to a specific field. + * @returns {*} The data at the specified field location or undefined if no + * such field exists. + * + * @example + * ``` + * let p = firestore.pipeline().collection('col'); + * + * p.execute().then(results => { + * let field = results[0].get('a.b'); + * console.log(`Retrieved field value: ${field}`); + * }); + * ``` + */ + // We deliberately use `any` in the external API to not impose type-checking + // on end users. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get(fieldPath: string | FieldPath): any { + if (this._fields === undefined) { + return undefined; + } + + const value = this._fields.field( + fieldPathFromArgument('DocumentSnapshot.get', fieldPath) + ); + if (value !== null) { + return this._userDataWriter.convertValue(value); + } + } +} + +export function pipelineResultEqual( + left: PipelineResult, + right: PipelineResult +): boolean { + if (left === right) { + return true; + } + + return ( + isOptionalEqual(left._ref, right._ref, refEqual) && + isOptionalEqual(left._fields, right._fields, (l, r) => l.isEqual(r)) + ); +} diff --git a/packages/firestore/src/lite-api/pipeline-source.ts b/packages/firestore/src/lite-api/pipeline-source.ts new file mode 100644 index 00000000000..a79c5cafc31 --- /dev/null +++ b/packages/firestore/src/lite-api/pipeline-source.ts @@ -0,0 +1,91 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { DocumentKey } from '../model/document_key'; + +import { Firestore } from './database'; +import { Pipeline } from './pipeline'; +import { DocumentReference } from './reference'; +import { + CollectionGroupSource, + CollectionSource, + DatabaseSource, + DocumentsSource +} from './stage'; +import { UserDataReader } from './user_data_reader'; +import { AbstractUserDataWriter } from './user_data_writer'; + +/** + * Represents the source of a Firestore {@link Pipeline}. + * @beta + */ +export class PipelineSource { + /** + * @internal + * @private + * @param db + * @param userDataReader + * @param userDataWriter + * @param documentReferenceFactory + */ + constructor( + private db: Firestore, + private userDataReader: UserDataReader, + private userDataWriter: AbstractUserDataWriter, + private documentReferenceFactory: (id: DocumentKey) => DocumentReference + ) {} + + collection(collectionPath: string): Pipeline { + return new Pipeline( + this.db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + [new CollectionSource(collectionPath)] + ); + } + + collectionGroup(collectionId: string): Pipeline { + return new Pipeline( + this.db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + [new CollectionGroupSource(collectionId)] + ); + } + + database(): Pipeline { + return new Pipeline( + this.db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + [new DatabaseSource()] + ); + } + + documents(docs: DocumentReference[]): Pipeline { + return new Pipeline( + this.db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + [DocumentsSource.of(docs)] + ); + } +} diff --git a/packages/firestore/src/lite-api/pipeline.ts b/packages/firestore/src/lite-api/pipeline.ts new file mode 100644 index 00000000000..4d13d5dfddd --- /dev/null +++ b/packages/firestore/src/lite-api/pipeline.ts @@ -0,0 +1,853 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* eslint @typescript-eslint/no-explicit-any: 0 */ + +import { DocumentKey } from '../model/document_key'; +import { ObjectValue } from '../model/object_value'; +import { + ExecutePipelineRequest, + StructuredPipeline, + Stage as ProtoStage +} from '../protos/firestore_proto_api'; +import { invokeExecutePipeline } from '../remote/datastore'; +import { + getEncodedDatabaseId, + JsonProtoSerializer, + ProtoSerializable +} from '../remote/serializer'; + +import { getDatastore } from './components'; +import { Firestore } from './database'; +import { + Accumulator, + AccumulatorTarget, + Expr, + ExprWithAlias, + Field, + Fields, + FilterCondition, + Ordering, + Selectable +} from './expressions'; +import { PipelineResult } from './pipeline-result'; +import { PipelineSource } from './pipeline-source'; +import { DocumentData, DocumentReference, Query } from './reference'; +import { + AddFields, + Aggregate, + Distinct, + FindNearest, + FindNearestOptions, + GenericStage, + Limit, + Offset, + Select, + Sort, + Stage, + Where +} from './stage'; +import { + parseVectorValue, + UserDataReader, + UserDataSource +} from './user_data_reader'; +import { AbstractUserDataWriter } from './user_data_writer'; + +interface ReadableUserData { + _readUserData(dataReader: UserDataReader): void; +} + +function isReadableUserData(value: any): value is ReadableUserData { + return typeof (value as ReadableUserData)._readUserData === 'function'; +} + +/** + * @beta + * + * The Pipeline class provides a flexible and expressive framework for building complex data + * transformation and query pipelines for Firestore. + * + * A pipeline takes data sources, such as Firestore collections or collection groups, and applies + * a series of stages that are chained together. Each stage takes the output from the previous stage + * (or the data source) and produces an output for the next stage (or as the final output of the + * pipeline). + * + * Expressions can be used within each stage to filter and transform data through the stage. + * + * NOTE: The chained stages do not prescribe exactly how Firestore will execute the pipeline. + * Instead, Firestore only guarantees that the result is the same as if the chained stages were + * executed in order. + * + * Usage Examples: + * + * ```typescript + * const db: Firestore; // Assumes a valid firestore instance. + * + * // Example 1: Select specific fields and rename 'rating' to 'bookRating' + * const results1 = await db.pipeline() + * .collection("books") + * .select("title", "author", Field.of("rating").as("bookRating")) + * .execute(); + * + * // Example 2: Filter documents where 'genre' is "Science Fiction" and 'published' is after 1950 + * const results2 = await db.pipeline() + * .collection("books") + * .where(and(Field.of("genre").eq("Science Fiction"), Field.of("published").gt(1950))) + * .execute(); + * + * // Example 3: Calculate the average rating of books published after 1980 + * const results3 = await db.pipeline() + * .collection("books") + * .where(Field.of("published").gt(1980)) + * .aggregate(avg(Field.of("rating")).as("averageRating")) + * .execute(); + * ``` + */ + +/** + * Base-class implementation + */ +export class Pipeline + implements ProtoSerializable +{ + /** + * @internal + * @private + * @param _db + * @param userDataReader + * @param userDataWriter + * @param documentReferenceFactory + * @param stages + * @param converter + */ + constructor( + /** + * @internal + * @private + */ + public _db: Firestore, + private userDataReader: UserDataReader, + /** + * @internal + * @private + */ + protected userDataWriter: AbstractUserDataWriter, + /** + * @internal + * @private + */ + protected documentReferenceFactory: (id: DocumentKey) => DocumentReference, + private stages: Stage[], + // TODO(pipeline) support converter + //private converter: FirestorePipelineConverter = defaultPipelineConverter() + private converter: unknown = {} + ) {} + + /** + * Adds new fields to outputs from previous stages. + * + * This stage allows you to compute values on-the-fly based on existing data from previous + * stages or constants. You can use this to create new fields or overwrite existing ones (if there + * is name overlaps). + * + * The added fields are defined using {@link Selectable}s, which can be: + * + * - {@link Field}: References an existing document field. + * - {@link Function}: Performs a calculation using functions like `add`, `multiply` with + * assigned aliases using {@link Expr#as}. + * + * Example: + * + * ```typescript + * firestore.pipeline().collection("books") + * .addFields( + * Field.of("rating").as("bookRating"), // Rename 'rating' to 'bookRating' + * add(5, Field.of("quantity")).as("totalCost") // Calculate 'totalCost' + * ); + * ``` + * + * @param fields The fields to add to the documents, specified as {@link Selectable}s. + * @return A new Pipeline object with this stage appended to the stage list. + */ + addFields(...fields: Selectable[]): Pipeline { + const copy = this.stages.map(s => s); + copy.push( + new AddFields( + this.readUserData('addFields', this.selectablesToMap(fields)) + ) + ); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + /** + * Selects or creates a set of fields from the outputs of previous stages. + * + *

The selected fields are defined using {@link Selectable} expressions, which can be: + * + *

    + *
  • {@code string}: Name of an existing field
  • + *
  • {@link Field}: References an existing field.
  • + *
  • {@link Function}: Represents the result of a function with an assigned alias name using + * {@link Expr#as}
  • + *
+ * + *

If no selections are provided, the output of this stage is empty. Use {@link + * com.google.cloud.firestore.Pipeline#addFields} instead if only additions are + * desired. + * + *

Example: + * + * ```typescript + * firestore.pipeline().collection("books") + * .select( + * "firstName", + * Field.of("lastName"), + * Field.of("address").toUppercase().as("upperAddress"), + * ); + * ``` + * + * @param selections The fields to include in the output documents, specified as {@link + * Selectable} expressions or {@code string} values representing field names. + * @return A new Pipeline object with this stage appended to the stage list. + */ + select(...selections: Array): Pipeline { + const copy = this.stages.map(s => s); + let projections: Map = this.selectablesToMap(selections); + projections = this.readUserData('select', projections); + copy.push(new Select(projections)); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + private selectablesToMap( + selectables: Array + ): Map { + const result = new Map(); + for (const selectable of selectables) { + if (typeof selectable === 'string') { + result.set(selectable as string, Field.of(selectable)); + } else if (selectable instanceof Field) { + result.set((selectable as Field).fieldName(), selectable); + } else if (selectable instanceof Fields) { + const fields = selectable as Fields; + for (const field of fields.fieldList()) { + result.set(field.fieldName(), field); + } + } else if (selectable instanceof ExprWithAlias) { + const expr = selectable as ExprWithAlias; + result.set(expr.alias, expr.expr); + } + } + return result; + } + + /** + * Reads user data for each expression in the expressionMap. + * @param name Name of the calling function. Used for error messages when invalid user data is encountered. + * @param expressionMap + * @return the expressionMap argument. + * @private + */ + private readUserData< + T extends + | Map + | ReadableUserData[] + | ReadableUserData + >(name: string, expressionMap: T): T { + if (isReadableUserData(expressionMap)) { + expressionMap._readUserData(this.userDataReader); + } else if (Array.isArray(expressionMap)) { + expressionMap.forEach(readableData => + readableData._readUserData(this.userDataReader) + ); + } else { + expressionMap.forEach(expr => expr._readUserData(this.userDataReader)); + } + return expressionMap; + } + + /** + * Filters the documents from previous stages to only include those matching the specified {@link + * FilterCondition}. + * + *

This stage allows you to apply conditions to the data, similar to a "WHERE" clause in SQL. + * You can filter documents based on their field values, using implementations of {@link + * FilterCondition}, typically including but not limited to: + * + *

    + *
  • field comparators: {@link Function#eq}, {@link Function#lt} (less than), {@link + * Function#gt} (greater than), etc.
  • + *
  • logical operators: {@link Function#and}, {@link Function#or}, {@link Function#not}, etc.
  • + *
  • advanced functions: {@link Function#regexMatch}, {@link + * Function#arrayContains}, etc.
  • + *
+ * + *

Example: + * + * ```typescript + * firestore.pipeline().collection("books") + * .where( + * and( + * gt(Field.of("rating"), 4.0), // Filter for ratings greater than 4.0 + * Field.of("genre").eq("Science Fiction") // Equivalent to gt("genre", "Science Fiction") + * ) + * ); + * ``` + * + * @param condition The {@link FilterCondition} to apply. + * @return A new Pipeline object with this stage appended to the stage list. + */ + where(condition: FilterCondition & Expr): Pipeline { + const copy = this.stages.map(s => s); + this.readUserData('where', condition); + copy.push(new Where(condition)); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + /** + * Skips the first `offset` number of documents from the results of previous stages. + * + *

This stage is useful for implementing pagination in your pipelines, allowing you to retrieve + * results in chunks. It is typically used in conjunction with {@link #limit} to control the + * size of each page. + * + *

Example: + * + * ```typescript + * // Retrieve the second page of 20 results + * firestore.pipeline().collection("books") + * .sort(Field.of("published").descending()) + * .offset(20) // Skip the first 20 results + * .limit(20); // Take the next 20 results + * ``` + * + * @param offset The number of documents to skip. + * @return A new Pipeline object with this stage appended to the stage list. + */ + offset(offset: number): Pipeline { + const copy = this.stages.map(s => s); + copy.push(new Offset(offset)); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + /** + * Limits the maximum number of documents returned by previous stages to `limit`. + * + *

This stage is particularly useful when you want to retrieve a controlled subset of data from + * a potentially large result set. It's often used for: + * + *

    + *
  • **Pagination:** In combination with {@link #offset} to retrieve specific pages of + * results.
  • + *
  • **Limiting Data Retrieval:** To prevent excessive data transfer and improve performance, + * especially when dealing with large collections.
  • + *
+ * + *

Example: + * + * ```typescript + * // Limit the results to the top 10 highest-rated books + * firestore.pipeline().collection("books") + * .sort(Field.of("rating").descending()) + * .limit(10); + * ``` + * + * @param limit The maximum number of documents to return. + * @return A new Pipeline object with this stage appended to the stage list. + */ + limit(limit: number): Pipeline { + const copy = this.stages.map(s => s); + copy.push(new Limit(limit)); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + /** + * Returns a set of distinct {@link Expr} values from the inputs to this stage. + * + *

This stage run through the results from previous stages to include only results with unique + * combinations of {@link Expr} values ({@link Field}, {@link Function}, etc). + * + *

The parameters to this stage are defined using {@link Selectable} expressions or {@code string}s: + * + *

    + *
  • {@code string}: Name of an existing field
  • + *
  • {@link Field}: References an existing document field.
  • + *
  • {@link Function}: Represents the result of a function with an assigned alias name using + * {@link Expr#as}
  • + *
+ * + *

Example: + * + * ```typescript + * // Get a list of unique author names in uppercase and genre combinations. + * firestore.pipeline().collection("books") + * .distinct(toUppercase(Field.of("author")).as("authorName"), Field.of("genre"), "publishedAt") + * .select("authorName"); + * ``` + * + * @param selectables The {@link Selectable} expressions to consider when determining distinct + * value combinations or {@code string}s representing field names. + * @return A new {@code Pipeline} object with this stage appended to the stage list. + */ + distinct(...groups: Array): Pipeline { + const copy = this.stages.map(s => s); + copy.push( + new Distinct( + this.readUserData('distinct', this.selectablesToMap(groups || [])) + ) + ); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + /** + * Performs aggregation operations on the documents from previous stages. + * + *

This stage allows you to calculate aggregate values over a set of documents. You define the + * aggregations to perform using {@link AccumulatorTarget} expressions which are typically results of + * calling {@link Expr#as} on {@link Accumulator} instances. + * + *

Example: + * + * ```typescript + * // Calculate the average rating and the total number of books + * firestore.pipeline().collection("books") + * .aggregate( + * Field.of("rating").avg().as("averageRating"), + * countAll().as("totalBooks") + * ); + * ``` + * + * @param accumulators The {@link AccumulatorTarget} expressions, each wrapping an {@link Accumulator} + * and provide a name for the accumulated results. + * @return A new Pipeline object with this stage appended to the stage list. + */ + aggregate(...accumulators: AccumulatorTarget[]): Pipeline; + /** + * Performs optionally grouped aggregation operations on the documents from previous stages. + * + *

This stage allows you to calculate aggregate values over a set of documents, optionally + * grouped by one or more fields or functions. You can specify: + * + *

    + *
  • **Grouping Fields or Functions:** One or more fields or functions to group the documents + * by. For each distinct combination of values in these fields, a separate group is created. + * If no grouping fields are provided, a single group containing all documents is used. Not + * specifying groups is the same as putting the entire inputs into one group.
  • + *
  • **Accumulators:** One or more accumulation operations to perform within each group. These + * are defined using {@link AccumulatorTarget} expressions, which are typically created by + * calling {@link Expr#as} on {@link Accumulator} instances. Each aggregation + * calculates a value (e.g., sum, average, count) based on the documents within its group.
  • + *
+ * + *

Example: + * + * ```typescript + * // Calculate the average rating for each genre. + * firestore.pipeline().collection("books") + * .aggregate({ + * accumulators: [avg(Field.of("rating")).as("avg_rating")] + * groups: ["genre"] + * }); + * ``` + * + * @param aggregate An {@link Aggregate} object that specifies the grouping fields (if any) and + * the aggregation operations to perform. + * @return A new {@code Pipeline} object with this stage appended to the stage list. + */ + aggregate(options: { + accumulators: AccumulatorTarget[]; + groups?: Array; + }): Pipeline; + aggregate( + optionsOrTarget: + | AccumulatorTarget + | { + accumulators: AccumulatorTarget[]; + groups?: Array; + }, + ...rest: AccumulatorTarget[] + ): Pipeline { + const copy = this.stages.map(s => s); + if ('accumulators' in optionsOrTarget) { + copy.push( + new Aggregate( + new Map( + optionsOrTarget.accumulators.map((target: AccumulatorTarget) => [ + (target as unknown as AccumulatorTarget).alias, + this.readUserData( + 'aggregate', + (target as unknown as AccumulatorTarget).expr + ) + ]) + ), + this.readUserData( + 'aggregate', + this.selectablesToMap(optionsOrTarget.groups || []) + ) + ) + ); + } else { + copy.push( + new Aggregate( + new Map( + [optionsOrTarget, ...rest].map(target => [ + (target as unknown as AccumulatorTarget).alias, + this.readUserData( + 'aggregate', + (target as unknown as AccumulatorTarget).expr + ) + ]) + ), + new Map() + ) + ); + } + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + findNearest(options: FindNearestOptions): Pipeline; + findNearest(options: FindNearestOptions): Pipeline { + const copy = this.stages.map(s => s); + const parseContext = this.userDataReader.createContext( + UserDataSource.Argument, + 'findNearest' + ); + const value = parseVectorValue(options.vectorValue, parseContext); + const vectorObjectValue = new ObjectValue(value); + copy.push( + new FindNearest( + options.field, + vectorObjectValue, + options.distanceMeasure, + options.limit, + options.distanceField + ) + ); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy + ); + } + + /** + * Sorts the documents from previous stages based on one or more {@link Ordering} criteria. + * + *

This stage allows you to order the results of your pipeline. You can specify multiple {@link + * Ordering} instances to sort by multiple fields in ascending or descending order. If documents + * have the same value for a field used for sorting, the next specified ordering will be used. If + * all orderings result in equal comparison, the documents are considered equal and the order is + * unspecified. + * + *

Example: + * + * ```typescript + * // Sort books by rating in descending order, and then by title in ascending order for books + * // with the same rating + * firestore.pipeline().collection("books") + * .sort( + * Ordering.of(Field.of("rating")).descending(), + * Ordering.of(Field.of("title")) // Ascending order is the default + * ); + * ``` + * + * @param orders One or more {@link Ordering} instances specifying the sorting criteria. + * @return A new {@code Pipeline} object with this stage appended to the stage list. + */ + sort(...orderings: Ordering[]): Pipeline; + sort(options: { orderings: Ordering[] }): Pipeline; + sort( + optionsOrOrderings: + | Ordering + | { + orderings: Ordering[]; + }, + ...rest: Ordering[] + ): Pipeline { + const copy = this.stages.map(s => s); + // Option object + if ('orderings' in optionsOrOrderings) { + copy.push( + new Sort( + this.readUserData( + 'sort', + this.readUserData('sort', optionsOrOrderings.orderings) + ) + ) + ); + } else { + // Ordering object + copy.push( + new Sort(this.readUserData('sort', [optionsOrOrderings, ...rest])) + ); + } + + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + /** + * Adds a generic stage to the pipeline. + * + *

This method provides a flexible way to extend the pipeline's functionality by adding custom + * stages. Each generic stage is defined by a unique `name` and a set of `params` that control its + * behavior. + * + *

Example (Assuming there is no "where" stage available in SDK): + * + * ```typescript + * // Assume we don't have a built-in "where" stage + * firestore.pipeline().collection("books") + * .genericStage("where", [Field.of("published").lt(1900)]) // Custom "where" stage + * .select("title", "author"); + * ``` + * + * @param name The unique name of the generic stage to add. + * @param params A list of parameters to configure the generic stage's behavior. + * @return A new {@code Pipeline} object with this stage appended to the stage list. + */ + genericStage(name: string, params: any[]): Pipeline { + const copy = this.stages.map(s => s); + params.forEach(param => { + if (isReadableUserData(param)) { + param._readUserData(this.userDataReader); + } + }); + copy.push(new GenericStage(name, params)); + return new Pipeline( + this._db, + this.userDataReader, + this.userDataWriter, + this.documentReferenceFactory, + copy, + this.converter + ); + } + + // TODO(pipeline) support converter + // withConverter(converter: null): Pipeline; + // withConverter( + // converter: FirestorePipelineConverter + // ): Pipeline; + // /** + // * Applies a custom data converter to this Query, allowing you to use your + // * own custom model objects with Firestore. When you call get() on the + // * returned Query, the provided converter will convert between Firestore + // * data of type `NewDbModelType` and your custom type `NewAppModelType`. + // * + // * Using the converter allows you to specify generic type arguments when + // * storing and retrieving objects from Firestore. + // * + // * Passing in `null` as the converter parameter removes the current + // * converter. + // * + // * @example + // * ``` + // * class Post { + // * constructor(readonly title: string, readonly author: string) {} + // * + // * toString(): string { + // * return this.title + ', by ' + this.author; + // * } + // * } + // * + // * const postConverter = { + // * toFirestore(post: Post): FirebaseFirestore.DocumentData { + // * return {title: post.title, author: post.author}; + // * }, + // * fromFirestore( + // * snapshot: FirebaseFirestore.QueryDocumentSnapshot + // * ): Post { + // * const data = snapshot.data(); + // * return new Post(data.title, data.author); + // * } + // * }; + // * + // * const postSnap = await Firestore() + // * .collection('posts') + // * .withConverter(postConverter) + // * .doc().get(); + // * const post = postSnap.data(); + // * if (post !== undefined) { + // * post.title; // string + // * post.toString(); // Should be defined + // * post.someNonExistentProperty; // TS error + // * } + // * + // * ``` + // * @param {FirestoreDataConverter | null} converter Converts objects to and + // * from Firestore. Passing in `null` removes the current converter. + // * @return A Query that uses the provided converter. + // */ + // withConverter( + // converter: FirestorePipelineConverter | null + // ): Pipeline { + // const copy = this.stages.map(s => s); + // return new Pipeline( + // this.db, + // copy, + // converter ?? defaultPipelineConverter() + // ); + // } + + /** + * Executes this pipeline and returns a Promise to represent the asynchronous operation. + * + *

The returned Promise can be used to track the progress of the pipeline execution + * and retrieve the results (or handle any errors) asynchronously. + * + *

The pipeline results are returned as a list of {@link PipelineResult} objects. Each {@link + * PipelineResult} typically represents a single key/value map that has passed through all the + * stages of the pipeline, however this might differ depending on the stages involved in the + * pipeline. For example: + * + *

    + *
  • If there are no stages or only transformation stages, each {@link PipelineResult} + * represents a single document.
  • + *
  • If there is an aggregation, only a single {@link PipelineResult} is returned, + * representing the aggregated results over the entire dataset .
  • + *
  • If there is an aggregation stage with grouping, each {@link PipelineResult} represents a + * distinct group and its associated aggregated values.
  • + *
+ * + *

Example: + * + * ```typescript + * const futureResults = await firestore.pipeline().collection("books") + * .where(gt(Field.of("rating"), 4.5)) + * .select("title", "author", "rating") + * .execute(); + * ``` + * + * @return A Promise representing the asynchronous pipeline execution. + */ + execute(): Promise>> { + const datastore = getDatastore(this._db); + return invokeExecutePipeline(datastore, this).then(result => { + const docs = result + // Currently ignore any response from ExecutePipeline that does + // not contain any document data in the `fields` property. + .filter(element => !!element.fields) + .map( + element => + new PipelineResult( + this.userDataWriter, + element.key?.path + ? this.documentReferenceFactory(element.key) + : undefined, + element.fields, + element.executionTime?.toTimestamp(), + element.createTime?.toTimestamp(), + element.updateTime?.toTimestamp() + //this.converter + ) + ); + + return docs; + }); + } + + /** + * @internal + * @private + */ + _toProto(jsonProtoSerializer: JsonProtoSerializer): ExecutePipelineRequest { + const stages: ProtoStage[] = this.stages.map(stage => + stage._toProto(jsonProtoSerializer) + ); + const structuredPipeline: StructuredPipeline = { pipeline: { stages } }; + return { + database: getEncodedDatabaseId(jsonProtoSerializer), + structuredPipeline + }; + } +} + +/** + * Experimental Modular API for console testing. + * @param firestore + */ +export function pipeline(firestore: Firestore): PipelineSource; + +/** + * Experimental Modular API for console testing. + * @param query + */ +export function pipeline(query: Query): Pipeline; + +export function pipeline( + firestoreOrQuery: Firestore | Query +): PipelineSource | Pipeline { + return firestoreOrQuery.pipeline(); +} diff --git a/packages/firestore/src/lite-api/pipeline_impl.ts b/packages/firestore/src/lite-api/pipeline_impl.ts new file mode 100644 index 00000000000..cd490d31871 --- /dev/null +++ b/packages/firestore/src/lite-api/pipeline_impl.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Pipeline } from './pipeline'; +import { PipelineResult } from './pipeline-result'; + +/** + * Modular API for console experimentation. + * @param pipeline Execute this pipeline. + * @beta + */ +export function execute( + pipeline: Pipeline +): Promise>> { + return pipeline.execute(); +} diff --git a/packages/firestore/src/lite-api/reference.ts b/packages/firestore/src/lite-api/reference.ts index 26ae2fbd433..71b789227fc 100644 --- a/packages/firestore/src/lite-api/reference.ts +++ b/packages/firestore/src/lite-api/reference.ts @@ -37,6 +37,7 @@ import { AutoId } from '../util/misc'; import { Firestore } from './database'; import { FieldPath } from './field_path'; import { FieldValue } from './field_value'; +import type { Pipeline } from './pipeline'; import { FirestoreDataConverter } from './snapshot'; import { NestedUpdateFields, Primitive } from './types'; @@ -177,6 +178,15 @@ export class Query< this._query ); } + + /** + * Pipeline query. + */ + pipeline(): Pipeline { + throw new Error( + 'Pipelines not initialized. Your application must call `useFirestorePipelines()` before using Firestore Pipeline features.' + ); + } } /** diff --git a/packages/firestore/src/lite-api/stage.ts b/packages/firestore/src/lite-api/stage.ts new file mode 100644 index 00000000000..b4fe237f79c --- /dev/null +++ b/packages/firestore/src/lite-api/stage.ts @@ -0,0 +1,380 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ObjectValue } from '../model/object_value'; +import { + Stage as ProtoStage, + Value as ProtoValue +} from '../protos/firestore_proto_api'; +import { toNumber } from '../remote/number_serializer'; +import { + JsonProtoSerializer, + ProtoSerializable, + toMapValue, + toStringValue +} from '../remote/serializer'; + +import { + Accumulator, + Expr, + Field, + FilterCondition, + Ordering +} from './expressions'; +import { DocumentReference } from './reference'; +import { VectorValue } from './vector_value'; + +/** + * @beta + */ +export interface Stage extends ProtoSerializable { + name: string; +} + +/** + * @beta + */ +export class AddFields implements Stage { + name = 'add_fields'; + + constructor(private fields: Map) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [toMapValue(serializer, this.fields)] + }; + } +} + +/** + * @beta + */ +export class Aggregate implements Stage { + name = 'aggregate'; + + constructor( + private accumulators: Map, + private groups: Map + ) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [ + toMapValue(serializer, this.accumulators), + toMapValue(serializer, this.groups) + ] + }; + } +} + +/** + * @beta + */ +export class Distinct implements Stage { + name = 'distinct'; + + constructor(private groups: Map) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [toMapValue(serializer, this.groups)] + }; + } +} + +/** + * @beta + */ +export class CollectionSource implements Stage { + name = 'collection'; + + constructor(private collectionPath: string) { + if (!this.collectionPath.startsWith('/')) { + this.collectionPath = '/' + this.collectionPath; + } + } + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [{ referenceValue: this.collectionPath }] + }; + } +} + +/** + * @beta + */ +export class CollectionGroupSource implements Stage { + name = 'collection_group'; + + constructor(private collectionId: string) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [{ referenceValue: '' }, { stringValue: this.collectionId }] + }; + } +} + +/** + * @beta + */ +export class DatabaseSource implements Stage { + name = 'database'; + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name + }; + } +} + +/** + * @beta + */ +export class DocumentsSource implements Stage { + name = 'documents'; + + constructor(private docPaths: string[]) {} + + static of(refs: DocumentReference[]): DocumentsSource { + return new DocumentsSource(refs.map(ref => '/' + ref.path)); + } + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: this.docPaths.map(p => { + return { referenceValue: p }; + }) + }; + } +} + +/** + * @beta + */ +export class Where implements Stage { + name = 'where'; + + constructor(private condition: FilterCondition & Expr) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [(this.condition as unknown as Expr)._toProto(serializer)] + }; + } +} + +/** + * @beta + */ +export interface FindNearestOptions { + field: Field; + vectorValue: VectorValue | number[]; + distanceMeasure: 'euclidean' | 'cosine' | 'dot_product'; + limit?: number; + distanceField?: string; +} + +/** + * @beta + */ +export class FindNearest implements Stage { + name = 'find_nearest'; + + /** + * @private + * @internal + * + * @param _field + * @param _vectorValue + * @param _distanceMeasure + * @param _limit + * @param _distanceField + */ + constructor( + private _field: Field, + private _vectorValue: ObjectValue, + private _distanceMeasure: 'euclidean' | 'cosine' | 'dot_product', + private _limit?: number, + private _distanceField?: string + ) {} + + /** + * @private + * @internal + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + const options: { [k: string]: ProtoValue } = {}; + + if (this._limit) { + options.limit = toNumber(serializer, this._limit)!; + } + + if (this._distanceField) { + // eslint-disable-next-line camelcase + options.distance_field = Field.of(this._distanceField)._toProto( + serializer + ); + } + + return { + name: this.name, + args: [ + this._field._toProto(serializer), + this._vectorValue.value, + toStringValue(this._distanceMeasure) + ], + options + }; + } +} + +/** + * @beta + */ +export class Limit implements Stage { + name = 'limit'; + + constructor(private limit: number) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [toNumber(serializer, this.limit)] + }; + } +} + +/** + * @beta + */ +export class Offset implements Stage { + name = 'offset'; + + constructor(private offset: number) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [toNumber(serializer, this.offset)] + }; + } +} + +/** + * @beta + */ +export class Select implements Stage { + name = 'select'; + + constructor(private projections: Map) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: [toMapValue(serializer, this.projections)] + }; + } +} + +/** + * @beta + */ +export class Sort implements Stage { + name = 'sort'; + + constructor(private orders: Ordering[]) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + return { + name: this.name, + args: this.orders.map(o => o._toProto(serializer)) + }; + } +} + +/** + * @beta + */ +export class GenericStage implements Stage { + constructor(public name: string, params: unknown[]) {} + + /** + * @internal + * @private + */ + _toProto(serializer: JsonProtoSerializer): ProtoStage { + // TODO support generic stage + return {}; + } +} diff --git a/packages/firestore/src/lite-api/user_data_reader.ts b/packages/firestore/src/lite-api/user_data_reader.ts index ebd4b49085f..42f905d5cab 100644 --- a/packages/firestore/src/lite-api/user_data_reader.ts +++ b/packages/firestore/src/lite-api/user_data_reader.ts @@ -852,7 +852,7 @@ function parseSentinelFieldValue( * * @returns The parsed value */ -function parseScalarValue( +export function parseScalarValue( value: unknown, context: ParseContextImpl ): ProtoValue | null { @@ -920,9 +920,10 @@ function parseScalarValue( * Creates a new VectorValue proto value (using the internal format). */ export function parseVectorValue( - value: VectorValue, + value: VectorValue | number[], context: ParseContextImpl -): ProtoValue { +): { mapValue: ProtoMapValue } { + const values = value instanceof VectorValue ? value.toArray() : value; const mapValue: ProtoMapValue = { fields: { [TYPE_KEY]: { @@ -930,7 +931,7 @@ export function parseVectorValue( }, [VECTOR_MAP_VECTORS_KEY]: { arrayValue: { - values: value.toArray().map(value => { + values: values.map(value => { if (typeof value !== 'number') { throw context.createError( 'VectorValues must only contain numeric values.' diff --git a/packages/firestore/src/model/aggregate_result_value.ts b/packages/firestore/src/model/aggregate_result_value.ts new file mode 100644 index 00000000000..042dc29d345 --- /dev/null +++ b/packages/firestore/src/model/aggregate_result_value.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + MapValue as ProtoMapValue, + Value as ProtoValue +} from '../protos/firestore_proto_api'; + +import { valueEquals } from './values'; + +/** + * An AggregateResultValue represents a MapValue in the Firestore Proto. + */ +export class AggregateResultValue { + constructor(readonly value: { mapValue: ProtoMapValue }) {} + + static empty(): AggregateResultValue { + return new AggregateResultValue({ mapValue: {} }); + } + + aggregate(alias: string): ProtoValue | null { + return this.value.mapValue.fields?.[alias] ?? null; + } + + isEqual(other: AggregateResultValue): boolean { + return valueEquals(this.value, other.value); + } +} diff --git a/packages/firestore/src/model/pipeline_stream_element.ts b/packages/firestore/src/model/pipeline_stream_element.ts new file mode 100644 index 00000000000..efa27e2cc44 --- /dev/null +++ b/packages/firestore/src/model/pipeline_stream_element.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SnapshotVersion } from '../core/snapshot_version'; + +import { DocumentKey } from './document_key'; +import { ObjectValue } from './object_value'; + +export interface PipelineStreamElement { + transaction?: string; + key?: DocumentKey; + executionTime?: SnapshotVersion; + createTime?: SnapshotVersion; + updateTime?: SnapshotVersion; + fields?: ObjectValue; +} diff --git a/packages/firestore/src/protos/compile.sh b/packages/firestore/src/protos/compile.sh index 26c46d1a40d..9f9fb4b3217 100755 --- a/packages/firestore/src/protos/compile.sh +++ b/packages/firestore/src/protos/compile.sh @@ -18,10 +18,17 @@ set -euo pipefail # Variables PROTOS_DIR="." -PBJS="$(npm bin)/pbjs" +PBJS="../../node_modules/.bin/pbjs" +PBTS="../../node_modules/.bin/pbts" -"${PBJS}" --proto_path=. --target=json -o protos.json \ - -r firestore_v1 \ - "${PROTOS_DIR}/google/firestore/v1/*.proto" \ +"${PBJS}" --path=. --target=json -o protos.json \ + -r firestore/v1 "${PROTOS_DIR}/google/firestore/v1/*.proto" \ "${PROTOS_DIR}/google/protobuf/*.proto" "${PROTOS_DIR}/google/type/*.proto" \ "${PROTOS_DIR}/google/rpc/*.proto" "${PROTOS_DIR}/google/api/*.proto" + +"${PBJS}" --path="${PROTOS_DIR}" --target=static -o temp.js \ + -r firestore/v1 "${PROTOS_DIR}/google/firestore/v1/*.proto" \ + "${PROTOS_DIR}/google/protobuf/*.proto" "${PROTOS_DIR}/google/type/*.proto" \ + "${PROTOS_DIR}/google/rpc/*.proto" "${PROTOS_DIR}/google/api/*.proto" + +"${PBTS}" -o temp.d.ts --no-comments temp.js diff --git a/packages/firestore/src/protos/firestore_proto_api.ts b/packages/firestore/src/protos/firestore_proto_api.ts index 9618d71b86a..cc1c57259f5 100644 --- a/packages/firestore/src/protos/firestore_proto_api.ts +++ b/packages/firestore/src/protos/firestore_proto_api.ts @@ -145,9 +145,21 @@ export interface IValueNullValueEnum { } export declare const ValueNullValueEnum: IValueNullValueEnum; export declare namespace firestoreV1ApiClientInterfaces { + interface Aggregation { + count?: Count; + sum?: Sum; + avg?: Avg; + alias?: string; + } + interface AggregationResult { + aggregateFields?: ApiClientObjectMap; + } interface ArrayValue { values?: Value[]; } + interface Avg { + field?: FieldReference; + } interface BatchGetDocumentsRequest { database?: string; documents?: string[]; @@ -168,6 +180,14 @@ export declare namespace firestoreV1ApiClientInterfaces { interface BeginTransactionResponse { transaction?: string; } + interface BitSequence { + bitmap?: string | Uint8Array; + padding?: number; + } + interface BloomFilter { + bits?: BitSequence; + hashCount?: number; + } interface CollectionSelector { collectionId?: string; allDescendants?: boolean; @@ -185,6 +205,9 @@ export declare namespace firestoreV1ApiClientInterfaces { op?: CompositeFilterOp; filters?: Filter[]; } + interface Count { + upTo?: number; + } interface Cursor { values?: Value[]; before?: boolean; @@ -221,19 +244,23 @@ export declare namespace firestoreV1ApiClientInterfaces { documents?: string[]; } interface Empty {} + interface ExecutePipelineRequest { + database?: string; + structuredPipeline?: StructuredPipeline; + transaction?: string; + newTransaction?: TransactionOptions; + readTime?: string; + } + interface ExecutePipelineResponse { + transaction?: string; + results?: Document[]; + executionTime?: string; + } interface ExistenceFilter { targetId?: number; count?: number; unchangedNames?: BloomFilter; } - interface BloomFilter { - bits?: BitSequence; - hashCount?: number; - } - interface BitSequence { - bitmap?: string | Uint8Array; - padding?: number; - } interface FieldFilter { field?: FieldReference; op?: FieldFilterOp; @@ -254,6 +281,11 @@ export declare namespace firestoreV1ApiClientInterfaces { fieldFilter?: FieldFilter; unaryFilter?: UnaryFilter; } + interface Function { + name?: string; + args?: Value[]; + options?: ApiClientObjectMap; + } interface Index { name?: string; collectionId?: string; @@ -310,6 +342,9 @@ export declare namespace firestoreV1ApiClientInterfaces { field?: FieldReference; direction?: OrderDirection; } + interface Pipeline { + stages?: Stage[]; + } interface Precondition { exists?: boolean; updateTime?: Timestamp; @@ -355,33 +390,24 @@ export declare namespace firestoreV1ApiClientInterfaces { transaction?: string; readTime?: string; } - interface AggregationResult { - aggregateFields?: ApiClientObjectMap; - } interface StructuredAggregationQuery { structuredQuery?: StructuredQuery; aggregations?: Aggregation[]; } - interface Aggregation { - count?: Count; - sum?: Sum; - avg?: Avg; - alias?: string; - } - interface Count { - upTo?: number; - } - interface Sum { - field?: FieldReference; - } - interface Avg { - field?: FieldReference; + interface Stage { + name?: string; + args?: Value[]; + options?: ApiClientObjectMap; } interface Status { code?: number; message?: string; details?: Array>; } + interface StructuredPipeline { + pipeline?: Pipeline; + options?: ApiClientObjectMap; + } interface StructuredQuery { select?: Projection; from?: CollectionSelector[]; @@ -392,6 +418,9 @@ export declare namespace firestoreV1ApiClientInterfaces { offset?: number; limit?: number | { value: number }; } + interface Sum { + field?: FieldReference; + } interface Target { query?: QueryTarget; documents?: DocumentsTarget; @@ -428,6 +457,10 @@ export declare namespace firestoreV1ApiClientInterfaces { geoPointValue?: LatLng; arrayValue?: ArrayValue; mapValue?: MapValue; + fieldReferenceValue?: string; + // eslint-disable-next-line @typescript-eslint/ban-types + functionValue?: Function; + pipelineValue?: Pipeline; } interface Write { update?: Document; @@ -489,12 +522,17 @@ export declare type DocumentsTarget = export declare type Empty = firestoreV1ApiClientInterfaces.Empty; export declare type ExistenceFilter = firestoreV1ApiClientInterfaces.ExistenceFilter; +export declare type ExecutePipelineRequest = + firestoreV1ApiClientInterfaces.ExecutePipelineRequest; +export declare type ExecutePipelineResponse = + firestoreV1ApiClientInterfaces.ExecutePipelineResponse; export declare type FieldFilter = firestoreV1ApiClientInterfaces.FieldFilter; export declare type FieldReference = firestoreV1ApiClientInterfaces.FieldReference; export declare type FieldTransform = firestoreV1ApiClientInterfaces.FieldTransform; export declare type Filter = firestoreV1ApiClientInterfaces.Filter; +export declare type Function = firestoreV1ApiClientInterfaces.Function; export declare type Index = firestoreV1ApiClientInterfaces.Index; export declare type IndexField = firestoreV1ApiClientInterfaces.IndexField; export declare type LatLng = firestoreV1ApiClientInterfaces.LatLng; @@ -513,6 +551,7 @@ export declare type ListenResponse = export declare type MapValue = firestoreV1ApiClientInterfaces.MapValue; export declare type Operation = firestoreV1ApiClientInterfaces.Operation; export declare type Order = firestoreV1ApiClientInterfaces.Order; +export declare type Pipeline = firestoreV1ApiClientInterfaces.Pipeline; export declare type Precondition = firestoreV1ApiClientInterfaces.Precondition; export declare type Projection = firestoreV1ApiClientInterfaces.Projection; export declare type QueryTarget = firestoreV1ApiClientInterfaces.QueryTarget; @@ -529,9 +568,12 @@ export declare type RunAggregationQueryRequest = export declare type Aggregation = firestoreV1ApiClientInterfaces.Aggregation; export declare type RunAggregationQueryResponse = firestoreV1ApiClientInterfaces.RunAggregationQueryResponse; +export declare type Stage = firestoreV1ApiClientInterfaces.Stage; export declare type Status = firestoreV1ApiClientInterfaces.Status; export declare type StructuredQuery = firestoreV1ApiClientInterfaces.StructuredQuery; +export declare type StructuredPipeline = + firestoreV1ApiClientInterfaces.StructuredPipeline; export declare type Target = firestoreV1ApiClientInterfaces.Target; export declare type TargetChange = firestoreV1ApiClientInterfaces.TargetChange; export declare type TransactionOptions = diff --git a/packages/firestore/src/protos/google/api/launch_stage.proto b/packages/firestore/src/protos/google/api/launch_stage.proto new file mode 100644 index 00000000000..9863fc23d42 --- /dev/null +++ b/packages/firestore/src/protos/google/api/launch_stage.proto @@ -0,0 +1,72 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +option go_package = "google.golang.org/genproto/googleapis/api;api"; +option java_multiple_files = true; +option java_outer_classname = "LaunchStageProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// The launch stage as defined by [Google Cloud Platform +// Launch Stages](https://cloud.google.com/terms/launch-stages). +enum LaunchStage { + // Do not use this default value. + LAUNCH_STAGE_UNSPECIFIED = 0; + + // The feature is not yet implemented. Users can not use it. + UNIMPLEMENTED = 6; + + // Prelaunch features are hidden from users and are only visible internally. + PRELAUNCH = 7; + + // Early Access features are limited to a closed group of testers. To use + // these features, you must sign up in advance and sign a Trusted Tester + // agreement (which includes confidentiality provisions). These features may + // be unstable, changed in backward-incompatible ways, and are not + // guaranteed to be released. + EARLY_ACCESS = 1; + + // Alpha is a limited availability test for releases before they are cleared + // for widespread use. By Alpha, all significant design issues are resolved + // and we are in the process of verifying functionality. Alpha customers + // need to apply for access, agree to applicable terms, and have their + // projects allowlisted. Alpha releases don't have to be feature complete, + // no SLAs are provided, and there are no technical support obligations, but + // they will be far enough along that customers can actually use them in + // test environments or for limited-use tests -- just like they would in + // normal production cases. + ALPHA = 2; + + // Beta is the point at which we are ready to open a release for any + // customer to use. There are no SLA or technical support obligations in a + // Beta release. Products will be complete from a feature perspective, but + // may have some open outstanding issues. Beta releases are suitable for + // limited production use cases. + BETA = 3; + + // GA features are open to all developers and are considered stable and + // fully qualified for production use. + GA = 4; + + // Deprecated features are scheduled to be shut down and removed. For more + // information, see the "Deprecation Policy" section of our [Terms of + // Service](https://cloud.google.com/terms/) + // and the [Google Cloud Platform Subject to the Deprecation + // Policy](https://cloud.google.com/terms/deprecation) documentation. + DEPRECATED = 5; +} diff --git a/packages/firestore/src/protos/google/firestore/v1/document.proto b/packages/firestore/src/protos/google/firestore/v1/document.proto index 5238a943ce4..f7605750502 100644 --- a/packages/firestore/src/protos/google/firestore/v1/document.proto +++ b/packages/firestore/src/protos/google/firestore/v1/document.proto @@ -129,6 +129,48 @@ message Value { // A map value. MapValue map_value = 6; + // Value which references a field. + // + // This is considered relative (vs absolute) since it only refers to a field + // and not a field within a particular document. + // + // **Requires:** + // + // * Must follow [field reference][FieldReference.field_path] limitations. + // + // * Not allowed to be used when writing documents. + // + // (-- NOTE(batchik): long term, there is no reason this type should not be + // allowed to be used on the write path. --) + string field_reference_value = 19; + + // A value that represents an unevaluated expression. + // + // **Requires:** + // + // * Not allowed to be used when writing documents. + // + // (-- NOTE(batchik): similar to above, there is no reason to not allow + // storing expressions into the database, just no plan to support in + // the near term. + // + // This would actually be an interesting way to represent user-defined + // functions or more expressive rules-based systems. --) + Function function_value = 20; + + // A value that represents an unevaluated pipeline. + // + // **Requires:** + // + // * Not allowed to be used when writing documents. + // + // (-- NOTE(batchik): similar to above, there is no reason to not allow + // storing expressions into the database, just no plan to support in + // the near term. + // + // This would actually be an interesting way to represent user-defined + // functions or more expressive rules-based systems. --) + Pipeline pipeline_value = 21; } } @@ -148,3 +190,73 @@ message MapValue { // not exceed 1,500 bytes and cannot be empty. map fields = 1; } + + +// Represents an unevaluated scalar expression. +// +// For example, the expression `like(user_name, "%alice%")` is represented as: +// +// ``` +// name: "like" +// args { field_reference: "user_name" } +// args { string_value: "%alice%" } +// ``` +// +// (-- api-linter: core::0123::resource-annotation=disabled +// aip.dev/not-precedent: this is not a One Platform API resource. --) +message Function { + // The name of the function to evaluate. + // + // **Requires:** + // + // * must be in snake case (lower case with underscore separator). + // + string name = 1; + + // Ordered list of arguments the given function expects. + repeated Value args = 2; + + // Optional named arguments that certain functions may support. + map options = 3; +} + +// A Firestore query represented as an ordered list of operations / stages. +message Pipeline { + // A single operation within a pipeline. + // + // A stage is made up of a unique name, and a list of arguments. The exact + // number of arguments & types is dependent on the stage type. + // + // To give an example, the stage `filter(state = "MD")` would be encoded as: + // + // ``` + // name: "filter" + // args { + // function_value { + // name: "eq" + // args { field_reference_value: "state" } + // args { string_value: "MD" } + // } + // } + // ``` + // + // See public documentation for the full list. + message Stage { + // The name of the stage to evaluate. + // + // **Requires:** + // + // * must be in snake case (lower case with underscore separator). + // + string name = 1; + + // Ordered list of arguments the given stage expects. + repeated Value args = 2; + + // Optional named arguments that certain functions may support. + map options = 3; + } + + // Ordered list of stages to evaluate. + repeated Stage stages = 1; +} diff --git a/packages/firestore/src/protos/google/firestore/v1/firestore.proto b/packages/firestore/src/protos/google/firestore/v1/firestore.proto index a8fc0d54b51..3e7b62e0609 100644 --- a/packages/firestore/src/protos/google/firestore/v1/firestore.proto +++ b/packages/firestore/src/protos/google/firestore/v1/firestore.proto @@ -22,6 +22,7 @@ import "google/api/field_behavior.proto"; import "google/firestore/v1/aggregation_result.proto"; import "google/firestore/v1/common.proto"; import "google/firestore/v1/document.proto"; +import "google/firestore/v1/pipeline.proto"; import "google/firestore/v1/query.proto"; import "google/firestore/v1/write.proto"; import "google/protobuf/empty.proto"; @@ -135,6 +136,15 @@ service Firestore { }; } + // Executes a pipeline query. + rpc ExecutePipeline(ExecutePipelineRequest) + returns (stream ExecutePipelineResponse) { + option (google.api.http) = { + post: "/v1/{database=projects/*/databases/*}/documents:executePipeline" + body: "*" + }; + } + // Runs an aggregation query. // // Rather than producing [Document][google.firestore.v1.Document] results like [Firestore.RunQuery][google.firestore.v1.Firestore.RunQuery], @@ -157,7 +167,7 @@ service Firestore { } }; } - + // Partitions a query by returning partition cursors that can be used to run // the query in parallel. The returned partition cursors are split points that // can be used by RunQuery as starting/end points for the query results. @@ -547,6 +557,82 @@ message RunQueryResponse { int32 skipped_results = 4; } +// The request for [Firestore.ExecutePipeline][]. +message ExecutePipelineRequest { + // Database identifier, in the form `projects/{project}/databases/{database}`. + string database = 1 [ + (google.api.field_behavior) = REQUIRED + ]; + + oneof pipeline_type { + // A pipelined operation. + StructuredPipeline structured_pipeline = 2; + } + + // Optional consistency arguments, defaults to strong consistency. + oneof consistency_selector { + // Run the query within an already active transaction. + // + // The value here is the opaque transaction ID to execute the query in. + bytes transaction = 5; + + // Execute the pipeline in a new transaction. + // + // The identifier of the newly created transaction will be returned in the + // first response on the stream. This defaults to a read-only transaction. + TransactionOptions new_transaction = 6; + + // Execute the pipeline in a snapshot transaction at the given time. + // + // This must be a microsecond precision timestamp within the past one hour, + // or if Point-in-Time Recovery is enabled, can additionally be a whole + // minute timestamp within the past 7 days. + google.protobuf.Timestamp read_time = 7; + } + + // Explain / analyze options for the pipeline. + // ExplainOptions explain_options = 8 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response for [Firestore.Execute][]. +message ExecutePipelineResponse { + // Newly created transaction identifier. + // + // This field is only specified on the first response from the server when + // the request specified [ExecuteRequest.new_transaction][]. + bytes transaction = 1; + + // An ordered batch of results returned executing a pipeline. + // + // The batch size is variable, and can even be zero for when only a partial + // progress message is returned. + // + // The fields present in the returned documents are only those that were + // explicitly requested in the pipeline, this include those like + // [`__name__`][Document.name] & [`__update_time__`][Document.update_time]. + // This is explicitly a divergence from `Firestore.RunQuery` / + // `Firestore.GetDocument` RPCs which always return such fields even when they + // are not specified in the [`mask`][DocumentMask]. + repeated Document results = 2; + + // The time at which the document(s) were read. + // + // This may be monotonically increasing; in this case, the previous documents + // in the result stream are guaranteed not to have changed between their + // `execution_time` and this one. + // + // If the query returns no results, a response with `execution_time` and no + // `results` will be sent, and this represents the time at which the operation + // was run. + google.protobuf.Timestamp execution_time = 3; + + // Query explain metrics. + // + // Set on the last response when [ExecutePipelineRequest.explain_options][] + // was specified on the request. + // ExplainMetrics explain_metrics = 4; +} + // The request for [Firestore.RunAggregationQuery][google.firestore.v1.Firestore.RunAggregationQuery]. message RunAggregationQueryRequest { // Required. The parent resource name. In the format: diff --git a/packages/firestore/src/protos/google/firestore/v1/pipeline.proto b/packages/firestore/src/protos/google/firestore/v1/pipeline.proto new file mode 100644 index 00000000000..ea5b2309331 --- /dev/null +++ b/packages/firestore/src/protos/google/firestore/v1/pipeline.proto @@ -0,0 +1,41 @@ +/*! + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; +package google.firestore.v1; +import "google/firestore/v1/document.proto"; +option csharp_namespace = "Google.Cloud.Firestore.V1"; +option php_namespace = "Google\\Cloud\\Firestore\\V1"; +option ruby_package = "Google::Cloud::Firestore::V1"; +option java_multiple_files = true; +option java_package = "com.google.firestore.v1"; +option java_outer_classname = "PipelineProto"; +option objc_class_prefix = "GCFS"; +// A Firestore query represented as an ordered list of operations / stages. +// +// This is considered the top-level function which plans & executes a query. +// It is logically equivalent to `query(stages, options)`, but prevents the +// client from having to build a function wrapper. +message StructuredPipeline { + // The pipeline query to execute. + Pipeline pipeline = 1; + // Optional query-level arguments. + // + // (-- Think query statement hints. --) + // + // (-- TODO(batchik): define the api contract of using an unsupported hint --) + map options = 2; +} diff --git a/packages/firestore/src/protos/google/firestore/v1/query_profile.proto b/packages/firestore/src/protos/google/firestore/v1/query_profile.proto new file mode 100644 index 00000000000..de27144a038 --- /dev/null +++ b/packages/firestore/src/protos/google/firestore/v1/query_profile.proto @@ -0,0 +1,92 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.firestore.v1; + +import "google/api/field_behavior.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/struct.proto"; + +option csharp_namespace = "Google.Cloud.Firestore.V1"; +option go_package = "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb"; +option java_multiple_files = true; +option java_outer_classname = "QueryProfileProto"; +option java_package = "com.google.firestore.v1"; +option objc_class_prefix = "GCFS"; +option php_namespace = "Google\\Cloud\\Firestore\\V1"; +option ruby_package = "Google::Cloud::Firestore::V1"; + +// Specification of the Firestore Query Profile fields. + +// Explain options for the query. +message ExplainOptions { + // Optional. Whether to execute this query. + // + // When false (the default), the query will be planned, returning only + // metrics from the planning stages. + // + // When true, the query will be planned and executed, returning the full + // query results along with both planning and execution stage metrics. + bool analyze = 1 [(google.api.field_behavior) = OPTIONAL]; +} + +// Explain metrics for the query. +message ExplainMetrics { + // Planning phase information for the query. + PlanSummary plan_summary = 1; + + // Aggregated stats from the execution of the query. Only present when + // [ExplainOptions.analyze][google.firestore.v1.ExplainOptions.analyze] is set + // to true. + ExecutionStats execution_stats = 2; +} + +// Planning phase information for the query. +message PlanSummary { + // The indexes selected for the query. For example: + // [ + // {"query_scope": "Collection", "properties": "(foo ASC, __name__ ASC)"}, + // {"query_scope": "Collection", "properties": "(bar ASC, __name__ ASC)"} + // ] + repeated google.protobuf.Struct indexes_used = 1; +} + +// Execution statistics for the query. +message ExecutionStats { + // Total number of results returned, including documents, projections, + // aggregation results, keys. + int64 results_returned = 1; + + // Total time to execute the query in the backend. + google.protobuf.Duration execution_duration = 3; + + // Total billable read operations. + int64 read_operations = 4; + + // Debugging statistics from the execution of the query. Note that the + // debugging stats are subject to change as Firestore evolves. It could + // include: + // { + // "indexes_entries_scanned": "1000", + // "documents_scanned": "20", + // "billing_details" : { + // "documents_billable": "20", + // "index_entries_billable": "1000", + // "min_query_cost": "0" + // } + // } + google.protobuf.Struct debug_stats = 5; +} diff --git a/packages/firestore/src/protos/protos.json b/packages/firestore/src/protos/protos.json index b2c50ccaeeb..5b73c4647f8 100644 --- a/packages/firestore/src/protos/protos.json +++ b/packages/firestore/src/protos/protos.json @@ -4,16 +4,78 @@ "nested": { "protobuf": { "options": { - "csharp_namespace": "Google.Protobuf.WellKnownTypes", - "go_package": "github.com/golang/protobuf/ptypes/wrappers", + "go_package": "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor", "java_package": "com.google.protobuf", - "java_outer_classname": "WrappersProto", - "java_multiple_files": true, + "java_outer_classname": "DescriptorProtos", + "csharp_namespace": "Google.Protobuf.Reflection", "objc_class_prefix": "GPB", "cc_enable_arenas": true, "optimize_for": "SPEED" }, "nested": { + "Struct": { + "fields": { + "fields": { + "keyType": "string", + "type": "Value", + "id": 1 + } + } + }, + "Value": { + "oneofs": { + "kind": { + "oneof": [ + "nullValue", + "numberValue", + "stringValue", + "boolValue", + "structValue", + "listValue" + ] + } + }, + "fields": { + "nullValue": { + "type": "NullValue", + "id": 1 + }, + "numberValue": { + "type": "double", + "id": 2 + }, + "stringValue": { + "type": "string", + "id": 3 + }, + "boolValue": { + "type": "bool", + "id": 4 + }, + "structValue": { + "type": "Struct", + "id": 5 + }, + "listValue": { + "type": "ListValue", + "id": 6 + } + } + }, + "NullValue": { + "values": { + "NULL_VALUE": 0 + } + }, + "ListValue": { + "fields": { + "values": { + "rule": "repeated", + "type": "Value", + "id": 1 + } + } + }, "Timestamp": { "fields": { "seconds": { @@ -161,6 +223,10 @@ "end": { "type": "int32", "id": 2 + }, + "options": { + "type": "ExtensionRangeOptions", + "id": 3 } } }, @@ -178,6 +244,21 @@ } } }, + "ExtensionRangeOptions": { + "fields": { + "uninterpretedOption": { + "rule": "repeated", + "type": "UninterpretedOption", + "id": 999 + } + }, + "extensions": [ + [ + 1000, + 536870911 + ] + ] + }, "FieldDescriptorProto": { "fields": { "name": { @@ -279,6 +360,30 @@ "options": { "type": "EnumOptions", "id": 3 + }, + "reservedRange": { + "rule": "repeated", + "type": "EnumReservedRange", + "id": 4 + }, + "reservedName": { + "rule": "repeated", + "type": "string", + "id": 5 + } + }, + "nested": { + "EnumReservedRange": { + "fields": { + "start": { + "type": "int32", + "id": 1 + }, + "end": { + "type": "int32", + "id": 2 + } + } } } }, @@ -335,11 +440,17 @@ }, "clientStreaming": { "type": "bool", - "id": 5 + "id": 5, + "options": { + "default": false + } }, "serverStreaming": { "type": "bool", - "id": 6 + "id": 6, + "options": { + "default": false + } } } }, @@ -355,7 +466,10 @@ }, "javaMultipleFiles": { "type": "bool", - "id": 10 + "id": 10, + "options": { + "default": false + } }, "javaGenerateEqualsAndHash": { "type": "bool", @@ -366,7 +480,10 @@ }, "javaStringCheckUtf8": { "type": "bool", - "id": 27 + "id": 27, + "options": { + "default": false + } }, "optimizeFor": { "type": "OptimizeMode", @@ -381,23 +498,45 @@ }, "ccGenericServices": { "type": "bool", - "id": 16 + "id": 16, + "options": { + "default": false + } }, "javaGenericServices": { "type": "bool", - "id": 17 + "id": 17, + "options": { + "default": false + } }, "pyGenericServices": { "type": "bool", - "id": 18 + "id": 18, + "options": { + "default": false + } + }, + "phpGenericServices": { + "type": "bool", + "id": 42, + "options": { + "default": false + } }, "deprecated": { "type": "bool", - "id": 23 + "id": 23, + "options": { + "default": false + } }, "ccEnableArenas": { "type": "bool", - "id": 31 + "id": 31, + "options": { + "default": false + } }, "objcClassPrefix": { "type": "string", @@ -407,6 +546,26 @@ "type": "string", "id": 37 }, + "swiftPrefix": { + "type": "string", + "id": 39 + }, + "phpClassPrefix": { + "type": "string", + "id": 40 + }, + "phpNamespace": { + "type": "string", + "id": 41 + }, + "phpMetadataNamespace": { + "type": "string", + "id": 44 + }, + "rubyPackage": { + "type": "string", + "id": 45 + }, "uninterpretedOption": { "rule": "repeated", "type": "UninterpretedOption", @@ -439,15 +598,24 @@ "fields": { "messageSetWireFormat": { "type": "bool", - "id": 1 + "id": 1, + "options": { + "default": false + } }, "noStandardDescriptorAccessor": { "type": "bool", - "id": 2 + "id": 2, + "options": { + "default": false + } }, "deprecated": { "type": "bool", - "id": 3 + "id": 3, + "options": { + "default": false + } }, "mapEntry": { "type": "bool", @@ -469,6 +637,10 @@ [ 8, 8 + ], + [ + 9, + 9 ] ] }, @@ -494,15 +666,24 @@ }, "lazy": { "type": "bool", - "id": 5 + "id": 5, + "options": { + "default": false + } }, "deprecated": { "type": "bool", - "id": 3 + "id": 3, + "options": { + "default": false + } }, "weak": { "type": "bool", - "id": 10 + "id": 10, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -562,7 +743,10 @@ }, "deprecated": { "type": "bool", - "id": 3 + "id": 3, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -575,13 +759,22 @@ 1000, 536870911 ] + ], + "reserved": [ + [ + 5, + 5 + ] ] }, "EnumValueOptions": { "fields": { "deprecated": { "type": "bool", - "id": 1 + "id": 1, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -600,7 +793,10 @@ "fields": { "deprecated": { "type": "bool", - "id": 33 + "id": 33, + "options": { + "default": false + } }, "uninterpretedOption": { "rule": "repeated", @@ -619,7 +815,17 @@ "fields": { "deprecated": { "type": "bool", - "id": 33 + "id": 33, + "options": { + "default": false + } + }, + "idempotencyLevel": { + "type": "IdempotencyLevel", + "id": 34, + "options": { + "default": "IDEMPOTENCY_UNKNOWN" + } }, "uninterpretedOption": { "rule": "repeated", @@ -632,7 +838,16 @@ 1000, 536870911 ] - ] + ], + "nested": { + "IdempotencyLevel": { + "values": { + "IDEMPOTENCY_UNKNOWN": 0, + "NO_SIDE_EFFECTS": 1, + "IDEMPOTENT": 2 + } + } + } }, "UninterpretedOption": { "fields": { @@ -753,72 +968,6 @@ } } }, - "Struct": { - "fields": { - "fields": { - "keyType": "string", - "type": "Value", - "id": 1 - } - } - }, - "Value": { - "oneofs": { - "kind": { - "oneof": [ - "nullValue", - "numberValue", - "stringValue", - "boolValue", - "structValue", - "listValue" - ] - } - }, - "fields": { - "nullValue": { - "type": "NullValue", - "id": 1 - }, - "numberValue": { - "type": "double", - "id": 2 - }, - "stringValue": { - "type": "string", - "id": 3 - }, - "boolValue": { - "type": "bool", - "id": 4 - }, - "structValue": { - "type": "Struct", - "id": 5 - }, - "listValue": { - "type": "ListValue", - "id": 6 - } - } - }, - "NullValue": { - "values": { - "NULL_VALUE": 0 - } - }, - "ListValue": { - "fields": { - "values": { - "rule": "repeated", - "type": "Value", - "id": 1 - } - } - }, - "Empty": { - "fields": {} - }, "DoubleValue": { "fields": { "value": { @@ -891,9 +1040,12 @@ } } }, + "Empty": { + "fields": {} + }, "Any": { "fields": { - "typeUrl": { + "type_url": { "type": "string", "id": 1 }, @@ -902,6 +1054,18 @@ "id": 2 } } + }, + "Duration": { + "fields": { + "seconds": { + "type": "int64", + "id": 1 + }, + "nanos": { + "type": "int32", + "id": 2 + } + } } } }, @@ -910,9 +1074,9 @@ "v1": { "options": { "csharp_namespace": "Google.Cloud.Firestore.V1", - "go_package": "google.golang.org/genproto/googleapis/firestore/v1;firestore", + "go_package": "cloud.google.com/go/firestore/apiv1/firestorepb;firestorepb", "java_multiple_files": true, - "java_outer_classname": "WriteProto", + "java_outer_classname": "QueryProfileProto", "java_package": "com.google.firestore.v1", "objc_class_prefix": "GCFS", "php_namespace": "Google\\Cloud\\Firestore\\V1", @@ -928,104 +1092,6 @@ } } }, - "BitSequence": { - "fields": { - "bitmap": { - "type": "bytes", - "id": 1 - }, - "padding": { - "type": "int32", - "id": 2 - } - } - }, - "BloomFilter": { - "fields": { - "bits": { - "type": "BitSequence", - "id": 1 - }, - "hashCount": { - "type": "int32", - "id": 2 - } - } - }, - "DocumentMask": { - "fields": { - "fieldPaths": { - "rule": "repeated", - "type": "string", - "id": 1 - } - } - }, - "Precondition": { - "oneofs": { - "conditionType": { - "oneof": [ - "exists", - "updateTime" - ] - } - }, - "fields": { - "exists": { - "type": "bool", - "id": 1 - }, - "updateTime": { - "type": "google.protobuf.Timestamp", - "id": 2 - } - } - }, - "TransactionOptions": { - "oneofs": { - "mode": { - "oneof": [ - "readOnly", - "readWrite" - ] - } - }, - "fields": { - "readOnly": { - "type": "ReadOnly", - "id": 2 - }, - "readWrite": { - "type": "ReadWrite", - "id": 3 - } - }, - "nested": { - "ReadWrite": { - "fields": { - "retryTransaction": { - "type": "bytes", - "id": 1 - } - } - }, - "ReadOnly": { - "oneofs": { - "consistencySelector": { - "oneof": [ - "readTime" - ] - } - }, - "fields": { - "readTime": { - "type": "google.protobuf.Timestamp", - "id": 2 - } - } - } - } - }, "Document": { "fields": { "name": { @@ -1061,7 +1127,10 @@ "referenceValue", "geoPointValue", "arrayValue", - "mapValue" + "mapValue", + "fieldReferenceValue", + "functionValue", + "pipelineValue" ] } }, @@ -1106,27 +1175,184 @@ "type": "ArrayValue", "id": 9 }, - "mapValue": { - "type": "MapValue", - "id": 6 + "mapValue": { + "type": "MapValue", + "id": 6 + }, + "fieldReferenceValue": { + "type": "string", + "id": 19 + }, + "functionValue": { + "type": "Function", + "id": 20 + }, + "pipelineValue": { + "type": "Pipeline", + "id": 21 + } + } + }, + "ArrayValue": { + "fields": { + "values": { + "rule": "repeated", + "type": "Value", + "id": 1 + } + } + }, + "MapValue": { + "fields": { + "fields": { + "keyType": "string", + "type": "Value", + "id": 1 + } + } + }, + "Function": { + "fields": { + "name": { + "type": "string", + "id": 1 + }, + "args": { + "rule": "repeated", + "type": "Value", + "id": 2 + }, + "options": { + "keyType": "string", + "type": "Value", + "id": 3 + } + } + }, + "Pipeline": { + "fields": { + "stages": { + "rule": "repeated", + "type": "Stage", + "id": 1 + } + }, + "nested": { + "Stage": { + "fields": { + "name": { + "type": "string", + "id": 1 + }, + "args": { + "rule": "repeated", + "type": "Value", + "id": 2 + }, + "options": { + "keyType": "string", + "type": "Value", + "id": 3 + } + } + } + } + }, + "BitSequence": { + "fields": { + "bitmap": { + "type": "bytes", + "id": 1 + }, + "padding": { + "type": "int32", + "id": 2 + } + } + }, + "BloomFilter": { + "fields": { + "bits": { + "type": "BitSequence", + "id": 1 + }, + "hashCount": { + "type": "int32", + "id": 2 } } }, - "ArrayValue": { + "DocumentMask": { "fields": { - "values": { + "fieldPaths": { "rule": "repeated", - "type": "Value", + "type": "string", "id": 1 } } }, - "MapValue": { + "Precondition": { + "oneofs": { + "conditionType": { + "oneof": [ + "exists", + "updateTime" + ] + } + }, "fields": { - "fields": { - "keyType": "string", - "type": "Value", + "exists": { + "type": "bool", "id": 1 + }, + "updateTime": { + "type": "google.protobuf.Timestamp", + "id": 2 + } + } + }, + "TransactionOptions": { + "oneofs": { + "mode": { + "oneof": [ + "readOnly", + "readWrite" + ] + } + }, + "fields": { + "readOnly": { + "type": "ReadOnly", + "id": 2 + }, + "readWrite": { + "type": "ReadWrite", + "id": 3 + } + }, + "nested": { + "ReadWrite": { + "fields": { + "retryTransaction": { + "type": "bytes", + "id": 1 + } + } + }, + "ReadOnly": { + "oneofs": { + "consistencySelector": { + "oneof": [ + "readTime" + ] + } + }, + "fields": { + "readTime": { + "type": "google.protobuf.Timestamp", + "id": 2 + } + } } } }, @@ -1302,6 +1528,23 @@ } ] }, + "ExecutePipeline": { + "requestType": "ExecutePipelineRequest", + "responseType": "ExecutePipelineResponse", + "responseStream": true, + "options": { + "(google.api.http).post": "/v1/{database=projects/*/databases/*}/documents:executePipeline", + "(google.api.http).body": "*" + }, + "parsedOptions": [ + { + "(google.api.http)": { + "post": "/v1/{database=projects/*/databases/*}/documents:executePipeline", + "body": "*" + } + } + ] + }, "RunAggregationQuery": { "requestType": "RunAggregationQueryRequest", "responseType": "RunAggregationQueryResponse", @@ -1816,6 +2059,64 @@ } } }, + "ExecutePipelineRequest": { + "oneofs": { + "pipelineType": { + "oneof": [ + "structuredPipeline" + ] + }, + "consistencySelector": { + "oneof": [ + "transaction", + "newTransaction", + "readTime" + ] + } + }, + "fields": { + "database": { + "type": "string", + "id": 1, + "options": { + "(google.api.field_behavior)": "REQUIRED" + } + }, + "structuredPipeline": { + "type": "StructuredPipeline", + "id": 2 + }, + "transaction": { + "type": "bytes", + "id": 5 + }, + "newTransaction": { + "type": "TransactionOptions", + "id": 6 + }, + "readTime": { + "type": "google.protobuf.Timestamp", + "id": 7 + } + } + }, + "ExecutePipelineResponse": { + "fields": { + "transaction": { + "type": "bytes", + "id": 1 + }, + "results": { + "rule": "repeated", + "type": "Document", + "id": 2 + }, + "executionTime": { + "type": "google.protobuf.Timestamp", + "id": 3 + } + } + }, "RunAggregationQueryRequest": { "oneofs": { "queryType": { @@ -2216,6 +2517,19 @@ } } }, + "StructuredPipeline": { + "fields": { + "pipeline": { + "type": "Pipeline", + "id": 1 + }, + "options": { + "keyType": "string", + "type": "Value", + "id": 2 + } + } + }, "StructuredQuery": { "fields": { "select": { @@ -2474,7 +2788,7 @@ "Sum": { "fields": { "field": { - "type": "FieldReference", + "type": "StructuredQuery.FieldReference", "id": 1 } } @@ -2482,7 +2796,7 @@ "Avg": { "fields": { "field": { - "type": "FieldReference", + "type": "StructuredQuery.FieldReference", "id": 1 } } @@ -2694,6 +3008,82 @@ "id": 3 } } + }, + "ExplainOptions": { + "fields": { + "analyze": { + "type": "bool", + "id": 1, + "options": { + "(google.api.field_behavior)": "OPTIONAL" + } + } + } + }, + "ExplainMetrics": { + "fields": { + "planSummary": { + "type": "PlanSummary", + "id": 1 + }, + "executionStats": { + "type": "ExecutionStats", + "id": 2 + } + } + }, + "PlanSummary": { + "fields": { + "indexesUsed": { + "rule": "repeated", + "type": "google.protobuf.Struct", + "id": 1 + } + } + }, + "ExecutionStats": { + "fields": { + "resultsReturned": { + "type": "int64", + "id": 1 + }, + "executionDuration": { + "type": "google.protobuf.Duration", + "id": 3 + }, + "readOperations": { + "type": "int64", + "id": 4 + }, + "debugStats": { + "type": "google.protobuf.Struct", + "id": 5 + } + } + } + } + } + } + }, + "type": { + "options": { + "cc_enable_arenas": true, + "go_package": "google.golang.org/genproto/googleapis/type/latlng;latlng", + "java_multiple_files": true, + "java_outer_classname": "LatLngProto", + "java_package": "com.google.type", + "objc_class_prefix": "GTP" + }, + "nested": { + "LatLng": { + "fields": { + "latitude": { + "type": "double", + "id": 1 + }, + "longitude": { + "type": "double", + "id": 2 } } } @@ -2701,9 +3091,9 @@ }, "api": { "options": { - "go_package": "google.golang.org/genproto/googleapis/api/annotations;annotations", + "go_package": "google.golang.org/genproto/googleapis/api;api", "java_multiple_files": true, - "java_outer_classname": "HttpProto", + "java_outer_classname": "LaunchStageProto", "java_package": "com.google.api", "objc_class_prefix": "GAPI", "cc_enable_arenas": true @@ -2720,6 +3110,10 @@ "rule": "repeated", "type": "HttpRule", "id": 1 + }, + "fullyDecodeReservedExpansion": { + "type": "bool", + "id": 2 } } }, @@ -2737,6 +3131,10 @@ } }, "fields": { + "selector": { + "type": "string", + "id": 1 + }, "get": { "type": "string", "id": 2 @@ -2761,14 +3159,14 @@ "type": "CustomHttpPattern", "id": 8 }, - "selector": { - "type": "string", - "id": 1 - }, "body": { "type": "string", "id": 7 }, + "responseBody": { + "type": "string", + "id": 12 + }, "additionalBindings": { "rule": "repeated", "type": "HttpRule", @@ -2821,29 +3219,17 @@ "UNORDERED_LIST": 6, "NON_EMPTY_DEFAULT": 7 } - } - } - }, - "type": { - "options": { - "cc_enable_arenas": true, - "go_package": "google.golang.org/genproto/googleapis/type/latlng;latlng", - "java_multiple_files": true, - "java_outer_classname": "LatLngProto", - "java_package": "com.google.type", - "objc_class_prefix": "GTP" - }, - "nested": { - "LatLng": { - "fields": { - "latitude": { - "type": "double", - "id": 1 - }, - "longitude": { - "type": "double", - "id": 2 - } + }, + "LaunchStage": { + "values": { + "LAUNCH_STAGE_UNSPECIFIED": 0, + "UNIMPLEMENTED": 6, + "PRELAUNCH": 7, + "EARLY_ACCESS": 1, + "ALPHA": 2, + "BETA": 3, + "GA": 4, + "DEPRECATED": 5 } } } @@ -2880,4 +3266,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/firestore/src/remote/datastore.ts b/packages/firestore/src/remote/datastore.ts index ac47f0cb931..5fbff4392ac 100644 --- a/packages/firestore/src/remote/datastore.ts +++ b/packages/firestore/src/remote/datastore.ts @@ -20,10 +20,12 @@ import { User } from '../auth/user'; import { Aggregate } from '../core/aggregate'; import { DatabaseId } from '../core/database_info'; import { queryToAggregateTarget, Query, queryToTarget } from '../core/query'; +import { Pipeline } from '../lite-api/pipeline'; import { Document } from '../model/document'; import { DocumentKey } from '../model/document_key'; import { Mutation } from '../model/mutation'; import { ResourcePath } from '../model/path'; +import { PipelineStreamElement } from '../model/pipeline_stream_element'; import { ApiClientObjectMap, BatchGetDocumentsRequest as ProtoBatchGetDocumentsRequest, @@ -32,6 +34,8 @@ import { RunAggregationQueryResponse as ProtoRunAggregationQueryResponse, RunQueryRequest as ProtoRunQueryRequest, RunQueryResponse as ProtoRunQueryResponse, + ExecutePipelineRequest as ProtoExecutePipelineRequest, + ExecutePipelineResponse as ProtoExecutePipelineResponse, Value } from '../protos/firestore_proto_api'; import { debugAssert, debugCast, hardAssert } from '../util/assert'; @@ -54,7 +58,8 @@ import { toName, toQueryTarget, toResourcePath, - toRunAggregationQueryRequest + toRunAggregationQueryRequest, + fromPipelineResponse } from './serializer'; /** @@ -234,6 +239,36 @@ export async function invokeBatchGetDocumentsRpc( return result; } +export async function invokeExecutePipeline( + datastore: Datastore, + pipeline: Pipeline +): Promise { + const datastoreImpl = debugCast(datastore, DatastoreImpl); + const executePipelineRequest = pipeline._toProto(datastoreImpl.serializer); + + const response = await datastoreImpl.invokeStreamingRPC< + ProtoExecutePipelineRequest, + ProtoExecutePipelineResponse + >( + 'ExecutePipeline', + datastoreImpl.serializer.databaseId, + ResourcePath.emptyPath(), + executePipelineRequest + ); + + return response + .filter(proto => !!proto.results) + .flatMap(proto => { + if (proto.results!.length === 0) { + return fromPipelineResponse(datastoreImpl.serializer, proto); + } else { + return proto.results!.map(result => + fromPipelineResponse(datastoreImpl.serializer, proto, result) + ); + } + }); +} + export async function invokeRunQueryRpc( datastore: Datastore, query: Query diff --git a/packages/firestore/src/remote/internal_serializer.ts b/packages/firestore/src/remote/internal_serializer.ts index 8f278247581..29a68620efc 100644 --- a/packages/firestore/src/remote/internal_serializer.ts +++ b/packages/firestore/src/remote/internal_serializer.ts @@ -19,6 +19,8 @@ import { ensureFirestoreConfigured, Firestore } from '../api/database'; import { AggregateImpl } from '../core/aggregate'; import { queryToAggregateTarget, queryToTarget } from '../core/query'; import { AggregateSpec } from '../lite-api/aggregate_types'; +import { getDatastore } from '../lite-api/components'; +import { Pipeline } from '../lite-api/pipeline'; import { Query } from '../lite-api/reference'; import { cast } from '../util/input_validation'; import { mapToArray } from '../util/obj'; @@ -87,3 +89,28 @@ export function _internalAggregationQueryToProtoRunAggregationQueryRequest< /* skipAliasing= */ true ).request; } + +/** + * @internal + * @private + * + * This function is for internal use only. + * + * Returns the `ExecutePipelineRequest` representation of the given query. + * Returns `null` if the Firestore client associated with the given query has + * not been initialized or has been terminated. + * + * @param pipeline - The Pipeline to convert to proto representation. + */ +export function _internalPipelineToExecutePipelineRequestProto( + pipeline: Pipeline + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + const firestore = cast(pipeline._db, Firestore); + const datastore = getDatastore(firestore); + const serializer = datastore.serializer; + if (serializer === undefined) { + return null; + } + return pipeline._toProto(serializer); +} diff --git a/packages/firestore/src/remote/rest_connection.ts b/packages/firestore/src/remote/rest_connection.ts index 470cb332ce2..d1fa98f7e63 100644 --- a/packages/firestore/src/remote/rest_connection.ts +++ b/packages/firestore/src/remote/rest_connection.ts @@ -44,6 +44,7 @@ RPC_NAME_URL_MAPPING['BatchGetDocuments'] = 'batchGet'; RPC_NAME_URL_MAPPING['Commit'] = 'commit'; RPC_NAME_URL_MAPPING['RunQuery'] = 'runQuery'; RPC_NAME_URL_MAPPING['RunAggregationQuery'] = 'runAggregationQuery'; +RPC_NAME_URL_MAPPING['ExecutePipeline'] = 'executePipeline'; const RPC_URL_VERSION = 'v1'; diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts index 811c2ac4df6..4759571b4a5 100644 --- a/packages/firestore/src/remote/serializer.ts +++ b/packages/firestore/src/remote/serializer.ts @@ -37,7 +37,10 @@ import { import { SnapshotVersion } from '../core/snapshot_version'; import { targetIsDocumentTarget, Target } from '../core/target'; import { TargetId } from '../core/types'; +import { Bytes } from '../lite-api/bytes'; +import { GeoPoint } from '../lite-api/geo_point'; import { Timestamp } from '../lite-api/timestamp'; +import { UserDataReader } from '../lite-api/user_data_reader'; import { TargetData, TargetPurpose } from '../local/target_data'; import { MutableDocument } from '../model/document'; import { DocumentKey } from '../model/document_key'; @@ -55,6 +58,7 @@ import { import { normalizeTimestamp } from '../model/normalize'; import { ObjectValue } from '../model/object_value'; import { FieldPath, ResourcePath } from '../model/path'; +import { PipelineStreamElement } from '../model/pipeline_stream_element'; import { ArrayRemoveTransformOperation, ArrayUnionTransformOperation, @@ -87,7 +91,10 @@ import { TargetChangeTargetChangeType as ProtoTargetChangeTargetChangeType, Timestamp as ProtoTimestamp, Write as ProtoWrite, - WriteResult as ProtoWriteResult + WriteResult as ProtoWriteResult, + Value as ProtoValue, + MapValue as ProtoMapValue, + ExecutePipelineResponse as ProtoExecutePipelineResponse } from '../protos/firestore_proto_api'; import { debugAssert, fail, hardAssert } from '../util/assert'; import { ByteString } from '../util/byte_string'; @@ -173,7 +180,7 @@ function fromRpcStatus(status: ProtoStatus): FirestoreError { * our generated proto interfaces say Int32Value must be. But GRPC actually * expects a { value: } struct. */ -function toInt32Proto( +export function toInt32Proto( serializer: JsonProtoSerializer, val: number | null ): number | { value: number } | null { @@ -226,7 +233,7 @@ export function toTimestamp( } } -function fromTimestamp(date: ProtoTimestamp): Timestamp { +export function fromTimestamp(date: ProtoTimestamp): Timestamp { const timestamp = normalizeTimestamp(date); return new Timestamp(timestamp.seconds, timestamp.nanos); } @@ -422,6 +429,37 @@ export function toDocument( }; } +export function fromPipelineResponse( + serializer: JsonProtoSerializer, + proto: ProtoExecutePipelineResponse, + document?: ProtoDocument +): PipelineStreamElement { + const output: PipelineStreamElement = {}; + if (proto.transaction?.length) { + output.transaction = proto.transaction; + } + const executionTime = proto.executionTime + ? fromVersion(proto.executionTime) + : undefined; + output.executionTime = executionTime; + + if (!!document) { + output.key = document.name + ? fromName(serializer, document.name) + : undefined; + + output.fields = new ObjectValue({ mapValue: { fields: document.fields } }); + + output.createTime = document.createTime + ? fromVersion(document.createTime!) + : undefined; + output.updateTime = document.updateTime + ? fromVersion(document.updateTime!) + : undefined; + } + return output; +} + export function fromDocument( serializer: JsonProtoSerializer, document: ProtoDocument, @@ -1390,3 +1428,82 @@ export function isValidResourceName(path: ResourcePath): boolean { path.get(2) === 'databases' ); } + +export interface ProtoSerializable { + _toProto(serializer: JsonProtoSerializer): ProtoType; +} + +export interface UserData { + _readUserData(dataReader: UserDataReader): void; +} + +export function toMapValue( + serializer: JsonProtoSerializer, + input: Map> +): ProtoValue { + const map: ProtoMapValue = { fields: {} }; + input.forEach((exp: ProtoSerializable, key: string) => { + if (typeof key !== 'string') { + throw new Error(`Cannot encode map with non-string key: ${key}`); + } + + map.fields![key] = exp._toProto(serializer)!; + }); + return { + mapValue: map + }; +} + +export function toNullValue(value: null): ProtoValue { + return { nullValue: 'NULL_VALUE' }; +} + +export function toBooleanValue(value: boolean): ProtoValue { + return { booleanValue: value }; +} + +export function toStringValue(value: string): ProtoValue { + return { stringValue: value }; +} + +export function dateToTimestampValue( + serializer: JsonProtoSerializer, + value: Date +): ProtoValue { + const timestamp = Timestamp.fromDate(value); + return { + timestampValue: toTimestamp(serializer, timestamp) + }; +} + +export function timestampToTimestampValue( + serializer: JsonProtoSerializer, + value: Timestamp +): ProtoValue { + // Firestore backend truncates precision down to microseconds. To ensure + // offline mode works the same in regards to truncation, perform the + // truncation immediately without waiting for the backend to do that. + const timestamp = new Timestamp( + value.seconds, + Math.floor(value.nanoseconds / 1000) * 1000 + ); + return { + timestampValue: toTimestamp(serializer, timestamp) + }; +} + +export function toGeoPointValue(value: GeoPoint): ProtoValue { + return { + geoPointValue: { + latitude: value.latitude, + longitude: value.longitude + } + }; +} + +export function toBytesValue( + serializer: JsonProtoSerializer, + value: Bytes +): ProtoValue { + return { bytesValue: toBytes(serializer, value._byteString) }; +} diff --git a/packages/firestore/src/util/misc.ts b/packages/firestore/src/util/misc.ts index acaff77abb6..baed7e98a7d 100644 --- a/packages/firestore/src/util/misc.ts +++ b/packages/firestore/src/util/misc.ts @@ -89,6 +89,26 @@ export function arrayEquals( } return left.every((value, index) => comparator(value, right[index])); } + +/** + * Verifies equality for an optional value. + */ +export function isOptionalEqual( + left: T | undefined, + right: T | undefined, + equalityTest: (left: T, right: T) => boolean +): boolean { + if (left === undefined && right === undefined) { + return true; + } + + if (left === undefined || right === undefined) { + return false; + } + + return equalityTest(left, right); +} + /** * Returns the immediate lexicographically-following string. This is useful to * construct an inclusive range for indexeddb iterators. diff --git a/packages/firestore/src/util/obj.ts b/packages/firestore/src/util/obj.ts index c40bc86bc5c..2b61da9447f 100644 --- a/packages/firestore/src/util/obj.ts +++ b/packages/firestore/src/util/obj.ts @@ -32,7 +32,7 @@ export function objectSize(obj: object): number { } export function forEach( - obj: Dict | undefined, + obj: Record | undefined, fn: (key: string, val: V) => void ): void { for (const key in obj) { diff --git a/packages/firestore/test/integration/api/pipeline.test.ts b/packages/firestore/test/integration/api/pipeline.test.ts new file mode 100644 index 00000000000..a954d9b53e1 --- /dev/null +++ b/packages/firestore/test/integration/api/pipeline.test.ts @@ -0,0 +1,892 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, use } from 'chai'; +import chaiAsPromised from 'chai-as-promised'; + +import { addEqualityMatcher } from '../../util/equality_matcher'; +import { Deferred } from '../../util/promise'; +import { + pipeline, + execute, + _internalPipelineToExecutePipelineRequestProto, + add, + andFunction, + arrayContains, + arrayContainsAny, + avgFunction, + CollectionReference, + Constant, + cosineDistance, + countAll, + doc, + DocumentData, + dotProduct, + endsWith, + eq, + euclideanDistance, + Field, + Firestore, + gt, + like, + lt, + lte, + mapGet, + neq, + not, + orFunction, + PipelineResult, + regexContains, + regexMatch, + setDoc, + startsWith, + subtract, + useFirestorePipelines +} from '../util/firebase_export'; +import { apiDescribe, withTestCollection } from '../util/helpers'; + +use(chaiAsPromised); +useFirestorePipelines(); + +apiDescribe.skip('Pipelines', persistence => { + addEqualityMatcher(); + let firestore: Firestore; + let randomCol: CollectionReference; + + // async function addDocs( + // ...docs: DocumentData[] + // ): Promise { + // let id = 0; // Guarantees consistent ordering for the first documents + // const refs: DocumentReference[] = []; + // for (const data of docs) { + // const ref = doc(randomCol, 'doc' + id++); + // await setDoc(ref, data); + // refs.push(ref); + // } + // return refs; + // } + + async function testCollectionWithDocs(docs: { + [id: string]: DocumentData; + }): Promise> { + for (const id in docs) { + if (docs.hasOwnProperty(id)) { + const ref = doc(randomCol, id); + await setDoc(ref, docs[id]); + } + } + return randomCol; + } + + function expectResults( + result: Array>, + ...docs: string[] + ): void; + function expectResults( + result: Array>, + ...data: DocumentData[] + ): void; + + function expectResults( + result: Array>, + ...data: DocumentData[] | string[] + ): void { + expect(result.length).to.equal(data.length); + + if (data.length > 0) { + if (typeof data[0] === 'string') { + const actualIds = result.map(result => result.ref?.id); + expect(actualIds).to.deep.equal(data); + } else { + result.forEach(r => { + expect(r.data()).to.deep.equal(data.shift()); + }); + } + } + } + + // async function compareQueryAndPipeline(query: Query): Promise { + // const queryResults = await getDocs(query); + // const pipeline = query.pipeline(); + // const pipelineResults = await pipeline.execute(); + // + // expect(queryResults.docs.map(s => s._fieldsProto)).to.deep.equal( + // pipelineResults.map(r => r._fieldsProto) + // ); + // return queryResults; + // } + + async function setupBookDocs(): Promise> { + const bookDocs: { [id: string]: DocumentData } = { + book1: { + title: "The Hitchhiker's Guide to the Galaxy", + author: 'Douglas Adams', + genre: 'Science Fiction', + published: 1979, + rating: 4.2, + tags: ['comedy', 'space', 'adventure'], + awards: { + hugo: true, + nebula: false, + others: { unknown: { year: 1980 } } + }, + nestedField: { 'level.1': { 'level.2': true } } + }, + book2: { + title: 'Pride and Prejudice', + author: 'Jane Austen', + genre: 'Romance', + published: 1813, + rating: 4.5, + tags: ['classic', 'social commentary', 'love'], + awards: { none: true } + }, + book3: { + title: 'One Hundred Years of Solitude', + author: 'Gabriel García Márquez', + genre: 'Magical Realism', + published: 1967, + rating: 4.3, + tags: ['family', 'history', 'fantasy'], + awards: { nobel: true, nebula: false } + }, + book4: { + title: 'The Lord of the Rings', + author: 'J.R.R. Tolkien', + genre: 'Fantasy', + published: 1954, + rating: 4.7, + tags: ['adventure', 'magic', 'epic'], + awards: { hugo: false, nebula: false } + }, + book5: { + title: "The Handmaid's Tale", + author: 'Margaret Atwood', + genre: 'Dystopian', + published: 1985, + rating: 4.1, + tags: ['feminism', 'totalitarianism', 'resistance'], + awards: { 'arthur c. clarke': true, 'booker prize': false } + }, + book6: { + title: 'Crime and Punishment', + author: 'Fyodor Dostoevsky', + genre: 'Psychological Thriller', + published: 1866, + rating: 4.3, + tags: ['philosophy', 'crime', 'redemption'], + awards: { none: true } + }, + book7: { + title: 'To Kill a Mockingbird', + author: 'Harper Lee', + genre: 'Southern Gothic', + published: 1960, + rating: 4.2, + tags: ['racism', 'injustice', 'coming-of-age'], + awards: { pulitzer: true } + }, + book8: { + title: '1984', + author: 'George Orwell', + genre: 'Dystopian', + published: 1949, + rating: 4.2, + tags: ['surveillance', 'totalitarianism', 'propaganda'], + awards: { prometheus: true } + }, + book9: { + title: 'The Great Gatsby', + author: 'F. Scott Fitzgerald', + genre: 'Modernist', + published: 1925, + rating: 4.0, + tags: ['wealth', 'american dream', 'love'], + awards: { none: true } + }, + book10: { + title: 'Dune', + author: 'Frank Herbert', + genre: 'Science Fiction', + published: 1965, + rating: 4.6, + tags: ['politics', 'desert', 'ecology'], + awards: { hugo: true, nebula: true } + } + }; + return testCollectionWithDocs(bookDocs); + } + + let testDeferred: Deferred | undefined; + let withTestCollectionPromise: Promise | undefined; + + beforeEach(async () => { + const setupDeferred = new Deferred(); + testDeferred = new Deferred(); + withTestCollectionPromise = withTestCollection( + persistence, + {}, + async (collectionRef, firestoreInstance) => { + randomCol = collectionRef; + firestore = firestoreInstance; + await setupBookDocs(); + setupDeferred.resolve(); + + return testDeferred?.promise; + } + ); + + await setupDeferred.promise; + }); + + afterEach(async () => { + testDeferred?.resolve(); + await withTestCollectionPromise; + }); + + // setLogLevel('debug') + + it('empty results as expected', async () => { + const result = await firestore + .pipeline() + .collection(randomCol.path) + .limit(0) + .execute(); + expect(result.length).to.equal(0); + }); + + it('full results as expected', async () => { + const result = await firestore + .pipeline() + .collection(randomCol.path) + .execute(); + expect(result.length).to.equal(10); + }); + + it('returns aggregate results as expected', async () => { + let result = await firestore + .pipeline() + .collection(randomCol.path) + .aggregate(countAll().as('count')) + .execute(); + expectResults(result, { count: 10 }); + + result = await randomCol + .pipeline() + .where(eq('genre', 'Science Fiction')) + .aggregate( + countAll().as('count'), + avgFunction('rating').as('avgRating'), + Field.of('rating').max().as('maxRating') + ) + .execute(); + expectResults(result, { count: 2, avgRating: 4.4, maxRating: 4.6 }); + }); + + it('rejects groups without accumulators', async () => { + await expect( + randomCol + .pipeline() + .where(lt('published', 1900)) + .aggregate({ + accumulators: [], + groups: ['genre'] + }) + .execute() + ).to.be.rejected; + }); + + // skip: toLower not supported + // it.skip('returns distinct values as expected', async () => { + // const results = await randomCol + // .pipeline() + // .where(lt('published', 1900)) + // .distinct(Field.of('genre').toLower().as('lowerGenre')) + // .execute(); + // expectResults( + // results, + // { lowerGenre: 'romance' }, + // { lowerGenre: 'psychological thriller' } + // ); + // }); + + it('returns group and accumulate results', async () => { + const results = await randomCol + .pipeline() + .where(lt(Field.of('published'), 1984)) + .aggregate({ + accumulators: [avgFunction('rating').as('avgRating')], + groups: ['genre'] + }) + .where(gt('avgRating', 4.3)) + .sort(Field.of('avgRating').descending()) + .execute(); + expectResults( + results, + { avgRating: 4.7, genre: 'Fantasy' }, + { avgRating: 4.5, genre: 'Romance' }, + { avgRating: 4.4, genre: 'Science Fiction' } + ); + }); + + it('returns min and max accumulations', async () => { + const results = await randomCol + .pipeline() + .aggregate( + countAll().as('count'), + Field.of('rating').max().as('maxRating'), + Field.of('published').min().as('minPublished') + ) + .execute(); + expectResults(results, { + count: 10, + maxRating: 4.7, + minPublished: 1813 + }); + }); + + it('can select fields', async () => { + const results = await firestore + .pipeline() + .collection(randomCol.path) + .select('title', 'author') + .sort(Field.of('author').ascending()) + .execute(); + expectResults( + results, + { + title: "The Hitchhiker's Guide to the Galaxy", + author: 'Douglas Adams' + }, + { title: 'The Great Gatsby', author: 'F. Scott Fitzgerald' }, + { title: 'Dune', author: 'Frank Herbert' }, + { title: 'Crime and Punishment', author: 'Fyodor Dostoevsky' }, + { + title: 'One Hundred Years of Solitude', + author: 'Gabriel García Márquez' + }, + { title: '1984', author: 'George Orwell' }, + { title: 'To Kill a Mockingbird', author: 'Harper Lee' }, + { title: 'The Lord of the Rings', author: 'J.R.R. Tolkien' }, + { title: 'Pride and Prejudice', author: 'Jane Austen' }, + { title: "The Handmaid's Tale", author: 'Margaret Atwood' } + ); + }); + + it('where with and', async () => { + const results = await randomCol + .pipeline() + .where(andFunction(gt('rating', 4.5), eq('genre', 'Science Fiction'))) + .execute(); + expectResults(results, 'book10'); + }); + + it('where with or', async () => { + const results = await randomCol + .pipeline() + .where(orFunction(eq('genre', 'Romance'), eq('genre', 'Dystopian'))) + .select('title') + .execute(); + expectResults( + results, + { title: 'Pride and Prejudice' }, + { title: "The Handmaid's Tale" }, + { title: '1984' } + ); + }); + + it('offset and limits', async () => { + const results = await firestore + .pipeline() + .collection(randomCol.path) + .sort(Field.of('author').ascending()) + .offset(5) + .limit(3) + .select('title', 'author') + .execute(); + expectResults( + results, + { title: '1984', author: 'George Orwell' }, + { title: 'To Kill a Mockingbird', author: 'Harper Lee' }, + { title: 'The Lord of the Rings', author: 'J.R.R. Tolkien' } + ); + }); + + it('arrayContains works', async () => { + const results = await randomCol + .pipeline() + .where(arrayContains('tags', 'comedy')) + .select('title') + .execute(); + expectResults(results, { title: "The Hitchhiker's Guide to the Galaxy" }); + }); + + it('arrayContainsAny works', async () => { + const results = await randomCol + .pipeline() + .where(arrayContainsAny('tags', ['comedy', 'classic'])) + .select('title') + .execute(); + expectResults( + results, + { title: "The Hitchhiker's Guide to the Galaxy" }, + { title: 'Pride and Prejudice' } + ); + }); + + it('arrayContainsAll works', async () => { + const results = await randomCol + .pipeline() + .where(Field.of('tags').arrayContainsAll('adventure', 'magic')) + .select('title') + .execute(); + expectResults(results, { title: 'The Lord of the Rings' }); + }); + + it('arrayLength works', async () => { + const results = await randomCol + .pipeline() + .select(Field.of('tags').arrayLength().as('tagsCount')) + .where(eq('tagsCount', 3)) + .execute(); + expect(results.length).to.equal(10); + }); + + // skip: arrayConcat not supported + // it.skip('arrayConcat works', async () => { + // const results = await randomCol + // .pipeline() + // .select( + // Field.of('tags').arrayConcat(['newTag1', 'newTag2']).as('modifiedTags') + // ) + // .limit(1) + // .execute(); + // expectResults(results, { + // modifiedTags: ['comedy', 'space', 'adventure', 'newTag1', 'newTag2'] + // }); + // }); + + it('testStrConcat', async () => { + const results = await randomCol + .pipeline() + .select( + Field.of('author').strConcat(' - ', Field.of('title')).as('bookInfo') + ) + .limit(1) + .execute(); + expectResults(results, { + bookInfo: "Douglas Adams - The Hitchhiker's Guide to the Galaxy" + }); + }); + + it('testStartsWith', async () => { + const results = await randomCol + .pipeline() + .where(startsWith('title', 'The')) + .select('title') + .sort(Field.of('title').ascending()) + .execute(); + expectResults( + results, + { title: 'The Great Gatsby' }, + { title: "The Handmaid's Tale" }, + { title: "The Hitchhiker's Guide to the Galaxy" }, + { title: 'The Lord of the Rings' } + ); + }); + + it('testEndsWith', async () => { + const results = await randomCol + .pipeline() + .where(endsWith('title', 'y')) + .select('title') + .sort(Field.of('title').descending()) + .execute(); + expectResults( + results, + { title: "The Hitchhiker's Guide to the Galaxy" }, + { title: 'The Great Gatsby' } + ); + }); + + it('testLength', async () => { + const results = await randomCol + .pipeline() + .select( + Field.of('title').charLength().as('titleLength'), + Field.of('title') + ) + .where(gt('titleLength', 20)) + .sort(Field.of('title').ascending()) + .execute(); + + expectResults( + results, + + { + titleLength: 29, + title: 'One Hundred Years of Solitude' + }, + { + titleLength: 36, + title: "The Hitchhiker's Guide to the Galaxy" + }, + { + titleLength: 21, + title: 'The Lord of the Rings' + }, + { + titleLength: 21, + title: 'To Kill a Mockingbird' + } + ); + }); + + // skip: toLower not supported + // it.skip('testToLowercase', async () => { + // const results = await randomCol + // .pipeline() + // .select(Field.of('title').toLower().as('lowercaseTitle')) + // .limit(1) + // .execute(); + // expectResults(results, { + // lowercaseTitle: "the hitchhiker's guide to the galaxy" + // }); + // }); + + // skip: toUpper not supported + // it.skip('testToUppercase', async () => { + // const results = await randomCol + // .pipeline() + // .select(Field.of('author').toUpper().as('uppercaseAuthor')) + // .limit(1) + // .execute(); + // expectResults(results, { uppercaseAuthor: 'DOUGLAS ADAMS' }); + // }); + + // skip: trim not supported + // it.skip('testTrim', async () => { + // const results = await randomCol + // .pipeline() + // .addFields(strConcat(' ', Field.of('title'), ' ').as('spacedTitle')) + // .select( + // Field.of('spacedTitle').trim().as('trimmedTitle'), + // Field.of('spacedTitle') + // ) + // .limit(1) + // .execute(); + // expectResults(results, { + // spacedTitle: " The Hitchhiker's Guide to the Galaxy ", + // trimmedTitle: "The Hitchhiker's Guide to the Galaxy" + // }); + // }); + + it('testLike', async () => { + const results = await randomCol + .pipeline() + .where(like('title', '%Guide%')) + .select('title') + .execute(); + expectResults(results, { title: "The Hitchhiker's Guide to the Galaxy" }); + }); + + it('testRegexContains', async () => { + const results = await randomCol + .pipeline() + .where(regexContains('title', '(?i)(the|of)')) + .execute(); + expect(results.length).to.equal(5); + }); + + it('testRegexMatches', async () => { + const results = await randomCol + .pipeline() + .where(regexMatch('title', '.*(?i)(the|of).*')) + .execute(); + expect(results.length).to.equal(5); + }); + + it('testArithmeticOperations', async () => { + const results = await randomCol + .pipeline() + .select( + add(Field.of('rating'), 1).as('ratingPlusOne'), + subtract(Field.of('published'), 1900).as('yearsSince1900'), + Field.of('rating').multiply(10).as('ratingTimesTen'), + Field.of('rating').divide(2).as('ratingDividedByTwo') + ) + .limit(1) + .execute(); + expectResults(results, { + ratingPlusOne: 5.2, + yearsSince1900: 79, + ratingTimesTen: 42, + ratingDividedByTwo: 2.1 + }); + }); + + it('testComparisonOperators', async () => { + const results = await randomCol + .pipeline() + .where( + andFunction( + gt('rating', 4.2), + lte(Field.of('rating'), 4.5), + neq('genre', 'Science Fiction') + ) + ) + .select('rating', 'title') + .sort(Field.of('title').ascending()) + .execute(); + expectResults( + results, + { rating: 4.3, title: 'Crime and Punishment' }, + { + rating: 4.3, + title: 'One Hundred Years of Solitude' + }, + { rating: 4.5, title: 'Pride and Prejudice' } + ); + }); + + it('testLogicalOperators', async () => { + const results = await randomCol + .pipeline() + .where( + orFunction( + andFunction(gt('rating', 4.5), eq('genre', 'Science Fiction')), + lt('published', 1900) + ) + ) + .select('title') + .sort(Field.of('title').ascending()) + .execute(); + expectResults( + results, + { title: 'Crime and Punishment' }, + { title: 'Dune' }, + { title: 'Pride and Prejudice' } + ); + }); + + it('testChecks', async () => { + const results = await randomCol + .pipeline() + .where(not(Field.of('rating').isNaN())) + .select( + Field.of('rating').eq(null).as('ratingIsNull'), + not(Field.of('rating').isNaN()).as('ratingIsNotNaN') + ) + .limit(1) + .execute(); + expectResults(results, { ratingIsNull: false, ratingIsNotNaN: true }); + }); + + it('testMapGet', async () => { + const results = await randomCol + .pipeline() + .select( + Field.of('awards').mapGet('hugo').as('hugoAward'), + Field.of('awards').mapGet('others').as('others'), + Field.of('title') + ) + .where(eq('hugoAward', true)) + .execute(); + expectResults( + results, + { + hugoAward: true, + title: "The Hitchhiker's Guide to the Galaxy", + others: { unknown: { year: 1980 } } + }, + { hugoAward: true, title: 'Dune', others: null } + ); + }); + + // it('testParent', async () => { + // const results = await randomCol + // .pipeline() + // .select( + // parent(randomCol.doc('chile').collection('subCollection').path).as( + // 'parent' + // ) + // ) + // .limit(1) + // .execute(); + // expect(results[0].data().parent.endsWith('/books')).to.be.true; + // }); + // + // it('testCollectionId', async () => { + // const results = await randomCol + // .pipeline() + // .select(collectionId(randomCol.doc('chile')).as('collectionId')) + // .limit(1) + // .execute(); + // expectResults(results, {collectionId: 'books'}); + // }); + + it('testDistanceFunctions', async () => { + const sourceVector = [0.1, 0.1]; + const targetVector = [0.5, 0.8]; + const results = await randomCol + .pipeline() + .select( + cosineDistance(Constant.vector(sourceVector), targetVector).as( + 'cosineDistance' + ), + dotProduct(Constant.vector(sourceVector), targetVector).as( + 'dotProductDistance' + ), + euclideanDistance(Constant.vector(sourceVector), targetVector).as( + 'euclideanDistance' + ) + ) + .limit(1) + .execute(); + + expectResults(results, { + cosineDistance: 0.02560880430538015, + dotProductDistance: 0.13, + euclideanDistance: 0.806225774829855 + }); + }); + + it('testNestedFields', async () => { + const results = await randomCol + .pipeline() + .where(eq('awards.hugo', true)) + .select('title', 'awards.hugo') + .execute(); + expectResults( + results, + { title: "The Hitchhiker's Guide to the Galaxy", 'awards.hugo': true }, + { title: 'Dune', 'awards.hugo': true } + ); + }); + + it('test mapGet with field name including . notation', async () => { + const results = await randomCol + .pipeline() + .where(eq('awards.hugo', true)) + .select( + 'title', + Field.of('nestedField.level.1'), + mapGet('nestedField', 'level.1').mapGet('level.2').as('nested') + ) + .execute(); + expectResults( + results, + { + title: "The Hitchhiker's Guide to the Galaxy", + 'nestedField.level.`1`': null, + nested: true + }, + { title: 'Dune', 'nestedField.level.`1`': null, nested: null } + ); + }); + + it('supports internal serialization to proto', async () => { + const pipeline = firestore + .pipeline() + .collection('books') + .where(eq('awards.hugo', true)) + .select( + 'title', + Field.of('nestedField.level.1'), + mapGet('nestedField', 'level.1').mapGet('level.2').as('nested') + ); + + const proto = _internalPipelineToExecutePipelineRequestProto(pipeline); + expect(proto).not.to.be.null; + }); + + // TODO(pipeline) support converter + // it('pipeline converter works', async () => { + // interface AppModel {myTitle: string; myAuthor: string; myPublished: number} + // const converter: FirestorePipelineConverter = { + // fromFirestore(result: FirebaseFirestore.PipelineResult): AppModel { + // return { + // myTitle: result.data()!.title as string, + // myAuthor: result.data()!.author as string, + // myPublished: result.data()!.published as number, + // }; + // }, + // }; + // + // const results = await firestore + // .pipeline() + // .collection(randomCol.path) + // .sort(Field.of('published').ascending()) + // .limit(2) + // .withConverter(converter) + // .execute(); + // + // const objs = results.map(r => r.data()); + // expect(objs[0]).to.deep.equal({ + // myAuthor: 'Jane Austen', + // myPublished: 1813, + // myTitle: 'Pride and Prejudice', + // }); + // expect(objs[1]).to.deep.equal({ + // myAuthor: 'Fyodor Dostoevsky', + // myPublished: 1866, + // myTitle: 'Crime and Punishment', + // }); + // }); + describe('modular API', () => { + it('works when creating a pipeline from a Firestore instance', async () => { + const myPipeline = pipeline(firestore) + .collection(randomCol.path) + .where(lt(Field.of('published'), 1984)) + .aggregate({ + accumulators: [avgFunction('rating').as('avgRating')], + groups: ['genre'] + }) + .where(gt('avgRating', 4.3)) + .sort(Field.of('avgRating').descending()); + + const results = await execute(myPipeline); + + expectResults( + results, + { avgRating: 4.7, genre: 'Fantasy' }, + { avgRating: 4.5, genre: 'Romance' }, + { avgRating: 4.4, genre: 'Science Fiction' } + ); + }); + + it('works when creating a pipeline from a collection', async () => { + const myPipeline = pipeline(randomCol) + .where(lt(Field.of('published'), 1984)) + .aggregate({ + accumulators: [avgFunction('rating').as('avgRating')], + groups: ['genre'] + }) + .where(gt('avgRating', 4.3)) + .sort(Field.of('avgRating').descending()); + + const results = await execute(myPipeline); + + expectResults( + results, + { avgRating: 4.7, genre: 'Fantasy' }, + { avgRating: 4.5, genre: 'Romance' }, + { avgRating: 4.4, genre: 'Science Fiction' } + ); + }); + }); +}); diff --git a/packages/firestore/test/unit/api/document_change.test.ts b/packages/firestore/test/unit/api/document_change.test.ts index faae8b4d4c8..8ce40f599b8 100644 --- a/packages/firestore/test/unit/api/document_change.test.ts +++ b/packages/firestore/test/unit/api/document_change.test.ts @@ -18,8 +18,8 @@ import { expect } from 'chai'; import { Query } from '../../../src/api/reference'; -import { ExpUserDataWriter } from '../../../src/api/reference_impl'; import { QuerySnapshot } from '../../../src/api/snapshot'; +import { ExpUserDataWriter } from '../../../src/api/user_data_writer'; import { Query as InternalQuery } from '../../../src/core/query'; import { View } from '../../../src/core/view'; import { documentKeySet } from '../../../src/model/collections'; diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index d523c8fab83..451f7ddf7ae 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -28,7 +28,7 @@ import { serverTimestamp, Timestamp } from '../../../src'; -import { ExpUserDataWriter } from '../../../src/api/reference_impl'; +import { ExpUserDataWriter } from '../../../src/api/user_data_writer'; import { DatabaseId } from '../../../src/core/database_info'; import { ArrayContainsAnyFilter, diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index 80dcd6519de..52dea003e60 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -16,7 +16,7 @@ */ import { IndexConfiguration } from '../../../src/api/index_configuration'; -import { ExpUserDataWriter } from '../../../src/api/reference_impl'; +import { ExpUserDataWriter } from '../../../src/api/user_data_writer'; import { ListenOptions, ListenerDataSource as Source diff --git a/packages/firestore/test/util/api_helpers.ts b/packages/firestore/test/util/api_helpers.ts index 762b5258a29..517167be323 100644 --- a/packages/firestore/test/util/api_helpers.ts +++ b/packages/firestore/test/util/api_helpers.ts @@ -32,7 +32,7 @@ import { EmptyAppCheckTokenProvider, EmptyAuthCredentialsProvider } from '../../src/api/credentials'; -import { ExpUserDataWriter } from '../../src/api/reference_impl'; +import { ExpUserDataWriter } from '../../src/api/user_data_writer'; import { DatabaseId } from '../../src/core/database_info'; import { newQueryForPath, Query as InternalQuery } from '../../src/core/query'; import { diff --git a/repo-scripts/size-analysis/bundle-definitions/firestore.json b/repo-scripts/size-analysis/bundle-definitions/firestore.json index f5ddafd167c..6c1adcad52c 100644 --- a/repo-scripts/size-analysis/bundle-definitions/firestore.json +++ b/repo-scripts/size-analysis/bundle-definitions/firestore.json @@ -128,6 +128,71 @@ } ] }, + { + "name": "Pipeline Query with lt filter", + "dependencies": [ + { + "packageName": "firebase", + "versionOrTag": "latest", + "imports": [ + { + "path": "app", + "imports": [ + "initializeApp" + ] + } + ] + }, + { + "packageName": "firebase", + "versionOrTag": "latest", + "imports": [ + { + "path": "firestore", + "imports": [ + "getFirestore", + "lt", + "Field", + "useFirestorePipelines" + ] + } + ] + } + ] + }, + { + "name": "Pipeline Query with lt plus and function", + "dependencies": [ + { + "packageName": "firebase", + "versionOrTag": "latest", + "imports": [ + { + "path": "app", + "imports": [ + "initializeApp" + ] + } + ] + }, + { + "packageName": "firebase", + "versionOrTag": "latest", + "imports": [ + { + "path": "firestore", + "imports": [ + "getFirestore", + "lt", + "Field", + "useFirestorePipelines", + "andFunction" + ] + } + ] + } + ] + }, { "name": "Query Cursors", "dependencies": [ diff --git a/yarn.lock b/yarn.lock index bd59a038213..cb53a9075cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -430,6 +430,11 @@ resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz#f99c36d3593db9540705d0739a1f10b5e20c696e" integrity sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ== +"@babel/helper-string-parser@^7.25.7": + version "7.25.7" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz#d50e8d37b1176207b4fe9acedec386c565a44a54" + integrity sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g== + "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.22.5": version "7.22.20" resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" @@ -450,6 +455,11 @@ resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz#918b1a7fa23056603506370089bd990d8720db62" integrity sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA== +"@babel/helper-validator-identifier@^7.25.7": + version "7.25.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz#77b7f60c40b15c97df735b38a66ba1d7c3e93da5" + integrity sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz" @@ -525,6 +535,13 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz" integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== +"@babel/parser@^7.20.15": + version "7.25.8" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz#f6aaf38e80c36129460c1657c0762db584c9d5e2" + integrity sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ== + dependencies: + "@babel/types" "^7.25.8" + "@babel/parser@^7.24.0", "@babel/parser@^7.24.1": version "7.24.1" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz#1e416d3627393fab1cb5b0f2f1796a100ae9133a" @@ -1364,6 +1381,15 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.25.8": + version "7.25.8" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz#5cf6037258e8a9bcad533f4979025140cb9993e1" + integrity sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg== + dependencies: + "@babel/helper-string-parser" "^7.25.7" + "@babel/helper-validator-identifier" "^7.25.7" + to-fast-properties "^2.0.0" + "@changesets/apply-release-plan@^6.1.4": version "6.1.4" resolved "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-6.1.4.tgz" @@ -1928,6 +1954,13 @@ resolved "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== +"@jsdoc/salty@^0.2.1": + version "0.2.8" + resolved "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.8.tgz#8d29923a9429694a437a50ab75004b576131d597" + integrity sha512-5e+SFVavj1ORKlKaKr2BmTOekmXbelU7dC0cDkQLqag7xfuTPuGMUFx7KWJuv4bYZrTsoL2Z18VVCOKYxzoHcg== + dependencies: + lodash "^4.17.21" + "@kwsites/file-exists@^1.1.1": version "1.1.1" resolved "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz" @@ -3435,6 +3468,11 @@ dependencies: "@types/node" "*" +"@types/linkify-it@^5": + version "5.0.0" + resolved "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" + integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== + "@types/listr@0.14.9": version "0.14.9" resolved "https://registry.npmjs.org/@types/listr/-/listr-0.14.9.tgz#736581cfdfcdb821bace0a3e5b05e91182e00c85" @@ -3448,6 +3486,19 @@ resolved "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/markdown-it@^14.1.1": + version "14.1.2" + resolved "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61" + integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== + dependencies: + "@types/linkify-it" "^5" + "@types/mdurl" "^2" + +"@types/mdurl@^2": + version "2.0.0" + resolved "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" + integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== + "@types/mime@*": version "3.0.1" resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz" @@ -5044,9 +5095,9 @@ blocking-proxy@^1.0.0: dependencies: minimist "^1.2.0" -bluebird@3.7.2: +bluebird@3.7.2, bluebird@^3.7.2: version "3.7.2" - resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: @@ -5529,6 +5580,13 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= +catharsis@^0.9.0: + version "0.9.0" + resolved "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz#40382a168be0e6da308c277d3a2b3eb40c7d2121" + integrity sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A== + dependencies: + lodash "^4.17.15" + chai-as-promised@7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz" @@ -7247,6 +7305,11 @@ ent@~2.2.0: resolved "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + env-paths@^2.2.0: version "2.2.1" resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" @@ -7532,7 +7595,7 @@ escape-string-regexp@^2.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^1.8.1: +escodegen@^1.13.0, escodegen@^1.8.1: version "1.14.3" resolved "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== @@ -7671,7 +7734,7 @@ esniff@^2.0.1: event-emitter "^0.3.5" type "^2.7.2" -espree@^9.6.0, espree@^9.6.1: +espree@^9.0.0, espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -9034,7 +9097,7 @@ glob@7.2.3, glob@~7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1: +glob@^8.0.0, glob@^8.0.1: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== @@ -9255,7 +9318,7 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== -graceful-fs@^4.2.10, graceful-fs@^4.2.9: +graceful-fs@^4.1.9, graceful-fs@^4.2.10, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -11052,11 +11115,39 @@ js-yaml@~3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js2xmlparser@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz#2a1fdf01e90585ef2ae872a01bc169c6a8d5e60a" + integrity sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA== + dependencies: + xmlcreate "^2.0.4" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdoc@^4.0.0: + version "4.0.3" + resolved "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.3.tgz#bfee86c6a82f6823e12b5e8be698fd99ae46c061" + integrity sha512-Nu7Sf35kXJ1MWDZIMAuATRQTg1iIPdzh7tqJ6jjvaU/GfDf+qi5UV8zJR3Mo+/pYFvm8mzay4+6O5EWigaQBQw== + dependencies: + "@babel/parser" "^7.20.15" + "@jsdoc/salty" "^0.2.1" + "@types/markdown-it" "^14.1.1" + bluebird "^3.7.2" + catharsis "^0.9.0" + escape-string-regexp "^2.0.0" + js2xmlparser "^4.0.2" + klaw "^3.0.0" + markdown-it "^14.1.0" + markdown-it-anchor "^8.6.7" + marked "^4.0.10" + mkdirp "^1.0.4" + requizzle "^0.2.3" + strip-json-comments "^3.1.0" + underscore "~1.13.2" + jsesc@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz" @@ -11472,6 +11563,13 @@ klaw-sync@^6.0.0: dependencies: graceful-fs "^4.1.11" +klaw@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146" + integrity sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g== + dependencies: + graceful-fs "^4.1.9" + kleur@^4.1.4: version "4.1.5" resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz" @@ -11630,6 +11728,13 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + listr-silent-renderer@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz" @@ -12174,6 +12279,23 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-it-anchor@^8.6.7: + version "8.6.7" + resolved "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz#ee6926daf3ad1ed5e4e3968b1740eef1c6399634" + integrity sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA== + +markdown-it@^14.1.0: + version "14.1.0" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + marked-terminal@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/marked-terminal/-/marked-terminal-5.1.1.tgz" @@ -12191,6 +12313,11 @@ marked@^0.8.0: resolved "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz" integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw== +marked@^4.0.10: + version "4.3.0" + resolved "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== + marked@^4.0.14: version "4.0.18" resolved "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz" @@ -12215,6 +12342,11 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" @@ -14280,6 +14412,22 @@ proto3-json-serializer@^1.0.0: dependencies: protobufjs "^6.11.3" +protobufjs-cli@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.3.tgz#c58b8566784f0fa1aff11e8d875a31de999637fe" + integrity sha512-MqD10lqF+FMsOayFiNOdOGNlXc4iKDCf0ZQPkPR+gizYh9gqUeGTWulABUCdI+N67w5RfJ6xhgX4J8pa8qmMXQ== + dependencies: + chalk "^4.0.0" + escodegen "^1.13.0" + espree "^9.0.0" + estraverse "^5.1.0" + glob "^8.0.0" + jsdoc "^4.0.0" + minimist "^1.2.0" + semver "^7.1.2" + tmp "^0.2.1" + uglify-js "^3.7.7" + protobufjs@6.11.3: version "6.11.3" resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" @@ -14441,6 +14589,11 @@ pumpify@^1.3.5: inherits "^2.0.3" pump "^2.0.0" +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + punycode@1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" @@ -15074,6 +15227,13 @@ requires-port@^1.0.0: resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= +requizzle@^0.2.3: + version "0.2.4" + resolved "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz#319eb658b28c370f0c20f968fa8ceab98c13d27c" + integrity sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw== + dependencies: + lodash "^4.17.21" + resolve-alpn@^1.0.0: version "1.2.1" resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz" @@ -15569,7 +15729,7 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semve dependencies: lru-cache "^6.0.0" -semver@^7.5.4, semver@^7.6.0: +semver@^7.1.2, semver@^7.5.4, semver@^7.6.0: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -16497,7 +16657,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@3.1.1, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1, strip-json-comments@~3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -17319,11 +17479,21 @@ ua-parser-js@^0.7.30: resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832" integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA== +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + uglify-js@^3.1.4, uglify-js@^3.4.9: version "3.14.2" resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.2.tgz" integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A== +uglify-js@^3.7.7: + version "3.19.3" + resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + uid-number@0.0.6: version "0.0.6" resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz" @@ -17364,6 +17534,11 @@ underscore@>=1.8.3, underscore@^1.9.1: resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== +underscore@~1.13.2: + version "1.13.7" + resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + undertaker-registry@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz" @@ -18310,6 +18485,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xmlcreate@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be" + integrity sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg== + xregexp@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz"