diff --git a/.changeset/shiny-forks-pull.md b/.changeset/shiny-forks-pull.md deleted file mode 100644 index 76d7cdbcafc..00000000000 --- a/.changeset/shiny-forks-pull.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'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. diff --git a/packages/firebase/index.d.ts b/packages/firebase/index.d.ts index 7732e440c83..f907504dbbd 100644 --- a/packages/firebase/index.d.ts +++ b/packages/firebase/index.d.ts @@ -9073,20 +9073,17 @@ declare namespace firebase.firestore { /** * Filter conditions in a `Query.where()` clause are specified using the - * strings '<', '<=', '==', '!=', '>=', '>', 'array-contains', 'in', - * 'array-contains-any', and 'not-in'. + * strings '<', '<=', '==', '>=', '>', 'array-contains', 'in', and 'array-contains-any'. */ export type WhereFilterOp = | '<' | '<=' | '==' - | '!=' | '>=' | '>' | 'array-contains' | 'in' - | 'array-contains-any' - | 'not-in'; + | 'array-contains-any'; /** * A `Query` refers to a Query which you can read or listen to. You can also diff --git a/packages/firestore-types/index.d.ts b/packages/firestore-types/index.d.ts index 75ae5d4ef62..3fac06d74f0 100644 --- a/packages/firestore-types/index.d.ts +++ b/packages/firestore-types/index.d.ts @@ -294,13 +294,11 @@ export type WhereFilterOp = | '<' | '<=' | '==' - | '!=' | '>=' | '>' | 'array-contains' | 'in' - | 'array-contains-any' - | 'not-in'; + | 'array-contains-any'; export class Query { protected constructor(); diff --git a/packages/firestore/exp-types/index.d.ts b/packages/firestore/exp-types/index.d.ts index 13dffca924b..f14d6803b94 100644 --- a/packages/firestore/exp-types/index.d.ts +++ b/packages/firestore/exp-types/index.d.ts @@ -277,13 +277,11 @@ export type WhereFilterOp = | '<' | '<=' | '==' - | '!=' | '>=' | '>' | 'array-contains' | 'in' - | 'array-contains-any' - | 'not-in'; + | 'array-contains-any'; export class Query { protected constructor(); diff --git a/packages/firestore/lite-types/index.d.ts b/packages/firestore/lite-types/index.d.ts index be4e6f84846..1a7bb12e411 100644 --- a/packages/firestore/lite-types/index.d.ts +++ b/packages/firestore/lite-types/index.d.ts @@ -227,13 +227,11 @@ export type WhereFilterOp = | '<' | '<=' | '==' - | '!=' | '>=' | '>' | 'array-contains' | 'in' - | 'array-contains-any' - | 'not-in'; + | 'array-contains-any'; export class Query { protected constructor(); diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index abfabbcc643..1224ca821f2 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -1882,20 +1882,24 @@ export class Query implements PublicQuery { validateExactNumberOfArgs('Query.where', arguments, 3); validateDefined('Query.where', 3, value); - // 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); + // 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); + } const fieldPath = fieldPathFromArgument('Query.where', field); const filter = newQueryFilter( diff --git a/packages/firestore/src/core/query.ts b/packages/firestore/src/core/query.ts index 8081fc74beb..869b2a4e2f6 100644 --- a/packages/firestore/src/core/query.ts +++ b/packages/firestore/src/core/query.ts @@ -605,17 +605,19 @@ 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 only supports '==' and '!=' comparisons." + 'Invalid query. Null supports only equality 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 only supports '==' and '!=' comparisons." + 'Invalid query. NaN supports only equality comparisons.' ); } return new FieldFilter(field, op, value); @@ -709,8 +711,7 @@ export class FieldFilter extends Filter { Operator.LESS_THAN_OR_EQUAL, Operator.GREATER_THAN, Operator.GREATER_THAN_OR_EQUAL, - Operator.NOT_EQUAL, - Operator.NOT_IN + Operator.NOT_EQUAL ].indexOf(this.op) >= 0 ); } diff --git a/packages/firestore/test/integration/api/database.test.ts b/packages/firestore/test/integration/api/database.test.ts index abf591b6b47..aa549b6fe64 100644 --- a/packages/firestore/test/integration/api/database.test.ts +++ b/packages/firestore/test/integration/api/database.test.ts @@ -25,6 +25,8 @@ import { EventsAccumulator } from '../util/events_accumulator'; import * as firebaseExport from '../util/firebase_export'; import { apiDescribe, + notEqualOp, + notInOp, withTestCollection, withTestDb, withTestDbs, @@ -642,6 +644,14 @@ 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(() => @@ -659,8 +669,12 @@ apiDescribe('Database', (persistence: boolean) => { it('!= same as orderBy works.', () => { return withTestCollection(persistence, {}, async coll => { - expect(() => coll.where('x', '!=', 32).orderBy('x')).not.to.throw(); - expect(() => coll.orderBy('x').where('x', '!=', 32)).not.to.throw(); + expect(() => + coll.where('x', notEqualOp, 32).orderBy('x') + ).not.to.throw(); + expect(() => + coll.orderBy('x').where('x', notEqualOp, 32) + ).not.to.throw(); }); }); @@ -678,10 +692,10 @@ apiDescribe('Database', (persistence: boolean) => { it('!= same as first orderBy works.', () => { return withTestCollection(persistence, {}, async coll => { expect(() => - coll.where('x', '!=', 32).orderBy('x').orderBy('y') + coll.where('x', notEqualOp, 32).orderBy('x').orderBy('y') ).not.to.throw(); expect(() => - coll.orderBy('x').where('x', '!=', 32).orderBy('y') + coll.orderBy('x').where('x', notEqualOp, 32).orderBy('y') ).not.to.throw(); }); }); @@ -706,6 +720,14 @@ 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(() => diff --git a/packages/firestore/test/integration/api/query.test.ts b/packages/firestore/test/integration/api/query.test.ts index 4e7a2c1543e..b86bffb6462 100644 --- a/packages/firestore/test/integration/api/query.test.ts +++ b/packages/firestore/test/integration/api/query.test.ts @@ -24,6 +24,8 @@ import { EventsAccumulator } from '../util/events_accumulator'; import * as firebaseExport from '../util/firebase_export'; import { apiDescribe, + notEqualOp, + notInOp, toChangesArray, toDataArray, withTestCollection, @@ -685,52 +687,60 @@ apiDescribe('Queries', (persistence: boolean) => { }); it('can use != filters', async () => { - // These documents are ordered by value in "zip" since the '!=' filter is - // an inequality, which results in documents being sorted by value. const testDocs = { - a: { zip: Number.NaN }, + a: { zip: 98101 }, b: { zip: 91102 }, - c: { zip: 98101 }, - d: { zip: '98101' }, - e: { zip: [98101] }, - f: { zip: [98101, 98102] }, - g: { zip: ['98101', { zip: 98101 }] }, - h: { zip: { code: 500 } }, - i: { code: 500 }, - j: { zip: null } + c: { zip: '98101' }, + d: { zip: [98101] }, + e: { zip: ['98101', { zip: 98101 }] }, + f: { zip: { code: 500 } }, + g: { zip: [98101, 98102] }, + h: { code: 500 }, + i: { zip: null }, + j: { zip: Number.NaN } }; await withTestCollection(persistence, testDocs, async coll => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let expected: { [name: string]: any } = { ...testDocs }; - delete expected.c; + delete expected.a; + delete expected.h; delete expected.i; - delete expected.j; - const snapshot = await coll.where('zip', '!=', 98101).get(); - expect(toDataArray(snapshot)).to.deep.equal(Object.values(expected)); + const snapshot = await coll.where('zip', notEqualOp, 98101).get(); + expect(toDataArray(snapshot)).to.have.deep.members( + Object.values(expected) + ); // With objects. - const snapshot2 = await coll.where('zip', '!=', { code: 500 }).get(); + const snapshot2 = await coll + .where('zip', notEqualOp, { code: 500 }) + .get(); expected = { ...testDocs }; + delete expected.f; delete expected.h; delete expected.i; - delete expected.j; - expect(toDataArray(snapshot2)).to.deep.equal(Object.values(expected)); + expect(toDataArray(snapshot2)).to.have.deep.members( + Object.values(expected) + ); // With null. - const snapshot3 = await coll.where('zip', '!=', null).get(); + const snapshot3 = await coll.where('zip', notEqualOp, null).get(); expected = { ...testDocs }; + delete expected.h; delete expected.i; - delete expected.j; - expect(toDataArray(snapshot3)).to.deep.equal(Object.values(expected)); + expect(toDataArray(snapshot3)).to.have.deep.members( + Object.values(expected) + ); // With NaN. - const snapshot4 = await coll.where('zip', '!=', Number.NaN).get(); + const snapshot4 = await coll.where('zip', notEqualOp, Number.NaN).get(); expected = { ...testDocs }; - delete expected.a; + delete expected.h; delete expected.i; delete expected.j; - expect(toDataArray(snapshot4)).to.deep.equal(Object.values(expected)); + expect(toDataArray(snapshot4)).to.have.deep.members( + Object.values(expected) + ); }); }); @@ -743,7 +753,7 @@ apiDescribe('Queries', (persistence: boolean) => { }; await withTestCollection(persistence, testDocs, async coll => { const snapshot = await coll - .where(FieldPath.documentId(), '!=', 'aa') + .where(FieldPath.documentId(), notEqualOp, 'aa') .get(); expect(toDataArray(snapshot)).to.deep.equal([ @@ -822,65 +832,59 @@ apiDescribe('Queries', (persistence: boolean) => { }); }); - it('can use NOT_IN filters', async () => { - // These documents are ordered by value in "zip" since the 'not-in' filter is - // an inequality, which results in documents being sorted by value. + // TODO(ne-queries): re-enable in next PR to make public. + // eslint-disable-next-line no-restricted-properties + it.skip('can use NOT_IN filters', async () => { const testDocs = { - a: { zip: Number.NaN }, + a: { zip: 98101 }, b: { zip: 91102 }, - c: { zip: 98101 }, - d: { zip: 98103 }, - e: { zip: [98101] }, - f: { zip: [98101, 98102] }, - g: { zip: ['98101', { zip: 98101 }] }, - h: { zip: { code: 500 } }, - i: { code: 500 }, - j: { zip: null } + c: { zip: 98103 }, + d: { zip: [98101] }, + e: { zip: ['98101', { zip: 98101 }] }, + f: { zip: { code: 500 } }, + g: { zip: [98101, 98102] }, + h: { code: 500 }, + i: { zip: null }, + j: { zip: Number.NaN } }; await withTestCollection(persistence, testDocs, async coll => { // eslint-disable-next-line @typescript-eslint/no-explicit-any let expected: { [name: string]: any } = { ...testDocs }; + delete expected.a; delete expected.c; - delete expected.d; - delete expected.f; - delete expected.i; - delete expected.j; + delete expected.g; + delete expected.h; const snapshot = await coll - .where('zip', 'not-in', [98101, 98103, [98101, 98102]]) + .where('zip', notInOp, [98101, 98103, [98101, 98102]]) .get(); expect(toDataArray(snapshot)).to.deep.equal(Object.values(expected)); // With objects. - const snapshot2 = await coll - .where('zip', 'not-in', [{ code: 500 }]) - .get(); + const snapshot2 = await coll.where('zip', notInOp, [{ code: 500 }]).get(); expected = { ...testDocs }; + delete expected.f; delete expected.h; - delete expected.i; - delete expected.j; expect(toDataArray(snapshot2)).to.deep.equal(Object.values(expected)); // With null. - const snapshot3 = await coll.where('zip', 'not-in', [null]).get(); + const snapshot3 = await coll.where('zip', notInOp, [null]).get(); expect(toDataArray(snapshot3)).to.deep.equal([]); // With NaN. - const snapshot4 = await coll.where('zip', 'not-in', [Number.NaN]).get(); + const snapshot4 = await coll.where('zip', notInOp, [Number.NaN]).get(); expected = { ...testDocs }; - delete expected.a; - delete expected.i; + delete expected.h; delete expected.j; expect(toDataArray(snapshot4)).to.deep.equal(Object.values(expected)); // With NaN and a number. const snapshot5 = await coll - .where('zip', 'not-in', [Number.NaN, 98101]) + .where('zip', notInOp, [Number.NaN, 98101]) .get(); expected = { ...testDocs }; delete expected.a; - delete expected.c; - delete expected.i; + delete expected.h; delete expected.j; expect(toDataArray(snapshot5)).to.deep.equal(Object.values(expected)); }); @@ -895,7 +899,7 @@ apiDescribe('Queries', (persistence: boolean) => { }; await withTestCollection(persistence, testDocs, async coll => { const snapshot = await coll - .where(FieldPath.documentId(), 'not-in', ['aa', 'ab']) + .where(FieldPath.documentId(), notInOp, ['aa', 'ab']) .get(); expect(toDataArray(snapshot)).to.deep.equal([ diff --git a/packages/firestore/test/integration/api/validation.test.ts b/packages/firestore/test/integration/api/validation.test.ts index 9595e270f60..b425661f6ad 100644 --- a/packages/firestore/test/integration/api/validation.test.ts +++ b/packages/firestore/test/integration/api/validation.test.ts @@ -24,7 +24,9 @@ import { apiDescribe, withAlternateTestDb, withTestCollection, - withTestDb + withTestDb, + notInOp, + notEqualOp } from '../util/helpers'; import { ALT_PROJECT_ID, DEFAULT_PROJECT_ID } from '../util/settings'; @@ -939,7 +941,7 @@ apiDescribe('Validation:', (persistence: boolean) => { const collection = db.collection('test') as any; expect(() => collection.where('a', 'foo' as any, 'b')).to.throw( 'Invalid value "foo" provided to function Query.where() for its second argument. ' + - 'Acceptable values: <, <=, ==, !=, >=, >, array-contains, in, array-contains-any, not-in' + 'Acceptable values: <, <=, ==, >=, >, array-contains, in, array-contains-any' ); } ); @@ -950,15 +952,15 @@ apiDescribe('Validation:', (persistence: boolean) => { db => { const collection = db.collection('test'); expect(() => collection.where('a', '>', null)).to.throw( - "Invalid query. Null only supports '==' and '!=' comparisons." + 'Invalid query. Null supports only equality comparisons.' ); expect(() => collection.where('a', 'array-contains', null)).to.throw( - "Invalid query. Null only supports '==' and '!=' comparisons." + 'Invalid query. Null supports only equality comparisons.' ); expect(() => collection.where('a', 'in', null)).to.throw( "Invalid Query. A non-empty array is required for 'in' filters." ); - expect(() => collection.where('a', 'not-in', null)).to.throw( + expect(() => collection.where('a', notInOp, null)).to.throw( "Invalid Query. A non-empty array is required for 'not-in' filters." ); expect(() => @@ -968,17 +970,15 @@ apiDescribe('Validation:', (persistence: boolean) => { ); expect(() => collection.where('a', '>', Number.NaN)).to.throw( - "Invalid query. NaN only supports '==' and '!=' comparisons." + 'Invalid query. NaN supports only equality comparisons.' ); expect(() => collection.where('a', 'array-contains', Number.NaN) - ).to.throw( - "Invalid query. NaN only supports '==' and '!=' comparisons." - ); + ).to.throw('Invalid query. NaN supports only equality comparisons.'); expect(() => collection.where('a', 'in', Number.NaN)).to.throw( "Invalid Query. A non-empty array is required for 'in' filters." ); - expect(() => collection.where('a', 'not-in', Number.NaN)).to.throw( + expect(() => collection.where('a', notInOp, Number.NaN)).to.throw( "Invalid Query. A non-empty array is required for 'not-in' filters." ); expect(() => @@ -1131,7 +1131,7 @@ apiDescribe('Validation:', (persistence: boolean) => { validationIt(persistence, 'with more than one != query fail', db => { const collection = db.collection('test'); expect(() => - collection.where('x', '!=', 32).where('x', '!=', 33) + collection.where('x', notEqualOp, 32).where('x', notEqualOp, 33) ).to.throw("Invalid query. You cannot use more than one '!=' filter."); }); @@ -1141,22 +1141,7 @@ apiDescribe('Validation:', (persistence: boolean) => { db => { const collection = db.collection('test'); expect(() => - collection.where('y', '>', 32).where('x', '!=', 33) - ).to.throw( - 'Invalid query. All where filters with an ' + - 'inequality (<, <=, >, or >=) must be on the same field.' + - ` But you have inequality filters on 'y' and 'x` - ); - } - ); - - validationIt( - persistence, - 'with != and inequality queries on different fields fail', - db => { - const collection = db.collection('test'); - expect(() => - collection.where('y', '>', 32).where('x', 'not-in', [33]) + collection.where('y', '>', 32).where('x', notEqualOp, 33) ).to.throw( 'Invalid query. All where filters with an ' + 'inequality (<, <=, >, or >=) must be on the same field.' + @@ -1187,9 +1172,9 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => collection.orderBy('y').orderBy('x').where('x', '>', 32) ).to.throw(reason); - expect(() => collection.where('x', '!=', 32).orderBy('y')).to.throw( - reason - ); + expect(() => + collection.where('x', notEqualOp, 32).orderBy('y') + ).to.throw(reason); } ); @@ -1226,7 +1211,7 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) .where('foo', 'array-contains', 1) ).to.throw( "Invalid query. You cannot use 'array-contains' filters with " + @@ -1238,8 +1223,8 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', 'not-in', [2, 3]) - .where('foo', '!=', 4) + .where('foo', notInOp, [2, 3]) + .where('foo', notEqualOp, 4) ).to.throw( "Invalid query. You cannot use '!=' filters with 'not-in' filters." ); @@ -1247,8 +1232,8 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', '!=', 4) - .where('foo', 'not-in', [2, 3]) + .where('foo', notEqualOp, 4) + .where('foo', notInOp, [2, 3]) ).to.throw( "Invalid query. You cannot use 'not-in' filters with '!=' filters." ); @@ -1265,8 +1250,8 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', 'not-in', [1, 2]) - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [1, 2]) + .where('foo', notInOp, [2, 3]) ).to.throw( "Invalid query. You cannot use more than one 'not-in' filter." ); @@ -1304,7 +1289,7 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) .where('foo', 'array-contains-any', [2, 3]) ).to.throw( "Invalid query. You cannot use 'array-contains-any' filters with " + @@ -1315,7 +1300,7 @@ apiDescribe('Validation:', (persistence: boolean) => { db .collection('test') .where('foo', 'array-contains-any', [2, 3]) - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) ).to.throw( "Invalid query. You cannot use 'not-in' filters with " + "'array-contains-any' filters." @@ -1324,7 +1309,7 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) .where('foo', 'in', [2, 3]) ).to.throw( "Invalid query. You cannot use 'in' filters with 'not-in' filters." @@ -1334,7 +1319,7 @@ apiDescribe('Validation:', (persistence: boolean) => { db .collection('test') .where('foo', 'in', [2, 3]) - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) ).to.throw( "Invalid query. You cannot use 'not-in' filters with 'in' filters." ); @@ -1365,7 +1350,7 @@ apiDescribe('Validation:', (persistence: boolean) => { expect(() => db .collection('test') - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) .where('foo', 'array-contains', 2) .where('foo', 'array-contains-any', [2]) ).to.throw( @@ -1378,7 +1363,7 @@ apiDescribe('Validation:', (persistence: boolean) => { .collection('test') .where('foo', 'array-contains', 2) .where('foo', 'in', [2]) - .where('foo', 'not-in', [2, 3]) + .where('foo', notInOp, [2, 3]) ).to.throw( "Invalid query. You cannot use 'not-in' filters with " + "'array-contains' filters." diff --git a/packages/firestore/test/integration/util/helpers.ts b/packages/firestore/test/integration/util/helpers.ts index 16e44ff8a87..558fdfaf2eb 100644 --- a/packages/firestore/test/integration/util/helpers.ts +++ b/packages/firestore/test/integration/util/helpers.ts @@ -276,3 +276,13 @@ export function withTestCollectionSettings( } ); } + +// TODO(ne-queries): This exists just so we don't have to do the cast +// repeatedly. Once we expose '!=' publicly we can remove it and just use '!=' +// in all the tests. +export const notEqualOp = '!=' as firestore.WhereFilterOp; + +// TODO(ne-queries): This exists just so we don't have to do the cast +// repeatedly. Once we expose 'not-in' publicly we can remove it and just use 'in' +// in all the tests. +export const notInOp = 'not-in' as firestore.WhereFilterOp; diff --git a/packages/firestore/test/unit/core/query.test.ts b/packages/firestore/test/unit/core/query.test.ts index 931c3bbf243..5d8ff136ec2 100644 --- a/packages/firestore/test/unit/core/query.test.ts +++ b/packages/firestore/test/unit/core/query.test.ts @@ -677,7 +677,7 @@ describe('Query', () => { ); assertCanonicalId( query('collection', filter('a', 'not-in', [1, 2, 3])), - 'collection|f:anot-in[1,2,3]|ob:aasc,__name__asc' + 'collection|f:anot-in[1,2,3]|ob:__name__asc' ); assertCanonicalId( query('collection', filter('a', 'array-contains-any', [1, 2, 3])),