@firebase/firestore 3.4.6 → 3.4.7-2022222232915

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/firestore/src/core/bundle.d.ts +2 -2
  3. package/dist/firestore/src/core/bundle_impl.d.ts +2 -0
  4. package/dist/firestore/src/core/query.d.ts +7 -0
  5. package/dist/firestore/src/core/snapshot_version.d.ts +1 -0
  6. package/dist/firestore/src/core/sync_engine_impl.d.ts +1 -1
  7. package/dist/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
  8. package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +0 -17
  9. package/dist/firestore/src/local/indexeddb_schema.d.ts +16 -13
  10. package/dist/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
  11. package/dist/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
  12. package/dist/firestore/src/local/indexeddb_sentinels.d.ts +22 -17
  13. package/dist/firestore/src/local/local_documents_view.d.ts +3 -4
  14. package/dist/firestore/src/local/local_serializer.d.ts +2 -1
  15. package/dist/firestore/src/local/local_store_impl.d.ts +1 -7
  16. package/dist/firestore/src/local/remote_document_cache.d.ts +13 -4
  17. package/dist/firestore/src/local/shared_client_state.d.ts +6 -3
  18. package/dist/firestore/src/local/shared_client_state_schema.d.ts +1 -1
  19. package/dist/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
  20. package/dist/firestore/src/model/field_index.d.ts +8 -1
  21. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -1
  22. package/dist/index.esm2017.js +3206 -3134
  23. package/dist/index.esm2017.js.map +1 -1
  24. package/dist/index.esm5.js +3579 -3453
  25. package/dist/index.esm5.js.map +1 -1
  26. package/dist/index.node.cjs.js +403 -244
  27. package/dist/index.node.cjs.js.map +1 -1
  28. package/dist/index.node.mjs +403 -244
  29. package/dist/index.node.mjs.map +1 -1
  30. package/dist/index.rn.js +3160 -3088
  31. package/dist/index.rn.js.map +1 -1
  32. package/dist/internal.d.ts +19 -5
  33. package/dist/lite/firestore/src/core/bundle.d.ts +2 -2
  34. package/dist/lite/firestore/src/core/bundle_impl.d.ts +2 -0
  35. package/dist/lite/firestore/src/core/query.d.ts +7 -0
  36. package/dist/lite/firestore/src/core/snapshot_version.d.ts +1 -0
  37. package/dist/lite/firestore/src/core/sync_engine_impl.d.ts +1 -1
  38. package/dist/lite/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
  39. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +0 -17
  40. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +16 -13
  41. package/dist/lite/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
  42. package/dist/lite/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
  43. package/dist/lite/firestore/src/local/indexeddb_sentinels.d.ts +22 -17
  44. package/dist/lite/firestore/src/local/local_documents_view.d.ts +3 -4
  45. package/dist/lite/firestore/src/local/local_serializer.d.ts +2 -1
  46. package/dist/lite/firestore/src/local/local_store_impl.d.ts +1 -7
  47. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +13 -4
  48. package/dist/lite/firestore/src/local/shared_client_state.d.ts +6 -3
  49. package/dist/lite/firestore/src/local/shared_client_state_schema.d.ts +1 -1
  50. package/dist/lite/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
  51. package/dist/lite/firestore/src/model/field_index.d.ts +8 -1
  52. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -1
  53. package/dist/lite/index.browser.esm2017.js +5 -2
  54. package/dist/lite/index.browser.esm2017.js.map +1 -1
  55. package/dist/lite/index.browser.esm5.js +94 -92
  56. package/dist/lite/index.browser.esm5.js.map +1 -1
  57. package/dist/lite/index.node.cjs.js +5 -2
  58. package/dist/lite/index.node.cjs.js.map +1 -1
  59. package/dist/lite/index.node.mjs +5 -2
  60. package/dist/lite/index.node.mjs.map +1 -1
  61. package/dist/lite/index.rn.esm2017.js +5 -2
  62. package/dist/lite/index.rn.esm2017.js.map +1 -1
  63. package/dist/lite/internal.d.ts +1 -0
  64. package/dist/lite/packages/firestore/src/core/bundle.d.ts +2 -2
  65. package/dist/lite/packages/firestore/src/core/bundle_impl.d.ts +2 -0
  66. package/dist/lite/packages/firestore/src/core/query.d.ts +7 -0
  67. package/dist/lite/packages/firestore/src/core/snapshot_version.d.ts +1 -0
  68. package/dist/lite/packages/firestore/src/core/sync_engine_impl.d.ts +1 -1
  69. package/dist/lite/packages/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
  70. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +0 -17
  71. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +16 -13
  72. package/dist/lite/packages/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
  73. package/dist/lite/packages/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
  74. package/dist/lite/packages/firestore/src/local/indexeddb_sentinels.d.ts +22 -17
  75. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +3 -4
  76. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +2 -1
  77. package/dist/lite/packages/firestore/src/local/local_store_impl.d.ts +1 -7
  78. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +13 -4
  79. package/dist/lite/packages/firestore/src/local/shared_client_state.d.ts +6 -3
  80. package/dist/lite/packages/firestore/src/local/shared_client_state_schema.d.ts +1 -1
  81. package/dist/lite/packages/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
  82. package/dist/lite/packages/firestore/src/model/field_index.d.ts +8 -1
  83. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -1
  84. package/dist/lite/private.d.ts +1 -0
  85. package/dist/packages/firestore/dist/index.esm2017.d.ts +178 -178
  86. package/dist/packages/firestore/src/core/bundle.d.ts +2 -2
  87. package/dist/packages/firestore/src/core/bundle_impl.d.ts +2 -0
  88. package/dist/packages/firestore/src/core/query.d.ts +7 -0
  89. package/dist/packages/firestore/src/core/snapshot_version.d.ts +1 -0
  90. package/dist/packages/firestore/src/core/sync_engine_impl.d.ts +1 -1
  91. package/dist/packages/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
  92. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +0 -17
  93. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +16 -13
  94. package/dist/packages/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
  95. package/dist/packages/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
  96. package/dist/packages/firestore/src/local/indexeddb_sentinels.d.ts +22 -17
  97. package/dist/packages/firestore/src/local/local_documents_view.d.ts +3 -4
  98. package/dist/packages/firestore/src/local/local_serializer.d.ts +2 -1
  99. package/dist/packages/firestore/src/local/local_store_impl.d.ts +1 -7
  100. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +13 -4
  101. package/dist/packages/firestore/src/local/shared_client_state.d.ts +6 -3
  102. package/dist/packages/firestore/src/local/shared_client_state_schema.d.ts +1 -1
  103. package/dist/packages/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
  104. package/dist/packages/firestore/src/model/field_index.d.ts +8 -1
  105. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -1
  106. package/dist/private.d.ts +19 -5
  107. package/package.json +7 -7
@@ -8,7 +8,7 @@ import * as grpc from '@grpc/grpc-js';
8
8
  import * as protoLoader from '@grpc/proto-loader';
9
9
 
10
10
  const name = "@firebase/firestore";
11
- const version$1 = "3.4.6";
11
+ const version$1 = "3.4.7-2022222232915";
12
12
 
13
13
  /**
14
14
  * @license
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
61
61
  User.FIRST_PARTY = new User('first-party-uid');
62
62
  User.MOCK_USER = new User('mock-user');
63
63
 
64
- const version = "9.6.9";
64
+ const version = "9.6.10-2022222232915";
65
65
 
66
66
  /**
67
67
  * @license
@@ -1200,6 +1200,24 @@ function decodeResourcePath(path) {
1200
1200
  return new ResourcePath(segments);
1201
1201
  }
1202
1202
 
1203
+ /**
1204
+ * @license
1205
+ * Copyright 2022 Google LLC
1206
+ *
1207
+ * Licensed under the Apache License, Version 2.0 (the "License");
1208
+ * you may not use this file except in compliance with the License.
1209
+ * You may obtain a copy of the License at
1210
+ *
1211
+ * http://www.apache.org/licenses/LICENSE-2.0
1212
+ *
1213
+ * Unless required by applicable law or agreed to in writing, software
1214
+ * distributed under the License is distributed on an "AS IS" BASIS,
1215
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1216
+ * See the License for the specific language governing permissions and
1217
+ * limitations under the License.
1218
+ */
1219
+ const DbRemoteDocumentStore$1 = 'remoteDocuments';
1220
+
1203
1221
  /**
1204
1222
  * @license
1205
1223
  * Copyright 2022 Google LLC
@@ -1270,26 +1288,36 @@ function newDbDocumentMutationKey(userId, path, batchId) {
1270
1288
  */
