@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
@@ -33,7 +33,7 @@ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
33
33
  var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
34
34
 
35
35
  const name = "@firebase/firestore";
36
- const version$1 = "3.4.6";
36
+ const version$1 = "3.4.7-2022222232915";
37
37
 
38
38
  /**
39
39
  * @license
@@ -86,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
86
86
  User.FIRST_PARTY = new User('first-party-uid');
87
87
  User.MOCK_USER = new User('mock-user');
88
88
 
89
- const version = "9.6.9";
89
+ const version = "9.6.10-2022222232915";
90
90
 
91
91
  /**
92
92
  * @license
@@ -1225,6 +1225,24 @@ function decodeResourcePath(path) {
1225
1225
  return new ResourcePath(segments);
1226
1226
  }
1227
1227
 
1228
+ /**
1229
+ * @license
1230
+ * Copyright 2022 Google LLC
1231
+ *
1232
+ * Licensed under the Apache License, Version 2.0 (the "License");
1233
+ * you may not use this file except in compliance with the License.
1234
+ * You may obtain a copy of the License at
1235
+ *
1236
+ * http://www.apache.org/licenses/LICENSE-2.0
1237
+ *
1238
+ * Unless required by applicable law or agreed to in writing, software
1239
+ * distributed under the License is distributed on an "AS IS" BASIS,
1240
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1241
+ * See the License for the specific language governing permissions and
1242
+ * limitations under the License.
1243
+ */
1244
+ const DbRemoteDocumentStore$1 = 'remoteDocuments';
1245
+
1228
1246
  /**
1229
1247
  * @license
1230
1248
  * Copyright 2022 Google LLC
@@ -1295,26 +1313,36 @@ function newDbDocumentMutationKey(userId, path, batchId) {
1295
1313
  */
1296
1314
  const DbDocumentMutationPlaceholder = {};
1297
1315
  const DbDocumentMutationStore = 'documentMutations';
1298
- const DbRemoteDocumentStore = 'remoteDocuments';
1316
+ const DbRemoteDocumentStore = 'remoteDocumentsV14';
1299
1317
  /**
1300
- * An index that provides access to all entries sorted by read time (which
1301
- * corresponds to the last modification time of each row).
1302
- *
1303
- * This index is used to provide a changelog for Multi-Tab.
1318
+ * The primary key of the remote documents store, which allows for efficient
1319
+ * access by collection path and read time.
1304
1320
  */
1305
- const DbRemoteDocumentReadTimeIndex = 'readTimeIndex';
1306
- const DbRemoteDocumentReadTimeIndexPath = 'readTime';
1321
+ const DbRemoteDocumentKeyPath = [
1322
+ 'prefixPath',
1323
+ 'collectionGroup',
1324
+ 'readTime',
1325
+ 'documentId'
1326
+ ];
1327
+ /** An index that provides access to documents by key. */
1328
+ const DbRemoteDocumentDocumentKeyIndex = 'documentKeyIndex';
1329
+ const DbRemoteDocumentDocumentKeyIndexPath = [
1330
+ 'prefixPath',
1331
+ 'collectionGroup',
1332
+ 'documentId'
1333
+ ];
1307
1334
  /**
1308
- * An index that provides access to documents in a collection sorted by read
1335
+ * An index that provides access to documents by collection group and read
1309
1336
  * time.
1310
1337
  *
1311
- * This index is used to allow the RemoteDocumentCache to fetch newly changed
1312
- * documents in a collection.
1338
+ * This index is used by the index backfiller.
1313
1339
  */
