Skip to content

Commit

Permalink
Make != and NOT_IN publicly available (#3772)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Chen committed Sep 15, 2020
1 parent a865ae9 commit f900417
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 152 deletions.
7 changes: 7 additions & 0 deletions .changeset/shiny-forks-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'firebase': minor
'@firebase/firestore': minor
'@firebase/firestore-types': minor
---

[feature] Added `not-in` and `!=` query operators for use with `.where()`. `not-in` finds documents where a specified field’s value is not in a specified array. `!=` finds documents where a specified field's value does not equal the specified value. Neither query operator will match documents where the specified field is not present.
7 changes: 5 additions & 2 deletions packages/firebase/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9073,17 +9073,20 @@ declare namespace firebase.firestore {

/**
* Filter conditions in a `Query.where()` clause are specified using the
* strings '<', '<=', '==', '>=', '>', 'array-contains', 'in', and 'array-contains-any'.
* strings '<', '<=', '==', '!=', '>=', '>', 'array-contains', 'in',
* 'array-contains-any', and 'not-in'.
*/
export type WhereFilterOp =
| '<'
| '<='
| '=='
| '!='
| '>='
| '>'
| 'array-contains'
| 'in'
| 'array-contains-any';
| 'array-contains-any'
| 'not-in';

/**
* A `Query` refers to a Query which you can read or listen to. You can also
Expand Down
4 changes: 3 additions & 1 deletion packages/firestore-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,11 +294,13 @@ export type WhereFilterOp =
| '<'
| '<='
| '=='
| '!='
| '>='
| '>'
| 'array-contains'
| 'in'
| 'array-contains-any';
| 'array-contains-any'
| 'not-in';

export class Query<T = DocumentData> {
protected constructor();
Expand Down
4 changes: 3 additions & 1 deletion packages/firestore/exp-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,13 @@ export type WhereFilterOp =
| '<'
| '<='
| '=='
| '!='
| '>='
| '>'
| 'array-contains'
| 'in'
| 'array-contains-any';
| 'array-contains-any'
| 'not-in';

export class Query<T = DocumentData> {
protected constructor();
Expand Down
4 changes: 3 additions & 1 deletion packages/firestore/lite-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,13 @@ export type WhereFilterOp =
| '<'
| '<='
| '=='
| '!='
| '>='
| '>'
| 'array-contains'
| 'in'
| 'array-contains-any';
| 'array-contains-any'
| 'not-in';

export class Query<T = DocumentData> {
protected constructor();
Expand Down
32 changes: 14 additions & 18 deletions packages/firestore/src/api/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1882,24 +1882,20 @@ export class Query<T = DocumentData> implements PublicQuery<T> {
validateExactNumberOfArgs('Query.where', arguments, 3);
validateDefined('Query.where', 3, value);

// TODO(ne-queries): Add 'not-in' and '!=' to validation.
let op: Operator;
if ((opStr as unknown) === 'not-in' || (opStr as unknown) === '!=') {
op = opStr as Operator;
} else {
// Enumerated from the WhereFilterOp type in index.d.ts.
const whereFilterOpEnums = [
Operator.LESS_THAN,
Operator.LESS_THAN_OR_EQUAL,
Operator.EQUAL,
Operator.GREATER_THAN_OR_EQUAL,
Operator.GREATER_THAN,
Operator.ARRAY_CONTAINS,
Operator.IN,
Operator.ARRAY_CONTAINS_ANY
];
op = validateStringEnum('Query.where', whereFilterOpEnums, 2, opStr);
}
// Enumerated from the WhereFilterOp type in index.d.ts.
const whereFilterOpEnums = [
Operator.LESS_THAN,
Operator.LESS_THAN_OR_EQUAL,
Operator.EQUAL,
Operator.NOT_EQUAL,
Operator.GREATER_THAN_OR_EQUAL,
Operator.GREATER_THAN,
Operator.ARRAY_CONTAINS,
Operator.IN,
Operator.ARRAY_CONTAINS_ANY,
Operator.NOT_IN
];
const op = validateStringEnum('Query.where', whereFilterOpEnums, 2, opStr);

const fieldPath = fieldPathFromArgument('Query.where', field);
const filter = newQueryFilter(
Expand Down
9 changes: 4 additions & 5 deletions packages/firestore/src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,19 +605,17 @@ export class FieldFilter extends Filter {
}
} else if (isNullValue(value)) {
if (op !== Operator.EQUAL && op !== Operator.NOT_EQUAL) {
// TODO(ne-queries): Update error message to include != comparison.
throw new FirestoreError(
Code.INVALID_ARGUMENT,
'Invalid query. Null supports only equality comparisons.'
"Invalid query. Null only supports '==' and '!=' comparisons."
);
}
return new FieldFilter(field, op, value);
} else if (isNanValue(value)) {
if (op !== Operator.EQUAL && op !== Operator.NOT_EQUAL) {
// TODO(ne-queries): Update error message to include != comparison.
throw new FirestoreError(
Code.INVALID_ARGUMENT,
'Invalid query. NaN supports only equality comparisons.'
"Invalid query. NaN only supports '==' and '!=' comparisons."
);
}
return new FieldFilter(field, op, value);
Expand Down Expand Up @@ -711,7 +709,8 @@ export class FieldFilter extends Filter {
Operator.LESS_THAN_OR_EQUAL,
Operator.GREATER_THAN,
Operator.GREATER_THAN_OR_EQUAL,
Operator.NOT_EQUAL
Operator.NOT_EQUAL,
Operator.NOT_IN
].indexOf(this.op) >= 0
);
}
Expand Down
30 changes: 4 additions & 26 deletions packages/firestore/test/integration/api/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import { EventsAccumulator } from '../util/events_accumulator';
import * as firebaseExport from '../util/firebase_export';
import {
apiDescribe,
notEqualOp,
notInOp,
withTestCollection,
withTestDb,
withTestDbs,
Expand Down Expand Up @@ -644,14 +642,6 @@ apiDescribe('Database', (persistence: boolean) => {
});
});

it('inequality and NOT_IN on different fields works', () => {
return withTestCollection(persistence, {}, async coll => {
expect(() =>
coll.where('x', '>=', 32).where('y', notInOp, [1, 2])
).not.to.throw();
});
});

it('inequality and array-contains-any on different fields works', () => {
return withTestCollection(persistence, {}, async coll => {
expect(() =>
Expand All @@ -669,12 +659,8 @@ apiDescribe('Database', (persistence: boolean) => {

it('!= same as orderBy works.', () => {
return withTestCollection(persistence, {}, async coll => {
expect(() =>
coll.where('x', notEqualOp, 32).orderBy('x')
).not.to.throw();
expect(() =>
coll.orderBy('x').where('x', notEqualOp, 32)
).not.to.throw();
expect(() => coll.where('x', '!=', 32).orderBy('x')).not.to.throw();
expect(() => coll.orderBy('x').where('x', '!=', 32)).not.to.throw();
});
});

Expand All @@ -692,10 +678,10 @@ apiDescribe('Database', (persistence: boolean) => {
it('!= same as first orderBy works.', () => {
return withTestCollection(persistence, {}, async coll => {
expect(() =>
coll.where('x', notEqualOp, 32).orderBy('x').orderBy('y')
coll.where('x', '!=', 32).orderBy('x').orderBy('y')
).not.to.throw();
expect(() =>
coll.orderBy('x').where('x', notEqualOp, 32).orderBy('y')
coll.orderBy('x').where('x', '!=', 32).orderBy('y')
).not.to.throw();
});
});
Expand All @@ -720,14 +706,6 @@ apiDescribe('Database', (persistence: boolean) => {
});
});

it('NOT_IN different than orderBy works', () => {
return withTestCollection(persistence, {}, async coll => {
expect(() =>
coll.orderBy('x').where('y', notInOp, [1, 2])
).not.to.throw();
});
});

it('array-contains-any different than orderBy works', () => {
return withTestCollection(persistence, {}, async coll => {
expect(() =>
Expand Down

0 comments on commit f900417

Please sign in to comment.