Last active
April 26, 2024 05:29
-
-
Save ishowta/c769aefa037ae43ef87bc4eb905c6d5b to your computer and use it in GitHub Desktop.
Thin wrapper for firebase-admin firestore compatibility with firebase-js-sdk (experimental)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright 2017 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 { | |
AggregateField, | |
AggregateQuerySnapshot, | |
AggregateSpec, | |
} from '@google-cloud/firestore' | |
import { | |
DocumentSnapshot, | |
FieldPath, | |
FieldValue, | |
PartialWithFieldValue, | |
Query, | |
QuerySnapshot, | |
ReadWriteTransactionOptions, | |
SetOptions, | |
Transaction, | |
UpdateData, | |
WriteBatch, | |
Firestore, | |
CollectionReference, | |
DocumentData, | |
DocumentReference, | |
WithFieldValue, | |
getFirestore as _getFirestore, | |
} from 'firebase-admin/firestore' | |
import { isPartialObserver } from '~/api/firebase/alias/observer' | |
export * from 'firebase-admin/firestore' | |
export function getFirestore(): Firestore { | |
return _getFirestore() | |
} | |
export function terminate(firestore: Firestore): Promise<void> { | |
return firestore.terminate() | |
} | |
export function arrayRemove(...elements: unknown[]): FieldValue { | |
return FieldValue.arrayRemove(...elements) | |
} | |
export function arrayUnion(...elements: unknown[]): FieldValue { | |
return FieldValue.arrayUnion(...elements) | |
} | |
export function deleteField(): FieldValue { | |
return FieldValue.delete() | |
} | |
export function documentId(): FieldPath { | |
return FieldPath.documentId() | |
} | |
export function increment(n: number): FieldValue { | |
return FieldValue.increment(n) | |
} | |
export function serverTimestamp(): FieldValue { | |
return FieldValue.serverTimestamp() | |
} | |
export function collection( | |
firestore: Firestore, | |
path: string | |
): CollectionReference<DocumentData> | |
export function collection( | |
reference: CollectionReference<unknown>, | |
path: string | |
): CollectionReference<DocumentData> | |
export function collection( | |
reference: DocumentReference, | |
path: string | |
): CollectionReference<DocumentData> | |
export function collection( | |
parent: Firestore | DocumentReference<unknown> | CollectionReference<unknown>, | |
path: string | |
): CollectionReference<DocumentData> { | |
if (parent instanceof Firestore) { | |
return parent.collection(path) | |
} else if (parent instanceof CollectionReference) { | |
const [pathHead, pathTail] = path.split('/') | |
return parent.doc(pathHead).collection(pathTail) | |
} else { | |
return parent.collection(path) | |
} | |
} | |
export function collectionGroup( | |
firestore: Firestore, | |
collectionId: string | |
): Query<DocumentData> { | |
return firestore.collectionGroup(collectionId) | |
} | |
export function doc( | |
firestore: Firestore, | |
path: string | |
): DocumentReference<DocumentData> | |
export function doc<T>( | |
reference: CollectionReference<T>, | |
path?: string | |
): DocumentReference<T> | |
export function doc( | |
reference: DocumentReference<unknown>, | |
path: string | |
): DocumentReference<DocumentData> | |
export function doc<T>( | |
parent: Firestore | CollectionReference<T> | DocumentReference<unknown>, | |
path?: string | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
): DocumentReference<any> { | |
if (parent instanceof Firestore) { | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
return parent.doc(path!) | |
} else if (parent instanceof CollectionReference) { | |
if (path) { | |
return parent.doc(path) | |
} else { | |
return parent.doc() | |
} | |
} else { | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
const [pathHead, pathTail] = path!.split('/') | |
return parent.collection(pathHead).doc(pathTail) | |
} | |
} | |
export function getDoc<T>( | |
reference: DocumentReference<T> | |
): Promise<DocumentSnapshot<T>> { | |
return reference.get() | |
} | |
export * from './query' | |
export function getDocs<T>(query: Query<T>): Promise<QuerySnapshot<T>> { | |
return query.get() | |
} | |
export function getCountFromServer(query: Query<unknown>): Promise< | |
AggregateQuerySnapshot<{ | |
count: AggregateField<number> | |
}> | |
> { | |
return query.count().get() | |
} | |
export function addDoc<T>( | |
reference: CollectionReference<T>, | |
data: WithFieldValue<T> | |
): Promise<DocumentReference<T>> { | |
return reference.add(data) | |
} | |
export function updateDoc<T>( | |
reference: DocumentReference<T>, | |
data: UpdateData<T> | |
): Promise<void> | |
export function updateDoc( | |
reference: DocumentReference<unknown>, | |
field: string | FieldPath, | |
value: unknown | |
): Promise<void> | |
export async function updateDoc<T>( | |
reference: DocumentReference<unknown>, | |
fieldOrUpdateData: string | FieldPath | UpdateData<T>, | |
value?: unknown | |
): Promise<void> { | |
if ( | |
typeof fieldOrUpdateData === 'string' || | |
fieldOrUpdateData instanceof FieldPath | |
) { | |
await reference.update(fieldOrUpdateData, value) | |
return | |
} else { | |
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | |
await reference.update(fieldOrUpdateData!) | |
return | |
} | |
} | |
export function setDoc<T>( | |
reference: DocumentReference<T>, | |
data: WithFieldValue<T> | |
): Promise<void> | |
export function setDoc<T>( | |
reference: DocumentReference<T>, | |
data: PartialWithFieldValue<T>, | |
options: SetOptions | |
): Promise<void> | |
export async function setDoc<T>( | |
reference: DocumentReference<T>, | |
data: PartialWithFieldValue<T>, | |
options?: SetOptions | |
): Promise<void> { | |
if (options) { | |
await reference.set(data, options) | |
} else { | |
await reference.set(data as WithFieldValue<T>) | |
} | |
return | |
} | |
export async function deleteDoc( | |
reference: DocumentReference<unknown> | |
): Promise<void> { | |
await reference.delete() | |
return | |
} | |
export type FirestoreError = Error | |
/** | |
* A function returned by `onSnapshot()` that removes the listener when invoked. | |
*/ | |
export interface Unsubscribe { | |
/** Removes the listener when invoked. */ | |
(): void | |
} | |
export function onSnapshot<T>( | |
reference: DocumentReference<T>, | |
observer: { | |
next?: (snapshot: DocumentSnapshot<T>) => void | |
error?: (error: FirestoreError) => void | |
complete?: () => void | |
} | |
): Unsubscribe | |
export function onSnapshot<T>( | |
reference: DocumentReference<T>, | |
onNext: (snapshot: DocumentSnapshot<T>) => void, | |
onError?: (error: FirestoreError) => void, | |
onCompletion?: () => void | |
): Unsubscribe | |
export function onSnapshot<T>( | |
query: Query<T>, | |
observer: { | |
next?: (snapshot: QuerySnapshot<T>) => void | |
error?: (error: FirestoreError) => void | |
complete?: () => void | |
} | |
): Unsubscribe | |
export function onSnapshot<T>( | |
query: Query<T>, | |
onNext: (snapshot: QuerySnapshot<T>) => void, | |
onError?: (error: FirestoreError) => void, | |
onCompletion?: () => void | |
): Unsubscribe | |
export function onSnapshot<T>( | |
reference: Query<T> | DocumentReference<T>, | |
...args: unknown[] | |
): Unsubscribe { | |
if (isPartialObserver(args[1])) { | |
const observer = args[1] | |
// eslint-disable-next-line @typescript-eslint/no-empty-function | |
return reference.onSnapshot(observer.next ?? (() => {}), observer.error) | |
} else { | |
if (reference instanceof DocumentReference) { | |
return reference.onSnapshot( | |
args[1] as (snapshot: DocumentSnapshot<T>) => void, | |
args[2] as (error: FirestoreError) => void | |
) | |
} else { | |
return reference.onSnapshot( | |
args[1] as (snapshot: QuerySnapshot<T>) => void, | |
args[2] as (error: FirestoreError) => void | |
) | |
} | |
} | |
} | |
export type TransactionOptions = ReadWriteTransactionOptions | |
export function runTransaction<T>( | |
firestore: Firestore, | |
updateFunction: (transaction: Transaction) => Promise<T>, | |
options?: TransactionOptions | |
): Promise<T> { | |
return firestore.runTransaction(updateFunction, options) | |
} | |
export function writeBatch(firestore: Firestore): WriteBatch { | |
return firestore.batch() | |
} | |
export function queryEqual<T>(left: Query<T>, right: Query<T>): boolean { | |
return left.isEqual(right) | |
} | |
export function refEqual<T>( | |
left: DocumentReference<T>, | |
right: DocumentReference<T> | |
): boolean | |
export function refEqual<T>( | |
left: CollectionReference<T>, | |
right: CollectionReference<T> | |
): boolean | |
export function refEqual<T>( | |
left: DocumentReference<T> | CollectionReference<T>, | |
right: DocumentReference<T> | CollectionReference<T> | |
): boolean { | |
if (left instanceof DocumentReference) { | |
return left.isEqual(right as DocumentReference<T>) | |
} else { | |
return left.isEqual(right as CollectionReference<T>) | |
} | |
} | |
export function snapshotEqual<T>( | |
left: DocumentSnapshot<T>, | |
right: DocumentSnapshot<T> | |
): boolean | |
export function snapshotEqual<T>( | |
left: QuerySnapshot<T>, | |
right: QuerySnapshot<T> | |
): boolean | |
export function snapshotEqual<T>( | |
left: DocumentSnapshot<T> | QuerySnapshot<T>, | |
right: DocumentSnapshot<T> | QuerySnapshot<T> | |
): boolean { | |
if (left instanceof DocumentSnapshot) { | |
return left.isEqual(right as DocumentSnapshot<T>) | |
} else { | |
return left.isEqual(right as QuerySnapshot<T>) | |
} | |
} | |
export function aggregateQuerySnapshotEqual<T extends AggregateSpec>( | |
left: AggregateQuerySnapshot<T>, | |
right: AggregateQuerySnapshot<T> | |
): boolean { | |
return left.isEqual(right) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright 2017 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 { FirestoreError } from '~/api/firebase/alias' | |
export interface JsonObject<T> { | |
[name: string]: T | |
} | |
/** | |
* Observer/Subscribe interfaces. | |
*/ | |
export type NextFn<T> = (value: T) => void | |
export type ErrorFn = (error: FirestoreError) => void | |
export type CompleteFn = () => void | |
// Allow for any of the Observer methods to be undefined. | |
export interface PartialObserver<T> { | |
next?: NextFn<T> | |
error?: ErrorFn | |
complete?: CompleteFn | |
} | |
export function isPartialObserver<T>(obj: unknown): obj is PartialObserver<T> { | |
return implementsAnyMethods(obj, ['next', 'error', 'complete']) | |
} | |
/** | |
* Returns true if obj is an object and contains at least one of the specified | |
* methods. | |
*/ | |
function implementsAnyMethods(obj: unknown, methods: string[]): boolean { | |
if (typeof obj !== 'object' || obj === null) { | |
return false | |
} | |
const object = obj as JsonObject<unknown> | |
for (const method of methods) { | |
if (method in object && typeof object[method] === 'function') { | |
return true | |
} | |
} | |
return false | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @license | |
* Copyright 2020 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 { | |
DocumentSnapshot, | |
FieldPath, | |
Filter, | |
OrderByDirection, | |
Query, | |
WhereFilterOp, | |
} from 'firebase-admin/firestore' | |
export const enum Operator { | |
LESS_THAN = '<', | |
LESS_THAN_OR_EQUAL = '<=', | |
EQUAL = '==', | |
NOT_EQUAL = '!=', | |
GREATER_THAN = '>', | |
GREATER_THAN_OR_EQUAL = '>=', | |
ARRAY_CONTAINS = 'array-contains', | |
IN = 'in', | |
NOT_IN = 'not-in', | |
ARRAY_CONTAINS_ANY = 'array-contains-any', | |
} | |
// The operator of a CompositeFilter | |
export const enum CompositeOperator { | |
OR = 'or', | |
AND = 'and', | |
} | |
/** Describes the different query constraints available in this SDK. */ | |
export type QueryConstraintType = | |
| 'where' | |
| 'orderBy' | |
| 'limit' | |
| 'limitToLast' | |
| 'startAt' | |
| 'startAfter' | |
| 'endAt' | |
| 'endBefore' | |
/** | |
* The direction of sorting in an order by. | |
*/ | |
export const enum Direction { | |
ASCENDING = 'asc', | |
DESCENDING = 'desc', | |
} | |
export const enum LimitType { | |
First = 'F', | |
Last = 'L', | |
} | |
/** | |
* An `AppliableConstraint` is an abstraction of a constraint that can be applied | |
* to a Firestore query. | |
*/ | |
export abstract class AppliableConstraint { | |
/** | |
* Takes the provided {@link Query} and returns a copy of the {@link Query} with this | |
* {@link AppliableConstraint} applied. | |
*/ | |
abstract _apply<T>(query: Query<T>): Query<T> | |
} | |
/** | |
* A `QueryConstraint` is used to narrow the set of documents returned by a | |
* Firestore query. `QueryConstraint`s are created by invoking {@link where}, | |
* {@link orderBy}, {@link (startAt:1)}, {@link (startAfter:1)}, {@link | |
* (endBefore:1)}, {@link (endAt:1)}, {@link limit}, {@link limitToLast} and | |
* can then be passed to {@link (query:1)} to create a new query instance that | |
* also contains this `QueryConstraint`. | |
*/ | |
export abstract class QueryConstraint extends AppliableConstraint { | |
/** The type of this query constraint */ | |
abstract readonly type: QueryConstraintType | |
/** | |
* Takes the provided {@link Query} and returns a copy of the {@link Query} with this | |
* {@link AppliableConstraint} applied. | |
*/ | |
abstract _apply<T>(query: Query<T>): Query<T> | |
} | |
/** | |
* Creates a new immutable instance of {@link Query} that is extended to also | |
* include additional query constraints. | |
* | |
* @param query - The {@link Query} instance to use as a base for the new | |
* constraints. | |
* @param compositeFilter - The {@link QueryCompositeFilterConstraint} to | |
* apply. Create {@link QueryCompositeFilterConstraint} using {@link and} or | |
* {@link or}. | |
* @param queryConstraints - Additional {@link QueryNonFilterConstraint}s to | |
* apply (e.g. {@link orderBy}, {@link limit}). | |
* @throws if any of the provided query constraints cannot be combined with the | |
* existing or new constraints. | |
*/ | |
export function query<T>( | |
query: Query<T>, | |
compositeFilter: QueryCompositeFilterConstraint, | |
...queryConstraints: QueryNonFilterConstraint[] | |
): Query<T> | |
/** | |
* Creates a new immutable instance of {@link Query} that is extended to also | |
* include additional query constraints. | |
* | |
* @param query - The {@link Query} instance to use as a base for the new | |
* constraints. | |
* @param queryConstraints - The list of {@link QueryConstraint}s to apply. | |
* @throws if any of the provided query constraints cannot be combined with the | |
* existing or new constraints. | |
*/ | |
export function query<T>( | |
query: Query<T>, | |
...queryConstraints: QueryConstraint[] | |
): Query<T> | |
export function query<T>( | |
query: Query<T>, | |
queryConstraint: QueryCompositeFilterConstraint | QueryConstraint | undefined, | |
...additionalQueryConstraints: Array< | |
QueryConstraint | QueryNonFilterConstraint | |
> | |
): Query<T> { | |
let queryConstraints: AppliableConstraint[] = [] | |
if (queryConstraint instanceof AppliableConstraint) { | |
queryConstraints.push(queryConstraint) | |
} | |
queryConstraints = queryConstraints.concat(additionalQueryConstraints) | |
for (const constraint of queryConstraints) { | |
query = constraint._apply(query) | |
} | |
return query | |
} | |
/** | |
* A `QueryFieldFilterConstraint` is used to narrow the set of documents returned by | |
* a Firestore query by filtering on one or more document fields. | |
* `QueryFieldFilterConstraint`s are created by invoking {@link where} and can then | |
* be passed to {@link (query:1)} to create a new query instance that also contains | |
* this `QueryFieldFilterConstraint`. | |
*/ | |
export class QueryFieldFilterConstraint extends QueryConstraint { | |
/** The type of this query constraint */ | |
readonly type = 'where' | |
/** | |
* @internal | |
*/ | |
protected constructor( | |
private readonly _field: string | FieldPath, | |
private _op: Operator, | |
private _value: unknown | |
) { | |
super() | |
} | |
static _create( | |
_field: string | FieldPath, | |
_op: Operator, | |
_value: unknown | |
): QueryFieldFilterConstraint { | |
return new QueryFieldFilterConstraint(_field, _op, _value) | |
} | |
_apply<T>(query: Query<T>): Query<T> { | |
return query.where(this._field, this._op, this._value) | |
} | |
_standalone(): Filter { | |
return Filter.where(this._field, this._op, this._value) | |
} | |
} | |
/** | |
* Creates a {@link QueryFieldFilterConstraint} that enforces that documents | |
* must contain the specified field and that the value should satisfy the | |
* relation constraint provided. | |
* | |
* @param fieldPath - The path to compare | |
* @param opStr - The operation string (e.g "<", "<=", "==", "<", | |
* "<=", "!="). | |
* @param value - The value for comparison | |
* @returns The created {@link QueryFieldFilterConstraint}. | |
*/ | |
export function where( | |
fieldPath: string | FieldPath, | |
opStr: WhereFilterOp, | |
value: unknown | |
): QueryFieldFilterConstraint { | |
const op = opStr as Operator | |
return QueryFieldFilterConstraint._create(fieldPath, op, value) | |
} | |
/** | |
* A `QueryCompositeFilterConstraint` is used to narrow the set of documents | |
* returned by a Firestore query by performing the logical OR or AND of multiple | |
* {@link QueryFieldFilterConstraint}s or {@link QueryCompositeFilterConstraint}s. | |
* `QueryCompositeFilterConstraint`s are created by invoking {@link or} or | |
* {@link and} and can then be passed to {@link (query:1)} to create a new query | |
* instance that also contains the `QueryCompositeFilterConstraint`. | |
*/ | |
export class QueryCompositeFilterConstraint extends AppliableConstraint { | |
/** | |
* @internal | |
*/ | |
protected constructor( | |
/** The type of this query constraint */ | |
readonly type: 'or' | 'and', | |
private readonly _queryConstraints: QueryFilterConstraint[] | |
) { | |
super() | |
} | |
static _create( | |
type: 'or' | 'and', | |
_queryConstraints: QueryFilterConstraint[] | |
): QueryCompositeFilterConstraint { | |
return new QueryCompositeFilterConstraint(type, _queryConstraints) | |
} | |
_apply<T>(query: Query<T>): Query<T> { | |
return query.where(this._standalone()) | |
} | |
_standalone(): Filter { | |
if (this.type === 'or') { | |
return Filter.or( | |
...this._queryConstraints.map((queryConstraint) => | |
queryConstraint._standalone() | |
) | |
) | |
} else { | |
return Filter.and( | |
...this._queryConstraints.map((queryConstraint) => | |
queryConstraint._standalone() | |
) | |
) | |
} | |
} | |
_getQueryConstraints(): readonly AppliableConstraint[] { | |
return this._queryConstraints | |
} | |
_getOperator(): CompositeOperator { | |
return this.type === 'and' ? CompositeOperator.AND : CompositeOperator.OR | |
} | |
} | |
/** | |
* `QueryNonFilterConstraint` is a helper union type that represents | |
* QueryConstraints which are used to narrow or order the set of documents, | |
* but that do not explicitly filter on a document field. | |
* `QueryNonFilterConstraint`s are created by invoking {@link orderBy}, | |
* {@link (startAt:1)}, {@link (startAfter:1)}, {@link (endBefore:1)}, {@link (endAt:1)}, | |
* {@link limit} or {@link limitToLast} and can then be passed to {@link (query:1)} | |
* to create a new query instance that also contains the `QueryConstraint`. | |
*/ | |
export type QueryNonFilterConstraint = | |
| QueryOrderByConstraint | |
| QueryLimitConstraint | |
| QueryStartAtConstraint | |
| QueryEndAtConstraint | |
/** | |
* `QueryFilterConstraint` is a helper union type that represents | |
* {@link QueryFieldFilterConstraint} and {@link QueryCompositeFilterConstraint}. | |
*/ | |
export type QueryFilterConstraint = | |
| QueryFieldFilterConstraint | |
| QueryCompositeFilterConstraint | |
/** | |
* Creates a new {@link QueryCompositeFilterConstraint} that is a disjunction of | |
* the given filter constraints. A disjunction filter includes a document if it | |
* satisfies any of the given filters. | |
* | |
* @param queryConstraints - Optional. The list of | |
* {@link QueryFilterConstraint}s to perform a disjunction for. These must be | |
* created with calls to {@link where}, {@link or}, or {@link and}. | |
* @returns The newly created {@link QueryCompositeFilterConstraint}. | |
*/ | |
export function or( | |
...queryConstraints: QueryFilterConstraint[] | |
): QueryCompositeFilterConstraint { | |
return QueryCompositeFilterConstraint._create( | |
CompositeOperator.OR, | |
queryConstraints | |
) | |
} | |
/** | |
* Creates a new {@link QueryCompositeFilterConstraint} that is a conjunction of | |
* the given filter constraints. A conjunction filter includes a document if it | |
* satisfies all of the given filters. | |
* | |
* @param queryConstraints - Optional. The list of | |
* {@link QueryFilterConstraint}s to perform a conjunction for. These must be | |
* created with calls to {@link where}, {@link or}, or {@link and}. | |
* @returns The newly created {@link QueryCompositeFilterConstraint}. | |
*/ | |
export function and( | |
...queryConstraints: QueryFilterConstraint[] | |
): QueryCompositeFilterConstraint { | |
return QueryCompositeFilterConstraint._create( | |
CompositeOperator.AND, | |
queryConstraints | |
) | |
} | |
/** | |
* A `QueryOrderByConstraint` is used to sort the set of documents returned by a | |
* Firestore query. `QueryOrderByConstraint`s are created by invoking | |
* {@link orderBy} and can then be passed to {@link (query:1)} to create a new query | |
* instance that also contains this `QueryOrderByConstraint`. | |
* | |
* Note: Documents that do not contain the orderBy field will not be present in | |
* the query result. | |
*/ | |
export class QueryOrderByConstraint extends QueryConstraint { | |
/** The type of this query constraint */ | |
readonly type = 'orderBy' | |
/** | |
* @internal | |
*/ | |
protected constructor( | |
private readonly _field: string | FieldPath, | |
private _direction: Direction | |
) { | |
super() | |
} | |
static _create( | |
_field: string | FieldPath, | |
_direction: Direction | |
): QueryOrderByConstraint { | |
return new QueryOrderByConstraint(_field, _direction) | |
} | |
_apply<T>(query: Query<T>): Query<T> { | |
return query.orderBy(this._field, this._direction) | |
} | |
} | |
/** | |
* Creates a {@link QueryOrderByConstraint} that sorts the query result by the | |
* specified field, optionally in descending order instead of ascending. | |
* | |
* Note: Documents that do not contain the specified field will not be present | |
* in the query result. | |
* | |
* @param fieldPath - The field to sort by. | |
* @param directionStr - Optional direction to sort by ('asc' or 'desc'). If | |
* not specified, order will be ascending. | |
* @returns The created {@link QueryOrderByConstraint}. | |
*/ | |
export function orderBy( | |
fieldPath: string | FieldPath, | |
directionStr: OrderByDirection = 'asc' | |
): QueryOrderByConstraint { | |
const direction = directionStr as Direction | |
return QueryOrderByConstraint._create(fieldPath, direction) | |
} | |
/** | |
* A `QueryLimitConstraint` is used to limit the number of documents returned by | |
* a Firestore query. | |
* `QueryLimitConstraint`s are created by invoking {@link limit} or | |
* {@link limitToLast} and can then be passed to {@link (query:1)} to create a new | |
* query instance that also contains this `QueryLimitConstraint`. | |
*/ | |
export class QueryLimitConstraint extends QueryConstraint { | |
/** | |
* @internal | |
*/ | |
protected constructor( | |
/** The type of this query constraint */ | |
readonly type: 'limit' | 'limitToLast', | |
private readonly _limit: number, | |
private readonly _limitType: LimitType | |
) { | |
super() | |
} | |
static _create( | |
type: 'limit' | 'limitToLast', | |
_limit: number, | |
_limitType: LimitType | |
): QueryLimitConstraint { | |
return new QueryLimitConstraint(type, _limit, _limitType) | |
} | |
_apply<T>(query: Query<T>): Query<T> { | |
if (this.type === 'limit') { | |
return query.limit(this._limit) | |
} else { | |
return query.limitToLast(this._limit) | |
} | |
} | |
} | |
/** | |
* Creates a {@link QueryLimitConstraint} that only returns the first matching | |
* documents. | |
* | |
* @param limit - The maximum number of items to return. | |
* @returns The created {@link QueryLimitConstraint}. | |
*/ | |
export function limit(limit: number): QueryLimitConstraint { | |
return QueryLimitConstraint._create('limit', limit, LimitType.First) | |
} | |
/** | |
* Creates a {@link QueryLimitConstraint} that only returns the last matching | |
* documents. | |
* | |
* You must specify at least one `orderBy` clause for `limitToLast` queries, | |
* otherwise an exception will be thrown during execution. | |
* | |
* @param limit - The maximum number of items to return. | |
* @returns The created {@link QueryLimitConstraint}. | |
*/ | |
export function limitToLast(limit: number): QueryLimitConstraint { | |
return QueryLimitConstraint._create('limitToLast', limit, LimitType.Last) | |
} | |
/** | |
* A `QueryStartAtConstraint` is used to exclude documents from the start of a | |
* result set returned by a Firestore query. | |
* `QueryStartAtConstraint`s are created by invoking {@link (startAt:1)} or | |
* {@link (startAfter:1)} and can then be passed to {@link (query:1)} to create a | |
* new query instance that also contains this `QueryStartAtConstraint`. | |
*/ | |
export class QueryStartAtConstraint extends QueryConstraint { | |
/** | |
* @internal | |
*/ | |
protected constructor( | |
/** The type of this query constraint */ | |
readonly type: 'startAt' | 'startAfter', | |
private readonly _docOrFields: Array<unknown | DocumentSnapshot<unknown>>, | |
private readonly _inclusive: boolean | |
) { | |
super() | |
} | |
static _create( | |
type: 'startAt' | 'startAfter', | |
_docOrFields: Array<unknown | DocumentSnapshot<unknown>>, | |
_inclusive: boolean | |
): QueryStartAtConstraint { | |
return new QueryStartAtConstraint(type, _docOrFields, _inclusive) | |
} | |
_apply<T>(query: Query<T>): Query<T> { | |
if (this.type === 'startAt') { | |
return query.startAt(this._docOrFields) | |
} else { | |
return query.startAfter(this._docOrFields) | |
} | |
} | |
} | |
/** | |
* Creates a {@link QueryStartAtConstraint} that modifies the result set to | |
* start at the provided document (inclusive). The starting position is relative | |
* to the order of the query. The document must contain all of the fields | |
* provided in the `orderBy` of this query. | |
* | |
* @param snapshot - The snapshot of the document to start at. | |
* @returns A {@link QueryStartAtConstraint} to pass to `query()`. | |
*/ | |
export function startAt( | |
snapshot: DocumentSnapshot<unknown> | |
): QueryStartAtConstraint | |
/** | |
* Creates a {@link QueryStartAtConstraint} that modifies the result set to | |
* start at the provided fields relative to the order of the query. The order of | |
* the field values must match the order of the order by clauses of the query. | |
* | |
* @param fieldValues - The field values to start this query at, in order | |
* of the query's order by. | |
* @returns A {@link QueryStartAtConstraint} to pass to `query()`. | |
*/ | |
export function startAt(...fieldValues: unknown[]): QueryStartAtConstraint | |
export function startAt( | |
...docOrFields: Array<unknown | DocumentSnapshot<unknown>> | |
): QueryStartAtConstraint { | |
return QueryStartAtConstraint._create( | |
'startAt', | |
docOrFields, | |
/*inclusive=*/ true | |
) | |
} | |
/** | |
* Creates a {@link QueryStartAtConstraint} that modifies the result set to | |
* start after the provided document (exclusive). The starting position is | |
* relative to the order of the query. The document must contain all of the | |
* fields provided in the orderBy of the query. | |
* | |
* @param snapshot - The snapshot of the document to start after. | |
* @returns A {@link QueryStartAtConstraint} to pass to `query()` | |
*/ | |
export function startAfter( | |
snapshot: DocumentSnapshot<unknown> | |
): QueryStartAtConstraint | |
/** | |
* Creates a {@link QueryStartAtConstraint} that modifies the result set to | |
* start after the provided fields relative to the order of the query. The order | |
* of the field values must match the order of the order by clauses of the query. | |
* | |
* @param fieldValues - The field values to start this query after, in order | |
* of the query's order by. | |
* @returns A {@link QueryStartAtConstraint} to pass to `query()` | |
*/ | |
export function startAfter(...fieldValues: unknown[]): QueryStartAtConstraint | |
export function startAfter( | |
...docOrFields: Array<unknown | DocumentSnapshot<unknown>> | |
): QueryStartAtConstraint { | |
return QueryStartAtConstraint._create( | |
'startAfter', | |
docOrFields, | |
/*inclusive=*/ false | |
) | |
} | |
/** | |
* A `QueryEndAtConstraint` is used to exclude documents from the end of a | |
* result set returned by a Firestore query. | |
* `QueryEndAtConstraint`s are created by invoking {@link (endAt:1)} or | |
* {@link (endBefore:1)} and can then be passed to {@link (query:1)} to create a new | |
* query instance that also contains this `QueryEndAtConstraint`. | |
*/ | |
export class QueryEndAtConstraint extends QueryConstraint { | |
/** | |
* @internal | |
*/ | |
protected constructor( | |
/** The type of this query constraint */ | |
readonly type: 'endBefore' | 'endAt', | |
private readonly _docOrFields: Array<unknown | DocumentSnapshot<unknown>>, | |
private readonly _inclusive: boolean | |
) { | |
super() | |
} | |
static _create( | |
type: 'endBefore' | 'endAt', | |
_docOrFields: Array<unknown | DocumentSnapshot<unknown>>, | |
_inclusive: boolean | |
): QueryEndAtConstraint { | |
return new QueryEndAtConstraint(type, _docOrFields, _inclusive) | |
} | |
_apply<T>(query: Query<T>): Query<T> { | |
if (this.type === 'endAt') { | |
return query.endAt(this._docOrFields) | |
} else { | |
return query.endBefore(this._docOrFields) | |
} | |
} | |
} | |
/** | |
* Creates a {@link QueryEndAtConstraint} that modifies the result set to end | |
* before the provided document (exclusive). The end position is relative to the | |
* order of the query. The document must contain all of the fields provided in | |
* the orderBy of the query. | |
* | |
* @param snapshot - The snapshot of the document to end before. | |
* @returns A {@link QueryEndAtConstraint} to pass to `query()` | |
*/ | |
export function endBefore( | |
snapshot: DocumentSnapshot<unknown> | |
): QueryEndAtConstraint | |
/** | |
* Creates a {@link QueryEndAtConstraint} that modifies the result set to end | |
* before the provided fields relative to the order of the query. The order of | |
* the field values must match the order of the order by clauses of the query. | |
* | |
* @param fieldValues - The field values to end this query before, in order | |
* of the query's order by. | |
* @returns A {@link QueryEndAtConstraint} to pass to `query()` | |
*/ | |
export function endBefore(...fieldValues: unknown[]): QueryEndAtConstraint | |
export function endBefore( | |
...docOrFields: Array<unknown | DocumentSnapshot<unknown>> | |
): QueryEndAtConstraint { | |
return QueryEndAtConstraint._create( | |
'endBefore', | |
docOrFields, | |
/*inclusive=*/ false | |
) | |
} | |
/** | |
* Creates a {@link QueryEndAtConstraint} that modifies the result set to end at | |
* the provided document (inclusive). The end position is relative to the order | |
* of the query. The document must contain all of the fields provided in the | |
* orderBy of the query. | |
* | |
* @param snapshot - The snapshot of the document to end at. | |
* @returns A {@link QueryEndAtConstraint} to pass to `query()` | |
*/ | |
export function endAt(snapshot: DocumentSnapshot<unknown>): QueryEndAtConstraint | |
/** | |
* Creates a {@link QueryEndAtConstraint} that modifies the result set to end at | |
* the provided fields relative to the order of the query. The order of the field | |
* values must match the order of the order by clauses of the query. | |
* | |
* @param fieldValues - The field values to end this query at, in order | |
* of the query's order by. | |
* @returns A {@link QueryEndAtConstraint} to pass to `query()` | |
*/ | |
export function endAt(...fieldValues: unknown[]): QueryEndAtConstraint | |
export function endAt( | |
...docOrFields: Array<unknown | DocumentSnapshot<unknown>> | |
): QueryEndAtConstraint { | |
return QueryEndAtConstraint._create('endAt', docOrFields, /*inclusive=*/ true) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment