diff --git a/src/AbstractFirestoreRepository.ts b/src/AbstractFirestoreRepository.ts index d5c96763..0d6b033d 100644 --- a/src/AbstractFirestoreRepository.ts +++ b/src/AbstractFirestoreRepository.ts @@ -149,6 +149,21 @@ export abstract class AbstractFirestoreRepository extends Bas return new QueryBuilder(this).whereEqualTo(prop, val); } + /** + * Returns a new QueryBuilder with a filter specifying that the + * value in @param prop must not be equal to @param val. + * + * @param {IWherePropParam} prop field to be filtered on, where + * prop could be keyof T or a lambda where T is the first parameter + * @param {IFirestoreVal} val value to compare in the filter + * @returns {QueryBuilder} A new QueryBuilder with the specified + * query applied. + * @memberof AbstractFirestoreRepository + */ + whereNotEqualTo(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder { + return new QueryBuilder(this).whereNotEqualTo(prop, val); + } + /** * Returns a new QueryBuilder with a filter specifying that the * value in @param prop must be greater than @param val. @@ -254,6 +269,21 @@ export abstract class AbstractFirestoreRepository extends Bas return new QueryBuilder(this).whereIn(prop, val); } + /** + * Returns a new QueryBuilder with a filter specifying that the + * field @param prop matches none of the comparison values in @param val + * + * @param {IWherePropParam} prop field to be filtered on, where + * prop could be keyof T or a lambda where T is the first parameter + * @param {IFirestoreVal[]} val[] array of values to compare in the filter (max 10 items in array) + * @returns {QueryBuilder} A new QueryBuilder with the specified + * query applied. + * @memberof AbstractFirestoreRepository + */ + whereNotIn(prop: IWherePropParam, val: IFirestoreVal[]): IQueryBuilder { + return new QueryBuilder(this).whereNotIn(prop, val); + } + /** * Returns a new QueryBuilder with a maximum number of results * to return. Can only be used once per query. diff --git a/src/BaseFirestoreRepository.spec.ts b/src/BaseFirestoreRepository.spec.ts index 7ba8f50a..1ee5e8c3 100644 --- a/src/BaseFirestoreRepository.spec.ts +++ b/src/BaseFirestoreRepository.spec.ts @@ -399,6 +399,12 @@ describe('BaseFirestoreRepository', () => { expect(list[0].name).toEqual('Porcupine Tree'); }); + it('must filter with whereNotEqualTo', async () => { + const list = await bandRepository.whereNotEqualTo('name', 'Porcupine Tree').find(); + expect(list.length).toEqual(1); + expect(list[0].formationYear).toEqual(1983); + }); + it('must filter with whereGreaterThan', async () => { const list = await bandRepository.whereGreaterThan('formationYear', 1983).find(); expect(list.length).toEqual(1); @@ -437,6 +443,11 @@ describe('BaseFirestoreRepository', () => { expect(list.length).toEqual(3); }); + it('must filter with whereNotIn', async () => { + const list = await bandRepository.whereNotIn('formationYear', [1965]).find(); + expect(list.length).toEqual(2); + }); + it('should throw with whereArrayContainsAny and more than 10 items in val array', async () => { expect(async () => { await bandRepository @@ -451,6 +462,14 @@ describe('BaseFirestoreRepository', () => { }).rejects.toThrow(Error); }); + it('should throw with whereNotIn and more than 10 items in val array', async () => { + expect(async () => { + await bandRepository + .whereNotIn('formationYear', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + .find(); + }).rejects.toThrow(Error); + }); + it('must filter with two or more operators', async () => { const list = await bandRepository .whereLessOrEqualThan('formationYear', 1983) diff --git a/src/QueryBuilder.ts b/src/QueryBuilder.ts index 50f8b9a5..5040afe0 100644 --- a/src/QueryBuilder.ts +++ b/src/QueryBuilder.ts @@ -32,6 +32,15 @@ export default class QueryBuilder implements IQueryBuilder return this; } + whereNotEqualTo(param: IWherePropParam, val: IFirestoreVal) { + this.queries.push({ + prop: this.extractWhereParam(param), + val, + operator: FirestoreOperators.notEqual, + }); + return this; + } + whereGreaterThan(prop: IWherePropParam, val: IFirestoreVal) { this.queries.push({ prop: this.extractWhereParam(prop), @@ -81,7 +90,7 @@ export default class QueryBuilder implements IQueryBuilder if (val.length > 10) { throw new Error(` This query supports up to 10 values. You provided ${val.length}. - For details please visit: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any + For details please visit: https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any `); } this.queries.push({ @@ -96,7 +105,7 @@ export default class QueryBuilder implements IQueryBuilder if (val.length > 10) { throw new Error(` This query supports up to 10 values. You provided ${val.length}. - For details please visit: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any + For details please visit: https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any `); } this.queries.push({ @@ -107,6 +116,21 @@ export default class QueryBuilder implements IQueryBuilder return this; } + whereNotIn(prop: IWherePropParam, val: IFirestoreVal[]) { + if (val.length > 10) { + throw new Error(` + This query supports up to 10 values. You provided ${val.length}. + For details please visit: https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any + `); + } + this.queries.push({ + prop: this.extractWhereParam(prop), + val, + operator: FirestoreOperators.notIn, + }); + return this; + } + limit(limitVal: number) { if (this.limitVal) { throw new Error( diff --git a/src/types.ts b/src/types.ts index 5eb88d27..f4346b72 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,6 +10,7 @@ export type IFirestoreVal = string | number | Date | boolean | DocumentReference export enum FirestoreOperators { equal = '==', + notEqual = '!=', lessThan = '<', greaterThan = '>', lessThanEqual = '<=', @@ -17,6 +18,7 @@ export enum FirestoreOperators { arrayContains = 'array-contains', arrayContainsAny = 'array-contains-any', in = 'in', + notIn = 'not-in', } export interface IFireOrmQueryLine { @@ -36,6 +38,7 @@ export type IWherePropParam = keyof T | ((t: T) => unknown); export interface IQueryable { whereEqualTo(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder; + whereNotEqualTo(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder; whereGreaterThan(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder; whereGreaterOrEqualThan(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder; whereLessThan(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder; @@ -43,6 +46,7 @@ export interface IQueryable { whereArrayContains(prop: IWherePropParam, val: IFirestoreVal): IQueryBuilder; whereArrayContainsAny(prop: IWherePropParam, val: IFirestoreVal[]): IQueryBuilder; whereIn(prop: IWherePropParam, val: IFirestoreVal[]): IQueryBuilder; + whereNotIn(prop: IWherePropParam, val: IFirestoreVal[]): IQueryBuilder; find(): Promise; findOne(): Promise; }