Skip to content

Commit

Permalink
Merge pull request #246 from MarZab/feat/custom-query
Browse files Browse the repository at this point in the history
  • Loading branch information
wovalle authored May 25, 2021
2 parents dbd9a6b + 5919ac1 commit 2a98b9a
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 6 deletions.
17 changes: 16 additions & 1 deletion src/AbstractFirestoreRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
PartialBy,
IEntityConstructor,
ITransactionReferenceStorage,
ICustomQuery,
} from './types';

import { isDocumentReference, isGeoPoint, isObject, isTimestamp } from './TypeGuards';
Expand Down Expand Up @@ -356,6 +357,19 @@ export abstract class AbstractFirestoreRepository<T extends IEntity>
return new QueryBuilder<T>(this).findOne();
}

/**
* Returns a new QueryBuilder with an custom query
* specified by @param func. Can only be used once per query.
*
* @param {ICustomQuery<T>} func function to run in a new query
* @returns {QueryBuilder<T>} A new QueryBuilder with the specified
* custom query applied.
* @memberof AbstractFirestoreRepository
*/
customQuery(func: ICustomQuery<T>): IQueryBuilder<T> {
return new QueryBuilder<T>(this).customQuery(func);
}

/**
* Uses class-validator to validate an entity using decorators set in the collection class
*
Expand Down Expand Up @@ -403,7 +417,8 @@ export abstract class AbstractFirestoreRepository<T extends IEntity>
queries: IFireOrmQueryLine[],
limitVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
single?: boolean,
customQuery?: ICustomQuery<T>
): Promise<T[]>;

/**
Expand Down
8 changes: 7 additions & 1 deletion src/BaseFirestoreRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
IEntity,
PartialBy,
ITransactionRepository,
ICustomQuery,
} from './types';

import { getMetadataStorage } from './MetadataUtils';
Expand Down Expand Up @@ -93,7 +94,8 @@ export class BaseFirestoreRepository<T extends IEntity>
queries: Array<IFireOrmQueryLine>,
limitVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
single?: boolean,
customQuery?: ICustomQuery<T>
): Promise<T[]> {
let query = queries.reduce<Query>((acc, cur) => {
const op = cur.operator as WhereFilterOp;
Expand All @@ -110,6 +112,10 @@ export class BaseFirestoreRepository<T extends IEntity>
query = query.limit(limitVal);
}

if (customQuery) {
query = await customQuery(query, this.firestoreColRef);
}

return query.get().then(this.extractTFromColSnap);
}
}
23 changes: 21 additions & 2 deletions src/QueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
IQueryExecutor,
IEntity,
IWherePropParam,
ICustomQuery,
} from './types';

export class QueryBuilder<T extends IEntity> implements IQueryBuilder<T> {
protected queries: Array<IFireOrmQueryLine> = [];
protected limitVal: number;
protected orderByObj: IOrderByParams;
protected customQueryFunction?: ICustomQuery<T>;

constructor(protected executor: IQueryExecutor<T>) {}

Expand Down Expand Up @@ -172,15 +174,32 @@ export class QueryBuilder<T extends IEntity> implements IQueryBuilder<T> {
}

find() {
return this.executor.execute(this.queries, this.limitVal, this.orderByObj);
return this.executor.execute(
this.queries,
this.limitVal,
this.orderByObj,
false,
this.customQueryFunction
);
}

customQuery(func: ICustomQuery<T>) {
if (this.customQueryFunction) {
throw new Error('Only one custom query can be used per query expression');
}

this.customQueryFunction = func;

return this;
}

async findOne() {
const queryResult = await this.executor.execute(
this.queries,
this.limitVal,
this.orderByObj,
true
true,
this.customQueryFunction
);

return queryResult.length ? queryResult[0] : null;
Expand Down
12 changes: 10 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { OrderByDirection, DocumentReference } from '@google-cloud/firestore';
import { OrderByDirection, DocumentReference, CollectionReference } from '@google-cloud/firestore';
import { Query } from '@google-cloud/firestore';

export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export type PartialWithRequiredBy<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>;
Expand Down Expand Up @@ -49,6 +50,7 @@ export interface IQueryable<T extends IEntity> {
whereNotIn(prop: IWherePropParam<T>, val: IFirestoreVal[]): IQueryBuilder<T>;
find(): Promise<T[]>;
findOne(): Promise<T | null>;
customQuery(func: ICustomQuery<T>): IQueryBuilder<T>;
}

export interface IOrderable<T extends IEntity> {
Expand All @@ -62,12 +64,18 @@ export interface ILimitable<T extends IEntity> {

export type IQueryBuilder<T extends IEntity> = IQueryable<T> & IOrderable<T> & ILimitable<T>;

export type ICustomQuery<T> = (
query: Query,
firestoreColRef: CollectionReference
) => Promise<Query>;

export interface IQueryExecutor<T> {
execute(
queries: IFireOrmQueryLine[],
limitVal?: number,
orderByObj?: IOrderByParams,
single?: boolean
single?: boolean,
customQuery?: ICustomQuery<T>
): Promise<T[]>;
}

Expand Down

0 comments on commit 2a98b9a

Please sign in to comment.