1271
1289
  const DbDocumentMutationPlaceholder = {};
1272
1290
  const DbDocumentMutationStore = 'documentMutations';
1273
- const DbRemoteDocumentStore = 'remoteDocuments';
1291
+ const DbRemoteDocumentStore = 'remoteDocumentsV14';
1274
1292
  /**
1275
- * An index that provides access to all entries sorted by read time (which
1276
- * corresponds to the last modification time of each row).
1277
- *
1278
- * This index is used to provide a changelog for Multi-Tab.
1293
+ * The primary key of the remote documents store, which allows for efficient
1294
+ * access by collection path and read time.
1279
1295
  */
1280
- const DbRemoteDocumentReadTimeIndex = 'readTimeIndex';
1281
- const DbRemoteDocumentReadTimeIndexPath = 'readTime';
1296
+ const DbRemoteDocumentKeyPath = [
1297
+ 'prefixPath',
1298
+ 'collectionGroup',
1299
+ 'readTime',
1300
+ 'documentId'
1301
+ ];
1302
+ /** An index that provides access to documents by key. */
1303
+ const DbRemoteDocumentDocumentKeyIndex = 'documentKeyIndex';
1304
+ const DbRemoteDocumentDocumentKeyIndexPath = [
1305
+ 'prefixPath',
1306
+ 'collectionGroup',
1307
+ 'documentId'
1308
+ ];
1282
1309
  /**
1283
- * An index that provides access to documents in a collection sorted by read
1310
+ * An index that provides access to documents by collection group and read
1284
1311
  * time.
1285
1312
  *
1286
- * This index is used to allow the RemoteDocumentCache to fetch newly changed
1287
- * documents in a collection.
1313
+ * This index is used by the index backfiller.
1288
1314
  */
