From 60569b2d64e76fa6f73ce233eca129e238d42955 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Jun 2020 14:55:26 -0700 Subject: [PATCH 1/6] Tree-shakeable FieldFilter --- packages/firestore/src/api/database.ts | 103 +++--- packages/firestore/src/core/query.ts | 324 ++++++------------ packages/firestore/src/core/sync_engine.ts | 2 +- packages/firestore/src/core/target.ts | 27 +- packages/firestore/src/remote/serializer.ts | 19 +- .../test/unit/remote/serializer.helper.ts | 17 +- .../firestore/test/unit/specs/spec_builder.ts | 20 +- packages/firestore/test/util/helpers.ts | 8 +- 8 files changed, 207 insertions(+), 313 deletions(-) diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index cc56523fd50..4a6dc1588e0 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -32,7 +32,7 @@ import { Bound, Direction, FieldFilter, - Filter, + isInequalityFilter, Operator, OrderBy, Query as InternalQuery @@ -45,7 +45,7 @@ import { DocumentKey } from '../model/document_key'; import { DeleteMutation, Mutation, Precondition } from '../model/mutation'; import { FieldPath, ResourcePath } from '../model/path'; import { isServerTimestamp } from '../model/server_timestamps'; -import { refValue } from '../model/values'; +import { isNanValue, isNullValue, refValue } from '../model/values'; import { debugAssert, fail } from '../util/assert'; import { AsyncObserver } from '../util/async_observer'; import { AsyncQueue } from '../util/async_queue'; @@ -1490,7 +1490,7 @@ export class BaseQuery { /** allowArrays = */ op === Operator.IN ); } - const filter = FieldFilter.create(fieldPath, op, fieldValue); + const filter = new FieldFilter(fieldPath, op, fieldValue); this.validateNewFilter(filter); return filter; } @@ -1739,58 +1739,63 @@ export class BaseQuery { } } - private validateNewFilter(filter: Filter): void { - if (filter instanceof FieldFilter) { - const arrayOps = [Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY]; - const disjunctiveOps = [Operator.IN, Operator.ARRAY_CONTAINS_ANY]; - const isArrayOp = arrayOps.indexOf(filter.op) >= 0; - const isDisjunctiveOp = disjunctiveOps.indexOf(filter.op) >= 0; + private validateNewFilter(filter: FieldFilter): void { + const arrayOps = [Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY]; + const disjunctiveOps = [Operator.IN, Operator.ARRAY_CONTAINS_ANY]; + const isArrayOp = arrayOps.indexOf(filter.op) >= 0; + const isDisjunctiveOp = disjunctiveOps.indexOf(filter.op) >= 0; - if (filter.isInequality()) { - const existingField = this._query.getInequalityFilterField(); - if (existingField !== null && !existingField.isEqual(filter.field)) { + if (isNullValue(filter.value) && filter.op !== Operator.EQUAL) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `Invalid query. Null supports only equality comparisons.` + ); + } else if (isNanValue(filter.value) && filter.op !== Operator.EQUAL) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `Invalid query. NaN supports only equality comparisons.` + ); + } else if (isInequalityFilter(filter)) { + const existingField = this._query.getInequalityFilterField(); + if (existingField !== null && !existingField.isEqual(filter.field)) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Invalid query. All where filters with an inequality' + + ' (<, <=, >, or >=) must be on the same field. But you have' + + ` inequality filters on '${existingField.toString()}'` + + ` and '${filter.field.toString()}'` + ); + } + + const firstOrderByField = this._query.getFirstOrderByField(); + if (firstOrderByField !== null) { + this.validateOrderByAndInequalityMatch(filter.field, firstOrderByField); + } + } else if (isDisjunctiveOp || isArrayOp) { + // You can have at most 1 disjunctive filter and 1 array filter. Check if + // the new filter conflicts with an existing one. + let conflictingOp: Operator | null = null; + if (isDisjunctiveOp) { + conflictingOp = this._query.findFilterOperator(disjunctiveOps); + } + if (conflictingOp === null && isArrayOp) { + conflictingOp = this._query.findFilterOperator(arrayOps); + } + if (conflictingOp !== null) { + // We special case when it's a duplicate op to give a slightly clearer error message. + if (conflictingOp === filter.op) { throw new FirestoreError( Code.INVALID_ARGUMENT, - 'Invalid query. All where filters with an inequality' + - ' (<, <=, >, or >=) must be on the same field. But you have' + - ` inequality filters on '${existingField.toString()}'` + - ` and '${filter.field.toString()}'` + 'Invalid query. You cannot use more than one ' + + `'${filter.op.toString()}' filter.` ); - } - - const firstOrderByField = this._query.getFirstOrderByField(); - if (firstOrderByField !== null) { - this.validateOrderByAndInequalityMatch( - filter.field, - firstOrderByField + } else { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `Invalid query. You cannot use '${filter.op.toString()}' filters ` + + `with '${conflictingOp.toString()}' filters.` ); } - } else if (isDisjunctiveOp || isArrayOp) { - // You can have at most 1 disjunctive filter and 1 array filter. Check if - // the new filter conflicts with an existing one. - let conflictingOp: Operator | null = null; - if (isDisjunctiveOp) { - conflictingOp = this._query.findFilterOperator(disjunctiveOps); - } - if (conflictingOp === null && isArrayOp) { - conflictingOp = this._query.findFilterOperator(arrayOps); - } - if (conflictingOp != null) { - // We special case when it's a duplicate op to give a slightly clearer error message. - if (conflictingOp === filter.op) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'Invalid query. You cannot use more than one ' + - `'${filter.op.toString()}' filter.` - ); - } else { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - `Invalid query. You cannot use '${filter.op.toString()}' filters ` + - `with '${conflictingOp.toString()}' filters.` - ); - } - } } } } diff --git a/packages/firestore/src/core/query.ts b/packages/firestore/src/core/query.ts index 9f0204cb979..553913332ac 100644 --- a/packages/firestore/src/core/query.ts +++ b/packages/firestore/src/core/query.ts @@ -25,14 +25,11 @@ import { arrayValueContains, valueEquals, isArray, - isNanValue, - isNullValue, isReferenceValue, typeOrder } from '../model/values'; import { FieldPath, ResourcePath } from '../model/path'; import { debugAssert, fail } from '../util/assert'; -import { Code, FirestoreError } from '../util/error'; import { isNullOrUndefined } from '../util/types'; import { canonifyTarget, @@ -74,7 +71,7 @@ export class Query { readonly path: ResourcePath, readonly collectionGroup: string | null = null, readonly explicitOrderBy: OrderBy[] = [], - readonly filters: Filter[] = [], + readonly filters: FieldFilter[] = [], readonly limit: number | null = null, readonly limitType: LimitType = LimitType.First, readonly startAt: Bound | null = null, @@ -134,11 +131,10 @@ export class Query { return this.memoizedOrderBy; } - addFilter(filter: Filter): Query { + addFilter(filter: FieldFilter): Query { debugAssert( this.getInequalityFilterField() == null || - !(filter instanceof FieldFilter) || - !filter.isInequality() || + !isInequalityFilter(filter) || filter.field.isEqual(this.getInequalityFilterField()!), 'Query must only have one inequality field.' ); @@ -329,7 +325,7 @@ export class Query { getInequalityFilterField(): FieldPath | null { for (const filter of this.filters) { - if (filter instanceof FieldFilter && filter.isInequality()) { + if (isInequalityFilter(filter)) { return filter.field; } } @@ -340,10 +336,8 @@ export class Query { // returns the first one that is, or null if none are. findFilterOperator(operators: Operator[]): Operator | null { for (const filter of this.filters) { - if (filter instanceof FieldFilter) { - if (operators.indexOf(filter.op) >= 0) { - return filter.op; - } + if (operators.indexOf(filter.op) >= 0) { + return filter.op; } } return null; @@ -441,7 +435,7 @@ export class Query { private matchesFilters(doc: Document): boolean { for (const filter of this.filters) { - if (!filter.matches(doc)) { + if (!fieldFilterMatches(filter, doc)) { return false; } } @@ -469,12 +463,6 @@ export class Query { } } -export abstract class Filter { - abstract matches(doc: Document): boolean; - abstract canonicalId(): string; - abstract isEqual(filter: Filter): boolean; -} - export const enum Operator { LESS_THAN = '<', LESS_THAN_OR_EQUAL = '<=', @@ -486,228 +474,136 @@ export const enum Operator { ARRAY_CONTAINS_ANY = 'array-contains-any' } -export class FieldFilter extends Filter { - protected constructor( +export class FieldFilter { + constructor( public field: FieldPath, public op: Operator, public value: api.Value - ) { - super(); - } + ) {} +} - /** - * Creates a filter based on the provided arguments. - */ - static create(field: FieldPath, op: Operator, value: api.Value): FieldFilter { - if (field.isKeyField()) { - if (op === Operator.IN) { - debugAssert( - isArray(value), - 'Comparing on key with IN, but filter value not an ArrayValue' - ); - debugAssert( - (value.arrayValue.values || []).every(elem => isReferenceValue(elem)), - 'Comparing on key with IN, but an array value was not a RefValue' - ); - return new KeyFieldInFilter(field, value); - } else { - debugAssert( - isReferenceValue(value), - 'Comparing on key, but filter value not a RefValue' - ); +/** Returns a debug description for `fieldFilter`. */ +export function stringifyFieldFilter(fieldFilter: FieldFilter): string { + return `${fieldFilter.field.canonicalString()} ${ + fieldFilter.op + } ${canonicalId(fieldFilter.value)}`; +} + +export function canonifyFieldFilter(fieldFilter: FieldFilter): string { + // TODO(b/29183165): Technically, this won't be unique if two values have + // the same description, such as the int 3 and the string "3". So we should + // add the types in here somehow, too. + return ( + fieldFilter.field.canonicalString() + + fieldFilter.op.toString() + + canonicalId(fieldFilter.value) + ); +} + +/** Returns whether this filter filters by <, <=, => or >. */ +export function isInequalityFilter(filter: FieldFilter): boolean { + return ( + [ + Operator.LESS_THAN, + Operator.LESS_THAN_OR_EQUAL, + Operator.GREATER_THAN, + Operator.GREATER_THAN_OR_EQUAL + ].indexOf(filter.op) >= 0 + ); +} + +function fieldFilterMatches(filter: FieldFilter, doc: Document): boolean { + const op = filter.op; + const filterValue = filter.value; + + if (filter.field.isKeyField()) { + if (op === Operator.IN) { + // Filter that matches on key fields within an array. + debugAssert( + isArray(filterValue), + 'Comparing on key with IN, but filter value not an ArrayValue' + ); + const keys = (filterValue.arrayValue.values || []).map(v => { debugAssert( - op !== Operator.ARRAY_CONTAINS && op !== Operator.ARRAY_CONTAINS_ANY, - `'${op.toString()}' queries don't make sense on document keys.` - ); - return new KeyFieldFilter(field, op, value); - } - } else if (isNullValue(value)) { - if (op !== Operator.EQUAL) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'Invalid query. Null supports only equality comparisons.' - ); - } - return new FieldFilter(field, op, value); - } else if (isNanValue(value)) { - if (op !== Operator.EQUAL) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'Invalid query. NaN supports only equality comparisons.' + isReferenceValue(v), + 'Comparing on key with IN, but an array value was not a ReferenceValue' ); - } - return new FieldFilter(field, op, value); - } else if (op === Operator.ARRAY_CONTAINS) { - return new ArrayContainsFilter(field, value); - } else if (op === Operator.IN) { + return DocumentKey.fromName(v.referenceValue); + }); + return keys.some(key => key.isEqual(doc.key)); + } else { + // Filter that matches on a single key field (i.e. '__name__'). debugAssert( - isArray(value), - 'IN filter has invalid value: ' + value.toString() + isReferenceValue(filterValue), + 'Comparing on key, but filter value not a RefValue' ); - return new InFilter(field, value); - } else if (op === Operator.ARRAY_CONTAINS_ANY) { debugAssert( - isArray(value), - 'ARRAY_CONTAINS_ANY filter has invalid value: ' + value.toString() + op !== Operator.ARRAY_CONTAINS && op !== Operator.ARRAY_CONTAINS_ANY, + `'${op}' queries don't make sense on document keys.` ); - return new ArrayContainsAnyFilter(field, value); - } else { - return new FieldFilter(field, op, value); + const key = DocumentKey.fromName(filterValue.referenceValue); + const comparison = DocumentKey.comparator(doc.key, key); + return matchesComparison(filter, comparison); } } - matches(doc: Document): boolean { - const other = doc.field(this.field); - - // Only compare types with matching backend order (such as double and int). + const fieldValue = doc.field(filter.field); + if (op === Operator.ARRAY_CONTAINS) { return ( - other !== null && - typeOrder(this.value) === typeOrder(other) && - this.matchesComparison(valueCompare(other, this.value)) + isArray(fieldValue) && + arrayValueContains(fieldValue.arrayValue, filter.value) ); - } - - protected matchesComparison(comparison: number): boolean { - switch (this.op) { - case Operator.LESS_THAN: - return comparison < 0; - case Operator.LESS_THAN_OR_EQUAL: - return comparison <= 0; - case Operator.EQUAL: - return comparison === 0; - case Operator.GREATER_THAN: - return comparison > 0; - case Operator.GREATER_THAN_OR_EQUAL: - return comparison >= 0; - default: - return fail('Unknown FieldFilter operator: ' + this.op); + } else if (op === Operator.ARRAY_CONTAINS_ANY) { + debugAssert( + isArray(filterValue), + 'ArrayContainsAnyFilter expects an ArrayValue' + ); + if (!isArray(fieldValue) || !fieldValue.arrayValue.values) { + return false; } - } - - isInequality(): boolean { - return ( - [ - Operator.LESS_THAN, - Operator.LESS_THAN_OR_EQUAL, - Operator.GREATER_THAN, - Operator.GREATER_THAN_OR_EQUAL - ].indexOf(this.op) >= 0 + return fieldValue.arrayValue.values.some(val => + arrayValueContains(filter.value.arrayValue!, val) ); - } - - canonicalId(): string { - // TODO(b/29183165): Technically, this won't be unique if two values have - // the same description, such as the int 3 and the string "3". So we should - // add the types in here somehow, too. + } else if (op === Operator.IN) { + debugAssert(isArray(filterValue), 'InFilter expects an ArrayValue'); return ( - this.field.canonicalString() + - this.op.toString() + - canonicalId(this.value) + fieldValue !== null && + arrayValueContains(filter.value.arrayValue!, fieldValue) ); - } - - isEqual(other: Filter): boolean { - if (other instanceof FieldFilter) { - return ( - this.op === other.op && - this.field.isEqual(other.field) && - valueEquals(this.value, other.value) - ); - } else { - return false; - } - } - - toString(): string { - return `${this.field.canonicalString()} ${this.op} ${canonicalId( - this.value - )}`; - } -} - -/** Filter that matches on key fields (i.e. '__name__'). */ -export class KeyFieldFilter extends FieldFilter { - private readonly key: DocumentKey; - - constructor(field: FieldPath, op: Operator, value: api.Value) { - super(field, op, value); - debugAssert( - isReferenceValue(value), - 'KeyFieldFilter expects a ReferenceValue' + } else { + // Only compare types with matching backend order (such as double and int). + return ( + fieldValue !== null && + typeOrder(filter.value) === typeOrder(fieldValue) && + matchesComparison(filter, valueCompare(fieldValue, filter.value)) ); - this.key = DocumentKey.fromName(value.referenceValue); - } - - matches(doc: Document): boolean { - const comparison = DocumentKey.comparator(doc.key, this.key); - return this.matchesComparison(comparison); - } -} - -/** Filter that matches on key fields within an array. */ -export class KeyFieldInFilter extends FieldFilter { - private readonly keys: DocumentKey[]; - - constructor(field: FieldPath, value: api.Value) { - super(field, Operator.IN, value); - debugAssert(isArray(value), 'KeyFieldInFilter expects an ArrayValue'); - this.keys = (value.arrayValue.values || []).map(v => { - debugAssert( - isReferenceValue(v), - 'Comparing on key with IN, but an array value was not a ReferenceValue' - ); - return DocumentKey.fromName(v.referenceValue); - }); - } - - matches(doc: Document): boolean { - return this.keys.some(key => key.isEqual(doc.key)); - } -} - -/** A Filter that implements the array-contains operator. */ -export class ArrayContainsFilter extends FieldFilter { - constructor(field: FieldPath, value: api.Value) { - super(field, Operator.ARRAY_CONTAINS, value); - } - - matches(doc: Document): boolean { - const other = doc.field(this.field); - return isArray(other) && arrayValueContains(other.arrayValue, this.value); } } -/** A Filter that implements the IN operator. */ -export class InFilter extends FieldFilter { - constructor(field: FieldPath, value: api.Value) { - super(field, Operator.IN, value); - debugAssert(isArray(value), 'InFilter expects an ArrayValue'); - } - - matches(doc: Document): boolean { - const other = doc.field(this.field); - return other !== null && arrayValueContains(this.value.arrayValue!, other); +function matchesComparison(filter: FieldFilter, comparison: number): boolean { + switch (filter.op) { + case Operator.LESS_THAN: + return comparison < 0; + case Operator.LESS_THAN_OR_EQUAL: + return comparison <= 0; + case Operator.EQUAL: + return comparison === 0; + case Operator.GREATER_THAN: + return comparison > 0; + case Operator.GREATER_THAN_OR_EQUAL: + return comparison >= 0; + default: + return fail('Unknown FieldFilter operator: ' + filter.op); } } -/** A Filter that implements the array-contains-any operator. */ -export class ArrayContainsAnyFilter extends FieldFilter { - constructor(field: FieldPath, value: api.Value) { - super(field, Operator.ARRAY_CONTAINS_ANY, value); - debugAssert(isArray(value), 'ArrayContainsAnyFilter expects an ArrayValue'); - } - - matches(doc: Document): boolean { - const other = doc.field(this.field); - if (!isArray(other) || !other.arrayValue.values) { - return false; - } - return other.arrayValue.values.some(val => - arrayValueContains(this.value.arrayValue!, val) - ); - } +export function fieldFilterEquals(f1: FieldFilter, f2: FieldFilter): boolean { + return ( + f1.op === f2.op && + f1.field.isEqual(f2.field) && + valueEquals(f1.value, f2.value) + ); } - /** * The direction of sorting in an order by. */ diff --git a/packages/firestore/src/core/sync_engine.ts b/packages/firestore/src/core/sync_engine.ts index ba640411957..52f3506af2a 100644 --- a/packages/firestore/src/core/sync_engine.ts +++ b/packages/firestore/src/core/sync_engine.ts @@ -913,7 +913,7 @@ export class SyncEngine implements RemoteSyncer { } /** - * An impplementation of SyncEngine that implement SharedClientStateSyncer for + * An implementation of SyncEngine that implement SharedClientStateSyncer for * Multi-Tab synchronization. */ // PORTING NOTE: Web only diff --git a/packages/firestore/src/core/target.ts b/packages/firestore/src/core/target.ts index cd04d0ba565..55d72c7aaa5 100644 --- a/packages/firestore/src/core/target.ts +++ b/packages/firestore/src/core/target.ts @@ -18,7 +18,16 @@ import { DocumentKey } from '../model/document_key'; import { ResourcePath } from '../model/path'; import { isNullOrUndefined } from '../util/types'; -import { Bound, boundEquals, canonifyBound, Filter, OrderBy } from './query'; +import { + Bound, + boundEquals, + canonifyBound, + canonifyFieldFilter, + FieldFilter, + fieldFilterEquals, + OrderBy, + stringifyFieldFilter +} from './query'; import { debugCast } from '../util/assert'; /** @@ -33,7 +42,7 @@ export class Target { readonly path: ResourcePath, readonly collectionGroup: string | null, readonly orderBy: OrderBy[], - readonly filters: Filter[], + readonly filters: FieldFilter[], readonly limit: number | null, readonly startAt: Bound | null, readonly endAt: Bound | null @@ -46,7 +55,7 @@ class TargetImpl extends Target { path: ResourcePath, collectionGroup: string | null = null, orderBy: OrderBy[] = [], - filters: Filter[] = [], + filters: FieldFilter[] = [], limit: number | null = null, startAt: Bound | null = null, endAt: Bound | null = null @@ -67,7 +76,7 @@ export function newTarget( path: ResourcePath, collectionGroup: string | null = null, orderBy: OrderBy[] = [], - filters: Filter[] = [], + filters: FieldFilter[] = [], limit: number | null = null, startAt: Bound | null = null, endAt: Bound | null = null @@ -92,7 +101,9 @@ export function canonifyTarget(target: Target): string { canonicalId += '|cg:' + targetImpl.collectionGroup; } canonicalId += '|f:'; - canonicalId += targetImpl.filters.map(f => f.canonicalId()).join(','); + canonicalId += targetImpl.filters + .map(f => canonifyFieldFilter(f)) + .join(','); canonicalId += '|ob:'; canonicalId += targetImpl.orderBy.map(o => o.canonicalId()).join(','); @@ -119,7 +130,9 @@ export function stringifyTarget(target: Target): string { str += ' collectionGroup=' + target.collectionGroup; } if (target.filters.length > 0) { - str += `, filters: [${target.filters.join(', ')}]`; + str += `, filters: [${target.filters + .map(f => stringifyFieldFilter(f)) + .join(', ')}]`; } if (!isNullOrUndefined(target.limit)) { str += ', limit: ' + target.limit; @@ -156,7 +169,7 @@ export function targetEquals(left: Target, right: Target): boolean { } for (let i = 0; i < left.filters.length; i++) { - if (!left.filters[i].isEqual(right.filters[i])) { + if (!fieldFilterEquals(left.filters[i], right.filters[i])) { return false; } } diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts index ea51deb1c9b..5d2ec3667c5 100644 --- a/packages/firestore/src/remote/serializer.ts +++ b/packages/firestore/src/remote/serializer.ts @@ -22,7 +22,6 @@ import { Bound, Direction, FieldFilter, - Filter, LimitType, Operator, OrderBy, @@ -884,7 +883,7 @@ export function fromQueryTarget(target: api.QueryTarget): Target { } } - let filterBy: Filter[] = []; + let filterBy: FieldFilter[] = []; if (query.where) { filterBy = fromFilter(query.where); } @@ -973,7 +972,7 @@ export function toTarget( return result; } -function toFilter(filters: Filter[]): api.Filter | undefined { +function toFilter(filters: FieldFilter[]): api.Filter | undefined { if (filters.length === 0) { return; } @@ -990,7 +989,7 @@ function toFilter(filters: Filter[]): api.Filter | undefined { return { compositeFilter: { op: 'AND', filters: protos } }; } -function fromFilter(filter: api.Filter | undefined): Filter[] { +function fromFilter(filter: api.Filter | undefined): FieldFilter[] { if (!filter) { return []; } else if (filter.unaryFilter !== undefined) { @@ -1104,8 +1103,8 @@ export function fromPropertyOrder(orderBy: api.Order): OrderBy { ); } -export function fromFieldFilter(filter: api.Filter): Filter { - return FieldFilter.create( +export function fromFieldFilter(filter: api.Filter): FieldFilter { + return new FieldFilter( fromFieldPathReference(filter.fieldFilter!.field!), fromOperatorName(filter.fieldFilter!.op!), filter.fieldFilter!.value! @@ -1140,16 +1139,14 @@ export function toUnaryOrFieldFilter(filter: FieldFilter): api.Filter { }; } -export function fromUnaryFilter(filter: api.Filter): Filter { +export function fromUnaryFilter(filter: api.Filter): FieldFilter { switch (filter.unaryFilter!.op!) { case 'IS_NAN': const nanField = fromFieldPathReference(filter.unaryFilter!.field!); - return FieldFilter.create(nanField, Operator.EQUAL, { - doubleValue: NaN - }); + return new FieldFilter(nanField, Operator.EQUAL, { doubleValue: NaN }); case 'IS_NULL': const nullField = fromFieldPathReference(filter.unaryFilter!.field!); - return FieldFilter.create(nullField, Operator.EQUAL, { + return new FieldFilter(nullField, Operator.EQUAL, { nullValue: 'NULL_VALUE' }); case 'OPERATOR_UNSPECIFIED': diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index 55e4027c573..41386455b7d 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -24,12 +24,9 @@ import { GeoPoint } from '../../../src/api/geo_point'; import { Timestamp } from '../../../src/api/timestamp'; import { DatabaseId } from '../../../src/core/database_info'; import { - ArrayContainsAnyFilter, - ArrayContainsFilter, Direction, FieldFilter, - InFilter, - KeyFieldFilter, + fieldFilterEquals, Operator, OrderBy, Query @@ -136,7 +133,7 @@ export function serializerTest( } describe('converts value', () => { - addEqualityMatcher(); + addEqualityMatcher({ equalsFn: fieldFilterEquals, forType: FieldFilter }); /** * Verifies full round-trip of encoding/decoding fieldValue objects: @@ -754,11 +751,11 @@ export function serializerTest( }); describe('to/from FieldFilter', () => { - addEqualityMatcher(); + addEqualityMatcher({ equalsFn: fieldFilterEquals, forType: FieldFilter }); it('makes dotted-property names', () => { const path = new FieldPath(['item', 'part', 'top']); - const input = FieldFilter.create(path, Operator.EQUAL, wrap('food')); + const input = new FieldFilter(path, Operator.EQUAL, wrap('food')); const actual = toUnaryOrFieldFilter(input); expect(actual).to.deep.equal({ fieldFilter: { @@ -847,7 +844,6 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); - expect(roundtripped).to.be.instanceof(KeyFieldFilter); }); it('converts array-contains', () => { @@ -862,7 +858,6 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); - expect(roundtripped).to.be.instanceof(ArrayContainsFilter); }); it('converts IN', () => { @@ -885,7 +880,6 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); - expect(roundtripped).to.be.instanceof(InFilter); }); it('converts array-contains-any', () => { @@ -908,12 +902,11 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); - expect(roundtripped).to.be.instanceof(ArrayContainsAnyFilter); }); }); describe('to/from UnaryFilter', () => { - addEqualityMatcher(); + addEqualityMatcher({ equalsFn: fieldFilterEquals, forType: FieldFilter }); it('converts null', () => { const input = filter('field', '==', null); diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index af29c65f2dd..baa4bba4215 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { FieldFilter, Filter, Query } from '../../../src/core/query'; +import { FieldFilter, Query } from '../../../src/core/query'; import { canonifyTarget, Target, targetEquals } from '../../../src/core/target'; import { TargetIdGenerator } from '../../../src/core/target_id_generator'; import { TargetId } from '../../../src/core/types'; @@ -950,17 +950,13 @@ export class SpecBuilder { spec.limitType = 'LimitToLast'; } if (query.filters) { - spec.filters = query.filters.map((filter: Filter) => { - if (filter instanceof FieldFilter) { - // TODO(dimond): Support non-JSON primitive values? - return [ - filter.field.canonicalString(), - filter.op, - userDataWriter.convertValue(filter.value) - ] as SpecQueryFilter; - } else { - return fail('Unknown filter: ' + filter); - } + spec.filters = query.filters.map((filter: FieldFilter) => { + // TODO(dimond): Support non-JSON primitive values? + return [ + filter.field.canonicalString(), + filter.op, + userDataWriter.convertValue(filter.value) + ] as SpecQueryFilter; }); } if (query.explicitOrderBy) { diff --git a/packages/firestore/test/util/helpers.ts b/packages/firestore/test/util/helpers.ts index b92cf3cc589..5a63298a3e7 100644 --- a/packages/firestore/test/util/helpers.ts +++ b/packages/firestore/test/util/helpers.ts @@ -208,13 +208,7 @@ export function blob(...bytes: number[]): Blob { export function filter(path: string, op: string, value: unknown): FieldFilter { const dataValue = wrap(value); const operator = op as Operator; - const filter = FieldFilter.create(field(path), operator, dataValue); - - if (filter instanceof FieldFilter) { - return filter; - } else { - return fail('Unrecognized filter: ' + JSON.stringify(filter)); - } + return new FieldFilter(field(path), operator, dataValue); } export function setMutation( From 881c3315399b9ccd9f6004c04d5319e8fb0a0247 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Jun 2020 14:57:40 -0700 Subject: [PATCH 2/6] Re-gen deps --- .../firestore/lite/test/dependencies.json | 178 ++++++------------ 1 file changed, 57 insertions(+), 121 deletions(-) diff --git a/packages/firestore/lite/test/dependencies.json b/packages/firestore/lite/test/dependencies.json index 5fc28063386..9ced0969cac 100644 --- a/packages/firestore/lite/test/dependencies.json +++ b/packages/firestore/lite/test/dependencies.json @@ -76,6 +76,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -98,6 +99,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -115,6 +118,7 @@ "isArray", "isDocumentTarget", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -132,6 +136,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -154,6 +159,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -177,8 +183,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -202,16 +206,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -242,7 +242,7 @@ ], "variables": [] }, - "sizeInBytes": 97991 + "sizeInBytes": 96701 }, "DocumentReference": { "dependencies": { @@ -617,18 +617,9 @@ "functions": [ "argToString", "arrayEquals", - "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", - "canonicalId", - "canonifyArray", - "canonifyByteString", - "canonifyGeoPoint", - "canonifyMap", - "canonifyReference", - "canonifyTimestamp", - "canonifyValue", "cast", "compareArrays", "compareBlobs", @@ -658,14 +649,13 @@ "getPreviousValue", "hardAssert", "invalidClassError", - "isArray", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullValue", "isPlainObject", - "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -717,8 +707,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -741,16 +729,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -778,7 +762,7 @@ ], "variables": [] }, - "sizeInBytes": 87202 + "sizeInBytes": 81651 }, "QueryDocumentSnapshot": { "dependencies": { @@ -1283,6 +1267,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -1307,6 +1292,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -1327,6 +1314,7 @@ "isDocumentTarget", "isDouble", "isEmpty", + "isInequalityFilter", "isInteger", "isMapValue", "isNanValue", @@ -1346,6 +1334,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -1370,6 +1359,7 @@ "registerFirestore", "serverTimestamp", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -1401,8 +1391,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AutoId", @@ -1430,16 +1418,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "NoDocument", @@ -1474,7 +1458,7 @@ ], "variables": [] }, - "sizeInBytes": 108269 + "sizeInBytes": 106979 }, "arrayRemove": { "dependencies": { @@ -1710,6 +1694,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -1733,6 +1718,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -1750,6 +1737,7 @@ "isArray", "isDocumentTarget", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -1767,6 +1755,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -1789,6 +1778,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -1813,8 +1803,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -1838,16 +1826,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -1878,7 +1862,7 @@ ], "variables": [] }, - "sizeInBytes": 98619 + "sizeInBytes": 97329 }, "collectionGroup": { "dependencies": { @@ -1894,6 +1878,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -1917,6 +1902,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -1934,6 +1921,7 @@ "isArray", "isDocumentTarget", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -1951,6 +1939,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -1973,6 +1962,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -1996,8 +1986,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -2020,16 +2008,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -2060,7 +2044,7 @@ ], "variables": [] }, - "sizeInBytes": 98050 + "sizeInBytes": 96760 }, "deleteDoc": { "dependencies": { @@ -2256,6 +2240,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -2279,6 +2264,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -2296,6 +2283,7 @@ "isArray", "isDocumentTarget", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -2313,6 +2301,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -2336,6 +2325,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -2360,8 +2350,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "AutoId", "BaseFieldPath", "BasePath", @@ -2386,16 +2374,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -2426,7 +2410,7 @@ ], "variables": [] }, - "sizeInBytes": 99440 + "sizeInBytes": 98150 }, "documentId": { "dependencies": { @@ -2664,18 +2648,9 @@ "functions": [ "argToString", "arrayEquals", - "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", - "canonicalId", - "canonifyArray", - "canonifyByteString", - "canonifyGeoPoint", - "canonifyMap", - "canonifyReference", - "canonifyTimestamp", - "canonifyValue", "cast", "compareArrays", "compareBlobs", @@ -2715,15 +2690,14 @@ "hardAssert", "invalidClassError", "invokeRunQueryRpc", - "isArray", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullOrUndefined", "isNullValue", "isPlainObject", - "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -2786,8 +2760,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -2810,16 +2782,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -2848,7 +2816,7 @@ ], "variables": [] }, - "sizeInBytes": 93121 + "sizeInBytes": 87570 }, "increment": { "dependencies": { @@ -3030,6 +2998,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -3052,6 +3021,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -3069,6 +3040,7 @@ "isArray", "isDocumentTarget", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -3086,6 +3058,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -3109,6 +3082,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -3132,8 +3106,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3157,16 +3129,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -3197,25 +3165,16 @@ ], "variables": [] }, - "sizeInBytes": 98334 + "sizeInBytes": 97044 }, "queryEqual": { "dependencies": { "functions": [ "argToString", "arrayEquals", - "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", - "canonicalId", - "canonifyArray", - "canonifyByteString", - "canonifyGeoPoint", - "canonifyMap", - "canonifyReference", - "canonifyTimestamp", - "canonifyValue", "cast", "compareArrays", "compareBlobs", @@ -3245,14 +3204,13 @@ "getPreviousValue", "hardAssert", "invalidClassError", - "isArray", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullValue", "isPlainObject", - "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -3305,8 +3263,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3329,16 +3285,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -3366,7 +3318,7 @@ ], "variables": [] }, - "sizeInBytes": 87406 + "sizeInBytes": 81855 }, "refEqual": { "dependencies": { @@ -3382,6 +3334,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", + "canonifyFieldFilter", "canonifyGeoPoint", "canonifyMap", "canonifyReference", @@ -3404,6 +3357,8 @@ "encodeBase64", "errorMessage", "fail", + "fieldFilterEquals", + "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", @@ -3421,6 +3376,7 @@ "isArray", "isDocumentTarget", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -3438,6 +3394,7 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", + "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -3461,6 +3418,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", + "stringifyFieldFilter", "stringifyTarget", "targetEquals", "timestampEquals", @@ -3484,8 +3442,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3509,16 +3465,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -3549,7 +3501,7 @@ ], "variables": [] }, - "sizeInBytes": 98276 + "sizeInBytes": 96986 }, "runTransaction": { "dependencies": { @@ -4006,18 +3958,9 @@ "functions": [ "argToString", "arrayEquals", - "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", - "canonicalId", - "canonifyArray", - "canonifyByteString", - "canonifyGeoPoint", - "canonifyMap", - "canonifyReference", - "canonifyTimestamp", - "canonifyValue", "cast", "compareArrays", "compareBlobs", @@ -4047,14 +3990,13 @@ "getPreviousValue", "hardAssert", "invalidClassError", - "isArray", "isEmpty", + "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullValue", "isPlainObject", - "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -4108,8 +4050,6 @@ "valueEquals" ], "classes": [ - "ArrayContainsAnyFilter", - "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -4132,16 +4072,12 @@ "FieldPath", "FieldPath$1", "FieldPath$2", - "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", - "InFilter", "JsonProtoSerializer", - "KeyFieldFilter", - "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -4170,7 +4106,7 @@ ], "variables": [] }, - "sizeInBytes": 88139 + "sizeInBytes": 82588 }, "terminate": { "dependencies": { From c3c92dbe1a38f9e609461fd1b6376f95282fca29 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 22 Jun 2020 15:21:22 -0700 Subject: [PATCH 3/6] Fix test --- packages/firestore/test/unit/remote/serializer.helper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index 41386455b7d..28fc5c5f5fe 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -32,7 +32,7 @@ import { Query } from '../../../src/core/query'; import { SnapshotVersion } from '../../../src/core/snapshot_version'; -import { Target } from '../../../src/core/target'; +import { Target, targetEquals } from '../../../src/core/target'; import { TargetData, TargetPurpose } from '../../../src/local/target_data'; import { DeleteMutation, @@ -957,7 +957,7 @@ export function serializerTest( }); describe('toTarget', () => { - addEqualityMatcher(); + addEqualityMatcher({ equalsFn: targetEquals, forType: Target }); it('converts first-level key queries', () => { const q = Query.atPath(path('docs/1')).toTarget(); From 688199aea2f5e318970a4cd04d63d0ba921a8200 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 29 Jun 2020 10:46:11 -0700 Subject: [PATCH 4/6] Revert Filter/FieldFilter changes --- packages/firestore/src/api/database.ts | 103 +++--- packages/firestore/src/core/query.ts | 325 ++++++++++++------ packages/firestore/src/core/sync_engine.ts | 1 - packages/firestore/src/core/target.ts | 24 +- packages/firestore/src/remote/serializer.ts | 19 +- .../test/unit/remote/serializer.helper.ts | 22 +- .../firestore/test/unit/specs/spec_builder.ts | 20 +- packages/firestore/test/util/helpers.ts | 2 +- 8 files changed, 316 insertions(+), 200 deletions(-) diff --git a/packages/firestore/src/api/database.ts b/packages/firestore/src/api/database.ts index ced0232e94d..4e9929a9c98 100644 --- a/packages/firestore/src/api/database.ts +++ b/packages/firestore/src/api/database.ts @@ -32,7 +32,7 @@ import { Bound, Direction, FieldFilter, - isInequalityFilter, + Filter, Operator, OrderBy, Query as InternalQuery @@ -45,7 +45,7 @@ import { DocumentKey } from '../model/document_key'; import { DeleteMutation, Mutation, Precondition } from '../model/mutation'; import { FieldPath, ResourcePath } from '../model/path'; import { isServerTimestamp } from '../model/server_timestamps'; -import { isNanValue, isNullValue, refValue } from '../model/values'; +import { refValue } from '../model/values'; import { debugAssert, fail } from '../util/assert'; import { AsyncObserver } from '../util/async_observer'; import { AsyncQueue } from '../util/async_queue'; @@ -1547,7 +1547,7 @@ export class BaseQuery { /** allowArrays = */ op === Operator.IN ); } - const filter = new FieldFilter(fieldPath, op, fieldValue); + const filter = FieldFilter.create(fieldPath, op, fieldValue); this.validateNewFilter(filter); return filter; } @@ -1796,63 +1796,58 @@ export class BaseQuery { } } - private validateNewFilter(filter: FieldFilter): void { - const arrayOps = [Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY]; - const disjunctiveOps = [Operator.IN, Operator.ARRAY_CONTAINS_ANY]; - const isArrayOp = arrayOps.indexOf(filter.op) >= 0; - const isDisjunctiveOp = disjunctiveOps.indexOf(filter.op) >= 0; + private validateNewFilter(filter: Filter): void { + if (filter instanceof FieldFilter) { + const arrayOps = [Operator.ARRAY_CONTAINS, Operator.ARRAY_CONTAINS_ANY]; + const disjunctiveOps = [Operator.IN, Operator.ARRAY_CONTAINS_ANY]; + const isArrayOp = arrayOps.indexOf(filter.op) >= 0; + const isDisjunctiveOp = disjunctiveOps.indexOf(filter.op) >= 0; - if (isNullValue(filter.value) && filter.op !== Operator.EQUAL) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - `Invalid query. Null supports only equality comparisons.` - ); - } else if (isNanValue(filter.value) && filter.op !== Operator.EQUAL) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - `Invalid query. NaN supports only equality comparisons.` - ); - } else if (isInequalityFilter(filter)) { - const existingField = this._query.getInequalityFilterField(); - if (existingField !== null && !existingField.isEqual(filter.field)) { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - 'Invalid query. All where filters with an inequality' + - ' (<, <=, >, or >=) must be on the same field. But you have' + - ` inequality filters on '${existingField.toString()}'` + - ` and '${filter.field.toString()}'` - ); - } - - const firstOrderByField = this._query.getFirstOrderByField(); - if (firstOrderByField !== null) { - this.validateOrderByAndInequalityMatch(filter.field, firstOrderByField); - } - } else if (isDisjunctiveOp || isArrayOp) { - // You can have at most 1 disjunctive filter and 1 array filter. Check if - // the new filter conflicts with an existing one. - let conflictingOp: Operator | null = null; - if (isDisjunctiveOp) { - conflictingOp = this._query.findFilterOperator(disjunctiveOps); - } - if (conflictingOp === null && isArrayOp) { - conflictingOp = this._query.findFilterOperator(arrayOps); - } - if (conflictingOp !== null) { - // We special case when it's a duplicate op to give a slightly clearer error message. - if (conflictingOp === filter.op) { + if (filter.isInequality()) { + const existingField = this._query.getInequalityFilterField(); + if (existingField !== null && !existingField.isEqual(filter.field)) { throw new FirestoreError( Code.INVALID_ARGUMENT, - 'Invalid query. You cannot use more than one ' + - `'${filter.op.toString()}' filter.` + 'Invalid query. All where filters with an inequality' + + ' (<, <=, >, or >=) must be on the same field. But you have' + + ` inequality filters on '${existingField.toString()}'` + + ` and '${filter.field.toString()}'` ); - } else { - throw new FirestoreError( - Code.INVALID_ARGUMENT, - `Invalid query. You cannot use '${filter.op.toString()}' filters ` + - `with '${conflictingOp.toString()}' filters.` + } + + const firstOrderByField = this._query.getFirstOrderByField(); + if (firstOrderByField !== null) { + this.validateOrderByAndInequalityMatch( + filter.field, + firstOrderByField ); } + } else if (isDisjunctiveOp || isArrayOp) { + // You can have at most 1 disjunctive filter and 1 array filter. Check if + // the new filter conflicts with an existing one. + let conflictingOp: Operator | null = null; + if (isDisjunctiveOp) { + conflictingOp = this._query.findFilterOperator(disjunctiveOps); + } + if (conflictingOp === null && isArrayOp) { + conflictingOp = this._query.findFilterOperator(arrayOps); + } + if (conflictingOp !== null) { + // We special case when it's a duplicate op to give a slightly clearer error message. + if (conflictingOp === filter.op) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Invalid query. You cannot use more than one ' + + `'${filter.op.toString()}' filter.` + ); + } else { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + `Invalid query. You cannot use '${filter.op.toString()}' filters ` + + `with '${conflictingOp.toString()}' filters.` + ); + } + } } } } diff --git a/packages/firestore/src/core/query.ts b/packages/firestore/src/core/query.ts index e0540f6128b..712539139a4 100644 --- a/packages/firestore/src/core/query.ts +++ b/packages/firestore/src/core/query.ts @@ -25,11 +25,14 @@ import { arrayValueContains, valueEquals, isArray, + isNanValue, + isNullValue, isReferenceValue, typeOrder } from '../model/values'; import { FieldPath, ResourcePath } from '../model/path'; import { debugAssert, fail } from '../util/assert'; +import { Code, FirestoreError } from '../util/error'; import { isNullOrUndefined } from '../util/types'; import { canonifyTarget, @@ -71,7 +74,7 @@ export class Query { readonly path: ResourcePath, readonly collectionGroup: string | null = null, readonly explicitOrderBy: OrderBy[] = [], - readonly filters: FieldFilter[] = [], + readonly filters: Filter[] = [], readonly limit: number | null = null, readonly limitType: LimitType = LimitType.First, readonly startAt: Bound | null = null, @@ -131,10 +134,11 @@ export class Query { return this.memoizedOrderBy; } - addFilter(filter: FieldFilter): Query { + addFilter(filter: Filter): Query { debugAssert( this.getInequalityFilterField() == null || - !isInequalityFilter(filter) || + !(filter instanceof FieldFilter) || + !filter.isInequality() || filter.field.isEqual(this.getInequalityFilterField()!), 'Query must only have one inequality field.' ); @@ -325,7 +329,7 @@ export class Query { getInequalityFilterField(): FieldPath | null { for (const filter of this.filters) { - if (isInequalityFilter(filter)) { + if (filter instanceof FieldFilter && filter.isInequality()) { return filter.field; } } @@ -336,8 +340,10 @@ export class Query { // returns the first one that is, or null if none are. findFilterOperator(operators: Operator[]): Operator | null { for (const filter of this.filters) { - if (operators.indexOf(filter.op) >= 0) { - return filter.op; + if (filter instanceof FieldFilter) { + if (operators.indexOf(filter.op) >= 0) { + return filter.op; + } } } return null; @@ -435,7 +441,7 @@ export class Query { private matchesFilters(doc: Document): boolean { for (const filter of this.filters) { - if (!fieldFilterMatches(filter, doc)) { + if (!filter.matches(doc)) { return false; } } @@ -463,6 +469,10 @@ export class Query { } } +export abstract class Filter { + abstract matches(doc: Document): boolean; +} + export const enum Operator { LESS_THAN = '<', LESS_THAN_OR_EQUAL = '<=', @@ -474,136 +484,235 @@ export const enum Operator { ARRAY_CONTAINS_ANY = 'array-contains-any' } -export class FieldFilter { - constructor( +export class FieldFilter extends Filter { + protected constructor( public field: FieldPath, public op: Operator, public value: api.Value - ) {} -} - -/** Returns a debug description for `fieldFilter`. */ -export function stringifyFieldFilter(fieldFilter: FieldFilter): string { - return `${fieldFilter.field.canonicalString()} ${ - fieldFilter.op - } ${canonicalId(fieldFilter.value)}`; -} - -export function canonifyFieldFilter(fieldFilter: FieldFilter): string { - // TODO(b/29183165): Technically, this won't be unique if two values have - // the same description, such as the int 3 and the string "3". So we should - // add the types in here somehow, too. - return ( - fieldFilter.field.canonicalString() + - fieldFilter.op.toString() + - canonicalId(fieldFilter.value) - ); -} - -/** Returns whether this filter filters by <, <=, => or >. */ -export function isInequalityFilter(filter: FieldFilter): boolean { - return ( - [ - Operator.LESS_THAN, - Operator.LESS_THAN_OR_EQUAL, - Operator.GREATER_THAN, - Operator.GREATER_THAN_OR_EQUAL - ].indexOf(filter.op) >= 0 - ); -} - -function fieldFilterMatches(filter: FieldFilter, doc: Document): boolean { - const op = filter.op; - const filterValue = filter.value; + ) { + super(); + } - if (filter.field.isKeyField()) { - if (op === Operator.IN) { - // Filter that matches on key fields within an array. - debugAssert( - isArray(filterValue), - 'Comparing on key with IN, but filter value not an ArrayValue' - ); - const keys = (filterValue.arrayValue.values || []).map(v => { + /** + * Creates a filter based on the provided arguments. + */ + static create(field: FieldPath, op: Operator, value: api.Value): FieldFilter { + if (field.isKeyField()) { + if (op === Operator.IN) { debugAssert( - isReferenceValue(v), - 'Comparing on key with IN, but an array value was not a ReferenceValue' + isArray(value), + 'Comparing on key with IN, but filter value not an ArrayValue' ); - return DocumentKey.fromName(v.referenceValue); - }); - return keys.some(key => key.isEqual(doc.key)); - } else { - // Filter that matches on a single key field (i.e. '__name__'). + debugAssert( + (value.arrayValue.values || []).every(elem => isReferenceValue(elem)), + 'Comparing on key with IN, but an array value was not a RefValue' + ); + return new KeyFieldInFilter(field, value); + } else { + debugAssert( + isReferenceValue(value), + 'Comparing on key, but filter value not a RefValue' + ); + debugAssert( + op !== Operator.ARRAY_CONTAINS && op !== Operator.ARRAY_CONTAINS_ANY, + `'${op.toString()}' queries don't make sense on document keys.` + ); + return new KeyFieldFilter(field, op, value); + } + } else if (isNullValue(value)) { + if (op !== Operator.EQUAL) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Invalid query. Null supports only equality comparisons.' + ); + } + return new FieldFilter(field, op, value); + } else if (isNanValue(value)) { + if (op !== Operator.EQUAL) { + throw new FirestoreError( + Code.INVALID_ARGUMENT, + 'Invalid query. NaN supports only equality comparisons.' + ); + } + return new FieldFilter(field, op, value); + } else if (op === Operator.ARRAY_CONTAINS) { + return new ArrayContainsFilter(field, value); + } else if (op === Operator.IN) { debugAssert( - isReferenceValue(filterValue), - 'Comparing on key, but filter value not a RefValue' + isArray(value), + 'IN filter has invalid value: ' + value.toString() ); + return new InFilter(field, value); + } else if (op === Operator.ARRAY_CONTAINS_ANY) { debugAssert( - op !== Operator.ARRAY_CONTAINS && op !== Operator.ARRAY_CONTAINS_ANY, - `'${op}' queries don't make sense on document keys.` + isArray(value), + 'ARRAY_CONTAINS_ANY filter has invalid value: ' + value.toString() ); - const key = DocumentKey.fromName(filterValue.referenceValue); - const comparison = DocumentKey.comparator(doc.key, key); - return matchesComparison(filter, comparison); + return new ArrayContainsAnyFilter(field, value); + } else { + return new FieldFilter(field, op, value); } } - const fieldValue = doc.field(filter.field); - if (op === Operator.ARRAY_CONTAINS) { + matches(doc: Document): boolean { + const other = doc.field(this.field); + + // Only compare types with matching backend order (such as double and int). return ( - isArray(fieldValue) && - arrayValueContains(fieldValue.arrayValue, filter.value) - ); - } else if (op === Operator.ARRAY_CONTAINS_ANY) { - debugAssert( - isArray(filterValue), - 'ArrayContainsAnyFilter expects an ArrayValue' + other !== null && + typeOrder(this.value) === typeOrder(other) && + this.matchesComparison(valueCompare(other, this.value)) ); - if (!isArray(fieldValue) || !fieldValue.arrayValue.values) { - return false; + } + + protected matchesComparison(comparison: number): boolean { + switch (this.op) { + case Operator.LESS_THAN: + return comparison < 0; + case Operator.LESS_THAN_OR_EQUAL: + return comparison <= 0; + case Operator.EQUAL: + return comparison === 0; + case Operator.GREATER_THAN: + return comparison > 0; + case Operator.GREATER_THAN_OR_EQUAL: + return comparison >= 0; + default: + return fail('Unknown FieldFilter operator: ' + this.op); } - return fieldValue.arrayValue.values.some(val => - arrayValueContains(filter.value.arrayValue!, val) - ); - } else if (op === Operator.IN) { - debugAssert(isArray(filterValue), 'InFilter expects an ArrayValue'); - return ( - fieldValue !== null && - arrayValueContains(filter.value.arrayValue!, fieldValue) - ); - } else { - // Only compare types with matching backend order (such as double and int). + } + + isInequality(): boolean { return ( - fieldValue !== null && - typeOrder(filter.value) === typeOrder(fieldValue) && - matchesComparison(filter, valueCompare(fieldValue, filter.value)) + [ + Operator.LESS_THAN, + Operator.LESS_THAN_OR_EQUAL, + Operator.GREATER_THAN, + Operator.GREATER_THAN_OR_EQUAL + ].indexOf(this.op) >= 0 ); } } -function matchesComparison(filter: FieldFilter, comparison: number): boolean { - switch (filter.op) { - case Operator.LESS_THAN: - return comparison < 0; - case Operator.LESS_THAN_OR_EQUAL: - return comparison <= 0; - case Operator.EQUAL: - return comparison === 0; - case Operator.GREATER_THAN: - return comparison > 0; - case Operator.GREATER_THAN_OR_EQUAL: - return comparison >= 0; - default: - return fail('Unknown FieldFilter operator: ' + filter.op); - } +export function canonifyFilter(filter: Filter): string { + debugAssert( + filter instanceof FieldFilter, + 'canonifyFilter() only supports FieldFilters' + ); + // TODO(b/29183165): Technically, this won't be unique if two values have + // the same description, such as the int 3 and the string "3". So we should + // add the types in here somehow, too. + return ( + filter.field.canonicalString() + + filter.op.toString() + + canonicalId(filter.value) + ); } -export function fieldFilterEquals(f1: FieldFilter, f2: FieldFilter): boolean { +export function filterEquals(f1: Filter, f2: Filter): boolean { return ( + f1 instanceof FieldFilter && + f2 instanceof FieldFilter && f1.op === f2.op && f1.field.isEqual(f2.field) && valueEquals(f1.value, f2.value) ); } + +/** Returns a debug description for `filter`. */ +export function stringifyFilter(filter: Filter): string { + debugAssert( + filter instanceof FieldFilter, + 'stringifyFilter() only supports FieldFilters' + ); + return `${filter.field.canonicalString()} ${filter.op} ${canonicalId( + filter.value + )}`; +} + +/** Filter that matches on key fields (i.e. '__name__'). */ +export class KeyFieldFilter extends FieldFilter { + private readonly key: DocumentKey; + + constructor(field: FieldPath, op: Operator, value: api.Value) { + super(field, op, value); + debugAssert( + isReferenceValue(value), + 'KeyFieldFilter expects a ReferenceValue' + ); + this.key = DocumentKey.fromName(value.referenceValue); + } + + matches(doc: Document): boolean { + const comparison = DocumentKey.comparator(doc.key, this.key); + return this.matchesComparison(comparison); + } +} + +/** Filter that matches on key fields within an array. */ +export class KeyFieldInFilter extends FieldFilter { + private readonly keys: DocumentKey[]; + + constructor(field: FieldPath, value: api.Value) { + super(field, Operator.IN, value); + debugAssert(isArray(value), 'KeyFieldInFilter expects an ArrayValue'); + this.keys = (value.arrayValue.values || []).map(v => { + debugAssert( + isReferenceValue(v), + 'Comparing on key with IN, but an array value was not a ReferenceValue' + ); + return DocumentKey.fromName(v.referenceValue); + }); + } + + matches(doc: Document): boolean { + return this.keys.some(key => key.isEqual(doc.key)); + } +} + +/** A Filter that implements the array-contains operator. */ +export class ArrayContainsFilter extends FieldFilter { + constructor(field: FieldPath, value: api.Value) { + super(field, Operator.ARRAY_CONTAINS, value); + } + + matches(doc: Document): boolean { + const other = doc.field(this.field); + return isArray(other) && arrayValueContains(other.arrayValue, this.value); + } +} + +/** A Filter that implements the IN operator. */ +export class InFilter extends FieldFilter { + constructor(field: FieldPath, value: api.Value) { + super(field, Operator.IN, value); + debugAssert(isArray(value), 'InFilter expects an ArrayValue'); + } + + matches(doc: Document): boolean { + const other = doc.field(this.field); + return other !== null && arrayValueContains(this.value.arrayValue!, other); + } +} + +/** A Filter that implements the array-contains-any operator. */ +export class ArrayContainsAnyFilter extends FieldFilter { + constructor(field: FieldPath, value: api.Value) { + super(field, Operator.ARRAY_CONTAINS_ANY, value); + debugAssert(isArray(value), 'ArrayContainsAnyFilter expects an ArrayValue'); + } + + matches(doc: Document): boolean { + const other = doc.field(this.field); + if (!isArray(other) || !other.arrayValue.values) { + return false; + } + return other.arrayValue.values.some(val => + arrayValueContains(this.value.arrayValue!, val) + ); + } +} + /** * The direction of sorting in an order by. */ diff --git a/packages/firestore/src/core/sync_engine.ts b/packages/firestore/src/core/sync_engine.ts index 4c6845657b4..a4a289781bb 100644 --- a/packages/firestore/src/core/sync_engine.ts +++ b/packages/firestore/src/core/sync_engine.ts @@ -254,7 +254,6 @@ class SyncEngineImpl implements SyncEngine { protected activeLimboTargetsByKey = new SortedMap( DocumentKey.comparator ); - /** * Keeps track of the information about an active limbo resolution for each * active target ID that was started for the purpose of limbo resolution. diff --git a/packages/firestore/src/core/target.ts b/packages/firestore/src/core/target.ts index 524293dc88c..82908f8d58f 100644 --- a/packages/firestore/src/core/target.ts +++ b/packages/firestore/src/core/target.ts @@ -22,14 +22,14 @@ import { Bound, boundEquals, canonifyBound, - canonifyFieldFilter, - FieldFilter, - fieldFilterEquals, - stringifyFieldFilter, + canonifyFilter, + filterEquals, + stringifyFilter, OrderBy, orderByEquals, stringifyOrderBy, - canonifyOrderBy + canonifyOrderBy, + Filter } from './query'; import { debugCast } from '../util/assert'; @@ -44,7 +44,7 @@ export interface Target { readonly path: ResourcePath; readonly collectionGroup: string | null; readonly orderBy: OrderBy[]; - readonly filters: FieldFilter[]; + readonly filters: Filter[]; readonly limit: number | null; readonly startAt: Bound | null; readonly endAt: Bound | null; @@ -57,7 +57,7 @@ export class TargetImpl implements Target { readonly path: ResourcePath, readonly collectionGroup: string | null = null, readonly orderBy: OrderBy[] = [], - readonly filters: FieldFilter[] = [], + readonly filters: Filter[] = [], readonly limit: number | null = null, readonly startAt: Bound | null = null, readonly endAt: Bound | null = null @@ -76,7 +76,7 @@ export function newTarget( path: ResourcePath, collectionGroup: string | null = null, orderBy: OrderBy[] = [], - filters: FieldFilter[] = [], + filters: Filter[] = [], limit: number | null = null, startAt: Bound | null = null, endAt: Bound | null = null @@ -101,9 +101,7 @@ export function canonifyTarget(target: Target): string { canonicalId += '|cg:' + targetImpl.collectionGroup; } canonicalId += '|f:'; - canonicalId += targetImpl.filters - .map(f => canonifyFieldFilter(f)) - .join(','); + canonicalId += targetImpl.filters.map(f => canonifyFilter(f)).join(','); canonicalId += '|ob:'; canonicalId += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(','); @@ -131,7 +129,7 @@ export function stringifyTarget(target: Target): string { } if (target.filters.length > 0) { str += `, filters: [${target.filters - .map(f => stringifyFieldFilter(f)) + .map(f => stringifyFilter(f)) .join(', ')}]`; } if (!isNullOrUndefined(target.limit)) { @@ -171,7 +169,7 @@ export function targetEquals(left: Target, right: Target): boolean { } for (let i = 0; i < left.filters.length; i++) { - if (!fieldFilterEquals(left.filters[i], right.filters[i])) { + if (!filterEquals(left.filters[i], right.filters[i])) { return false; } } diff --git a/packages/firestore/src/remote/serializer.ts b/packages/firestore/src/remote/serializer.ts index 3be82256f58..3e0dd5e7182 100644 --- a/packages/firestore/src/remote/serializer.ts +++ b/packages/firestore/src/remote/serializer.ts @@ -22,6 +22,7 @@ import { Bound, Direction, FieldFilter, + Filter, LimitType, Operator, OrderBy, @@ -883,7 +884,7 @@ export function fromQueryTarget(target: api.QueryTarget): Target { } } - let filterBy: FieldFilter[] = []; + let filterBy: Filter[] = []; if (query.where) { filterBy = fromFilter(query.where); } @@ -972,7 +973,7 @@ export function toTarget( return result; } -function toFilter(filters: FieldFilter[]): api.Filter | undefined { +function toFilter(filters: Filter[]): api.Filter | undefined { if (filters.length === 0) { return; } @@ -989,7 +990,7 @@ function toFilter(filters: FieldFilter[]): api.Filter | undefined { return { compositeFilter: { op: 'AND', filters: protos } }; } -function fromFilter(filter: api.Filter | undefined): FieldFilter[] { +function fromFilter(filter: api.Filter | undefined): Filter[] { if (!filter) { return []; } else if (filter.unaryFilter !== undefined) { @@ -1103,8 +1104,8 @@ export function fromPropertyOrder(orderBy: api.Order): OrderBy { ); } -export function fromFieldFilter(filter: api.Filter): FieldFilter { - return new FieldFilter( +export function fromFieldFilter(filter: api.Filter): Filter { + return FieldFilter.create( fromFieldPathReference(filter.fieldFilter!.field!), fromOperatorName(filter.fieldFilter!.op!), filter.fieldFilter!.value! @@ -1139,14 +1140,16 @@ export function toUnaryOrFieldFilter(filter: FieldFilter): api.Filter { }; } -export function fromUnaryFilter(filter: api.Filter): FieldFilter { +export function fromUnaryFilter(filter: api.Filter): Filter { switch (filter.unaryFilter!.op!) { case 'IS_NAN': const nanField = fromFieldPathReference(filter.unaryFilter!.field!); - return new FieldFilter(nanField, Operator.EQUAL, { doubleValue: NaN }); + return FieldFilter.create(nanField, Operator.EQUAL, { + doubleValue: NaN + }); case 'IS_NULL': const nullField = fromFieldPathReference(filter.unaryFilter!.field!); - return new FieldFilter(nullField, Operator.EQUAL, { + return FieldFilter.create(nullField, Operator.EQUAL, { nullValue: 'NULL_VALUE' }); case 'OPERATOR_UNSPECIFIED': diff --git a/packages/firestore/test/unit/remote/serializer.helper.ts b/packages/firestore/test/unit/remote/serializer.helper.ts index 28fc5c5f5fe..859e08c8819 100644 --- a/packages/firestore/test/unit/remote/serializer.helper.ts +++ b/packages/firestore/test/unit/remote/serializer.helper.ts @@ -24,15 +24,19 @@ import { GeoPoint } from '../../../src/api/geo_point'; import { Timestamp } from '../../../src/api/timestamp'; import { DatabaseId } from '../../../src/core/database_info'; import { + ArrayContainsAnyFilter, + ArrayContainsFilter, Direction, FieldFilter, - fieldFilterEquals, + InFilter, + KeyFieldFilter, Operator, + filterEquals, OrderBy, Query } from '../../../src/core/query'; import { SnapshotVersion } from '../../../src/core/snapshot_version'; -import { Target, targetEquals } from '../../../src/core/target'; +import { Target, targetEquals, TargetImpl } from '../../../src/core/target'; import { TargetData, TargetPurpose } from '../../../src/local/target_data'; import { DeleteMutation, @@ -133,7 +137,7 @@ export function serializerTest( } describe('converts value', () => { - addEqualityMatcher({ equalsFn: fieldFilterEquals, forType: FieldFilter }); + addEqualityMatcher({ equalsFn: filterEquals, forType: FieldFilter }); /** * Verifies full round-trip of encoding/decoding fieldValue objects: @@ -751,11 +755,11 @@ export function serializerTest( }); describe('to/from FieldFilter', () => { - addEqualityMatcher({ equalsFn: fieldFilterEquals, forType: FieldFilter }); + addEqualityMatcher({ equalsFn: filterEquals, forType: FieldFilter }); it('makes dotted-property names', () => { const path = new FieldPath(['item', 'part', 'top']); - const input = new FieldFilter(path, Operator.EQUAL, wrap('food')); + const input = FieldFilter.create(path, Operator.EQUAL, wrap('food')); const actual = toUnaryOrFieldFilter(input); expect(actual).to.deep.equal({ fieldFilter: { @@ -844,6 +848,7 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); + expect(roundtripped).to.be.instanceof(KeyFieldFilter); }); it('converts array-contains', () => { @@ -858,6 +863,7 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); + expect(roundtripped).to.be.instanceof(ArrayContainsFilter); }); it('converts IN', () => { @@ -880,6 +886,7 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); + expect(roundtripped).to.be.instanceof(InFilter); }); it('converts array-contains-any', () => { @@ -902,11 +909,12 @@ export function serializerTest( }); const roundtripped = fromFieldFilter(actual); expect(roundtripped).to.deep.equal(input); + expect(roundtripped).to.be.instanceof(ArrayContainsAnyFilter); }); }); describe('to/from UnaryFilter', () => { - addEqualityMatcher({ equalsFn: fieldFilterEquals, forType: FieldFilter }); + addEqualityMatcher({ equalsFn: filterEquals, forType: FieldFilter }); it('converts null', () => { const input = filter('field', '==', null); @@ -957,7 +965,7 @@ export function serializerTest( }); describe('toTarget', () => { - addEqualityMatcher({ equalsFn: targetEquals, forType: Target }); + addEqualityMatcher({ equalsFn: targetEquals, forType: TargetImpl }); it('converts first-level key queries', () => { const q = Query.atPath(path('docs/1')).toTarget(); diff --git a/packages/firestore/test/unit/specs/spec_builder.ts b/packages/firestore/test/unit/specs/spec_builder.ts index baa4bba4215..af29c65f2dd 100644 --- a/packages/firestore/test/unit/specs/spec_builder.ts +++ b/packages/firestore/test/unit/specs/spec_builder.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { FieldFilter, Query } from '../../../src/core/query'; +import { FieldFilter, Filter, Query } from '../../../src/core/query'; import { canonifyTarget, Target, targetEquals } from '../../../src/core/target'; import { TargetIdGenerator } from '../../../src/core/target_id_generator'; import { TargetId } from '../../../src/core/types'; @@ -950,13 +950,17 @@ export class SpecBuilder { spec.limitType = 'LimitToLast'; } if (query.filters) { - spec.filters = query.filters.map((filter: FieldFilter) => { - // TODO(dimond): Support non-JSON primitive values? - return [ - filter.field.canonicalString(), - filter.op, - userDataWriter.convertValue(filter.value) - ] as SpecQueryFilter; + spec.filters = query.filters.map((filter: Filter) => { + if (filter instanceof FieldFilter) { + // TODO(dimond): Support non-JSON primitive values? + return [ + filter.field.canonicalString(), + filter.op, + userDataWriter.convertValue(filter.value) + ] as SpecQueryFilter; + } else { + return fail('Unknown filter: ' + filter); + } }); } if (query.explicitOrderBy) { diff --git a/packages/firestore/test/util/helpers.ts b/packages/firestore/test/util/helpers.ts index 90df0db3715..1aca63f36bd 100644 --- a/packages/firestore/test/util/helpers.ts +++ b/packages/firestore/test/util/helpers.ts @@ -208,7 +208,7 @@ export function blob(...bytes: number[]): Blob { export function filter(path: string, op: string, value: unknown): FieldFilter { const dataValue = wrap(value); const operator = op as Operator; - return new FieldFilter(field(path), operator, dataValue); + return FieldFilter.create(field(path), operator, dataValue); } export function setMutation( From 8d55c93e61429e2dce89350064b97935181f4e43 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 29 Jun 2020 10:48:40 -0700 Subject: [PATCH 5/6] Re-gen deps --- .../firestore/lite/test/dependencies.json | 209 ++++++++++++------ 1 file changed, 147 insertions(+), 62 deletions(-) diff --git a/packages/firestore/lite/test/dependencies.json b/packages/firestore/lite/test/dependencies.json index 1ce12b47faa..df88ad5b687 100644 --- a/packages/firestore/lite/test/dependencies.json +++ b/packages/firestore/lite/test/dependencies.json @@ -73,7 +73,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -99,12 +99,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -118,7 +117,6 @@ "isArray", "isDocumentTarget", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -136,7 +134,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -160,7 +157,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -187,6 +184,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -210,12 +209,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -245,7 +248,7 @@ ], "variables": [] }, - "sizeInBytes": 97605 + "sizeInBytes": 99162 }, "DocumentReference": { "dependencies": { @@ -586,10 +589,18 @@ "functions": [ "argToString", "arrayEquals", + "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", "cast", + "compareArrays", + "compareBlobs", + "compareGeoPoints", + "compareMaps", + "compareNumbers", + "compareReferences", + "compareTimestamps", "createError", "createMetadata", "debugAssert", @@ -612,13 +623,14 @@ "getPreviousValue", "hardAssert", "invalidClassError", + "isArray", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullValue", "isPlainObject", + "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -667,10 +679,13 @@ "validatePlainObject", "validatePositiveNumber", "validateType", + "valueCompare", "valueDescription", "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -693,12 +708,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -726,7 +745,7 @@ ], "variables": [] }, - "sizeInBytes": 78685 + "sizeInBytes": 85476 }, "QueryDocumentSnapshot": { "dependencies": { @@ -1046,7 +1065,7 @@ ], "variables": [] }, - "sizeInBytes": 69580 + "sizeInBytes": 69588 }, "WriteBatch": { "dependencies": { @@ -1201,7 +1220,7 @@ ], "variables": [] }, - "sizeInBytes": 72504 + "sizeInBytes": 72512 }, "addDoc": { "dependencies": { @@ -1219,7 +1238,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -1247,12 +1266,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -1269,7 +1287,6 @@ "isDocumentTarget", "isDouble", "isEmpty", - "isInequalityFilter", "isInteger", "isMapValue", "isNanValue", @@ -1289,7 +1306,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -1315,7 +1331,7 @@ "registerFirestore", "serverTimestamp", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -1350,6 +1366,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "ArrayRemoveTransformOperation", "ArrayUnionTransformOperation", "AutoId", @@ -1377,12 +1395,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "NoDocument", @@ -1416,7 +1438,7 @@ ], "variables": [] }, - "sizeInBytes": 107956 + "sizeInBytes": 109513 }, "arrayRemove": { "dependencies": { @@ -1654,7 +1676,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -1681,12 +1703,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -1700,7 +1721,6 @@ "isArray", "isDocumentTarget", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -1718,7 +1738,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -1742,7 +1761,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -1770,6 +1789,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -1793,12 +1814,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -1828,7 +1853,7 @@ ], "variables": [] }, - "sizeInBytes": 98233 + "sizeInBytes": 99790 }, "collectionGroup": { "dependencies": { @@ -1844,7 +1869,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -1871,12 +1896,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -1890,7 +1914,6 @@ "isArray", "isDocumentTarget", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -1908,7 +1931,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -1932,7 +1954,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -1959,6 +1981,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -1981,12 +2005,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -2016,7 +2044,7 @@ ], "variables": [] }, - "sizeInBytes": 97665 + "sizeInBytes": 99222 }, "deleteDoc": { "dependencies": { @@ -2191,7 +2219,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -2218,12 +2246,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -2237,7 +2264,6 @@ "isArray", "isDocumentTarget", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -2255,7 +2281,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -2280,7 +2305,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -2308,6 +2333,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "AutoId", "BaseFieldPath", "BasePath", @@ -2332,12 +2359,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -2367,7 +2398,7 @@ ], "variables": [] }, - "sizeInBytes": 99054 + "sizeInBytes": 100611 }, "documentId": { "dependencies": { @@ -2596,10 +2627,18 @@ "functions": [ "argToString", "arrayEquals", + "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", "cast", + "compareArrays", + "compareBlobs", + "compareGeoPoints", + "compareMaps", + "compareNumbers", + "compareReferences", + "compareTimestamps", "createError", "createMetadata", "debugAssert", @@ -2631,14 +2670,15 @@ "hardAssert", "invalidClassError", "invokeRunQueryRpc", + "isArray", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullOrUndefined", "isNullValue", "isPlainObject", + "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -2698,10 +2738,13 @@ "validatePlainObject", "validatePositiveNumber", "validateType", + "valueCompare", "valueDescription", "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -2724,12 +2767,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -2758,7 +2805,7 @@ ], "variables": [] }, - "sizeInBytes": 84444 + "sizeInBytes": 91235 }, "increment": { "dependencies": { @@ -2930,7 +2977,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -2956,12 +3003,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -2975,7 +3021,6 @@ "isArray", "isDocumentTarget", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -2993,7 +3038,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -3018,7 +3062,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -3045,6 +3089,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3068,12 +3114,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -3103,17 +3153,25 @@ ], "variables": [] }, - "sizeInBytes": 97948 + "sizeInBytes": 99505 }, "queryEqual": { "dependencies": { "functions": [ "argToString", "arrayEquals", + "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", "cast", + "compareArrays", + "compareBlobs", + "compareGeoPoints", + "compareMaps", + "compareNumbers", + "compareReferences", + "compareTimestamps", "createError", "createMetadata", "debugAssert", @@ -3136,13 +3194,14 @@ "getPreviousValue", "hardAssert", "invalidClassError", + "isArray", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullValue", "isPlainObject", + "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -3192,10 +3251,13 @@ "validatePlainObject", "validatePositiveNumber", "validateType", + "valueCompare", "valueDescription", "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3218,12 +3280,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -3251,7 +3317,7 @@ ], "variables": [] }, - "sizeInBytes": 78889 + "sizeInBytes": 85680 }, "refEqual": { "dependencies": { @@ -3267,7 +3333,7 @@ "canonifyArray", "canonifyBound", "canonifyByteString", - "canonifyFieldFilter", + "canonifyFilter", "canonifyGeoPoint", "canonifyMap", "canonifyOrderBy", @@ -3293,12 +3359,11 @@ "encodeBase64", "errorMessage", "fail", - "fieldFilterEquals", - "fieldFilterMatches", "fieldMaskContains", "fieldPathFromArgument", "fieldPathFromArgument$1", "fieldPathFromDotSeparatedString", + "filterEquals", "forEach", "formatJSON", "formatPlural", @@ -3312,7 +3377,6 @@ "isArray", "isDocumentTarget", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", @@ -3330,7 +3394,6 @@ "logWarn", "looksLikeJsonObject", "mapCodeFromRpcCode", - "matchesComparison", "newConnection", "newDatastore", "newSerializer", @@ -3355,7 +3418,7 @@ "refValue", "registerFirestore", "sortsBeforeDocument", - "stringifyFieldFilter", + "stringifyFilter", "stringifyOrderBy", "stringifyTarget", "targetEquals", @@ -3382,6 +3445,8 @@ "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3405,12 +3470,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -3440,7 +3509,7 @@ ], "variables": [] }, - "sizeInBytes": 97890 + "sizeInBytes": 99447 }, "runTransaction": { "dependencies": { @@ -3623,7 +3692,7 @@ ], "variables": [] }, - "sizeInBytes": 91955 + "sizeInBytes": 91963 }, "serverTimestamp": { "dependencies": { @@ -3828,7 +3897,7 @@ ], "variables": [] }, - "sizeInBytes": 70793 + "sizeInBytes": 70801 }, "setLogLevel": { "dependencies": { @@ -3880,10 +3949,18 @@ "functions": [ "argToString", "arrayEquals", + "arrayValueContains", "assertUint8ArrayAvailable", "binaryStringFromUint8Array", "blobEquals", "cast", + "compareArrays", + "compareBlobs", + "compareGeoPoints", + "compareMaps", + "compareNumbers", + "compareReferences", + "compareTimestamps", "createError", "createMetadata", "debugAssert", @@ -3906,13 +3983,14 @@ "getPreviousValue", "hardAssert", "invalidClassError", + "isArray", "isEmpty", - "isInequalityFilter", "isMapValue", "isNanValue", "isNegativeZero", "isNullValue", "isPlainObject", + "isReferenceValue", "isSafeInteger", "isServerTimestamp", "isValidResourceName", @@ -3963,10 +4041,13 @@ "validatePlainObject", "validatePositiveNumber", "validateType", + "valueCompare", "valueDescription", "valueEquals" ], "classes": [ + "ArrayContainsAnyFilter", + "ArrayContainsFilter", "BaseFieldPath", "BasePath", "BaseQuery", @@ -3989,12 +4070,16 @@ "FieldPath", "FieldPath$1", "FieldPath$2", + "Filter", "FirebaseCredentialsProvider", "Firestore", "FirestoreError", "GeoPoint", "GrpcConnection", + "InFilter", "JsonProtoSerializer", + "KeyFieldFilter", + "KeyFieldInFilter", "MaybeDocument", "Mutation", "OAuthToken", @@ -4023,7 +4108,7 @@ ], "variables": [] }, - "sizeInBytes": 79622 + "sizeInBytes": 86413 }, "terminate": { "dependencies": { @@ -4376,6 +4461,6 @@ ], "variables": [] }, - "sizeInBytes": 72584 + "sizeInBytes": 72592 } } \ No newline at end of file From 526cddf36dadcb721a4b892fece1e2ccb97fa003 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 29 Jun 2020 10:57:42 -0700 Subject: [PATCH 6/6] Create nice-seas-type.md --- .changeset/nice-seas-type.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/nice-seas-type.md diff --git a/.changeset/nice-seas-type.md b/.changeset/nice-seas-type.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/nice-seas-type.md @@ -0,0 +1,2 @@ +--- +---