From 4a09512b9ad44e2f39725ec065875614f56cd5dd Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Fri, 22 Mar 2024 11:28:37 -0400 Subject: [PATCH 1/8] parse timestamp, migrate schema --- .../src/index/firestore_index_value_writer.ts | 20 +++++++++++++------ .../firestore/src/local/indexeddb_schema.ts | 3 ++- .../src/local/indexeddb_schema_converter.ts | 13 ++++++++++++ .../src/local/indexeddb_sentinels.ts | 5 ++++- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index 9bbcd6fcc63..b50fc3f422e 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -15,8 +15,13 @@ * limitations under the License. */ +import { Timestamp } from '../api'; import { DocumentKey } from '../model/document_key'; -import { normalizeByteString, normalizeNumber } from '../model/normalize'; +import { + normalizeByteString, + normalizeNumber, + normalizeTimestamp +} from '../model/normalize'; import { isMaxValue } from '../model/values'; import { ArrayValue, MapValue, Value } from '../protos/firestore_proto_api'; import { fail } from '../util/assert'; @@ -93,14 +98,17 @@ export class FirestoreIndexValueWriter { } } } else if ('timestampValue' in indexValue) { - const timestamp = indexValue.timestampValue!; + let timestamp = indexValue.timestampValue!; this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP); if (typeof timestamp === 'string') { - encoder.writeString(timestamp); - } else { - encoder.writeString(`${timestamp.seconds || ''}`); - encoder.writeNumber(timestamp.nanos || 0); + const normalizedValue = normalizeTimestamp(timestamp); + timestamp = new Timestamp( + normalizedValue.seconds, + normalizedValue.nanos + ); } + encoder.writeString(`${timestamp.seconds || ''}`); + encoder.writeNumber(timestamp.nanos || 0); } else if ('stringValue' in indexValue) { this.writeIndexString(indexValue.stringValue!, encoder); this.writeTruncationMarker(encoder); diff --git a/packages/firestore/src/local/indexeddb_schema.ts b/packages/firestore/src/local/indexeddb_schema.ts index c4373f3881e..b832f29a035 100644 --- a/packages/firestore/src/local/indexeddb_schema.ts +++ b/packages/firestore/src/local/indexeddb_schema.ts @@ -51,9 +51,10 @@ import { DbTimestampKey } from './indexeddb_sentinels'; * document lookup via `getAll()`. * 14. Add overlays. * 15. Add indexing support. + * 16. Parse timestamp strings before creating index entries. */ -export const SCHEMA_VERSION = 15; +export const SCHEMA_VERSION = 16; /** * Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects. diff --git a/packages/firestore/src/local/indexeddb_schema_converter.ts b/packages/firestore/src/local/indexeddb_schema_converter.ts index 3509db11a14..b65a63a627f 100644 --- a/packages/firestore/src/local/indexeddb_schema_converter.ts +++ b/packages/firestore/src/local/indexeddb_schema_converter.ts @@ -256,6 +256,19 @@ export class SchemaConverter implements SimpleDbSchemaConverter { p = p.next(() => createFieldIndex(db)); } + if (fromVersion < 16 && toVersion >= 16) { + // Clear the object stores to remove possibly corrupted index entries + p = p + .next(() => { + const indexStateStore = txn.objectStore(DbIndexStateStore); + indexStateStore.clear(); + }) + .next(() => { + const indexEntryStore = txn.objectStore(DbIndexEntryStore); + indexEntryStore.clear(); + }); + } + return p; } diff --git a/packages/firestore/src/local/indexeddb_sentinels.ts b/packages/firestore/src/local/indexeddb_sentinels.ts index f0b92c1224a..854b146ed7f 100644 --- a/packages/firestore/src/local/indexeddb_sentinels.ts +++ b/packages/firestore/src/local/indexeddb_sentinels.ts @@ -414,6 +414,7 @@ export const V15_STORES = [ DbIndexStateStore, DbIndexEntryStore ]; +export const V16_STORES = V15_STORES; /** * The list of all default IndexedDB stores used throughout the SDK. This is @@ -424,7 +425,9 @@ export const ALL_STORES = V12_STORES; /** Returns the object stores for the provided schema. */ export function getObjectStores(schemaVersion: number): string[] { - if (schemaVersion === 15) { + if (schemaVersion === 16) { + return V16_STORES; + } else if (schemaVersion === 15) { return V15_STORES; } else if (schemaVersion === 14) { return V14_STORES; From 075a99e7438bea343676e001845d3cd281311217 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:36:28 -0400 Subject: [PATCH 2/8] resolve circular dependency --- packages/firestore/src/index/firestore_index_value_writer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index b50fc3f422e..5a2bf8663b3 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { Timestamp } from '../api'; +import { Timestamp } from '../api/timestamp'; import { DocumentKey } from '../model/document_key'; import { normalizeByteString, From 59bf9886dd7d864d6e74df5a13873397093e00f6 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:39:46 -0400 Subject: [PATCH 3/8] add unit tests --- .../src/index/firestore_index_value_writer.ts | 7 +- .../firestore_index_value_writer.test.ts | 92 +++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 packages/firestore/test/unit/index/firestore_index_value_writer.test.ts diff --git a/packages/firestore/src/index/firestore_index_value_writer.ts b/packages/firestore/src/index/firestore_index_value_writer.ts index 5a2bf8663b3..9d5d86ab640 100644 --- a/packages/firestore/src/index/firestore_index_value_writer.ts +++ b/packages/firestore/src/index/firestore_index_value_writer.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import { Timestamp } from '../api/timestamp'; import { DocumentKey } from '../model/document_key'; import { normalizeByteString, @@ -101,11 +100,7 @@ export class FirestoreIndexValueWriter { let timestamp = indexValue.timestampValue!; this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP); if (typeof timestamp === 'string') { - const normalizedValue = normalizeTimestamp(timestamp); - timestamp = new Timestamp( - normalizedValue.seconds, - normalizedValue.nanos - ); + timestamp = normalizeTimestamp(timestamp); } encoder.writeString(`${timestamp.seconds || ''}`); encoder.writeNumber(timestamp.nanos || 0); diff --git a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts new file mode 100644 index 00000000000..de28ac1da0d --- /dev/null +++ b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0x00 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0x00 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect } from 'chai'; + +import { FirestoreIndexValueWriter } from '../../../src/index/firestore_index_value_writer'; +import { IndexByteEncoder } from '../../../src/index/index_byte_encoder'; +import { Timestamp } from '../../../src/lite-api/timestamp'; +import { IndexKind } from '../../../src/model/field_index'; +import type { Value } from '../../../src/protos/firestore_proto_api'; +import { toTimestamp } from '../../../src/remote/serializer'; +import { JSON_SERIALIZER } from '../local/persistence_test_helpers'; + +describe('Firestore Index Value Writer', () => { + describe('can gracefully handle different format of timestamp', () => { + function compareIndexValues( + value1: Value, + value2: Value, + direction: IndexKind + ): void { + const encoder1 = new IndexByteEncoder(); + const encoder2 = new IndexByteEncoder(); + FirestoreIndexValueWriter.INSTANCE.writeIndexValue( + value1, + encoder1.forKind(direction) + ); + + FirestoreIndexValueWriter.INSTANCE.writeIndexValue( + value2, + encoder2.forKind(direction) + ); + expect(encoder1.encodedBytes()).to.deep.equal(encoder2.encodedBytes()); + } + + it('can handle different format of timestamp', () => { + const value1 = { timestampValue: '2016-01-02T10:20:50.850Z' }; + const value2 = { timestampValue: '2016-01-02T10:20:50.850000Z' }; + const value3 = { timestampValue: '2016-01-02T10:20:50.850000000Z' }; + const value4 = { + timestampValue: { seconds: 1451730050, nanos: 850000000 } + }; + const value5 = { + timestampValue: toTimestamp( + JSON_SERIALIZER, + new Timestamp(1451730050, 850000000) + ) + }; + compareIndexValues(value1, value2, IndexKind.ASCENDING); + compareIndexValues(value1, value3, IndexKind.ASCENDING); + compareIndexValues(value1, value4, IndexKind.ASCENDING); + compareIndexValues(value1, value5, IndexKind.ASCENDING); + + compareIndexValues(value1, value2, IndexKind.DESCENDING); + compareIndexValues(value1, value3, IndexKind.DESCENDING); + compareIndexValues(value1, value4, IndexKind.DESCENDING); + compareIndexValues(value1, value5, IndexKind.DESCENDING); + }); + + it('can handle timestamps with 0 nanoseconds', () => { + const value1 = { timestampValue: '2016-01-02T10:20:50Z' }; + const value2 = { timestampValue: '2016-01-02T10:20:50.000000000Z' }; + const value3 = { timestampValue: { seconds: 1451730050, nanos: 0 } }; + const value4 = { + timestampValue: toTimestamp( + JSON_SERIALIZER, + new Timestamp(1451730050, 0) + ) + }; + + compareIndexValues(value1, value2, IndexKind.ASCENDING); + compareIndexValues(value1, value3, IndexKind.ASCENDING); + compareIndexValues(value1, value4, IndexKind.ASCENDING); + + compareIndexValues(value1, value2, IndexKind.DESCENDING); + compareIndexValues(value1, value3, IndexKind.DESCENDING); + compareIndexValues(value1, value4, IndexKind.DESCENDING); + }); + }); +}); From a6505ce5c13bac85b022fb81100bdbbff6be9b9b Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Mon, 25 Mar 2024 15:11:41 -0400 Subject: [PATCH 4/8] prettier --- .../firestore_index_value_writer.test.ts | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts index de28ac1da0d..52b2531adab 100644 --- a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts +++ b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts @@ -25,26 +25,26 @@ import { toTimestamp } from '../../../src/remote/serializer'; import { JSON_SERIALIZER } from '../local/persistence_test_helpers'; describe('Firestore Index Value Writer', () => { - describe('can gracefully handle different format of timestamp', () => { - function compareIndexValues( - value1: Value, - value2: Value, - direction: IndexKind - ): void { - const encoder1 = new IndexByteEncoder(); - const encoder2 = new IndexByteEncoder(); - FirestoreIndexValueWriter.INSTANCE.writeIndexValue( - value1, - encoder1.forKind(direction) - ); + function compareIndexValues( + value1: Value, + value2: Value, + direction: IndexKind + ): void { + const encoder1 = new IndexByteEncoder(); + const encoder2 = new IndexByteEncoder(); + FirestoreIndexValueWriter.INSTANCE.writeIndexValue( + value1, + encoder1.forKind(direction) + ); - FirestoreIndexValueWriter.INSTANCE.writeIndexValue( - value2, - encoder2.forKind(direction) - ); - expect(encoder1.encodedBytes()).to.deep.equal(encoder2.encodedBytes()); - } + FirestoreIndexValueWriter.INSTANCE.writeIndexValue( + value2, + encoder2.forKind(direction) + ); + expect(encoder1.encodedBytes()).to.deep.equal(encoder2.encodedBytes()); + } + describe('can gracefully handle different format of timestamp', () => { it('can handle different format of timestamp', () => { const value1 = { timestampValue: '2016-01-02T10:20:50.850Z' }; const value2 = { timestampValue: '2016-01-02T10:20:50.850000Z' }; @@ -79,7 +79,6 @@ describe('Firestore Index Value Writer', () => { new Timestamp(1451730050, 0) ) }; - compareIndexValues(value1, value2, IndexKind.ASCENDING); compareIndexValues(value1, value3, IndexKind.ASCENDING); compareIndexValues(value1, value4, IndexKind.ASCENDING); From e0e4e9415ec18fa9511bd824d29a73a8d183dc98 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:15:21 -0400 Subject: [PATCH 5/8] resolve comments --- .../firestore_index_value_writer.test.ts | 116 +++++++++++++++--- .../unit/index/ordered_code_writer.test.ts | 2 +- 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts index 52b2531adab..8f7cd7c973f 100644 --- a/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts +++ b/packages/firestore/test/unit/index/firestore_index_value_writer.test.ts @@ -24,12 +24,14 @@ import type { Value } from '../../../src/protos/firestore_proto_api'; import { toTimestamp } from '../../../src/remote/serializer'; import { JSON_SERIALIZER } from '../local/persistence_test_helpers'; +import { compare } from './ordered_code_writer.test'; + describe('Firestore Index Value Writer', () => { - function compareIndexValues( + function compareIndexEncodedValues( value1: Value, value2: Value, direction: IndexKind - ): void { + ): number { const encoder1 = new IndexByteEncoder(); const encoder2 = new IndexByteEncoder(); FirestoreIndexValueWriter.INSTANCE.writeIndexValue( @@ -41,7 +43,8 @@ describe('Firestore Index Value Writer', () => { value2, encoder2.forKind(direction) ); - expect(encoder1.encodedBytes()).to.deep.equal(encoder2.encodedBytes()); + + return compare(encoder1.encodedBytes(), encoder2.encodedBytes()); } describe('can gracefully handle different format of timestamp', () => { @@ -58,15 +61,31 @@ describe('Firestore Index Value Writer', () => { new Timestamp(1451730050, 850000000) ) }; - compareIndexValues(value1, value2, IndexKind.ASCENDING); - compareIndexValues(value1, value3, IndexKind.ASCENDING); - compareIndexValues(value1, value4, IndexKind.ASCENDING); - compareIndexValues(value1, value5, IndexKind.ASCENDING); + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value5, IndexKind.ASCENDING) + ).to.equal(0); - compareIndexValues(value1, value2, IndexKind.DESCENDING); - compareIndexValues(value1, value3, IndexKind.DESCENDING); - compareIndexValues(value1, value4, IndexKind.DESCENDING); - compareIndexValues(value1, value5, IndexKind.DESCENDING); + expect( + compareIndexEncodedValues(value1, value2, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value5, IndexKind.DESCENDING) + ).to.equal(0); }); it('can handle timestamps with 0 nanoseconds', () => { @@ -79,13 +98,76 @@ describe('Firestore Index Value Writer', () => { new Timestamp(1451730050, 0) ) }; - compareIndexValues(value1, value2, IndexKind.ASCENDING); - compareIndexValues(value1, value3, IndexKind.ASCENDING); - compareIndexValues(value1, value4, IndexKind.ASCENDING); + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.ASCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.ASCENDING) + ).to.equal(0); + + expect( + compareIndexEncodedValues(value1, value2, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.DESCENDING) + ).to.equal(0); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.DESCENDING) + ).to.equal(0); + }); + + it('can compare timestamps with different formats', () => { + const value1 = { timestampValue: '2016-01-02T10:20:50Z' }; + const value2 = { timestampValue: '2016-01-02T10:20:50.000001Z' }; + const value3 = { + timestampValue: { seconds: 1451730050, nanos: 999999999 } + }; + const value4 = { + timestampValue: toTimestamp( + JSON_SERIALIZER, + new Timestamp(1451730050, 1) + ) + }; + expect( + compareIndexEncodedValues(value1, value2, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.ASCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value2, value4, IndexKind.ASCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value3, value4, IndexKind.ASCENDING) + ).to.equal(1); - compareIndexValues(value1, value2, IndexKind.DESCENDING); - compareIndexValues(value1, value3, IndexKind.DESCENDING); - compareIndexValues(value1, value4, IndexKind.DESCENDING); + expect( + compareIndexEncodedValues(value1, value2, IndexKind.DESCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value3, IndexKind.DESCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value1, value4, IndexKind.DESCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value2, value3, IndexKind.DESCENDING) + ).to.equal(1); + expect( + compareIndexEncodedValues(value2, value4, IndexKind.DESCENDING) + ).to.equal(-1); + expect( + compareIndexEncodedValues(value3, value4, IndexKind.DESCENDING) + ).to.equal(-1); }); }); }); diff --git a/packages/firestore/test/unit/index/ordered_code_writer.test.ts b/packages/firestore/test/unit/index/ordered_code_writer.test.ts index b3f58987f49..6d87ddb4849 100644 --- a/packages/firestore/test/unit/index/ordered_code_writer.test.ts +++ b/packages/firestore/test/unit/index/ordered_code_writer.test.ts @@ -226,7 +226,7 @@ function fromHex(hexString: string): Uint8Array { return bytes; } -function compare(left: Uint8Array, right: Uint8Array): number { +export function compare(left: Uint8Array, right: Uint8Array): number { for (let i = 0; i < Math.min(left.length, right.length); ++i) { if (left[i] < right[i]) { return -1; From 555314e9ef5c8d8b2d9a049ecac20d5b9fe8d781 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Mar 2024 12:00:04 -0400 Subject: [PATCH 6/8] add index manager test --- .../test/unit/local/index_manager.test.ts | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/packages/firestore/test/unit/local/index_manager.test.ts b/packages/firestore/test/unit/local/index_manager.test.ts index 0788dcabbde..5d27fcc257f 100644 --- a/packages/firestore/test/unit/local/index_manager.test.ts +++ b/packages/firestore/test/unit/local/index_manager.test.ts @@ -30,6 +30,7 @@ import { queryWithLimit, queryWithStartAt } from '../../../src/core/query'; +import { Timestamp } from '../../../src/lite-api/timestamp'; import { displayNameForIndexType, IndexType @@ -37,7 +38,7 @@ import { import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; import { Persistence } from '../../../src/local/persistence'; import { documentMap } from '../../../src/model/collections'; -import { Document } from '../../../src/model/document'; +import { Document, MutableDocument } from '../../../src/model/document'; import { IndexKind, IndexOffset, @@ -45,6 +46,18 @@ import { } from '../../../src/model/field_index'; import { JsonObject } from '../../../src/model/object_value'; import { canonicalId } from '../../../src/model/values'; +import { + ApiClientObjectMap as ProtoObjectMap, + Document as ProtoDocument, + Value as ProtoValue +} from '../../../src/protos/firestore_proto_api'; +import { + fromDocument, + JsonProtoSerializer, + toName, + toTimestamp, + toVersion +} from '../../../src/remote/serializer'; import { addEqualityMatcher } from '../../util/equality_matcher'; import { bound, @@ -95,6 +108,11 @@ describe('IndexedDbIndexManager', async () => { } let persistencePromise: Promise; + const serializer = new JsonProtoSerializer( + persistenceHelpers.TEST_DATABASE_ID, + /* useProto3Json= */ true + ); + beforeEach(async () => { persistencePromise = persistenceHelpers.testIndexedDbPersistence(); }); @@ -1026,6 +1044,62 @@ describe('IndexedDbIndexManager', async () => { await verifyResults(testingQuery, 'coll/val3'); }); + it('can index timestamp fields of different format', async () => { + await indexManager.addFieldIndex( + fieldIndex('coll', { fields: [['date', IndexKind.ASCENDING]] }) + ); + + await addDocFromProto('coll/val1', { + 'date': { timestampValue: '2016-01-02T10:20:50Z' } + }); + await addDocFromProto('coll/val2', { + 'date': { timestampValue: '2016-01-02T10:20:50.000000000Z' } + }); + await addDocFromProto('coll/val3', { + 'date': { timestampValue: '2016-01-02T10:20:50.850Z' } + }); + await addDocFromProto('coll/val4', { + 'date': { timestampValue: '2016-01-02T10:20:50.850000000Z' } + }); + await addDocFromProto('coll/val5', { + 'date': { timestampValue: { seconds: 1451730050, nanos: 999999999 } } + }); + await addDocFromProto('coll/val6', { + 'date': { + timestampValue: toTimestamp(serializer, new Timestamp(1451730050, 1)) + } + }); + + let q = queryWithAddedOrderBy(query('coll'), orderBy('date')); + await verifyResults( + q, + 'coll/val1', + 'coll/val2', + 'coll/val6', + 'coll/val3', + 'coll/val4', + 'coll/val5' + ); + + q = queryWithAddedFilter( + query('coll'), + filter('date', '==', new Timestamp(1451730050, 850000000)) + ); + await verifyResults(q, 'coll/val3', 'coll/val4'); + + q = queryWithAddedFilter( + query('coll'), + filter('date', '>=', new Timestamp(1451730050, 850000000)) + ); + await verifyResults(q, 'coll/val3', 'coll/val4', 'coll/val5'); + + q = queryWithAddedFilter( + query('coll'), + filter('date', '>', new Timestamp(1451730050, 0)) + ); + await verifyResults(q, 'coll/val6', 'coll/val3', 'coll/val4', 'coll/val5'); + }); + it('support advances queries', async () => { // This test compares local query results with those received from the Java // Server SDK. @@ -1828,6 +1902,31 @@ describe('IndexedDbIndexManager', async () => { return addDocs(doc(key, 1, data)); } + function addDocFromProto( + key: string, + data: ProtoObjectMap | undefined + ): Promise { + return addDocs(docFromProto(key, 1, data)); + } + + function docFromProto( + keyStr: string, + versionNumber: number, + data: ProtoObjectMap | undefined + ): MutableDocument { + const proto: ProtoDocument = { + name: toName(serializer, key(keyStr)), + fields: data, + updateTime: toVersion(serializer, version(versionNumber)), + createTime: toVersion(serializer, version(versionNumber)) + }; + return fromDocument( + serializer, + proto, + /* hasCommittedMutations= */ undefined + ); + } + async function verifyResults(query: Query, ...keys: string[]): Promise { const target = queryToTarget(query); const actualResults = await indexManager.getDocumentsMatchingTarget(target); From 697062a77d212572c6cf4991d3c38415b0b5a48f Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:33:54 -0400 Subject: [PATCH 7/8] add changeset --- .changeset/witty-stingrays-repair.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/witty-stingrays-repair.md diff --git a/.changeset/witty-stingrays-repair.md b/.changeset/witty-stingrays-repair.md new file mode 100644 index 00000000000..b6815d6adcf --- /dev/null +++ b/.changeset/witty-stingrays-repair.md @@ -0,0 +1,6 @@ +--- +'@firebase/firestore': patch +'firebase':patch +--- + +Fixed the CSI issue where indexing on timestamp fields leads to incorrect query results. From 7fb399bc6bbd2605f9fcc8111c5a588235aca895 Mon Sep 17 00:00:00 2001 From: milaGGL <107142260+milaGGL@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:11:09 -0400 Subject: [PATCH 8/8] update changeset --- .../{witty-stingrays-repair.md => friendly-eyes-destroy.md} | 1 - 1 file changed, 1 deletion(-) rename .changeset/{witty-stingrays-repair.md => friendly-eyes-destroy.md} (88%) diff --git a/.changeset/witty-stingrays-repair.md b/.changeset/friendly-eyes-destroy.md similarity index 88% rename from .changeset/witty-stingrays-repair.md rename to .changeset/friendly-eyes-destroy.md index b6815d6adcf..5c805043700 100644 --- a/.changeset/witty-stingrays-repair.md +++ b/.changeset/friendly-eyes-destroy.md @@ -1,6 +1,5 @@ --- '@firebase/firestore': patch -'firebase':patch --- Fixed the CSI issue where indexing on timestamp fields leads to incorrect query results.