1289
- const DbRemoteDocumentCollectionReadTimeIndex = 'collectionReadTimeIndex';
1290
- const DbRemoteDocumentCollectionReadTimeIndexPath = [
1291
- 'parentPath',
1292
- 'readTime'
1315
+ const DbRemoteDocumentCollectionGroupIndex = 'collectionGroupIndex';
1316
+ const DbRemoteDocumentCollectionGroupIndexPath = [
1317
+ 'collectionGroup',
1318
+ 'readTime',
1319
+ 'prefixPath',
1320
+ 'documentId'
1293
1321
  ];
1294
1322
  const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
1295
1323
  const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
@@ -1397,7 +1425,7 @@ const V1_STORES = [
1397
1425
  DbMutationQueueStore,
1398
1426
  DbMutationBatchStore,
1399
1427
  DbDocumentMutationStore,
1400
- DbRemoteDocumentStore,
1428
+ DbRemoteDocumentStore$1,
1401
1429
  DbTargetStore,
1402
1430
  DbPrimaryClientStore,
1403
1431
  DbTargetGlobalStore,
@@ -1412,14 +1440,33 @@ const V8_STORES = [...V6_STORES, DbCollectionParentStore];
1412
1440
  const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
1413
1441
  const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
1414
1442
  const V13_STORES = [
1415
- ...V12_STORES,
1443
+ DbMutationQueueStore,
1444
+ DbMutationBatchStore,
1445
+ DbDocumentMutationStore,
1446
+ DbRemoteDocumentStore,
1447
+ DbTargetStore,
1448
+ DbPrimaryClientStore,
1449
+ DbTargetGlobalStore,
1450
+ DbTargetDocumentStore,
1451
+ DbClientMetadataStore,
1452
+ DbRemoteDocumentGlobalStore,
1453
+ DbCollectionParentStore,
1454
+ DbBundleStore,
1455
+ DbNamedQueryStore,
1456
+ DbDocumentOverlayStore
1457
+ ];
1458
+ const V14_STORES = [
1459
+ ...V13_STORES,
1416
1460
  DbIndexConfigurationStore,
1417
1461
  DbIndexStateStore,
1418
1462
  DbIndexEntryStore
1419
1463
  ];
1420
1464
  /** Returns the object stores for the provided schema. */
1421
1465
  function getObjectStores(schemaVersion) {
1422
- if (schemaVersion === 13) {
1466
+ if (schemaVersion === 14) {
1467
+ return V14_STORES;
1468
+ }
1469
+ else if (schemaVersion === 13) {
1423
1470
  return V13_STORES;
1424
1471
  }
1425
1472
  else if (schemaVersion === 12) {
@@ -2630,6 +2677,9 @@ class SnapshotVersion {
2630
2677
  static min() {
2631
2678
  return new SnapshotVersion(new Timestamp(0, 0));
2632
2679
  }
2680
+ static max() {
2681
+ return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
2682
+ }
2633
2683
  compareTo(other) {
2634
2684
  return this.timestamp._compareTo(other.timestamp);
2635
2685
  }
@@ -4181,6 +4231,28 @@ class IndexState {
4181
4231
  return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
4182
4232
  }
4183
4233
  }
4234
+ /**
4235
+ * Creates an offset that matches all documents with a read time higher than
4236
+ * `readTime`.
4237
+ */
4238
+ function newIndexOffsetSuccessorFromReadTime(readTime, largestBatchId) {
4239
+ // We want to create an offset that matches all documents with a read time
4240
+ // greater than the provided read time. To do so, we technically need to
4241
+ // create an offset for `(readTime, MAX_DOCUMENT_KEY)`. While we could use
4242
+ // Unicode codepoints to generate MAX_DOCUMENT_KEY, it is much easier to use
4243
+ // `(readTime + 1, DocumentKey.empty())` since `> DocumentKey.empty()` matches
4244
+ // all valid document IDs.
4245
+ const successorSeconds = readTime.toTimestamp().seconds;
4246
+ const successorNanos = readTime.toTimestamp().nanoseconds + 1;
4247
+ const successor = SnapshotVersion.fromTimestamp(successorNanos === 1e9
4248
+ ? new Timestamp(successorSeconds + 1, 0)
4249
+ : new Timestamp(successorSeconds, successorNanos));
4250
+ return new IndexOffset(successor, DocumentKey.empty(), largestBatchId);
4251
+ }
4252
+ /** Creates a new offset based on the provided document. */
4253
+ function newIndexOffsetFromDocument(document) {
4254
+ return new IndexOffset(document.readTime, document.key, INITIAL_LARGEST_BATCH_ID);
4255
+ }
4184
4256
  /**
4185
4257
  * Stores the latest read time, document and batch ID that were processed for an
4186
4258
  * index.
@@ -4205,10 +4277,25 @@ class IndexOffset {
4205
4277
  this.documentKey = documentKey;
4206
4278
  this.largestBatchId = largestBatchId;
4207
4279
  }
4208
- /** The state of an index that has not yet been backfilled. */
4280
+ /** Returns an offset that sorts before all regular offsets. */
4209
4281
  static min() {
4210
4282
  return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
4211
4283
  }
4284
+ /** Returns an offset that sorts after all regular offsets. */
4285
+ static max() {
4286
+ return new IndexOffset(SnapshotVersion.max(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
4287
+ }
4288
+ }
4289
+ function indexOffsetComparator(left, right) {
4290
+ let cmp = left.readTime.compareTo(right.readTime);
4291
+ if (cmp !== 0) {
4292
+ return cmp;
4293
+ }
4294
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
4295
+ if (cmp !== 0) {
4296
+ return cmp;
4297
+ }
4298
+ return primitiveComparator(left.largestBatchId, right.largestBatchId);
4212
4299
  }
4213
4300
 
4214
4301
  /**
@@ -5101,6 +5188,18 @@ function queryMatchesBounds(query, doc) {
5101
5188
  }
5102
5189
  return true;
5103
5190
  }
5191
+ /**
5192
+ * Returns the collection group that this query targets.
5193
+ *
5194
+ * PORTING NOTE: This is only used in the Web SDK to facilitate multi-tab
5195
+ * synchronization for query results.
5196
+ */
5197
+ function queryCollectionGroup(query) {
5198
+ return (query.collectionGroup ||
5199
+ (query.path.length % 2 === 1
5200
+ ? query.path.lastSegment()
5201
+ : query.path.get(query.path.length - 2)));
5202
+ }
5104
5203
  /**
5105
5204
  * Returns a new comparator function that can be used to compare two documents
5106
5205
  * based on the Query's ordering constraint.
@@ -8634,40 +8733,33 @@ function fromDbRemoteDocument(localSerializer, remoteDoc) {
8634
8733
  }
8635
8734
  /** Encodes a document for storage locally. */
8636
8735
  function toDbRemoteDocument(localSerializer, document) {
8637
- const parentPath = document.key.path.popLast().toArray();
8638
- const readTime = toDbTimestampKey(document.readTime);
8736
+ const key = document.key;
8737
+ const remoteDoc = {
8738
+ prefixPath: key.getCollectionPath().popLast().toArray(),
8739
+ collectionGroup: key.collectionGroup,
8740
+ documentId: key.path.lastSegment(),
8741
+ readTime: toDbTimestampKey(document.readTime),
8742
+ hasCommittedMutations: document.hasCommittedMutations
8743
+ };
8639
8744
  if (document.isFoundDocument()) {
8640
- const doc = toDocument(localSerializer.remoteSerializer, document);
8641
- const hasCommittedMutations = document.hasCommittedMutations;
8642
- return {
8643
- document: doc,
8644
- hasCommittedMutations,
8645
- readTime,
8646
- parentPath
8647
- };
8745
+ remoteDoc.document = toDocument(localSerializer.remoteSerializer, document);
8648
8746
  }
8649
8747
  else if (document.isNoDocument()) {
8650
- const path = document.key.path.toArray();
8651
- const hasCommittedMutations = document.hasCommittedMutations;
8652
- return {
8653
- noDocument: { path, readTime: toDbTimestamp(document.version) },
8654
- hasCommittedMutations,
8655
- readTime,
8656
- parentPath
8748
+ remoteDoc.noDocument = {
8749
+ path: key.path.toArray(),
8750
+ readTime: toDbTimestamp(document.version)
8657
8751
  };
8658
8752
  }
8659
8753
  else if (document.isUnknownDocument()) {
8660
- const path = document.key.path.toArray();
8661
- return {
8662
- unknownDocument: { path, version: toDbTimestamp(document.version) },
8663
- hasCommittedMutations: true,
8664
- readTime,
8665
- parentPath
8754
+ remoteDoc.unknownDocument = {
8755
+ path: key.path.toArray(),
8756
+ version: toDbTimestamp(document.version)
8666
8757
  };
8667
8758
  }
8668
8759
  else {
8669
8760
  return fail();
8670
8761
  }
8762
+ return remoteDoc;
8671
8763
  }
8672
8764
  function toDbTimestampKey(snapshotVersion) {
8673
8765
  const timestamp = snapshotVersion.toTimestamp();
@@ -11819,7 +11911,7 @@ class IndexedDbRemoteDocumentCacheImpl {
11819
11911
  */
11820
11912
  addEntry(transaction, key, doc) {
11821
11913
  const documentStore = remoteDocumentsStore(transaction);
11822
- return documentStore.put(dbKey(key), doc);
11914
+ return documentStore.put(doc);
11823
11915
  }
11824
11916
  /**
11825
11917
  * Removes a document from the cache.
@@ -11827,10 +11919,9 @@ class IndexedDbRemoteDocumentCacheImpl {
11827
11919
  * All calls of `removeEntry` are required to go through the RemoteDocumentChangeBuffer
11828
11920
  * returned by `newChangeBuffer()` to ensure proper accounting of metadata.
11829
11921
  */
11830
- removeEntry(transaction, documentKey) {
11922
+ removeEntry(transaction, documentKey, readTime) {
11831
11923
  const store = remoteDocumentsStore(transaction);
11832
- const key = dbKey(documentKey);
11833
- return store.delete(key);
11924
+ return store.delete(dbReadTimeKey(documentKey, readTime));
11834
11925
  }
11835
11926
  /**
11836
11927
  * Updates the current cache size.
@@ -11845,11 +11936,15 @@ class IndexedDbRemoteDocumentCacheImpl {
11845
11936
  });
11846
11937
  }
11847
11938
  getEntry(transaction, documentKey) {
11939
+ let doc = MutableDocument.newInvalidDocument(documentKey);
11848
11940
  return remoteDocumentsStore(transaction)
11849
- .get(dbKey(documentKey))
11850
- .next(dbRemoteDoc => {
11851
- return this.maybeDecodeDocument(documentKey, dbRemoteDoc);
11852
- });
11941
+ .iterate({
11942
+ index: DbRemoteDocumentDocumentKeyIndex,
11943
+ range: IDBKeyRange.only(dbKey(documentKey))
11944
+ }, (_, dbRemoteDoc) => {
11945
+ doc = this.maybeDecodeDocument(documentKey, dbRemoteDoc);
11946
+ })
11947
+ .next(() => doc);
11853
11948
  }
11854
11949
  /**
11855
11950
  * Looks up an entry in the cache.
@@ -11858,15 +11953,21 @@ class IndexedDbRemoteDocumentCacheImpl {
11858
11953
  * @returns The cached document entry and its size.
11859
11954
  */
11860
11955
  getSizedEntry(transaction, documentKey) {
11956
+ let result = {
11957
+ size: 0,
11958
+ document: MutableDocument.newInvalidDocument(documentKey)
11959
+ };
11861
11960
  return remoteDocumentsStore(transaction)
11862
- .get(dbKey(documentKey))
11863
- .next(dbRemoteDoc => {
11864
- const doc = this.maybeDecodeDocument(documentKey, dbRemoteDoc);
11865
- return {
11866
- document: doc,
11961
+ .iterate({
11962
+ index: DbRemoteDocumentDocumentKeyIndex,
11963
+ range: IDBKeyRange.only(dbKey(documentKey))
11964
+ }, (_, dbRemoteDoc) => {
11965
+ result = {
11966
+ document: this.maybeDecodeDocument(documentKey, dbRemoteDoc),
11867
11967
  size: dbDocumentSize(dbRemoteDoc)
11868
11968
  };
11869
- });
11969
+ })
11970
+ .next(() => result);
11870
11971
  }
11871
11972
  getEntries(transaction, documentKeys) {
11872
11973
  let results = mutableDocumentMap();
@@ -11897,14 +11998,20 @@ class IndexedDbRemoteDocumentCacheImpl {
11897
11998
  if (documentKeys.isEmpty()) {
11898
11999
  return PersistencePromise.resolve();
11899
12000
  }
11900
- const range = IDBKeyRange.bound(documentKeys.first().path.toArray(), documentKeys.last().path.toArray());
11901
- const keyIter = documentKeys.getIterator();
12001
+ let sortedKeys = new SortedSet(dbKeyComparator);
12002
+ documentKeys.forEach(e => (sortedKeys = sortedKeys.add(e)));
12003
+ const range = IDBKeyRange.bound(dbKey(sortedKeys.first()), dbKey(sortedKeys.last()));
12004
+ const keyIter = sortedKeys.getIterator();
11902
12005
  let nextKey = keyIter.getNext();
11903
12006
  return remoteDocumentsStore(transaction)
11904
- .iterate({ range }, (potentialKeyRaw, dbRemoteDoc, control) => {
11905
- const potentialKey = DocumentKey.fromSegments(potentialKeyRaw);
12007
+ .iterate({ index: DbRemoteDocumentDocumentKeyIndex, range }, (_, dbRemoteDoc, control) => {
12008
+ const potentialKey = DocumentKey.fromSegments([
12009
+ ...dbRemoteDoc.prefixPath,
12010
+ dbRemoteDoc.collectionGroup,
12011
+ dbRemoteDoc.documentId
12012
+ ]);
11906
12013
  // Go through keys not found in cache.
11907
- while (nextKey && DocumentKey.comparator(nextKey, potentialKey) < 0) {
12014
+ while (nextKey && dbKeyComparator(nextKey, potentialKey) < 0) {
11908
12015
  callback(nextKey, null);
11909
12016
  nextKey = keyIter.getNext();
11910
12017
  }
@@ -11915,7 +12022,7 @@ class IndexedDbRemoteDocumentCacheImpl {
11915
12022
  }
11916
12023
  // Skip to the next key (if there is one).
11917
12024
  if (nextKey) {
11918
- control.skip(nextKey.path.toArray());
12025
+ control.skip(dbKey(nextKey));
11919
12026
  }
11920
12027
  else {
11921
12028
  control.done();
@@ -11930,41 +12037,44 @@ class IndexedDbRemoteDocumentCacheImpl {
11930
12037
  }
11931
12038
  });
11932
12039
  }
11933
- getAll(transaction, collection, sinceReadTime) {
11934
- let results = mutableDocumentMap();
11935
- const immediateChildrenPathLength = collection.length + 1;
11936
- const iterationOptions = {};
11937
- if (sinceReadTime.isEqual(SnapshotVersion.min())) {
11938
- // Documents are ordered by key, so we can use a prefix scan to narrow
11939
- // down the documents we need to match the query against.
11940
- const startKey = collection.toArray();
11941
- iterationOptions.range = IDBKeyRange.lowerBound(startKey);
11942
- }
11943
- else {
11944
- // Execute an index-free query and filter by read time. This is safe
11945
- // since all document changes to queries that have a
11946
- // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
11947
- const collectionKey = collection.toArray();
11948
- const readTimeKey = toDbTimestampKey(sinceReadTime);
11949
- iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
11950
- /* open= */ true);
11951
- iterationOptions.index = DbRemoteDocumentCollectionReadTimeIndex;
11952
- }
12040
+ getAllFromCollection(transaction, collection, offset) {
12041
+ const startKey = [
12042
+ collection.popLast().toArray(),
12043
+ collection.lastSegment(),
12044
+ toDbTimestampKey(offset.readTime),
12045
+ offset.documentKey.path.isEmpty()
12046
+ ? ''
12047
+ : offset.documentKey.path.lastSegment()
12048
+ ];
12049
+ const endKey = [
12050
+ collection.popLast().toArray(),
12051
+ collection.lastSegment(),
12052
+ [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
12053
+ ''
12054
+ ];
11953
12055
  return remoteDocumentsStore(transaction)
11954
- .iterate(iterationOptions, (key, dbRemoteDoc, control) => {
11955
- // The query is actually returning any path that starts with the query
11956
- // path prefix which may include documents in subcollections. For
11957
- // example, a query on 'rooms' will return rooms/abc/messages/xyx but we
11958
- // shouldn't match it. Fix this by discarding rows with document keys
11959
- // more than one segment longer than the query path.
11960
- if (key.length !== immediateChildrenPathLength) {
11961
- return;
11962
- }
11963
- const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
11964
- if (collection.isPrefixOf(document.key.path)) {
12056
+ .loadAll(IDBKeyRange.bound(startKey, endKey, true))
12057
+ .next(dbRemoteDocs => {
12058
+ let results = mutableDocumentMap();
12059
+ for (const dbRemoteDoc of dbRemoteDocs) {
12060
+ const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
11965
12061
  results = results.insert(document.key, document);
11966
12062
  }
11967
- else {
12063
+ return results;
12064
+ });
12065
+ }
12066
+ getAllFromCollectionGroup(transaction, collectionGroup, offset, limit) {
12067
+ let results = mutableDocumentMap();
12068
+ const startKey = dbCollectionGroupKey(collectionGroup, offset);
12069
+ const endKey = dbCollectionGroupKey(collectionGroup, IndexOffset.max());
12070
+ return remoteDocumentsStore(transaction)
12071
+ .iterate({
12072
+ index: DbRemoteDocumentCollectionGroupIndex,
12073
+ range: IDBKeyRange.bound(startKey, endKey, true)
12074
+ }, (_, dbRemoteDoc, control) => {
12075
+ const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
12076
+ results = results.insert(document.key, document);
12077
+ if (results.size === limit) {
11968
12078
  control.done();
11969
12079
  }
11970
12080
  })
@@ -12008,50 +12118,6 @@ class IndexedDbRemoteDocumentCacheImpl {
12008
12118
  function newIndexedDbRemoteDocumentCache(serializer) {
12009
12119
  return new IndexedDbRemoteDocumentCacheImpl(serializer);
12010
12120
  }
12011
- /**
12012
- * Returns the set of documents that have changed since the specified read
12013
- * time.
12014
- */
12015
- // PORTING NOTE: This is only used for multi-tab synchronization.
12016
- function remoteDocumentCacheGetNewDocumentChanges(remoteDocumentCache, transaction, sinceReadTime) {
12017
- const remoteDocumentCacheImpl = debugCast(remoteDocumentCache);
12018
- let changedDocs = mutableDocumentMap();
12019
- let lastReadTime = toDbTimestampKey(sinceReadTime);
12020
- const documentsStore = remoteDocumentsStore(transaction);
12021
- const range = IDBKeyRange.lowerBound(lastReadTime, true);
12022
- return documentsStore
12023
- .iterate({ index: DbRemoteDocumentReadTimeIndex, range }, (_, dbRemoteDoc) => {
12024
- // Unlike `getEntry()` and others, `getNewDocumentChanges()` parses
12025
- // the documents directly since we want to keep sentinel deletes.
12026
- const doc = fromDbRemoteDocument(remoteDocumentCacheImpl.serializer, dbRemoteDoc);
12027
- changedDocs = changedDocs.insert(doc.key, doc);
12028
- lastReadTime = dbRemoteDoc.readTime;
12029
- })
12030
- .next(() => {
12031
- return {
12032
- changedDocs,
12033
- readTime: fromDbTimestampKey(lastReadTime)
12034
- };
12035
- });
12036
- }
12037
- /**
12038
- * Returns the read time of the most recently read document in the cache, or
12039
- * SnapshotVersion.min() if not available.
12040
- */
12041
- // PORTING NOTE: This is only used for multi-tab synchronization.
12042
- function remoteDocumentCacheGetLastReadTime(transaction) {
12043
- const documentsStore = remoteDocumentsStore(transaction);
12044
- // If there are no existing entries, we return SnapshotVersion.min().
12045
- let readTime = SnapshotVersion.min();
12046
- return documentsStore
12047
- .iterate({ index: DbRemoteDocumentReadTimeIndex, reverse: true }, (key, dbRemoteDoc, control) => {
12048
- if (dbRemoteDoc.readTime) {
12049
- readTime = fromDbTimestampKey(dbRemoteDoc.readTime);
12050
- }
12051
- control.done();
12052
- })
12053
- .next(() => readTime);
12054
- }
12055
12121
  /**
12056
12122
  * Handles the details of adding and updating documents in the IndexedDbRemoteDocumentCache.
12057
12123
  *
@@ -12069,24 +12135,26 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12069
12135
  super();
12070
12136
  this.documentCache = documentCache;
12071
12137
  this.trackRemovals = trackRemovals;
12072
- // A map of document sizes prior to applying the changes in this buffer.
12073
- this.documentSizes = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
12138
+ // A map of document sizes and read times prior to applying the changes in
12139
+ // this buffer.
12140
+ this.documentStates = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
12074
12141
  }
12075
12142
  applyChanges(transaction) {
12076
12143
  const promises = [];
12077
12144
  let sizeDelta = 0;
12078
12145
  let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
12079
12146
  this.changes.forEach((key, documentChange) => {
12080
- const previousSize = this.documentSizes.get(key);
12147
+ const previousDoc = this.documentStates.get(key);
12148
+ promises.push(this.documentCache.removeEntry(transaction, key, previousDoc.readTime));
12081
12149
  if (documentChange.isValidDocument()) {
12082
12150
  const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
12083
12151
  collectionParents = collectionParents.add(key.path.popLast());
12084
12152
  const size = dbDocumentSize(doc);
12085
- sizeDelta += size - previousSize;
12153
+ sizeDelta += size - previousDoc.size;
12086
12154
  promises.push(this.documentCache.addEntry(transaction, key, doc));
12087
12155
  }
12088
12156
  else {
12089
- sizeDelta -= previousSize;
12157
+ sizeDelta -= previousDoc.size;
12090
12158
  if (this.trackRemovals) {
12091
12159
  // In order to track removals, we store a "sentinel delete" in the
12092
12160
  // RemoteDocumentCache. This entry is represented by a NoDocument
@@ -12095,9 +12163,6 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12095
12163
  const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
12096
12164
  promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
12097
12165
  }
12098
- else {
12099
- promises.push(this.documentCache.removeEntry(transaction, key));
12100
- }
12101
12166
  }
12102
12167
  });
12103
12168
  collectionParents.forEach(parent => {
@@ -12111,7 +12176,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12111
12176
  return this.documentCache
12112
12177
  .getSizedEntry(transaction, documentKey)
12113
12178
  .next(getResult => {
12114
- this.documentSizes.set(documentKey, getResult.size);
12179
+ this.documentStates.set(documentKey, {
12180
+ size: getResult.size,
12181
+ readTime: getResult.document.readTime
12182
+ });
12115
12183
  return getResult.document;
12116
12184
  });
12117
12185
  }
@@ -12125,7 +12193,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12125
12193
  // keys to `DocumentSizeEntry`s. This is to allow returning the
12126
12194
  // `MutableDocumentMap` directly, without a conversion.
12127
12195
  sizeMap.forEach((documentKey, size) => {
12128
- this.documentSizes.set(documentKey, size);
12196
+ this.documentStates.set(documentKey, {
12197
+ size,
12198
+ readTime: documents.get(documentKey).readTime
12199
+ });
12129
12200
  });
12130
12201
  return documents;
12131
12202
  });
@@ -12140,8 +12211,52 @@ function documentGlobalStore(txn) {
12140
12211
  function remoteDocumentsStore(txn) {
12141
12212
  return getStore(txn, DbRemoteDocumentStore);
12142
12213
  }
12143
- function dbKey(docKey) {
12144
- return docKey.path.toArray();
12214
+ /**
12215
+ * Returns a key that can be used for document lookups on the
12216
+ * `DbRemoteDocumentDocumentKeyIndex` index.
12217
+ */
12218
+ function dbKey(documentKey) {
12219
+ const path = documentKey.path.toArray();
12220
+ return [
12221
+ /* prefix path */ path.slice(0, path.length - 2),
12222
+ /* collection id */ path[path.length - 2],
12223
+ /* document id */ path[path.length - 1]
12224
+ ];
12225
+ }
12226
+ /**
12227
+ * Returns a key that can be used for document lookups via the primary key of
12228
+ * the DbRemoteDocument object store.
12229
+ */
12230
+ function dbReadTimeKey(documentKey, readTime) {
12231
+ const path = documentKey.path.toArray();
12232
+ return [
12233
+ /* prefix path */ path.slice(0, path.length - 2),
12234
+ /* collection id */ path[path.length - 2],
12235
+ toDbTimestampKey(readTime),
12236
+ /* document id */ path[path.length - 1]
12237
+ ];
12238
+ }
12239
+ /**
12240
+ * Returns a key that can be used for document lookups on the
12241
+ * `DbRemoteDocumentDocumentCollectionGroupIndex` index.
12242
+ */
12243
+ function dbCollectionGroupKey(collectionGroup, offset) {
12244
+ const path = offset.documentKey.path.toArray();
12245
+ return [
12246
+ /* collection id */ collectionGroup,
12247
+ toDbTimestampKey(offset.readTime),
12248
+ /* prefix path */ path.slice(0, path.length - 2),
12249
+ /* document id */ path.length > 0 ? path[path.length - 1] : ''
12250
+ ];
12251
+ }
12252
+ /**
12253
+ * Comparator that compares document keys according to the primary key sorting
12254
+ * used by the `DbRemoteDocumentDocument` store (by collection path and then
12255
+ * document ID).
12256
+ */
12257
+ function dbKeyComparator(l, r) {
12258
+ const cmp = l.path.length - r.path.length;
12259
+ return cmp !== 0 ? cmp : DocumentKey.comparator(l, r);
12145
12260
  }
12146
12261
  /**
12147
12262
  * Schema Version for the Web client:
@@ -12162,9 +12277,11 @@ function dbKey(docKey) {
12162
12277
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
12163
12278
  * 11. Add bundles and named_queries for bundle support.
12164
12279
  * 12. Add document overlays.
12165
- * 13. Add indexing support.
12280
+ * 13. Rewrite the keys of the remote document cache to allow for efficient
12281
+ * document lookup via `getAll()`.
12282
+ * 14. Add indexing support.
12166
12283
  */
12167
- const SCHEMA_VERSION = 12;
12284
+ const SCHEMA_VERSION = 13;
12168
12285
 
12169
12286
  /**
12170
12287
  * @license
@@ -12200,7 +12317,7 @@ class SchemaConverter {
12200
12317
  createPrimaryClientStore(db);
12201
12318
  createMutationQueue(db);
12202
12319
  createQueryCache(db);
12203
- createRemoteDocumentCache(db);
12320
+ createLegacyRemoteDocumentCache(db);
12204
12321
  }
12205
12322
  // Migration 2 to populate the targetGlobal object no longer needed since
12206
12323
  // migration 3 unconditionally clears it.
@@ -12249,7 +12366,8 @@ class SchemaConverter {
12249
12366
  // to the DbRemoteDocument object store itself. Since the previous change
12250
12367
  // log only contained transient data, we can drop its object store.
12251
12368
  dropRemoteDocumentChangesStore(db);
12252
- createRemoteDocumentReadTimeIndex(txn);
12369
+ // Note: Schema version 9 used to create a read time index for the
12370
+ // RemoteDocumentCache. This is now done with schema version 13.
12253
12371
  });
12254
12372
  }
12255
12373
  if (fromVersion < 10 && toVersion >= 10) {
@@ -12267,6 +12385,12 @@ class SchemaConverter {
12267
12385
  });
12268
12386
  }
12269
12387
  if (fromVersion < 13 && toVersion >= 13) {
12388
+ p = p
12389
+ .next(() => createRemoteDocumentCache(db))
12390
+ .next(() => this.rewriteRemoteDocumentCache(db, simpleDbTransaction))
12391
+ .next(() => db.deleteObjectStore(DbRemoteDocumentStore$1));
12392
+ }
12393
+ if (fromVersion < 14 && toVersion >= 14) {
12270
12394
  p = p.next(() => {
12271
12395
  createFieldIndex(db);
12272
12396
  });
@@ -12276,7 +12400,7 @@ class SchemaConverter {
12276
12400
  addDocumentGlobal(txn) {
12277
12401
  let byteSize = 0;
12278
12402
  return txn
12279
- .store(DbRemoteDocumentStore)
12403
+ .store(DbRemoteDocumentStore$1)
12280
12404
  .iterate((_, doc) => {
12281
12405
  byteSize += dbDocumentSize(doc);
12282
12406
  })
@@ -12311,7 +12435,7 @@ class SchemaConverter {
12311
12435
  */
12312
12436
  ensureSequenceNumbers(txn) {
12313
12437
  const documentTargetStore = txn.store(DbTargetDocumentStore);
12314
- const documentsStore = txn.store(DbRemoteDocumentStore);
12438
+ const documentsStore = txn.store(DbRemoteDocumentStore$1);
12315
12439
  const globalTargetStore = txn.store(DbTargetGlobalStore);
12316
12440
  return globalTargetStore.get(DbTargetGlobalKey).next(metadata => {
12317
12441
  const writeSentinelKey = (path) => {
@@ -12358,7 +12482,7 @@ class SchemaConverter {
12358
12482
  };
12359
12483
  // Index existing remote documents.
12360
12484
  return txn
12361
- .store(DbRemoteDocumentStore)
12485
+ .store(DbRemoteDocumentStore$1)
12362
12486
  .iterate({ keysOnly: true }, (pathSegments, _) => {
12363
12487
  const path = new ResourcePath(pathSegments);
12364
12488
  return addEntry(path.popLast());
@@ -12381,6 +12505,27 @@ class SchemaConverter {
12381
12505
  return targetStore.put(updatedDbTarget);
12382
12506
  });
12383
12507
  }
12508
+ rewriteRemoteDocumentCache(db, transaction) {
12509
+ const legacyRemoteDocumentStore = transaction.store(DbRemoteDocumentStore$1);
12510
+ const writes = [];
12511
+ return legacyRemoteDocumentStore
12512
+ .iterate((_, legacyDocument) => {
12513
+ const remoteDocumentStore = transaction.store(DbRemoteDocumentStore);
12514
+ const path = extractKey(legacyDocument).path.toArray();
12515
+ const dbRemoteDocument = {
12516
+ prefixPath: path.slice(0, path.length - 2),
12517
+ collectionGroup: path[path.length - 2],
12518
+ documentId: path[path.length - 1],
12519
+ readTime: legacyDocument.readTime || [0, 0],
12520
+ unknownDocument: legacyDocument.unknownDocument,
12521
+ noDocument: legacyDocument.noDocument,
12522
+ document: legacyDocument.document,
12523
+ hasCommittedMutations: !!legacyDocument.hasCommittedMutations
12524
+ };
12525
+ writes.push(remoteDocumentStore.put(dbRemoteDocument));
12526
+ })
12527
+ .next(() => PersistencePromise.waitFor(writes));
12528
+ }
12384
12529
  }
12385
12530
  function sentinelKey(path) {
12386
12531
  return [0, encodeResourcePath(path)];
@@ -12417,8 +12562,15 @@ function upgradeMutationBatchSchemaAndMigrateData(db, txn) {
12417
12562
  return PersistencePromise.waitFor(writeAll);
12418
12563
  });
12419
12564
  }
12565
+ function createLegacyRemoteDocumentCache(db) {
12566
+ db.createObjectStore(DbRemoteDocumentStore$1);
12567
+ }
12420
12568
  function createRemoteDocumentCache(db) {
12421
- db.createObjectStore(DbRemoteDocumentStore);
12569
+ const remoteDocumentStore = db.createObjectStore(DbRemoteDocumentStore, {
12570
+ keyPath: DbRemoteDocumentKeyPath
12571
+ });
12572
+ remoteDocumentStore.createIndex(DbRemoteDocumentDocumentKeyIndex, DbRemoteDocumentDocumentKeyIndexPath);
12573
+ remoteDocumentStore.createIndex(DbRemoteDocumentCollectionGroupIndex, DbRemoteDocumentCollectionGroupIndexPath);
12422
12574
  }
12423
12575
  function createDocumentGlobalStore(db) {
12424
12576
  db.createObjectStore(DbRemoteDocumentGlobalStore);
@@ -12460,15 +12612,6 @@ function writeEmptyTargetGlobalEntry(txn) {
12460
12612
  };
12461
12613
  return globalStore.put(DbTargetGlobalKey, metadata);
12462
12614
  }
12463
- /**
12464
- * Creates indices on the RemoteDocuments store used for both multi-tab
12465
- * and Index-Free queries.
12466
- */
12467
- function createRemoteDocumentReadTimeIndex(txn) {
12468
- const remoteDocumentStore = txn.objectStore(DbRemoteDocumentStore);
12469
- remoteDocumentStore.createIndex(DbRemoteDocumentReadTimeIndex, DbRemoteDocumentReadTimeIndexPath, { unique: false });
12470
- remoteDocumentStore.createIndex(DbRemoteDocumentCollectionReadTimeIndex, DbRemoteDocumentCollectionReadTimeIndexPath, { unique: false });
12471
- }
12472
12615
  function createClientMetadataStore(db) {
12473
12616
  db.createObjectStore(DbClientMetadataStore, {
12474
12617
  keyPath: DbClientMetadataKeyPath
@@ -12485,11 +12628,11 @@ function createNamedQueriesStore(db) {
12485
12628
  });
12486
12629
  }
12487
12630
  function createFieldIndex(db) {
12488
- const indexConfiguratioStore = db.createObjectStore(DbIndexConfigurationStore, {
12631
+ const indexConfigurationStore = db.createObjectStore(DbIndexConfigurationStore, {
12489
12632
  keyPath: DbIndexConfigurationKeyPath,
12490
12633
  autoIncrement: true
12491
12634
  });
12492
- indexConfiguratioStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
12635
+ indexConfigurationStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
12493
12636
  const indexStateStore = db.createObjectStore(DbIndexStateStore, {
12494
12637
  keyPath: DbIndexStateKeyPath
12495
12638
  });
@@ -12505,6 +12648,20 @@ function createDocumentOverlayStore(db) {
12505
12648
  });
12506
12649
  documentOverlayStore.createIndex(DbDocumentOverlayCollectionPathOverlayIndex, DbDocumentOverlayCollectionPathOverlayIndexPath, { unique: false });
12507
12650
  documentOverlayStore.createIndex(DbDocumentOverlayCollectionGroupOverlayIndex, DbDocumentOverlayCollectionGroupOverlayIndexPath, { unique: false });
12651
+ }
12652
+ function extractKey(remoteDoc) {
12653
+ if (remoteDoc.document) {
12654
+ return new DocumentKey(ResourcePath.fromString(remoteDoc.document.name).popFirst(5));
12655
+ }
12656
+ else if (remoteDoc.noDocument) {
12657
+ return DocumentKey.fromSegments(remoteDoc.noDocument.path);
12658
+ }
12659
+ else if (remoteDoc.unknownDocument) {
12660
+ return DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
12661
+ }
12662
+ else {
12663
+ return fail();
12664
+ }
12508
12665
  }
12509
12666
 
12510
12667
  /**
@@ -13347,18 +13504,17 @@ class LocalDocumentsView {
13347
13504
  *
13348
13505
  * @param transaction - The persistence transaction.
13349
13506
  * @param query - The query to match documents against.
13350
- * @param sinceReadTime - If not set to SnapshotVersion.min(), return only
13351
- * documents that have been read since this snapshot version (exclusive).
13507
+ * @param offset - Read time and key to start scanning by (exclusive).
13352
13508
  */
13353
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
13509
+ getDocumentsMatchingQuery(transaction, query, offset) {
13354
13510
  if (isDocumentQuery$1(query)) {
13355
13511
  return this.getDocumentsMatchingDocumentQuery(transaction, query.path);
13356
13512
  }
13357
13513
  else if (isCollectionGroupQuery(query)) {
13358
- return this.getDocumentsMatchingCollectionGroupQuery(transaction, query, sinceReadTime);
13514
+ return this.getDocumentsMatchingCollectionGroupQuery(transaction, query, offset);
13359
13515
  }
13360
13516
  else {
13361
- return this.getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime);
13517
+ return this.getDocumentsMatchingCollectionQuery(transaction, query, offset);
13362
13518
  }
13363
13519
  }
13364
13520
  getDocumentsMatchingDocumentQuery(transaction, docPath) {
@@ -13371,7 +13527,7 @@ class LocalDocumentsView {
13371
13527
  return result;
13372
13528
  });
13373
13529
  }
13374
- getDocumentsMatchingCollectionGroupQuery(transaction, query, sinceReadTime) {
13530
+ getDocumentsMatchingCollectionGroupQuery(transaction, query, offset) {
13375
13531
  const collectionId = query.collectionGroup;
13376
13532
  let results = documentMap();
13377
13533
  return this.indexManager
@@ -13381,7 +13537,7 @@ class LocalDocumentsView {
13381
13537
  // collectionId and aggregate the results.
13382
13538
  return PersistencePromise.forEach(parents, (parent) => {
13383
13539
  const collectionQuery = asCollectionQueryAtPath(query, parent.child(collectionId));
13384
- return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery, sinceReadTime).next(r => {
13540
+ return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery, offset).next(r => {
13385
13541
  r.forEach((key, doc) => {
13386
13542
  results = results.insert(key, doc);
13387
13543
  });
@@ -13389,11 +13545,11 @@ class LocalDocumentsView {
13389
13545
  }).next(() => results);
13390
13546
  });
13391
13547
  }
13392
- getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
13548
+ getDocumentsMatchingCollectionQuery(transaction, query, offset) {
13393
13549
  // Query the remote documents and overlay mutations.
13394
13550
  let results;
13395
13551
  return this.remoteDocumentCache
13396
- .getAll(transaction, query.path, sinceReadTime)
13552
+ .getAllFromCollection(transaction, query.path, offset)
13397
13553
  .next(queryResults => {
13398
13554
  results = queryResults;
13399
13555
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
@@ -13479,11 +13635,12 @@ class LocalStoreImpl {
13479
13635
  // TODO(wuandy): Evaluate if TargetId can be part of Target.
13480
13636
  this.targetIdByTarget = new ObjectMap(t => canonifyTarget(t), targetEquals);
13481
13637
  /**
13482
- * The read time of the last entry processed by `getNewDocumentChanges()`.
13638
+ * A per collection group index of the last read time processed by
13639
+ * `getNewDocumentChanges()`.
13483
13640
  *
13484
13641
  * PORTING NOTE: This is only used for multi-tab synchronization.
13485
13642
  */
13486
- this.lastDocumentChangeReadTime = SnapshotVersion.min();
13643
+ this.collectionGroupReadTime = new Map();
13487
13644
  this.remoteDocuments = persistence.getRemoteDocumentCache();
13488
13645
  this.targetCache = persistence.getTargetCache();
13489
13646
  this.bundleCache = persistence.getBundleCache();
@@ -14018,6 +14175,7 @@ function localStoreExecuteQuery(localStore, query, usePreviousResults) {
14018
14175
  ? lastLimboFreeSnapshotVersion
14019
14176
  : SnapshotVersion.min(), usePreviousResults ? remoteKeys : documentKeySet()))
14020
14177
  .next(documents => {
14178
+ setMaxReadTime(localStoreImpl, queryCollectionGroup(query), documents);
14021
14179
  return { documents, remoteKeys };
14022
14180
  });
14023
14181
  });
@@ -14095,28 +14253,32 @@ function localStoreGetCachedTarget(localStore, targetId) {
14095
14253
  * since the prior call.
14096
14254
  */
14097
14255
  // PORTING NOTE: Multi-Tab only.
14098
- function localStoreGetNewDocumentChanges(localStore) {
14256
+ function localStoreGetNewDocumentChanges(localStore, collectionGroup) {
14099
14257
  const localStoreImpl = debugCast(localStore);
14258
+ // Get the current maximum read time for the collection. This should always
14259
+ // exist, but to reduce the chance for regressions we default to
14260
+ // SnapshotVersion.Min()
14261
+ // TODO(indexing): Consider removing the default value.
14262
+ const readTime = localStoreImpl.collectionGroupReadTime.get(collectionGroup) ||
14263
+ SnapshotVersion.min();
14100
14264
  return localStoreImpl.persistence
14101
- .runTransaction('Get new document changes', 'readonly', txn => remoteDocumentCacheGetNewDocumentChanges(localStoreImpl.remoteDocuments, txn, localStoreImpl.lastDocumentChangeReadTime))
14102
- .then(({ changedDocs, readTime }) => {
14103
- localStoreImpl.lastDocumentChangeReadTime = readTime;
14265
+ .runTransaction('Get new document changes', 'readonly', txn => localStoreImpl.remoteDocuments.getAllFromCollectionGroup(txn, collectionGroup, newIndexOffsetSuccessorFromReadTime(readTime, INITIAL_LARGEST_BATCH_ID),
14266
+ /* limit= */ Number.MAX_SAFE_INTEGER))
14267
+ .then(changedDocs => {
14268
+ setMaxReadTime(localStoreImpl, collectionGroup, changedDocs);
14104
14269
  return changedDocs;
14105
14270
  });
14106
14271
  }
14107
- /**
14108
- * Reads the newest document change from persistence and moves the internal
14109
- * synchronization marker forward so that calls to `getNewDocumentChanges()`
14110
- * only return changes that happened after client initialization.
14111
- */
14272
+ /** Sets the collection group's maximum read time from the given documents. */
14112
14273
  // PORTING NOTE: Multi-Tab only.
14113
- async function localStoreSynchronizeLastDocumentChangeReadTime(localStore) {
14114
- const localStoreImpl = debugCast(localStore);
14115
- return localStoreImpl.persistence
14116
- .runTransaction('Synchronize last document change read time', 'readonly', txn => remoteDocumentCacheGetLastReadTime(txn))
14117
- .then(readTime => {
14118
- localStoreImpl.lastDocumentChangeReadTime = readTime;
14274
+ function setMaxReadTime(localStoreImpl, collectionGroup, changedDocs) {
14275
+ let readTime = SnapshotVersion.min();
14276
+ changedDocs.forEach((_, doc) => {
14277
+ if (doc.readTime.compareTo(readTime) > 0) {
14278
+ readTime = doc.readTime;
14279
+ }
14119
14280
  });
14281
+ localStoreImpl.collectionGroupReadTime.set(collectionGroup, readTime);
14120
14282
  }
14121
14283
  /**
14122
14284
  * Creates a new target using the given bundle name, which will be used to
@@ -14799,7 +14961,7 @@ class MemoryRemoteDocumentCacheImpl {
14799
14961
  });
14800
14962
  return PersistencePromise.resolve(results);
14801
14963
  }
14802
- getAll(transaction, collectionPath, sinceReadTime) {
14964
+ getAllFromCollection(transaction, collectionPath, offset) {
14803
14965
  let results = mutableDocumentMap();
14804
14966
  // Documents are ordered by key, so we can use a prefix scan to narrow down
14805
14967
  // the documents we need to match the query against.
@@ -14814,13 +14976,19 @@ class MemoryRemoteDocumentCacheImpl {
14814
14976
  // Exclude entries from subcollections.
14815
14977
  continue;
14816
14978
  }
14817
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
14979
+ if (indexOffsetComparator(newIndexOffsetFromDocument(document), offset) <= 0) {
14980
+ // The document sorts before the offset.
14818
14981
  continue;
14819
14982
  }
14820
14983
  results = results.insert(document.key, document.mutableCopy());
14821
14984
  }
14822
14985
  return PersistencePromise.resolve(results);
14823
14986
  }
14987
+ getAllFromCollectionGroup(transaction, collectionGroup, offset, limti) {
14988
+ // This method should only be called from the IndexBackfiller if persistence
14989
+ // is enabled.
14990
+ fail();
14991
+ }
14824
14992
  forEachDocumentKey(transaction, f) {
14825
14993
  return PersistencePromise.forEach(this.docs, (key) => f(key));
14826
14994
  }
@@ -15273,7 +15441,7 @@ class QueryEngine {
15273
15441
  }
15274
15442
  // Retrieve all results for documents that were updated since the last
15275
15443
  // limbo-document free remote snapshot.
15276
- return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, lastLimboFreeSnapshotVersion).next(updatedResults => {
15444
+ return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, newIndexOffsetSuccessorFromReadTime(lastLimboFreeSnapshotVersion, INITIAL_LARGEST_BATCH_ID)).next(updatedResults => {
15277
15445
  // We merge `previousResults` into `updateResults`, since
15278
15446
  // `updateResults` is already a DocumentMap. If a document is
15279
15447
  // contained in both lists, then its contents are the same.
@@ -15335,7 +15503,7 @@ class QueryEngine {
15335
15503
  if (getLogLevel() <= LogLevel.DEBUG) {
15336
15504
  logDebug('QueryEngine', 'Using full collection scan to execute query:', stringifyQuery(query));
15337
15505
  }
15338
- return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, SnapshotVersion.min());
15506
+ return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, IndexOffset.min());
15339
15507
  }
15340
15508
  }
15341
15509
 
@@ -15396,8 +15564,9 @@ function createWebStorageOnlineStateKey(persistenceKey) {
15396
15564
  // The WebStorage prefix that plays as a event to indicate the remote documents
15397
15565
  // might have changed due to some secondary tabs loading a bundle.
15398
15566
  // format of the key is:
15399
- // firestore_bundle_loaded_<persistenceKey>
15400
- const BUNDLE_LOADED_KEY_PREFIX = 'firestore_bundle_loaded';
15567
+ // firestore_bundle_loaded_v2_<persistenceKey>
15568
+ // The version ending with "v2" stores the list of modified collection groups.
15569
+ const BUNDLE_LOADED_KEY_PREFIX = 'firestore_bundle_loaded_v2';
15401
15570
  function createBundleLoadedKey(persistenceKey) {
15402
15571
  return `${BUNDLE_LOADED_KEY_PREFIX}_${persistenceKey}`;
15403
15572
  }
@@ -15775,8 +15944,8 @@ class WebStorageSharedClientState {
15775
15944
  setOnlineState(onlineState) {
15776
15945
  this.persistOnlineState(onlineState);
15777
15946
  }
15778
- notifyBundleLoaded() {
15779
- this.persistBundleLoadedState();
15947
+ notifyBundleLoaded(collectionGroups) {
15948
+ this.persistBundleLoadedState(collectionGroups);
15780
15949
  }
15781
15950
  shutdown() {
15782
15951
  if (this.started) {
@@ -15860,7 +16029,8 @@ class WebStorageSharedClientState {
15860
16029
  }
15861
16030
  }
15862
16031
  else if (storageEvent.key === this.bundleLoadedKey) {
15863
- return this.syncEngine.synchronizeWithChangedDocuments();
16032
+ const collectionGroups = this.fromWebStoreBundleLoadedState(storageEvent.newValue);
16033
+ await Promise.all(collectionGroups.map(cg => this.syncEngine.synchronizeWithChangedDocuments(cg)));
15864
16034
  }
15865
16035
  });
15866
16036
  }
@@ -15892,8 +16062,9 @@ class WebStorageSharedClientState {
15892
16062
  const targetMetadata = new QueryTargetMetadata(targetId, state, error);
15893
16063
  this.setItem(targetKey, targetMetadata.toWebStorageJSON());
15894
16064
  }
15895
- persistBundleLoadedState() {
15896
- this.setItem(this.bundleLoadedKey, 'value-not-used');
16065
+ persistBundleLoadedState(collectionGroups) {
16066
+ const json = JSON.stringify(Array.from(collectionGroups));
16067
+ this.setItem(this.bundleLoadedKey, json);
15897
16068
  }
15898
16069
  /**
15899
16070
  * Parses a client state key in WebStorage. Returns null if the key does not
@@ -15937,6 +16108,9 @@ class WebStorageSharedClientState {
15937
16108
  fromWebStorageOnlineState(value) {
15938
16109
  return SharedOnlineState.fromWebStorageEntry(value);
15939
16110
  }
16111
+ fromWebStoreBundleLoadedState(value) {
16112
+ return JSON.parse(value);
16113
+ }
15940
16114
  async handleMutationBatchEvent(mutationBatch) {
15941
16115
  if (mutationBatch.user.uid !== this.currentUser.uid) {
15942
16116
  logDebug(LOG_TAG$a, `Ignoring mutation for non-active user ${mutationBatch.user.uid}`);
@@ -16053,7 +16227,7 @@ class MemorySharedClientState {
16053
16227
  }
16054
16228
  shutdown() { }
16055
16229
  writeSequenceNumber(sequenceNumber) { }
16056
- notifyBundleLoaded() {
16230
+ notifyBundleLoaded(collectionGroups) {
16057
16231
  // No op.
16058
16232
  }
16059
16233
  }
@@ -21353,29 +21527,6 @@ class LocalViewChanges {
21353
21527
  }
21354
21528
  }
21355
21529
 
21356
- /**
21357
- * @license
21358
- * Copyright 2020 Google LLC
21359
- *
21360
- * Licensed under the Apache License, Version 2.0 (the "License");
21361
- * you may not use this file except in compliance with the License.
21362
- * You may obtain a copy of the License at
21363
- *
21364
- * http://www.apache.org/licenses/LICENSE-2.0
21365
- *
21366
- * Unless required by applicable law or agreed to in writing, software
21367
- * distributed under the License is distributed on an "AS IS" BASIS,
21368
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21369
- * See the License for the specific language governing permissions and
21370
- * limitations under the License.
21371
- */
21372
- class BundleLoadResult {
21373
- constructor(progress, changedDocs) {
21374
- this.progress = progress;
21375
- this.changedDocs = changedDocs;
21376
- }
21377
- }
21378
-
21379
21530
  /**
21380
21531
  * @license
21381
21532
  * Copyright 2020 Google LLC
@@ -21430,6 +21581,8 @@ class BundleLoader {
21430
21581
  this.queries = [];
21431
21582
  /** Batched documents to be saved into storage */
21432
21583
  this.documents = [];
21584
+ /** The collection groups affected by this bundle. */
21585
+ this.collectionGroups = new Set();
21433
21586
  this.progress = bundleInitialProgress(bundleMetadata);
21434
21587
  }
21435
21588
  /**
@@ -21449,6 +21602,8 @@ class BundleLoader {
21449
21602
  if (!element.payload.documentMetadata.exists) {
21450
21603
  ++documentsLoaded;
21451
21604
  }
21605
+ const path = ResourcePath.fromString(element.payload.documentMetadata.name);
21606
+ this.collectionGroups.add(path.get(path.length - 2));
21452
21607
  }
21453
21608
  else if (element.payload.document) {
21454
21609
  this.documents[this.documents.length - 1].document =
@@ -21479,13 +21634,17 @@ class BundleLoader {
21479
21634
  * Update the progress to 'Success' and return the updated progress.
21480
21635
  */
21481
21636
  async complete() {
21482
- const changedDocuments = await localStoreApplyBundledDocuments(this.localStore, new BundleConverterImpl(this.serializer), this.documents, this.bundleMetadata.id);
21637
+ const changedDocs = await localStoreApplyBundledDocuments(this.localStore, new BundleConverterImpl(this.serializer), this.documents, this.bundleMetadata.id);
21483
21638
  const queryDocumentMap = this.getQueryDocumentMapping(this.documents);
21484
21639
  for (const q of this.queries) {
21485
21640
  await localStoreSaveNamedQuery(this.localStore, q, queryDocumentMap.get(q.name));
21486
21641
  }
21487
21642
  this.progress.taskState = 'Success';
21488
- return new BundleLoadResult(Object.assign({}, this.progress), changedDocuments);
21643
+ return {
21644
+ progress: this.progress,
21645
+ changedCollectionGroups: this.collectionGroups,
21646
+ changedDocs
21647
+ };
21489
21648
  }
21490
21649
  }
21491
21650
  /**
@@ -22564,9 +22723,9 @@ async function synchronizeViewAndComputeSnapshot(syncEngine, queryView) {
22564
22723
  * snapshots if needed.
22565
22724
  */
22566
22725
  // PORTING NOTE: Multi-Tab only.
22567
- async function syncEngineSynchronizeWithChangedDocuments(syncEngine) {
22726
+ async function syncEngineSynchronizeWithChangedDocuments(syncEngine, collectionGroup) {
22568
22727
  const syncEngineImpl = debugCast(syncEngine);
22569
- return localStoreGetNewDocumentChanges(syncEngineImpl.localStore).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
22728
+ return localStoreGetNewDocumentChanges(syncEngineImpl.localStore, collectionGroup).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
22570
22729
  }
22571
22730
  /** Applies a mutation state to an existing batch. */
22572
22731
  // PORTING NOTE: Multi-Tab only.
@@ -22731,11 +22890,12 @@ async function syncEngineApplyTargetState(syncEngine, targetId, state, error) {
22731
22890
  logDebug(LOG_TAG$3, 'Ignoring unexpected query state notification.');
22732
22891
  return;
22733
22892
  }
22734
- if (syncEngineImpl.queriesByTarget.has(targetId)) {
22893
+ const query = syncEngineImpl.queriesByTarget.get(targetId);
22894
+ if (query && query.length > 0) {
22735
22895
  switch (state) {
22736
22896
  case 'current':
22737
22897
  case 'not-current': {
22738
- const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore);
22898
+ const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore, queryCollectionGroup(query[0]));
22739
22899
  const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(targetId, state === 'current');
22740
22900
  await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes, synthesizedRemoteEvent);
22741
22901
  break;
@@ -22818,10 +22978,11 @@ function syncEngineEnsureWriteCallbacks(syncEngine) {
22818
22978
  function syncEngineLoadBundle(syncEngine, bundleReader, task) {
22819
22979
  const syncEngineImpl = debugCast(syncEngine);
22820
22980
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
22821
- loadBundleImpl(syncEngineImpl, bundleReader, task).then(() => {
22822
- syncEngineImpl.sharedClientState.notifyBundleLoaded();
22981
+ loadBundleImpl(syncEngineImpl, bundleReader, task).then(collectionGroups => {
22982
+ syncEngineImpl.sharedClientState.notifyBundleLoaded(collectionGroups);
22823
22983
  });
22824
22984
  }
22985
+ /** Loads a bundle and returns the list of affected collection groups. */
22825
22986
  async function loadBundleImpl(syncEngine, reader, task) {
22826
22987
  try {
22827
22988
  const metadata = await reader.getMetadata();
@@ -22829,7 +22990,7 @@ async function loadBundleImpl(syncEngine, reader, task) {
22829
22990
  if (skip) {
22830
22991
  await reader.close();
22831
22992
  task._completeWith(bundleSuccessProgress(metadata));
22832
- return;
22993
+ return Promise.resolve(new Set());
22833
22994
  }
22834
22995
  task._updateProgress(bundleInitialProgress(metadata));
22835
22996
  const loader = new BundleLoader(metadata, syncEngine.localStore, reader.serializer);
@@ -22843,18 +23004,17 @@ async function loadBundleImpl(syncEngine, reader, task) {
22843
23004
  element = await reader.nextElement();
22844
23005
  }
22845
23006
  const result = await loader.complete();
22846
- // TODO(b/160876443): This currently raises snapshots with
22847
- // `fromCache=false` if users already listen to some queries and bundles
22848
- // has newer version.
22849
23007
  await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, result.changedDocs,
22850
23008
  /* remoteEvent */ undefined);
22851
23009
  // Save metadata, so loading the same bundle will skip.
22852
23010
  await localStoreSaveBundle(syncEngine.localStore, metadata);
22853
23011
  task._completeWith(result.progress);
23012
+ return Promise.resolve(result.changedCollectionGroups);
22854
23013
  }
22855
23014
  catch (e) {
22856
23015
  logWarn(LOG_TAG$3, `Loading bundle failed with ${e}`);
22857
23016
  task._failWith(e);
23017
+ return Promise.resolve(new Set());
22858
23018
  }
22859
23019
  }
22860
23020
 
@@ -22923,7 +23083,6 @@ class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentProvider {
22923
23083
  }
22924
23084
  async initialize(cfg) {
22925
23085
  await super.initialize(cfg);
22926
- await localStoreSynchronizeLastDocumentChangeReadTime(this.localStore);
22927
23086
  await this.onlineComponentProvider.initialize(this, cfg);
22928
23087
  // Enqueue writes from a previous session
22929
23088
  await syncEngineEnsureWriteCallbacks(this.onlineComponentProvider.syncEngine);