1314
- const DbRemoteDocumentCollectionReadTimeIndex = 'collectionReadTimeIndex';
1315
- const DbRemoteDocumentCollectionReadTimeIndexPath = [
1316
- 'parentPath',
1317
- 'readTime'
1340
+ const DbRemoteDocumentCollectionGroupIndex = 'collectionGroupIndex';
1341
+ const DbRemoteDocumentCollectionGroupIndexPath = [
1342
+ 'collectionGroup',
1343
+ 'readTime',
1344
+ 'prefixPath',
1345
+ 'documentId'
1318
1346
  ];
1319
1347
  const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
1320
1348
  const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
@@ -1422,7 +1450,7 @@ const V1_STORES = [
1422
1450
  DbMutationQueueStore,
1423
1451
  DbMutationBatchStore,
1424
1452
  DbDocumentMutationStore,
1425
- DbRemoteDocumentStore,
1453
+ DbRemoteDocumentStore$1,
1426
1454
  DbTargetStore,
1427
1455
  DbPrimaryClientStore,
1428
1456
  DbTargetGlobalStore,
@@ -1437,14 +1465,33 @@ const V8_STORES = [...V6_STORES, DbCollectionParentStore];
1437
1465
  const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
1438
1466
  const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
1439
1467
  const V13_STORES = [
1440
- ...V12_STORES,
1468
+ DbMutationQueueStore,
1469
+ DbMutationBatchStore,
1470
+ DbDocumentMutationStore,
1471
+ DbRemoteDocumentStore,
1472
+ DbTargetStore,
1473
+ DbPrimaryClientStore,
1474
+ DbTargetGlobalStore,
1475
+ DbTargetDocumentStore,
1476
+ DbClientMetadataStore,
1477
+ DbRemoteDocumentGlobalStore,
1478
+ DbCollectionParentStore,
1479
+ DbBundleStore,
1480
+ DbNamedQueryStore,
1481
+ DbDocumentOverlayStore
1482
+ ];
1483
+ const V14_STORES = [
1484
+ ...V13_STORES,
1441
1485
  DbIndexConfigurationStore,
1442
1486
  DbIndexStateStore,
1443
1487
  DbIndexEntryStore
1444
1488
  ];
1445
1489
  /** Returns the object stores for the provided schema. */
1446
1490
  function getObjectStores(schemaVersion) {
1447
- if (schemaVersion === 13) {
1491
+ if (schemaVersion === 14) {
1492
+ return V14_STORES;
1493
+ }
1494
+ else if (schemaVersion === 13) {
1448
1495
  return V13_STORES;
1449
1496
  }
1450
1497
  else if (schemaVersion === 12) {
@@ -2655,6 +2702,9 @@ class SnapshotVersion {
2655
2702
  static min() {
2656
2703
  return new SnapshotVersion(new Timestamp(0, 0));
2657
2704
  }
2705
+ static max() {
2706
+ return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
2707
+ }
2658
2708
  compareTo(other) {
2659
2709
  return this.timestamp._compareTo(other.timestamp);
2660
2710
  }
@@ -4206,6 +4256,28 @@ class IndexState {
4206
4256
  return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
4207
4257
  }
4208
4258
  }
4259
+ /**
4260
+ * Creates an offset that matches all documents with a read time higher than
4261
+ * `readTime`.
4262
+ */
4263
+ function newIndexOffsetSuccessorFromReadTime(readTime, largestBatchId) {
4264
+ // We want to create an offset that matches all documents with a read time
4265
+ // greater than the provided read time. To do so, we technically need to
4266
+ // create an offset for `(readTime, MAX_DOCUMENT_KEY)`. While we could use
4267
+ // Unicode codepoints to generate MAX_DOCUMENT_KEY, it is much easier to use
4268
+ // `(readTime + 1, DocumentKey.empty())` since `> DocumentKey.empty()` matches
4269
+ // all valid document IDs.
4270
+ const successorSeconds = readTime.toTimestamp().seconds;
4271
+ const successorNanos = readTime.toTimestamp().nanoseconds + 1;
4272
+ const successor = SnapshotVersion.fromTimestamp(successorNanos === 1e9
4273
+ ? new Timestamp(successorSeconds + 1, 0)
4274
+ : new Timestamp(successorSeconds, successorNanos));
4275
+ return new IndexOffset(successor, DocumentKey.empty(), largestBatchId);
4276
+ }
4277
+ /** Creates a new offset based on the provided document. */
4278
+ function newIndexOffsetFromDocument(document) {
4279
+ return new IndexOffset(document.readTime, document.key, INITIAL_LARGEST_BATCH_ID);
4280
+ }
4209
4281
  /**
4210
4282
  * Stores the latest read time, document and batch ID that were processed for an
4211
4283
  * index.
@@ -4230,10 +4302,25 @@ class IndexOffset {
4230
4302
  this.documentKey = documentKey;
4231
4303
  this.largestBatchId = largestBatchId;
4232
4304
  }
4233
- /** The state of an index that has not yet been backfilled. */
4305
+ /** Returns an offset that sorts before all regular offsets. */
4234
4306
  static min() {
4235
4307
  return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
4236
4308
  }
4309
+ /** Returns an offset that sorts after all regular offsets. */
4310
+ static max() {
4311
+ return new IndexOffset(SnapshotVersion.max(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
4312
+ }
4313
+ }
4314
+ function indexOffsetComparator(left, right) {
4315
+ let cmp = left.readTime.compareTo(right.readTime);
4316
+ if (cmp !== 0) {
4317
+ return cmp;
4318
+ }
4319
+ cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
4320
+ if (cmp !== 0) {
4321
+ return cmp;
4322
+ }
4323
+ return primitiveComparator(left.largestBatchId, right.largestBatchId);
4237
4324
  }
4238
4325
 
4239
4326
  /**
@@ -5126,6 +5213,18 @@ function queryMatchesBounds(query, doc) {
5126
5213
  }
5127
5214
  return true;
5128
5215
  }
5216
+ /**
5217
+ * Returns the collection group that this query targets.
5218
+ *
5219
+ * PORTING NOTE: This is only used in the Web SDK to facilitate multi-tab
5220
+ * synchronization for query results.
5221
+ */
5222
+ function queryCollectionGroup(query) {
5223
+ return (query.collectionGroup ||
5224
+ (query.path.length % 2 === 1
5225
+ ? query.path.lastSegment()
5226
+ : query.path.get(query.path.length - 2)));
5227
+ }
5129
5228
  /**
5130
5229
  * Returns a new comparator function that can be used to compare two documents
5131
5230
  * based on the Query's ordering constraint.
@@ -8659,40 +8758,33 @@ function fromDbRemoteDocument(localSerializer, remoteDoc) {
8659
8758
  }
8660
8759
  /** Encodes a document for storage locally. */
8661
8760
  function toDbRemoteDocument(localSerializer, document) {
8662
- const parentPath = document.key.path.popLast().toArray();
8663
- const readTime = toDbTimestampKey(document.readTime);
8761
+ const key = document.key;
8762
+ const remoteDoc = {
8763
+ prefixPath: key.getCollectionPath().popLast().toArray(),
8764
+ collectionGroup: key.collectionGroup,
8765
+ documentId: key.path.lastSegment(),
8766
+ readTime: toDbTimestampKey(document.readTime),
8767
+ hasCommittedMutations: document.hasCommittedMutations
8768
+ };
8664
8769
  if (document.isFoundDocument()) {
8665
- const doc = toDocument(localSerializer.remoteSerializer, document);
8666
- const hasCommittedMutations = document.hasCommittedMutations;
8667
- return {
8668
- document: doc,
8669
- hasCommittedMutations,
8670
- readTime,
8671
- parentPath
8672
- };
8770
+ remoteDoc.document = toDocument(localSerializer.remoteSerializer, document);
8673
8771
  }
8674
8772
  else if (document.isNoDocument()) {
8675
- const path = document.key.path.toArray();
8676
- const hasCommittedMutations = document.hasCommittedMutations;
8677
- return {
8678
- noDocument: { path, readTime: toDbTimestamp(document.version) },
8679
- hasCommittedMutations,
8680
- readTime,
8681
- parentPath
8773
+ remoteDoc.noDocument = {
8774
+ path: key.path.toArray(),
8775
+ readTime: toDbTimestamp(document.version)
8682
8776
  };
8683
8777
  }
8684
8778
  else if (document.isUnknownDocument()) {
8685
- const path = document.key.path.toArray();
8686
- return {
8687
- unknownDocument: { path, version: toDbTimestamp(document.version) },
8688
- hasCommittedMutations: true,
8689
- readTime,
8690
- parentPath
8779
+ remoteDoc.unknownDocument = {
8780
+ path: key.path.toArray(),
8781
+ version: toDbTimestamp(document.version)
8691
8782
  };
8692
8783
  }
8693
8784
  else {
8694
8785
  return fail();
8695
8786
  }
8787
+ return remoteDoc;
8696
8788
  }
8697
8789
  function toDbTimestampKey(snapshotVersion) {
8698
8790
  const timestamp = snapshotVersion.toTimestamp();
@@ -11844,7 +11936,7 @@ class IndexedDbRemoteDocumentCacheImpl {
11844
11936
  */
11845
11937
  addEntry(transaction, key, doc) {
11846
11938
  const documentStore = remoteDocumentsStore(transaction);
11847
- return documentStore.put(dbKey(key), doc);
11939
+ return documentStore.put(doc);
11848
11940
  }
11849
11941
  /**
11850
11942
  * Removes a document from the cache.
@@ -11852,10 +11944,9 @@ class IndexedDbRemoteDocumentCacheImpl {
11852
11944
  * All calls of `removeEntry` are required to go through the RemoteDocumentChangeBuffer
11853
11945
  * returned by `newChangeBuffer()` to ensure proper accounting of metadata.
11854
11946
  */
11855
- removeEntry(transaction, documentKey) {
11947
+ removeEntry(transaction, documentKey, readTime) {
11856
11948
  const store = remoteDocumentsStore(transaction);
11857
- const key = dbKey(documentKey);
11858
- return store.delete(key);
11949
+ return store.delete(dbReadTimeKey(documentKey, readTime));
11859
11950
  }
11860
11951
  /**
11861
11952
  * Updates the current cache size.
@@ -11870,11 +11961,15 @@ class IndexedDbRemoteDocumentCacheImpl {
11870
11961
  });
11871
11962
  }
11872
11963
  getEntry(transaction, documentKey) {
11964
+ let doc = MutableDocument.newInvalidDocument(documentKey);
11873
11965
  return remoteDocumentsStore(transaction)
11874
- .get(dbKey(documentKey))
11875
- .next(dbRemoteDoc => {
11876
- return this.maybeDecodeDocument(documentKey, dbRemoteDoc);
11877
- });
11966
+ .iterate({
11967
+ index: DbRemoteDocumentDocumentKeyIndex,
11968
+ range: IDBKeyRange.only(dbKey(documentKey))
11969
+ }, (_, dbRemoteDoc) => {
11970
+ doc = this.maybeDecodeDocument(documentKey, dbRemoteDoc);
11971
+ })
11972
+ .next(() => doc);
11878
11973
  }
11879
11974
  /**
11880
11975
  * Looks up an entry in the cache.
@@ -11883,15 +11978,21 @@ class IndexedDbRemoteDocumentCacheImpl {
11883
11978
  * @returns The cached document entry and its size.
11884
11979
  */
11885
11980
  getSizedEntry(transaction, documentKey) {
11981
+ let result = {
11982
+ size: 0,
11983
+ document: MutableDocument.newInvalidDocument(documentKey)
11984
+ };
11886
11985
  return remoteDocumentsStore(transaction)
11887
- .get(dbKey(documentKey))
11888
- .next(dbRemoteDoc => {
11889
- const doc = this.maybeDecodeDocument(documentKey, dbRemoteDoc);
11890
- return {
11891
- document: doc,
11986
+ .iterate({
11987
+ index: DbRemoteDocumentDocumentKeyIndex,
11988
+ range: IDBKeyRange.only(dbKey(documentKey))
11989
+ }, (_, dbRemoteDoc) => {
11990
+ result = {
11991
+ document: this.maybeDecodeDocument(documentKey, dbRemoteDoc),
11892
11992
  size: dbDocumentSize(dbRemoteDoc)
11893
11993
  };
11894
- });
11994
+ })
11995
+ .next(() => result);
11895
11996
  }
11896
11997
  getEntries(transaction, documentKeys) {
11897
11998
  let results = mutableDocumentMap();
@@ -11922,14 +12023,20 @@ class IndexedDbRemoteDocumentCacheImpl {
11922
12023
  if (documentKeys.isEmpty()) {
11923
12024
  return PersistencePromise.resolve();
11924
12025
  }
11925
- const range = IDBKeyRange.bound(documentKeys.first().path.toArray(), documentKeys.last().path.toArray());
11926
- const keyIter = documentKeys.getIterator();
12026
+ let sortedKeys = new SortedSet(dbKeyComparator);
12027
+ documentKeys.forEach(e => (sortedKeys = sortedKeys.add(e)));
12028
+ const range = IDBKeyRange.bound(dbKey(sortedKeys.first()), dbKey(sortedKeys.last()));
12029
+ const keyIter = sortedKeys.getIterator();
11927
12030
  let nextKey = keyIter.getNext();
11928
12031
  return remoteDocumentsStore(transaction)
11929
- .iterate({ range }, (potentialKeyRaw, dbRemoteDoc, control) => {
11930
- const potentialKey = DocumentKey.fromSegments(potentialKeyRaw);
12032
+ .iterate({ index: DbRemoteDocumentDocumentKeyIndex, range }, (_, dbRemoteDoc, control) => {
12033
+ const potentialKey = DocumentKey.fromSegments([
12034
+ ...dbRemoteDoc.prefixPath,
12035
+ dbRemoteDoc.collectionGroup,
12036
+ dbRemoteDoc.documentId
12037
+ ]);
11931
12038
  // Go through keys not found in cache.
11932
- while (nextKey && DocumentKey.comparator(nextKey, potentialKey) < 0) {
12039
+ while (nextKey && dbKeyComparator(nextKey, potentialKey) < 0) {
11933
12040
  callback(nextKey, null);
11934
12041
  nextKey = keyIter.getNext();
11935
12042
  }
@@ -11940,7 +12047,7 @@ class IndexedDbRemoteDocumentCacheImpl {
11940
12047
  }
11941
12048
  // Skip to the next key (if there is one).
11942
12049
  if (nextKey) {
11943
- control.skip(nextKey.path.toArray());
12050
+ control.skip(dbKey(nextKey));
11944
12051
  }
11945
12052
  else {
11946
12053
  control.done();
@@ -11955,41 +12062,44 @@ class IndexedDbRemoteDocumentCacheImpl {
11955
12062
  }
11956
12063
  });
11957
12064
  }
11958
- getAll(transaction, collection, sinceReadTime) {
11959
- let results = mutableDocumentMap();
11960
- const immediateChildrenPathLength = collection.length + 1;
11961
- const iterationOptions = {};
11962
- if (sinceReadTime.isEqual(SnapshotVersion.min())) {
11963
- // Documents are ordered by key, so we can use a prefix scan to narrow
11964
- // down the documents we need to match the query against.
11965
- const startKey = collection.toArray();
11966
- iterationOptions.range = IDBKeyRange.lowerBound(startKey);
11967
- }
11968
- else {
11969
- // Execute an index-free query and filter by read time. This is safe
11970
- // since all document changes to queries that have a
11971
- // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
11972
- const collectionKey = collection.toArray();
11973
- const readTimeKey = toDbTimestampKey(sinceReadTime);
11974
- iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
11975
- /* open= */ true);
11976
- iterationOptions.index = DbRemoteDocumentCollectionReadTimeIndex;
11977
- }
12065
+ getAllFromCollection(transaction, collection, offset) {
12066
+ const startKey = [
12067
+ collection.popLast().toArray(),
12068
+ collection.lastSegment(),
12069
+ toDbTimestampKey(offset.readTime),
12070
+ offset.documentKey.path.isEmpty()
12071
+ ? ''
12072
+ : offset.documentKey.path.lastSegment()
12073
+ ];
12074
+ const endKey = [
12075
+ collection.popLast().toArray(),
12076
+ collection.lastSegment(),
12077
+ [Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
12078
+ ''
12079
+ ];
11978
12080
  return remoteDocumentsStore(transaction)
11979
- .iterate(iterationOptions, (key, dbRemoteDoc, control) => {
11980
- // The query is actually returning any path that starts with the query
11981
- // path prefix which may include documents in subcollections. For
11982
- // example, a query on 'rooms' will return rooms/abc/messages/xyx but we
11983
- // shouldn't match it. Fix this by discarding rows with document keys
11984
- // more than one segment longer than the query path.
11985
- if (key.length !== immediateChildrenPathLength) {
11986
- return;
11987
- }
11988
- const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
11989
- if (collection.isPrefixOf(document.key.path)) {
12081
+ .loadAll(IDBKeyRange.bound(startKey, endKey, true))
12082
+ .next(dbRemoteDocs => {
12083
+ let results = mutableDocumentMap();
12084
+ for (const dbRemoteDoc of dbRemoteDocs) {
12085
+ const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
11990
12086
  results = results.insert(document.key, document);
11991
12087
  }
11992
- else {
12088
+ return results;
12089
+ });
12090
+ }
12091
+ getAllFromCollectionGroup(transaction, collectionGroup, offset, limit) {
12092
+ let results = mutableDocumentMap();
12093
+ const startKey = dbCollectionGroupKey(collectionGroup, offset);
12094
+ const endKey = dbCollectionGroupKey(collectionGroup, IndexOffset.max());
12095
+ return remoteDocumentsStore(transaction)
12096
+ .iterate({
12097
+ index: DbRemoteDocumentCollectionGroupIndex,
12098
+ range: IDBKeyRange.bound(startKey, endKey, true)
12099
+ }, (_, dbRemoteDoc, control) => {
12100
+ const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
12101
+ results = results.insert(document.key, document);
12102
+ if (results.size === limit) {
11993
12103
  control.done();
11994
12104
  }
11995
12105
  })
@@ -12033,50 +12143,6 @@ class IndexedDbRemoteDocumentCacheImpl {
12033
12143
  function newIndexedDbRemoteDocumentCache(serializer) {
12034
12144
  return new IndexedDbRemoteDocumentCacheImpl(serializer);
12035
12145
  }
12036
- /**
12037
- * Returns the set of documents that have changed since the specified read
12038
- * time.
12039
- */
12040
- // PORTING NOTE: This is only used for multi-tab synchronization.
12041
- function remoteDocumentCacheGetNewDocumentChanges(remoteDocumentCache, transaction, sinceReadTime) {
12042
- const remoteDocumentCacheImpl = debugCast(remoteDocumentCache);
12043
- let changedDocs = mutableDocumentMap();
12044
- let lastReadTime = toDbTimestampKey(sinceReadTime);
12045
- const documentsStore = remoteDocumentsStore(transaction);
12046
- const range = IDBKeyRange.lowerBound(lastReadTime, true);
12047
- return documentsStore
12048
- .iterate({ index: DbRemoteDocumentReadTimeIndex, range }, (_, dbRemoteDoc) => {
12049
- // Unlike `getEntry()` and others, `getNewDocumentChanges()` parses
12050
- // the documents directly since we want to keep sentinel deletes.
12051
- const doc = fromDbRemoteDocument(remoteDocumentCacheImpl.serializer, dbRemoteDoc);
12052
- changedDocs = changedDocs.insert(doc.key, doc);
12053
- lastReadTime = dbRemoteDoc.readTime;
12054
- })
12055
- .next(() => {
12056
- return {
12057
- changedDocs,
12058
- readTime: fromDbTimestampKey(lastReadTime)
12059
- };
12060
- });
12061
- }
12062
- /**
12063
- * Returns the read time of the most recently read document in the cache, or
12064
- * SnapshotVersion.min() if not available.
12065
- */
12066
- // PORTING NOTE: This is only used for multi-tab synchronization.
12067
- function remoteDocumentCacheGetLastReadTime(transaction) {
12068
- const documentsStore = remoteDocumentsStore(transaction);
12069
- // If there are no existing entries, we return SnapshotVersion.min().
12070
- let readTime = SnapshotVersion.min();
12071
- return documentsStore
12072
- .iterate({ index: DbRemoteDocumentReadTimeIndex, reverse: true }, (key, dbRemoteDoc, control) => {
12073
- if (dbRemoteDoc.readTime) {
12074
- readTime = fromDbTimestampKey(dbRemoteDoc.readTime);
12075
- }
12076
- control.done();
12077
- })
12078
- .next(() => readTime);
12079
- }
12080
12146
  /**
12081
12147
  * Handles the details of adding and updating documents in the IndexedDbRemoteDocumentCache.
12082
12148
  *
@@ -12094,24 +12160,26 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12094
12160
  super();
12095
12161
  this.documentCache = documentCache;
12096
12162
  this.trackRemovals = trackRemovals;
12097
- // A map of document sizes prior to applying the changes in this buffer.
12098
- this.documentSizes = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
12163
+ // A map of document sizes and read times prior to applying the changes in
12164
+ // this buffer.
12165
+ this.documentStates = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
12099
12166
  }
12100
12167
  applyChanges(transaction) {
12101
12168
  const promises = [];
12102
12169
  let sizeDelta = 0;
12103
12170
  let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
12104
12171
  this.changes.forEach((key, documentChange) => {
12105
- const previousSize = this.documentSizes.get(key);
12172
+ const previousDoc = this.documentStates.get(key);
12173
+ promises.push(this.documentCache.removeEntry(transaction, key, previousDoc.readTime));
12106
12174
  if (documentChange.isValidDocument()) {
12107
12175
  const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
12108
12176
  collectionParents = collectionParents.add(key.path.popLast());
12109
12177
  const size = dbDocumentSize(doc);
12110
- sizeDelta += size - previousSize;
12178
+ sizeDelta += size - previousDoc.size;
12111
12179
  promises.push(this.documentCache.addEntry(transaction, key, doc));
12112
12180
  }
12113
12181
  else {
12114
- sizeDelta -= previousSize;
12182
+ sizeDelta -= previousDoc.size;
12115
12183
  if (this.trackRemovals) {
12116
12184
  // In order to track removals, we store a "sentinel delete" in the
12117
12185
  // RemoteDocumentCache. This entry is represented by a NoDocument
@@ -12120,9 +12188,6 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12120
12188
  const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
12121
12189
  promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
12122
12190
  }
12123
- else {
12124
- promises.push(this.documentCache.removeEntry(transaction, key));
12125
- }
12126
12191
  }
12127
12192
  });
12128
12193
  collectionParents.forEach(parent => {
@@ -12136,7 +12201,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12136
12201
  return this.documentCache
12137
12202
  .getSizedEntry(transaction, documentKey)
12138
12203
  .next(getResult => {
12139
- this.documentSizes.set(documentKey, getResult.size);
12204
+ this.documentStates.set(documentKey, {
12205
+ size: getResult.size,
12206
+ readTime: getResult.document.readTime
12207
+ });
12140
12208
  return getResult.document;
12141
12209
  });
12142
12210
  }
@@ -12150,7 +12218,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
12150
12218
  // keys to `DocumentSizeEntry`s. This is to allow returning the
12151
12219
  // `MutableDocumentMap` directly, without a conversion.
12152
12220
  sizeMap.forEach((documentKey, size) => {
12153
- this.documentSizes.set(documentKey, size);
12221
+ this.documentStates.set(documentKey, {
12222
+ size,
12223
+ readTime: documents.get(documentKey).readTime
12224
+ });
12154
12225
  });
12155
12226
  return documents;
12156
12227
  });
@@ -12165,8 +12236,52 @@ function documentGlobalStore(txn) {
12165
12236
  function remoteDocumentsStore(txn) {
12166
12237
  return getStore(txn, DbRemoteDocumentStore);
12167
12238
  }
12168
- function dbKey(docKey) {
12169
- return docKey.path.toArray();
12239
+ /**
12240
+ * Returns a key that can be used for document lookups on the
12241
+ * `DbRemoteDocumentDocumentKeyIndex` index.
12242
+ */
12243
+ function dbKey(documentKey) {
12244
+ const path = documentKey.path.toArray();
12245
+ return [
12246
+ /* prefix path */ path.slice(0, path.length - 2),
12247
+ /* collection id */ path[path.length - 2],
12248
+ /* document id */ path[path.length - 1]
12249
+ ];
12250
+ }
12251
+ /**
12252
+ * Returns a key that can be used for document lookups via the primary key of
12253
+ * the DbRemoteDocument object store.
12254
+ */
12255
+ function dbReadTimeKey(documentKey, readTime) {
12256
+ const path = documentKey.path.toArray();
12257
+ return [
12258
+ /* prefix path */ path.slice(0, path.length - 2),
12259
+ /* collection id */ path[path.length - 2],
12260
+ toDbTimestampKey(readTime),
12261
+ /* document id */ path[path.length - 1]
12262
+ ];
12263
+ }
12264
+ /**
12265
+ * Returns a key that can be used for document lookups on the
12266
+ * `DbRemoteDocumentDocumentCollectionGroupIndex` index.
12267
+ */
12268
+ function dbCollectionGroupKey(collectionGroup, offset) {
12269
+ const path = offset.documentKey.path.toArray();
12270
+ return [
12271
+ /* collection id */ collectionGroup,
12272
+ toDbTimestampKey(offset.readTime),
12273
+ /* prefix path */ path.slice(0, path.length - 2),
12274
+ /* document id */ path.length > 0 ? path[path.length - 1] : ''
12275
+ ];
12276
+ }
12277
+ /**
12278
+ * Comparator that compares document keys according to the primary key sorting
12279
+ * used by the `DbRemoteDocumentDocument` store (by collection path and then
12280
+ * document ID).
12281
+ */
12282
+ function dbKeyComparator(l, r) {
12283
+ const cmp = l.path.length - r.path.length;
12284
+ return cmp !== 0 ? cmp : DocumentKey.comparator(l, r);
12170
12285
  }
12171
12286
  /**
12172
12287
  * Schema Version for the Web client:
@@ -12187,9 +12302,11 @@ function dbKey(docKey) {
12187
12302
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
12188
12303
  * 11. Add bundles and named_queries for bundle support.
12189
12304
  * 12. Add document overlays.
12190
- * 13. Add indexing support.
12305
+ * 13. Rewrite the keys of the remote document cache to allow for efficient
12306
+ * document lookup via `getAll()`.
12307
+ * 14. Add indexing support.
12191
12308
  */
12192
- const SCHEMA_VERSION = 12;
12309
+ const SCHEMA_VERSION = 13;
12193
12310
 
12194
12311
  /**
12195
12312
  * @license
@@ -12225,7 +12342,7 @@ class SchemaConverter {
12225
12342
  createPrimaryClientStore(db);
12226
12343
  createMutationQueue(db);
12227
12344
  createQueryCache(db);
12228
- createRemoteDocumentCache(db);
12345
+ createLegacyRemoteDocumentCache(db);
12229
12346
  }
12230
12347
  // Migration 2 to populate the targetGlobal object no longer needed since
12231
12348
  // migration 3 unconditionally clears it.
@@ -12274,7 +12391,8 @@ class SchemaConverter {
12274
12391
  // to the DbRemoteDocument object store itself. Since the previous change
12275
12392
  // log only contained transient data, we can drop its object store.
12276
12393
  dropRemoteDocumentChangesStore(db);
12277
- createRemoteDocumentReadTimeIndex(txn);
12394
+ // Note: Schema version 9 used to create a read time index for the
12395
+ // RemoteDocumentCache. This is now done with schema version 13.
12278
12396
  });
12279
12397
  }
12280
12398
  if (fromVersion < 10 && toVersion >= 10) {
@@ -12292,6 +12410,12 @@ class SchemaConverter {
12292
12410
  });
12293
12411
  }
12294
12412
  if (fromVersion < 13 && toVersion >= 13) {
12413
+ p = p
12414
+ .next(() => createRemoteDocumentCache(db))
12415
+ .next(() => this.rewriteRemoteDocumentCache(db, simpleDbTransaction))
12416
+ .next(() => db.deleteObjectStore(DbRemoteDocumentStore$1));
12417
+ }
12418
+ if (fromVersion < 14 && toVersion >= 14) {
12295
12419
  p = p.next(() => {
12296
12420
  createFieldIndex(db);
12297
12421
  });
@@ -12301,7 +12425,7 @@ class SchemaConverter {
12301
12425
  addDocumentGlobal(txn) {
12302
12426
  let byteSize = 0;
12303
12427
  return txn
12304
- .store(DbRemoteDocumentStore)
12428
+ .store(DbRemoteDocumentStore$1)
12305
12429
  .iterate((_, doc) => {
12306
12430
  byteSize += dbDocumentSize(doc);
12307
12431
  })
@@ -12336,7 +12460,7 @@ class SchemaConverter {
12336
12460
  */
12337
12461
  ensureSequenceNumbers(txn) {
12338
12462
  const documentTargetStore = txn.store(DbTargetDocumentStore);
12339
- const documentsStore = txn.store(DbRemoteDocumentStore);
12463
+ const documentsStore = txn.store(DbRemoteDocumentStore$1);
12340
12464
  const globalTargetStore = txn.store(DbTargetGlobalStore);
12341
12465
  return globalTargetStore.get(DbTargetGlobalKey).next(metadata => {
12342
12466
  const writeSentinelKey = (path) => {
@@ -12383,7 +12507,7 @@ class SchemaConverter {
12383
12507
  };
12384
12508
  // Index existing remote documents.
12385
12509
  return txn
12386
- .store(DbRemoteDocumentStore)
12510
+ .store(DbRemoteDocumentStore$1)
12387
12511
  .iterate({ keysOnly: true }, (pathSegments, _) => {
12388
12512
  const path = new ResourcePath(pathSegments);
12389
12513
  return addEntry(path.popLast());
@@ -12406,6 +12530,27 @@ class SchemaConverter {
12406
12530
  return targetStore.put(updatedDbTarget);
12407
12531
  });
12408
12532
  }
12533
+ rewriteRemoteDocumentCache(db, transaction) {
12534
+ const legacyRemoteDocumentStore = transaction.store(DbRemoteDocumentStore$1);
12535
+ const writes = [];
12536
+ return legacyRemoteDocumentStore
12537
+ .iterate((_, legacyDocument) => {
12538
+ const remoteDocumentStore = transaction.store(DbRemoteDocumentStore);
12539
+ const path = extractKey(legacyDocument).path.toArray();
12540
+ const dbRemoteDocument = {
12541
+ prefixPath: path.slice(0, path.length - 2),
12542
+ collectionGroup: path[path.length - 2],
12543
+ documentId: path[path.length - 1],
12544
+ readTime: legacyDocument.readTime || [0, 0],
12545
+ unknownDocument: legacyDocument.unknownDocument,
12546
+ noDocument: legacyDocument.noDocument,
12547
+ document: legacyDocument.document,
12548
+ hasCommittedMutations: !!legacyDocument.hasCommittedMutations
12549
+ };
12550
+ writes.push(remoteDocumentStore.put(dbRemoteDocument));
12551
+ })
12552
+ .next(() => PersistencePromise.waitFor(writes));
12553
+ }
12409
12554
  }
12410
12555
  function sentinelKey(path) {
12411
12556
  return [0, encodeResourcePath(path)];
@@ -12442,8 +12587,15 @@ function upgradeMutationBatchSchemaAndMigrateData(db, txn) {
12442
12587
  return PersistencePromise.waitFor(writeAll);
12443
12588
  });
12444
12589
  }
12590
+ function createLegacyRemoteDocumentCache(db) {
12591
+ db.createObjectStore(DbRemoteDocumentStore$1);
12592
+ }
12445
12593
  function createRemoteDocumentCache(db) {
12446
- db.createObjectStore(DbRemoteDocumentStore);
12594
+ const remoteDocumentStore = db.createObjectStore(DbRemoteDocumentStore, {
12595
+ keyPath: DbRemoteDocumentKeyPath
12596
+ });
12597
+ remoteDocumentStore.createIndex(DbRemoteDocumentDocumentKeyIndex, DbRemoteDocumentDocumentKeyIndexPath);
12598
+ remoteDocumentStore.createIndex(DbRemoteDocumentCollectionGroupIndex, DbRemoteDocumentCollectionGroupIndexPath);
12447
12599
  }
12448
12600
  function createDocumentGlobalStore(db) {
12449
12601
  db.createObjectStore(DbRemoteDocumentGlobalStore);
@@ -12485,15 +12637,6 @@ function writeEmptyTargetGlobalEntry(txn) {
12485
12637
  };
12486
12638
  return globalStore.put(DbTargetGlobalKey, metadata);
12487
12639
  }
12488
- /**
12489
- * Creates indices on the RemoteDocuments store used for both multi-tab
12490
- * and Index-Free queries.
12491
- */
12492
- function createRemoteDocumentReadTimeIndex(txn) {
12493
- const remoteDocumentStore = txn.objectStore(DbRemoteDocumentStore);
12494
- remoteDocumentStore.createIndex(DbRemoteDocumentReadTimeIndex, DbRemoteDocumentReadTimeIndexPath, { unique: false });
12495
- remoteDocumentStore.createIndex(DbRemoteDocumentCollectionReadTimeIndex, DbRemoteDocumentCollectionReadTimeIndexPath, { unique: false });
12496
- }
12497
12640
  function createClientMetadataStore(db) {
12498
12641
  db.createObjectStore(DbClientMetadataStore, {
12499
12642
  keyPath: DbClientMetadataKeyPath
@@ -12510,11 +12653,11 @@ function createNamedQueriesStore(db) {
12510
12653
  });
12511
12654
  }
12512
12655
  function createFieldIndex(db) {
12513
- const indexConfiguratioStore = db.createObjectStore(DbIndexConfigurationStore, {
12656
+ const indexConfigurationStore = db.createObjectStore(DbIndexConfigurationStore, {
12514
12657
  keyPath: DbIndexConfigurationKeyPath,
12515
12658
  autoIncrement: true
12516
12659
  });
12517
- indexConfiguratioStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
12660
+ indexConfigurationStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
12518
12661
  const indexStateStore = db.createObjectStore(DbIndexStateStore, {
12519
12662
  keyPath: DbIndexStateKeyPath
12520
12663
  });
@@ -12530,6 +12673,20 @@ function createDocumentOverlayStore(db) {
12530
12673
  });
12531
12674
  documentOverlayStore.createIndex(DbDocumentOverlayCollectionPathOverlayIndex, DbDocumentOverlayCollectionPathOverlayIndexPath, { unique: false });
12532
12675
  documentOverlayStore.createIndex(DbDocumentOverlayCollectionGroupOverlayIndex, DbDocumentOverlayCollectionGroupOverlayIndexPath, { unique: false });
12676
+ }
12677
+ function extractKey(remoteDoc) {
12678
+ if (remoteDoc.document) {
12679
+ return new DocumentKey(ResourcePath.fromString(remoteDoc.document.name).popFirst(5));
12680
+ }
12681
+ else if (remoteDoc.noDocument) {
12682
+ return DocumentKey.fromSegments(remoteDoc.noDocument.path);
12683
+ }
12684
+ else if (remoteDoc.unknownDocument) {
12685
+ return DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
12686
+ }
12687
+ else {
12688
+ return fail();
12689
+ }
12533
12690
  }
12534
12691
 
12535
12692
  /**
@@ -13372,18 +13529,17 @@ class LocalDocumentsView {
13372
13529
  *
13373
13530
  * @param transaction - The persistence transaction.
13374
13531
  * @param query - The query to match documents against.
13375
- * @param sinceReadTime - If not set to SnapshotVersion.min(), return only
13376
- * documents that have been read since this snapshot version (exclusive).
13532
+ * @param offset - Read time and key to start scanning by (exclusive).
13377
13533
  */
13378
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
13534
+ getDocumentsMatchingQuery(transaction, query, offset) {
13379
13535
  if (isDocumentQuery$1(query)) {
13380
13536
  return this.getDocumentsMatchingDocumentQuery(transaction, query.path);
13381
13537
  }
13382
13538
  else if (isCollectionGroupQuery(query)) {
13383
- return this.getDocumentsMatchingCollectionGroupQuery(transaction, query, sinceReadTime);
13539
+ return this.getDocumentsMatchingCollectionGroupQuery(transaction, query, offset);
13384
13540
  }
13385
13541
  else {
13386
- return this.getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime);
13542
+ return this.getDocumentsMatchingCollectionQuery(transaction, query, offset);
13387
13543
  }
13388
13544
  }
13389
13545
  getDocumentsMatchingDocumentQuery(transaction, docPath) {
@@ -13396,7 +13552,7 @@ class LocalDocumentsView {
13396
13552
  return result;
13397
13553
  });
13398
13554
  }
13399
- getDocumentsMatchingCollectionGroupQuery(transaction, query, sinceReadTime) {
13555
+ getDocumentsMatchingCollectionGroupQuery(transaction, query, offset) {
13400
13556
  const collectionId = query.collectionGroup;
13401
13557
  let results = documentMap();
13402
13558
  return this.indexManager
@@ -13406,7 +13562,7 @@ class LocalDocumentsView {
13406
13562
  // collectionId and aggregate the results.
13407
13563
  return PersistencePromise.forEach(parents, (parent) => {
13408
13564
  const collectionQuery = asCollectionQueryAtPath(query, parent.child(collectionId));
13409
- return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery, sinceReadTime).next(r => {
13565
+ return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery, offset).next(r => {
13410
13566
  r.forEach((key, doc) => {
13411
13567
  results = results.insert(key, doc);
13412
13568
  });
@@ -13414,11 +13570,11 @@ class LocalDocumentsView {
13414
13570
  }).next(() => results);
13415
13571
  });
13416
13572
  }
13417
- getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
13573
+ getDocumentsMatchingCollectionQuery(transaction, query, offset) {
13418
13574
  // Query the remote documents and overlay mutations.
13419
13575
  let results;
13420
13576
  return this.remoteDocumentCache
13421
- .getAll(transaction, query.path, sinceReadTime)
13577
+ .getAllFromCollection(transaction, query.path, offset)
13422
13578
  .next(queryResults => {
13423
13579
  results = queryResults;
13424
13580
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
@@ -13504,11 +13660,12 @@ class LocalStoreImpl {
13504
13660
  // TODO(wuandy): Evaluate if TargetId can be part of Target.
13505
13661
  this.targetIdByTarget = new ObjectMap(t => canonifyTarget(t), targetEquals);
13506
13662
  /**
13507
- * The read time of the last entry processed by `getNewDocumentChanges()`.
13663
+ * A per collection group index of the last read time processed by
13664
+ * `getNewDocumentChanges()`.
13508
13665
  *
13509
13666
  * PORTING NOTE: This is only used for multi-tab synchronization.
13510
13667
  */
13511
- this.lastDocumentChangeReadTime = SnapshotVersion.min();
13668
+ this.collectionGroupReadTime = new Map();
13512
13669
  this.remoteDocuments = persistence.getRemoteDocumentCache();
13513
13670
  this.targetCache = persistence.getTargetCache();
13514
13671
  this.bundleCache = persistence.getBundleCache();
@@ -14043,6 +14200,7 @@ function localStoreExecuteQuery(localStore, query, usePreviousResults) {
14043
14200
  ? lastLimboFreeSnapshotVersion
14044
14201
  : SnapshotVersion.min(), usePreviousResults ? remoteKeys : documentKeySet()))
14045
14202
  .next(documents => {
14203
+ setMaxReadTime(localStoreImpl, queryCollectionGroup(query), documents);
14046
14204
  return { documents, remoteKeys };
14047
14205
  });
14048
14206
  });
@@ -14120,28 +14278,32 @@ function localStoreGetCachedTarget(localStore, targetId) {
14120
14278
  * since the prior call.
14121
14279
  */
14122
14280
  // PORTING NOTE: Multi-Tab only.
14123
- function localStoreGetNewDocumentChanges(localStore) {
14281
+ function localStoreGetNewDocumentChanges(localStore, collectionGroup) {
14124
14282
  const localStoreImpl = debugCast(localStore);
14283
+ // Get the current maximum read time for the collection. This should always
14284
+ // exist, but to reduce the chance for regressions we default to
14285
+ // SnapshotVersion.Min()
14286
+ // TODO(indexing): Consider removing the default value.
14287
+ const readTime = localStoreImpl.collectionGroupReadTime.get(collectionGroup) ||
14288
+ SnapshotVersion.min();
14125
14289
  return localStoreImpl.persistence
14126
- .runTransaction('Get new document changes', 'readonly', txn => remoteDocumentCacheGetNewDocumentChanges(localStoreImpl.remoteDocuments, txn, localStoreImpl.lastDocumentChangeReadTime))
14127
- .then(({ changedDocs, readTime }) => {
14128
- localStoreImpl.lastDocumentChangeReadTime = readTime;
14290
+ .runTransaction('Get new document changes', 'readonly', txn => localStoreImpl.remoteDocuments.getAllFromCollectionGroup(txn, collectionGroup, newIndexOffsetSuccessorFromReadTime(readTime, INITIAL_LARGEST_BATCH_ID),
14291
+ /* limit= */ Number.MAX_SAFE_INTEGER))
14292
+ .then(changedDocs => {
14293
+ setMaxReadTime(localStoreImpl, collectionGroup, changedDocs);
14129
14294
  return changedDocs;
14130
14295
  });
14131
14296
  }
14132
- /**
14133
- * Reads the newest document change from persistence and moves the internal
14134
- * synchronization marker forward so that calls to `getNewDocumentChanges()`
14135
- * only return changes that happened after client initialization.
14136
- */
14297
+ /** Sets the collection group's maximum read time from the given documents. */
14137
14298
  // PORTING NOTE: Multi-Tab only.
14138
- async function localStoreSynchronizeLastDocumentChangeReadTime(localStore) {
14139
- const localStoreImpl = debugCast(localStore);
14140
- return localStoreImpl.persistence
14141
- .runTransaction('Synchronize last document change read time', 'readonly', txn => remoteDocumentCacheGetLastReadTime(txn))
14142
- .then(readTime => {
14143
- localStoreImpl.lastDocumentChangeReadTime = readTime;
14299
+ function setMaxReadTime(localStoreImpl, collectionGroup, changedDocs) {
14300
+ let readTime = SnapshotVersion.min();
14301
+ changedDocs.forEach((_, doc) => {
14302
+ if (doc.readTime.compareTo(readTime) > 0) {
14303
+ readTime = doc.readTime;
14304
+ }
14144
14305
  });
14306
+ localStoreImpl.collectionGroupReadTime.set(collectionGroup, readTime);
14145
14307
  }
14146
14308
  /**
14147
14309
  * Creates a new target using the given bundle name, which will be used to
@@ -14824,7 +14986,7 @@ class MemoryRemoteDocumentCacheImpl {
14824
14986
  });
14825
14987
  return PersistencePromise.resolve(results);
14826
14988
  }
14827
- getAll(transaction, collectionPath, sinceReadTime) {
14989
+ getAllFromCollection(transaction, collectionPath, offset) {
14828
14990
  let results = mutableDocumentMap();
14829
14991
  // Documents are ordered by key, so we can use a prefix scan to narrow down
14830
14992
  // the documents we need to match the query against.
@@ -14839,13 +15001,19 @@ class MemoryRemoteDocumentCacheImpl {
14839
15001
  // Exclude entries from subcollections.
14840
15002
  continue;
14841
15003
  }
14842
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
15004
+ if (indexOffsetComparator(newIndexOffsetFromDocument(document), offset) <= 0) {
15005
+ // The document sorts before the offset.
14843
15006
  continue;
14844
15007
  }
14845
15008
  results = results.insert(document.key, document.mutableCopy());
14846
15009
  }
14847
15010
  return PersistencePromise.resolve(results);
14848
15011
  }
15012
+ getAllFromCollectionGroup(transaction, collectionGroup, offset, limti) {
15013
+ // This method should only be called from the IndexBackfiller if persistence
15014
+ // is enabled.
15015
+ fail();
15016
+ }
14849
15017
  forEachDocumentKey(transaction, f) {
14850
15018
  return PersistencePromise.forEach(this.docs, (key) => f(key));
14851
15019
  }
@@ -15298,7 +15466,7 @@ class QueryEngine {
15298
15466
  }
15299
15467
  // Retrieve all results for documents that were updated since the last
15300
15468
  // limbo-document free remote snapshot.
15301
- return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, lastLimboFreeSnapshotVersion).next(updatedResults => {
15469
+ return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, newIndexOffsetSuccessorFromReadTime(lastLimboFreeSnapshotVersion, INITIAL_LARGEST_BATCH_ID)).next(updatedResults => {
15302
15470
  // We merge `previousResults` into `updateResults`, since
15303
15471
  // `updateResults` is already a DocumentMap. If a document is
15304
15472
  // contained in both lists, then its contents are the same.
@@ -15360,7 +15528,7 @@ class QueryEngine {
15360
15528
  if (getLogLevel() <= logger.LogLevel.DEBUG) {
15361
15529
  logDebug('QueryEngine', 'Using full collection scan to execute query:', stringifyQuery(query));
15362
15530
  }
15363
- return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, SnapshotVersion.min());
15531
+ return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, IndexOffset.min());
15364
15532
  }
15365
15533
  }
15366
15534
 
@@ -15421,8 +15589,9 @@ function createWebStorageOnlineStateKey(persistenceKey) {
15421
15589
  // The WebStorage prefix that plays as a event to indicate the remote documents
15422
15590
  // might have changed due to some secondary tabs loading a bundle.
15423
15591
  // format of the key is:
15424
- // firestore_bundle_loaded_<persistenceKey>
15425
- const BUNDLE_LOADED_KEY_PREFIX = 'firestore_bundle_loaded';
15592
+ // firestore_bundle_loaded_v2_<persistenceKey>
15593
+ // The version ending with "v2" stores the list of modified collection groups.
15594
+ const BUNDLE_LOADED_KEY_PREFIX = 'firestore_bundle_loaded_v2';
15426
15595
  function createBundleLoadedKey(persistenceKey) {
15427
15596
  return `${BUNDLE_LOADED_KEY_PREFIX}_${persistenceKey}`;
15428
15597
  }
@@ -15800,8 +15969,8 @@ class WebStorageSharedClientState {
15800
15969
  setOnlineState(onlineState) {
15801
15970
  this.persistOnlineState(onlineState);
15802
15971
  }
15803
- notifyBundleLoaded() {
15804
- this.persistBundleLoadedState();
15972
+ notifyBundleLoaded(collectionGroups) {
15973
+ this.persistBundleLoadedState(collectionGroups);
15805
15974
  }
15806
15975
  shutdown() {
15807
15976
  if (this.started) {
@@ -15885,7 +16054,8 @@ class WebStorageSharedClientState {
15885
16054
  }
15886
16055
  }
15887
16056
  else if (storageEvent.key === this.bundleLoadedKey) {
15888
- return this.syncEngine.synchronizeWithChangedDocuments();
16057
+ const collectionGroups = this.fromWebStoreBundleLoadedState(storageEvent.newValue);
16058
+ await Promise.all(collectionGroups.map(cg => this.syncEngine.synchronizeWithChangedDocuments(cg)));
15889
16059
  }
15890
16060
  });
15891
16061
  }
@@ -15917,8 +16087,9 @@ class WebStorageSharedClientState {
15917
16087
  const targetMetadata = new QueryTargetMetadata(targetId, state, error);
15918
16088
  this.setItem(targetKey, targetMetadata.toWebStorageJSON());
15919
16089
  }
15920
- persistBundleLoadedState() {
15921
- this.setItem(this.bundleLoadedKey, 'value-not-used');
16090
+ persistBundleLoadedState(collectionGroups) {
16091
+ const json = JSON.stringify(Array.from(collectionGroups));
16092
+ this.setItem(this.bundleLoadedKey, json);
15922
16093
  }
15923
16094
  /**
15924
16095
  * Parses a client state key in WebStorage. Returns null if the key does not
@@ -15962,6 +16133,9 @@ class WebStorageSharedClientState {
15962
16133
  fromWebStorageOnlineState(value) {
15963
16134
  return SharedOnlineState.fromWebStorageEntry(value);
15964
16135
  }
16136
+ fromWebStoreBundleLoadedState(value) {
16137
+ return JSON.parse(value);
16138
+ }
15965
16139
  async handleMutationBatchEvent(mutationBatch) {
15966
16140
  if (mutationBatch.user.uid !== this.currentUser.uid) {
15967
16141
  logDebug(LOG_TAG$a, `Ignoring mutation for non-active user ${mutationBatch.user.uid}`);
@@ -16078,7 +16252,7 @@ class MemorySharedClientState {
16078
16252
  }
16079
16253
  shutdown() { }
16080
16254
  writeSequenceNumber(sequenceNumber) { }
16081
- notifyBundleLoaded() {
16255
+ notifyBundleLoaded(collectionGroups) {
16082
16256
  // No op.
16083
16257
  }
16084
16258
  }
@@ -21378,29 +21552,6 @@ class LocalViewChanges {
21378
21552
  }
21379
21553
  }
21380
21554
 
21381
- /**
21382
- * @license
21383
- * Copyright 2020 Google LLC
21384
- *
21385
- * Licensed under the Apache License, Version 2.0 (the "License");
21386
- * you may not use this file except in compliance with the License.
21387
- * You may obtain a copy of the License at
21388
- *
21389
- * http://www.apache.org/licenses/LICENSE-2.0
21390
- *
21391
- * Unless required by applicable law or agreed to in writing, software
21392
- * distributed under the License is distributed on an "AS IS" BASIS,
21393
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21394
- * See the License for the specific language governing permissions and
21395
- * limitations under the License.
21396
- */
21397
- class BundleLoadResult {
21398
- constructor(progress, changedDocs) {
21399
- this.progress = progress;
21400
- this.changedDocs = changedDocs;
21401
- }
21402
- }
21403
-
21404
21555
  /**
21405
21556
  * @license
21406
21557
  * Copyright 2020 Google LLC
@@ -21455,6 +21606,8 @@ class BundleLoader {
21455
21606
  this.queries = [];
21456
21607
  /** Batched documents to be saved into storage */
21457
21608
  this.documents = [];
21609
+ /** The collection groups affected by this bundle. */
21610
+ this.collectionGroups = new Set();
21458
21611
  this.progress = bundleInitialProgress(bundleMetadata);
21459
21612
  }
21460
21613
  /**
@@ -21474,6 +21627,8 @@ class BundleLoader {
21474
21627
  if (!element.payload.documentMetadata.exists) {
21475
21628
  ++documentsLoaded;
21476
21629
  }
21630
+ const path = ResourcePath.fromString(element.payload.documentMetadata.name);
21631
+ this.collectionGroups.add(path.get(path.length - 2));
21477
21632
  }
21478
21633
  else if (element.payload.document) {
21479
21634
  this.documents[this.documents.length - 1].document =
@@ -21504,13 +21659,17 @@ class BundleLoader {
21504
21659
  * Update the progress to 'Success' and return the updated progress.
21505
21660
  */
21506
21661
  async complete() {
21507
- const changedDocuments = await localStoreApplyBundledDocuments(this.localStore, new BundleConverterImpl(this.serializer), this.documents, this.bundleMetadata.id);
21662
+ const changedDocs = await localStoreApplyBundledDocuments(this.localStore, new BundleConverterImpl(this.serializer), this.documents, this.bundleMetadata.id);
21508
21663
  const queryDocumentMap = this.getQueryDocumentMapping(this.documents);
21509
21664
  for (const q of this.queries) {
21510
21665
  await localStoreSaveNamedQuery(this.localStore, q, queryDocumentMap.get(q.name));
21511
21666
  }
21512
21667
  this.progress.taskState = 'Success';
21513
- return new BundleLoadResult(Object.assign({}, this.progress), changedDocuments);
21668
+ return {
21669
+ progress: this.progress,
21670
+ changedCollectionGroups: this.collectionGroups,
21671
+ changedDocs
21672
+ };
21514
21673
  }
21515
21674
  }
21516
21675
  /**
@@ -22589,9 +22748,9 @@ async function synchronizeViewAndComputeSnapshot(syncEngine, queryView) {
22589
22748
  * snapshots if needed.
22590
22749
  */
22591
22750
  // PORTING NOTE: Multi-Tab only.
22592
- async function syncEngineSynchronizeWithChangedDocuments(syncEngine) {
22751
+ async function syncEngineSynchronizeWithChangedDocuments(syncEngine, collectionGroup) {
22593
22752
  const syncEngineImpl = debugCast(syncEngine);
22594
- return localStoreGetNewDocumentChanges(syncEngineImpl.localStore).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
22753
+ return localStoreGetNewDocumentChanges(syncEngineImpl.localStore, collectionGroup).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
22595
22754
  }
22596
22755
  /** Applies a mutation state to an existing batch. */
22597
22756
  // PORTING NOTE: Multi-Tab only.
@@ -22756,11 +22915,12 @@ async function syncEngineApplyTargetState(syncEngine, targetId, state, error) {
22756
22915
  logDebug(LOG_TAG$3, 'Ignoring unexpected query state notification.');
22757
22916
  return;
22758
22917
  }
22759
- if (syncEngineImpl.queriesByTarget.has(targetId)) {
22918
+ const query = syncEngineImpl.queriesByTarget.get(targetId);
22919
+ if (query && query.length > 0) {
22760
22920
  switch (state) {
22761
22921
  case 'current':
22762
22922
  case 'not-current': {
22763
- const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore);
22923
+ const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore, queryCollectionGroup(query[0]));
22764
22924
  const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(targetId, state === 'current');
22765
22925
  await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes, synthesizedRemoteEvent);
22766
22926
  break;
@@ -22843,10 +23003,11 @@ function syncEngineEnsureWriteCallbacks(syncEngine) {
22843
23003
  function syncEngineLoadBundle(syncEngine, bundleReader, task) {
22844
23004
  const syncEngineImpl = debugCast(syncEngine);
22845
23005
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
22846
- loadBundleImpl(syncEngineImpl, bundleReader, task).then(() => {
22847
- syncEngineImpl.sharedClientState.notifyBundleLoaded();
23006
+ loadBundleImpl(syncEngineImpl, bundleReader, task).then(collectionGroups => {
23007
+ syncEngineImpl.sharedClientState.notifyBundleLoaded(collectionGroups);
22848
23008
  });
22849
23009
  }
23010
+ /** Loads a bundle and returns the list of affected collection groups. */
22850
23011
  async function loadBundleImpl(syncEngine, reader, task) {
22851
23012
  try {
22852
23013
  const metadata = await reader.getMetadata();
@@ -22854,7 +23015,7 @@ async function loadBundleImpl(syncEngine, reader, task) {
22854
23015
  if (skip) {
22855
23016
  await reader.close();
22856
23017
  task._completeWith(bundleSuccessProgress(metadata));
22857
- return;
23018
+ return Promise.resolve(new Set());
22858
23019
  }
22859
23020
  task._updateProgress(bundleInitialProgress(metadata));
22860
23021
  const loader = new BundleLoader(metadata, syncEngine.localStore, reader.serializer);
@@ -22868,18 +23029,17 @@ async function loadBundleImpl(syncEngine, reader, task) {
22868
23029
  element = await reader.nextElement();
22869
23030
  }
22870
23031
  const result = await loader.complete();
22871
- // TODO(b/160876443): This currently raises snapshots with
22872
- // `fromCache=false` if users already listen to some queries and bundles
22873
- // has newer version.
22874
23032
  await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, result.changedDocs,
22875
23033
  /* remoteEvent */ undefined);
22876
23034
  // Save metadata, so loading the same bundle will skip.
22877
23035
  await localStoreSaveBundle(syncEngine.localStore, metadata);
22878
23036
  task._completeWith(result.progress);
23037
+ return Promise.resolve(result.changedCollectionGroups);
22879
23038
  }
22880
23039
  catch (e) {
22881
23040
  logWarn(LOG_TAG$3, `Loading bundle failed with ${e}`);
22882
23041
  task._failWith(e);
23042
+ return Promise.resolve(new Set());
22883
23043
  }
22884
23044
  }
22885
23045
 
@@ -22948,7 +23108,6 @@ class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentProvider {
22948
23108
  }
22949
23109
  async initialize(cfg) {
22950
23110
  await super.initialize(cfg);
22951
- await localStoreSynchronizeLastDocumentChangeReadTime(this.localStore);
22952
23111
  await this.onlineComponentProvider.initialize(this, cfg);
22953
23112
  // Enqueue writes from a previous session
22954
23113
  await syncEngineEnsureWriteCallbacks(this.onlineComponentProvider.syncEngine);