@firebase/firestore 3.4.4-canary.2dd99ae2d → 3.4.4-canary.3fe2c4a03

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 (120) hide show
  1. package/dist/firestore/src/api/snapshot.d.ts +1 -2
  2. package/dist/firestore/src/core/target.d.ts +8 -4
  3. package/dist/firestore/src/lite-api/query.d.ts +2 -2
  4. package/dist/firestore/src/local/index_manager.d.ts +52 -0
  5. package/dist/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  6. package/dist/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  7. package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  8. package/dist/firestore/src/local/indexeddb_schema.d.ts +149 -7
  9. package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
  10. package/dist/firestore/src/local/local_serializer.d.ts +7 -2
  11. package/dist/firestore/src/local/memory_index_manager.d.ts +11 -0
  12. package/dist/firestore/src/local/memory_persistence.d.ts +3 -2
  13. package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  14. package/dist/firestore/src/local/persistence.d.ts +2 -2
  15. package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
  16. package/dist/firestore/src/local/simple_db.d.ts +1 -0
  17. package/dist/firestore/src/model/field_index.d.ts +2 -2
  18. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  19. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  20. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  21. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  22. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  23. package/dist/firestore/test/util/helpers.d.ts +1 -1
  24. package/dist/index.d.ts +1 -2
  25. package/dist/index.esm2017.js +3357 -3133
  26. package/dist/index.esm2017.js.map +1 -1
  27. package/dist/index.esm5.js +3588 -3382
  28. package/dist/index.esm5.js.map +1 -1
  29. package/dist/index.node.cjs.js +475 -212
  30. package/dist/index.node.cjs.js.map +1 -1
  31. package/dist/index.node.mjs +475 -212
  32. package/dist/index.node.mjs.map +1 -1
  33. package/dist/index.rn.js +3535 -3311
  34. package/dist/index.rn.js.map +1 -1
  35. package/dist/internal.d.ts +178 -14
  36. package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
  37. package/dist/lite/firestore/src/core/target.d.ts +8 -4
  38. package/dist/lite/firestore/src/lite-api/query.d.ts +2 -2
  39. package/dist/lite/firestore/src/local/index_manager.d.ts +52 -0
  40. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  41. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  42. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  43. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +149 -7
  44. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  45. package/dist/lite/firestore/src/local/local_serializer.d.ts +7 -2
  46. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +11 -0
  47. package/dist/lite/firestore/src/local/memory_persistence.d.ts +3 -2
  48. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  49. package/dist/lite/firestore/src/local/persistence.d.ts +2 -2
  50. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  51. package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
  52. package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
  53. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  54. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  55. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  56. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  57. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  58. package/dist/lite/firestore/test/util/helpers.d.ts +1 -1
  59. package/dist/lite/index.browser.esm2017.js +1146 -1143
  60. package/dist/lite/index.browser.esm2017.js.map +1 -1
  61. package/dist/lite/index.browser.esm5.js +332 -331
  62. package/dist/lite/index.browser.esm5.js.map +1 -1
  63. package/dist/lite/index.node.cjs.js +36 -29
  64. package/dist/lite/index.node.cjs.js.map +1 -1
  65. package/dist/lite/index.node.mjs +36 -29
  66. package/dist/lite/index.node.mjs.map +1 -1
  67. package/dist/lite/index.rn.esm2017.js +301 -298
  68. package/dist/lite/index.rn.esm2017.js.map +1 -1
  69. package/dist/lite/internal.d.ts +2 -2
  70. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +121 -121
  71. package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
  72. package/dist/lite/packages/firestore/src/core/target.d.ts +8 -4
  73. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +2 -2
  74. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +52 -0
  75. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  76. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  77. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  78. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +149 -7
  79. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  80. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +7 -2
  81. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  82. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +3 -2
  83. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  84. package/dist/lite/packages/firestore/src/local/persistence.d.ts +2 -2
  85. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  86. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
  87. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
  88. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  89. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  90. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  91. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  92. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  93. package/dist/lite/packages/firestore/test/util/helpers.d.ts +1 -1
  94. package/dist/lite/private.d.ts +2 -2
  95. package/dist/packages/firestore/dist/index.esm2017.d.ts +177 -177
  96. package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
  97. package/dist/packages/firestore/src/core/target.d.ts +8 -4
  98. package/dist/packages/firestore/src/lite-api/query.d.ts +2 -2
  99. package/dist/packages/firestore/src/local/index_manager.d.ts +52 -0
  100. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  101. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  102. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  103. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +149 -7
  104. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  105. package/dist/packages/firestore/src/local/local_serializer.d.ts +7 -2
  106. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  107. package/dist/packages/firestore/src/local/memory_persistence.d.ts +3 -2
  108. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  109. package/dist/packages/firestore/src/local/persistence.d.ts +2 -2
  110. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  111. package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
  112. package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
  113. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  114. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  115. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  116. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  117. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  118. package/dist/packages/firestore/test/util/helpers.d.ts +1 -1
  119. package/dist/private.d.ts +178 -14
  120. package/package.json +9 -9
@@ -12,7 +12,7 @@ var grpcJs = require('@grpc/grpc-js');
12
12
  var protoLoader = require('@grpc/proto-loader');
13
13
 
14
14
  const name = "@firebase/firestore";
15
- const version$1 = "3.4.4-canary.2dd99ae2d";
15
+ const version$1 = "3.4.4-canary.3fe2c4a03";
16
16
 
17
17
  /**
18
18
  * @license
@@ -65,7 +65,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
65
65
  User.FIRST_PARTY = new User('first-party-uid');
66
66
  User.MOCK_USER = new User('mock-user');
67
67
 
68
- const version = "9.6.5-canary.2dd99ae2d";
68
+ const version = "9.6.6-canary.3fe2c4a03";
69
69
 
70
70
  /**
71
71
  * @license
@@ -1196,23 +1196,6 @@ function decodeResourcePath(path) {
1196
1196
  }
1197
1197
  return new ResourcePath(segments);
1198
1198
  }
1199
-
1200
- /**
1201
- * @license
1202
- * Copyright 2017 Google LLC
1203
- *
1204
- * Licensed under the Apache License, Version 2.0 (the "License");
1205
- * you may not use this file except in compliance with the License.
1206
- * You may obtain a copy of the License at
1207
- *
1208
- * http://www.apache.org/licenses/LICENSE-2.0
1209
- *
1210
- * Unless required by applicable law or agreed to in writing, software
1211
- * distributed under the License is distributed on an "AS IS" BASIS,
1212
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1213
- * See the License for the specific language governing permissions and
1214
- * limitations under the License.
1215
- */
1216
1199
  /**
1217
1200
  * Schema Version for the Web client:
1218
1201
  * 1. Initial version including Mutation Queue, Query Cache, and Remote
@@ -1231,6 +1214,7 @@ function decodeResourcePath(path) {
1231
1214
  * an auto-incrementing ID. This is required for Index-Free queries.
1232
1215
  * 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
1233
1216
  * 11. Add bundles and named_queries for bundle support.
1217
+ * 12. Add indexing support.
1234
1218
  */
1235
1219
  const SCHEMA_VERSION = 11;
1236
1220
  /**
@@ -1757,9 +1741,7 @@ class DbClientMetadata {
1757
1741
  DbClientMetadata.store = 'clientMetadata';
1758
1742
  /** Keys are automatically assigned via the clientId properties. */
1759
1743
  DbClientMetadata.keyPath = 'clientId';
1760
- /**
1761
- * A object representing a bundle loaded by the SDK.
1762
- */
1744
+ /** An object representing a bundle loaded by the SDK. */
1763
1745
  class DbBundle {
1764
1746
  constructor(
1765
1747
  /** The ID of the loaded bundle. */
@@ -1776,9 +1758,7 @@ class DbBundle {
1776
1758
  /** Name of the IndexedDb object store. */
1777
1759
  DbBundle.store = 'bundles';
1778
1760
  DbBundle.keyPath = 'bundleId';
1779
- /**
1780
- * A object representing a named query loaded by the SDK via a bundle.
1781
- */
1761
+ /** An object representing a named query loaded by the SDK via a bundle. */
1782
1762
  class DbNamedQuery {
1783
1763
  constructor(
1784
1764
  /** The name of the query. */
@@ -1795,6 +1775,116 @@ class DbNamedQuery {
1795
1775
  /** Name of the IndexedDb object store. */
1796
1776
  DbNamedQuery.store = 'namedQueries';
1797
1777
  DbNamedQuery.keyPath = 'name';
1778
+ /** An object representing the global configuration for a field index. */
1779
+ class DbIndexConfiguration {
1780
+ constructor(
1781
+ /**
1782
+ * The index id for this entry. Undefined for indexes that are not yet
1783
+ * persisted.
1784
+ */
1785
+ indexId,
1786
+ /** The collection group this index belongs to. */
1787
+ collectionGroup,
1788
+ /** The fields to index for this index. */
1789
+ fields) {
1790
+ this.indexId = indexId;
1791
+ this.collectionGroup = collectionGroup;
1792
+ this.fields = fields;
1793
+ }
1794
+ }
1795
+ /** Name of the IndexedDb object store. */
1796
+ DbIndexConfiguration.store = 'indexConfiguration';
1797
+ DbIndexConfiguration.keyPath = 'indexId';
1798
+ /**
1799
+ * An index that provides access to the index configurations by collection
1800
+ * group.
1801
+ *
1802
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1803
+ * not possible here as the Web client supports concurrent access to
1804
+ * persistence via multi-tab.
1805
+ */
1806
+ DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
1807
+ DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
1808
+ /**
1809
+ * An object describing how up-to-date the index backfill is for each user and
1810
+ * index.
1811
+ */
1812
+ class DbIndexState {
1813
+ constructor(
1814
+ /** The index id for this entry. */
1815
+ indexId,
1816
+ /** The user id for this entry. */
1817
+ uid,
1818
+ /**
1819
+ * A number that indicates when the index was last updated (relative to
1820
+ * other indexes).
1821
+ */
1822
+ sequenceNumber,
1823
+ /**
1824
+ * The latest read time that has been indexed by Firestore for this field
1825
+ * index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
1826
+ */
1827
+ readTime,
1828
+ /**
1829
+ * The last document that has been indexed for this field index. Empty if
1830
+ * no documents have been indexed.
1831
+ */
1832
+ documentKey,
1833
+ /**
1834
+ * The largest mutation batch id that has been processed for this index. -1
1835
+ * if no mutations have been indexed.
1836
+ */
1837
+ largestBatchId) {
1838
+ this.indexId = indexId;
1839
+ this.uid = uid;
1840
+ this.sequenceNumber = sequenceNumber;
1841
+ this.readTime = readTime;
1842
+ this.documentKey = documentKey;
1843
+ this.largestBatchId = largestBatchId;
1844
+ }
1845
+ }
1846
+ /** Name of the IndexedDb object store. */
1847
+ DbIndexState.store = 'indexState';
1848
+ DbIndexState.keyPath = ['indexId', 'uid'];
1849
+ /**
1850
+ * An index that provides access to documents in a collection sorted by last
1851
+ * update time. Used by the backfiller.
1852
+ *
1853
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1854
+ * not possible here as the Web client supports concurrent access to
1855
+ * persistence via multi-tab.
1856
+ */
1857
+ DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1858
+ DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1859
+ /** An object that stores the encoded entries for all documents and fields. */
1860
+ class DbIndexEntry {
1861
+ constructor(
1862
+ /** The index id for this entry. */
1863
+ indexId,
1864
+ /** The user id for this entry. */
1865
+ uid,
1866
+ /** The encoded array index value for this entry. */
1867
+ arrayValue,
1868
+ /** The encoded directional value for equality and inequality filters. */
1869
+ directionalValue,
1870
+ /** The document key this entry points to. */
1871
+ documentKey) {
1872
+ this.indexId = indexId;
1873
+ this.uid = uid;
1874
+ this.arrayValue = arrayValue;
1875
+ this.directionalValue = directionalValue;
1876
+ this.documentKey = documentKey;
1877
+ }
1878
+ }
1879
+ /** Name of the IndexedDb object store. */
1880
+ DbIndexEntry.store = 'indexEntries';
1881
+ DbIndexEntry.keyPath = [
1882
+ 'indexId',
1883
+ 'uid',
1884
+ 'arrayValue',
1885
+ 'directionalValue',
1886
+ 'documentKey'
1887
+ ];
1798
1888
  // Visible for testing
1799
1889
  const V1_STORES = [
1800
1890
  DbMutationQueue.store,
@@ -1809,7 +1899,6 @@ const V1_STORES = [
1809
1899
  // V2 is no longer usable (see comment at top of file)
1810
1900
  // Visible for testing
1811
1901
  const V3_STORES = V1_STORES;
1812
- // Visible for testing
1813
1902
  // Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
1814
1903
  const V4_STORES = [...V3_STORES, DbClientMetadata.store];
1815
1904
  // V5 does not change the set of stores.
@@ -1819,12 +1908,24 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1819
1908
  // V9 does not change the set of stores.
1820
1909
  // V10 does not change the set of stores.
1821
1910
  const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1822
- /**
1823
- * The list of all default IndexedDB stores used throughout the SDK. This is
1824
- * used when creating transactions so that access across all stores is done
1825
- * atomically.
1826
- */
1827
- const ALL_STORES = V11_STORES;
1911
+ const V12_STORES = [
1912
+ ...V11_STORES,
1913
+ DbIndexConfiguration.store,
1914
+ DbIndexState.store,
1915
+ DbIndexEntry.store
1916
+ ];
1917
+ /** Returns the object stores for the provided schema. */
1918
+ function getObjectStores(schemaVersion) {
1919
+ if (schemaVersion === 12) {
1920
+ return V12_STORES;
1921
+ }
1922
+ else if (schemaVersion === 11) {
1923
+ return V11_STORES;
1924
+ }
1925
+ else {
1926
+ fail();
1927
+ }
1928
+ }
1828
1929
 
1829
1930
  /**
1830
1931
  * @license
@@ -2121,6 +2222,15 @@ class SimpleDbTransaction {
2121
2222
  this.transaction.abort();
2122
2223
  }
2123
2224
  }
2225
+ maybeCommit() {
2226
+ // If the browser supports V3 IndexedDB, we invoke commit() explicitly to
2227
+ // speed up index DB processing if the event loop remains blocks.
2228
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2229
+ const maybeV3IndexedDb = this.transaction;
2230
+ if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
2231
+ maybeV3IndexedDb.commit();
2232
+ }
2233
+ }
2124
2234
  /**
2125
2235
  * Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
2126
2236
  * operations performed on the SimpleDbStore happen within the context of this
@@ -2317,6 +2427,10 @@ class SimpleDb {
2317
2427
  this.db = await this.ensureDb(action);
2318
2428
  const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
2319
2429
  const transactionFnResult = transactionFn(transaction)
2430
+ .next(result => {
2431
+ transaction.maybeCommit();
2432
+ return result;
2433
+ })
2320
2434
  .catch(error => {
2321
2435
  // Abort the transaction if there was an error.
2322
2436
  transaction.abort(error);
@@ -4349,12 +4463,12 @@ class FieldIndex {
4349
4463
  /** The collection ID this index applies to. */
4350
4464
  collectionGroup,
4351
4465
  /** The field segments for this index. */
4352
- segments,
4466
+ fields,
4353
4467
  /** Shows how up-to-date the index is for the current user. */
4354
4468
  indexState) {
4355
4469
  this.indexId = indexId;
4356
4470
  this.collectionGroup = collectionGroup;
4357
- this.segments = segments;
4471
+ this.fields = fields;
4358
4472
  this.indexState = indexState;
4359
4473
  }
4360
4474
  }
@@ -4464,27 +4578,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
4464
4578
  function canonifyTarget(target) {
4465
4579
  const targetImpl = debugCast(target);
4466
4580
  if (targetImpl.memoizedCanonicalId === null) {
4467
- let canonicalId = targetImpl.path.canonicalString();
4581
+ let str = targetImpl.path.canonicalString();
4468
4582
  if (targetImpl.collectionGroup !== null) {
4469
- canonicalId += '|cg:' + targetImpl.collectionGroup;
4583
+ str += '|cg:' + targetImpl.collectionGroup;
4470
4584
  }
4471
- canonicalId += '|f:';
4472
- canonicalId += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4473
- canonicalId += '|ob:';
4474
- canonicalId += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4585
+ str += '|f:';
4586
+ str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4587
+ str += '|ob:';
4588
+ str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4475
4589
  if (!isNullOrUndefined(targetImpl.limit)) {
4476
- canonicalId += '|l:';
4477
- canonicalId += targetImpl.limit;
4590
+ str += '|l:';
4591
+ str += targetImpl.limit;
4478
4592
  }
4479
4593
  if (targetImpl.startAt) {
4480
- canonicalId += '|lb:';
4481
- canonicalId += canonifyBound(targetImpl.startAt);
4594
+ str += '|lb:';
4595
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
4596
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
4482
4597
  }
4483
4598
  if (targetImpl.endAt) {
4484
- canonicalId += '|ub:';
4485
- canonicalId += canonifyBound(targetImpl.endAt);
4599
+ str += '|ub:';
4600
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
4601
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
4486
4602
  }
4487
- targetImpl.memoizedCanonicalId = canonicalId;
4603
+ targetImpl.memoizedCanonicalId = str;
4488
4604
  }
4489
4605
  return targetImpl.memoizedCanonicalId;
4490
4606
  }
@@ -4507,10 +4623,14 @@ function stringifyTarget(target) {
4507
4623
  .join(', ')}]`;
4508
4624
  }
4509
4625
  if (target.startAt) {
4510
- str += ', startAt: ' + canonifyBound(target.startAt);
4626
+ str += ', startAt: ';
4627
+ str += target.startAt.inclusive ? 'b:' : 'a:';
4628
+ str += target.startAt.position.map(p => canonicalId(p)).join(',');
4511
4629
  }
4512
4630
  if (target.endAt) {
4513
- str += ', endAt: ' + canonifyBound(target.endAt);
4631
+ str += ', endAt: ';
4632
+ str += target.endAt.inclusive ? 'a:' : 'b:';
4633
+ str += target.endAt.position.map(p => canonicalId(p)).join(',');
4514
4634
  }
4515
4635
  return `Target(${str})`;
4516
4636
  }
@@ -4733,7 +4853,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
4733
4853
  return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
4734
4854
  }
4735
4855
  }
4736
- // TODO(indexing): Change Bound.before to "inclusive"
4737
4856
  /**
4738
4857
  * Represents a bound of a query.
4739
4858
  *
@@ -4749,17 +4868,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
4749
4868
  * just after the provided values.
4750
4869
  */
4751
4870
  class Bound {
4752
- constructor(position, before) {
4871
+ constructor(position, inclusive) {
4753
4872
  this.position = position;
4754
- this.before = before;
4873
+ this.inclusive = inclusive;
4755
4874
  }
4756
4875
  }
4757
- function canonifyBound(bound) {
4758
- // TODO(b/29183165): Make this collision robust.
4759
- return `${bound.before ? 'b' : 'a'}:${bound.position
4760
- .map(p => canonicalId(p))
4761
- .join(',')}`;
4762
- }
4763
4876
  /**
4764
4877
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
4765
4878
  */
@@ -4779,11 +4892,7 @@ function stringifyOrderBy(orderBy) {
4779
4892
  function orderByEquals(left, right) {
4780
4893
  return left.dir === right.dir && left.field.isEqual(right.field);
4781
4894
  }
4782
- /**
4783
- * Returns true if a document sorts before a bound using the provided sort
4784
- * order.
4785
- */
4786
- function sortsBeforeDocument(bound, orderBy, doc) {
4895
+ function boundCompareToDocument(bound, orderBy, doc) {
4787
4896
  let comparison = 0;
4788
4897
  for (let i = 0; i < bound.position.length; i++) {
4789
4898
  const orderByComponent = orderBy[i];
@@ -4802,7 +4911,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
4802
4911
  break;
4803
4912
  }
4804
4913
  }
4805
- return bound.before ? comparison <= 0 : comparison < 0;
4914
+ return comparison;
4915
+ }
4916
+ /**
4917
+ * Returns true if a document sorts after a bound using the provided sort
4918
+ * order.
4919
+ */
4920
+ function boundSortsAfterDocument(bound, orderBy, doc) {
4921
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
4922
+ return bound.inclusive ? comparison >= 0 : comparison > 0;
4923
+ }
4924
+ /**
4925
+ * Returns true if a document sorts before a bound using the provided sort
4926
+ * order.
4927
+ */
4928
+ function boundSortsBeforeDocument(bound, orderBy, doc) {
4929
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
4930
+ return bound.inclusive ? comparison <= 0 : comparison < 0;
4806
4931
  }
4807
4932
  function boundEquals(left, right) {
4808
4933
  if (left === null) {
@@ -4811,7 +4936,7 @@ function boundEquals(left, right) {
4811
4936
  else if (right === null) {
4812
4937
  return false;
4813
4938
  }
4814
- if (left.before !== right.before ||
4939
+ if (left.inclusive !== right.inclusive ||
4815
4940
  left.position.length !== right.position.length) {
4816
4941
  return false;
4817
4942
  }
@@ -5015,10 +5140,10 @@ function queryToTarget(query) {
5015
5140
  }
5016
5141
  // We need to swap the cursors to match the now-flipped query ordering.
5017
5142
  const startAt = queryImpl.endAt
5018
- ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.before)
5143
+ ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
5019
5144
  : null;
5020
5145
  const endAt = queryImpl.startAt
5021
- ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.before)
5146
+ ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
5022
5147
  : null;
5023
5148
  // Now return as a LimitType.First query.
5024
5149
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
@@ -5106,11 +5231,11 @@ function queryMatchesFilters(query, doc) {
5106
5231
  /** Makes sure a document is within the bounds, if provided. */
5107
5232
  function queryMatchesBounds(query, doc) {
5108
5233
  if (query.startAt &&
5109
- !sortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5234
+ !boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5110
5235
  return false;
5111
5236
  }
5112
5237
  if (query.endAt &&
5113
- sortsBeforeDocument(query.endAt, queryOrderBy(query), doc)) {
5238
+ !boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
5114
5239
  return false;
5115
5240
  }
5116
5241
  return true;
@@ -7989,10 +8114,10 @@ function toQueryTarget(serializer, target) {
7989
8114
  result.structuredQuery.limit = limit;
7990
8115
  }
7991
8116
  if (target.startAt) {
7992
- result.structuredQuery.startAt = toCursor(target.startAt);
8117
+ result.structuredQuery.startAt = toStartAtCursor(target.startAt);
7993
8118
  }
7994
8119
  if (target.endAt) {
7995
- result.structuredQuery.endAt = toCursor(target.endAt);
8120
+ result.structuredQuery.endAt = toEndAtCursor(target.endAt);
7996
8121
  }
7997
8122
  return result;
7998
8123
  }
@@ -8025,11 +8150,11 @@ function convertQueryTargetToQuery(target) {
8025
8150
  }
8026
8151
  let startAt = null;
8027
8152
  if (query.startAt) {
8028
- startAt = fromCursor(query.startAt);
8153
+ startAt = fromStartAtCursor(query.startAt);
8029
8154
  }
8030
8155
  let endAt = null;
8031
8156
  if (query.endAt) {
8032
- endAt = fromCursor(query.endAt);
8157
+ endAt = fromEndAtCursor(query.endAt);
8033
8158
  }
8034
8159
  return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
8035
8160
  }
@@ -8120,16 +8245,27 @@ function toOrder(orderBys) {
8120
8245
  function fromOrder(orderBys) {
8121
8246
  return orderBys.map(order => fromPropertyOrder(order));
8122
8247
  }
8123
- function toCursor(cursor) {
8248
+ function toStartAtCursor(cursor) {
8124
8249
  return {
8125
- before: cursor.before,
8250
+ before: cursor.inclusive,
8126
8251
  values: cursor.position
8127
8252
  };
8128
8253
  }
8129
- function fromCursor(cursor) {
8130
- const before = !!cursor.before;
8254
+ function toEndAtCursor(cursor) {
8255
+ return {
8256
+ before: !cursor.inclusive,
8257
+ values: cursor.position
8258
+ };
8259
+ }
8260
+ function fromStartAtCursor(cursor) {
8261
+ const inclusive = !!cursor.before;
8262
+ const position = cursor.values || [];
8263
+ return new Bound(position, inclusive);
8264
+ }
8265
+ function fromEndAtCursor(cursor) {
8266
+ const inclusive = !cursor.before;
8131
8267
  const position = cursor.values || [];
8132
- return new Bound(position, before);
8268
+ return new Bound(position, inclusive);
8133
8269
  }
8134
8270
  // visible for testing
8135
8271
  function toDirection(dir) {
@@ -8593,6 +8729,19 @@ function fromBundleMetadata(metadata) {
8593
8729
  version: metadata.version,
8594
8730
  createTime: fromVersion(metadata.createTime)
8595
8731
  };
8732
+ }
8733
+ function toDbIndexConfiguration(index) {
8734
+ return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8735
+ }
8736
+ function fromDbIndexConfiguration(index, state) {
8737
+ const decodedState = state
8738
+ ? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
8739
+ : IndexState.empty();
8740
+ const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
8741
+ return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8742
+ }
8743
+ function toDbIndexState(indexId, user, sequenceNumber, offset) {
8744
+ return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8596
8745
  }
8597
8746
 
8598
8747
  /**
@@ -8682,6 +8831,38 @@ class MemoryIndexManager {
8682
8831
  getCollectionParents(transaction, collectionId) {
8683
8832
  return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8684
8833
  }
8834
+ addFieldIndex(transaction, index) {
8835
+ // Field indices are not supported with memory persistence.
8836
+ return PersistencePromise.resolve();
8837
+ }
8838
+ deleteFieldIndex(transaction, index) {
8839
+ // Field indices are not supported with memory persistence.
8840
+ return PersistencePromise.resolve();
8841
+ }
8842
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8843
+ // Field indices are not supported with memory persistence.
8844
+ return PersistencePromise.resolve(documentKeySet());
8845
+ }
8846
+ getFieldIndex(transaction, target) {
8847
+ // Field indices are not supported with memory persistence.
8848
+ return PersistencePromise.resolve(null);
8849
+ }
8850
+ getFieldIndexes(transaction, collectionGroup) {
8851
+ // Field indices are not supported with memory persistence.
8852
+ return PersistencePromise.resolve([]);
8853
+ }
8854
+ getNextCollectionGroupToUpdate(transaction) {
8855
+ // Field indices are not supported with memory persistence.
8856
+ return PersistencePromise.resolve(null);
8857
+ }
8858
+ updateCollectionGroup(transaction, collectionGroup, offset) {
8859
+ // Field indices are not supported with memory persistence.
8860
+ return PersistencePromise.resolve();
8861
+ }
8862
+ updateIndexEntries(transaction, documents) {
8863
+ // Field indices are not supported with memory persistence.
8864
+ return PersistencePromise.resolve();
8865
+ }
8685
8866
  }
8686
8867
  /**
8687
8868
  * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
@@ -8733,17 +8914,22 @@ class MemoryCollectionParentIndex {
8733
8914
  */
8734
8915
  /**
8735
8916
  * A persisted implementation of IndexManager.
8917
+ *
8918
+ * PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
8919
+ * data as it supports multi-tab access.
8736
8920
  */
8737
8921
  class IndexedDbIndexManager {
8738
- constructor() {
8922
+ constructor(user) {
8923
+ this.user = user;
8739
8924
  /**
8740
8925
  * An in-memory copy of the index entries we've already written since the SDK
8741
8926
  * launched. Used to avoid re-writing the same entry repeatedly.
8742
8927
  *
8743
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8744
- * satisfy reads.
8928
+ * This is *NOT* a complete cache of what's in persistence and so can never be
8929
+ * used to satisfy reads.
8745
8930
  */
8746
8931
  this.collectionParentsCache = new MemoryCollectionParentIndex();
8932
+ this.uid = user.uid || '';
8747
8933
  }
8748
8934
  /**
8749
8935
  * Adds a new entry to the collection parent index.
@@ -8790,6 +8976,85 @@ class IndexedDbIndexManager {
8790
8976
  return parentPaths;
8791
8977
  });
8792
8978
  }
8979
+ addFieldIndex(transaction, index) {
8980
+ // TODO(indexing): Verify that the auto-incrementing index ID works in
8981
+ // Safari & Firefox.
8982
+ const indexes = indexConfigurationStore(transaction);
8983
+ const dbIndex = toDbIndexConfiguration(index);
8984
+ delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
8985
+ return indexes.add(dbIndex).next();
8986
+ }
8987
+ deleteFieldIndex(transaction, index) {
8988
+ const indexes = indexConfigurationStore(transaction);
8989
+ const states = indexStateStore(transaction);
8990
+ const entries = indexEntriesStore(transaction);
8991
+ return indexes
8992
+ .delete(index.indexId)
8993
+ .next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
8994
+ /*lowerOpen=*/ false,
8995
+ /*upperOpen=*/ true)))
8996
+ .next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
8997
+ /*lowerOpen=*/ false,
8998
+ /*upperOpen=*/ true)));
8999
+ }
9000
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9001
+ // TODO(indexing): Implement
9002
+ return PersistencePromise.resolve(documentKeySet());
9003
+ }
9004
+ getFieldIndex(transaction, target) {
9005
+ // TODO(indexing): Implement
9006
+ return PersistencePromise.resolve(null);
9007
+ }
9008
+ getFieldIndexes(transaction, collectionGroup) {
9009
+ const indexes = indexConfigurationStore(transaction);
9010
+ const states = indexStateStore(transaction);
9011
+ return (collectionGroup
9012
+ ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9013
+ : indexes.loadAll()).next(indexConfigs => {
9014
+ const result = [];
9015
+ return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
9016
+ return states
9017
+ .get([indexConfig.indexId, this.uid])
9018
+ .next(indexState => {
9019
+ result.push(fromDbIndexConfiguration(indexConfig, indexState));
9020
+ });
9021
+ }).next(() => result);
9022
+ });
9023
+ }
9024
+ getNextCollectionGroupToUpdate(transaction) {
9025
+ return this.getFieldIndexes(transaction).next(indexes => {
9026
+ if (indexes.length === 0) {
9027
+ return null;
9028
+ }
9029
+ indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
9030
+ return indexes[0].collectionGroup;
9031
+ });
9032
+ }
9033
+ updateCollectionGroup(transaction, collectionGroup, offset) {
9034
+ const indexes = indexConfigurationStore(transaction);
9035
+ const states = indexStateStore(transaction);
9036
+ return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
9037
+ .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9038
+ .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
9039
+ }
9040
+ updateIndexEntries(transaction, documents) {
9041
+ // TODO(indexing): Implement
9042
+ return PersistencePromise.resolve();
9043
+ }
9044
+ getNextSequenceNumber(transaction) {
9045
+ let nextSequenceNumber = 1;
9046
+ const states = indexStateStore(transaction);
9047
+ return states
9048
+ .iterate({
9049
+ index: DbIndexState.sequenceNumberIndex,
9050
+ reverse: true,
9051
+ range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
9052
+ }, (_, state, controller) => {
9053
+ controller.done();
9054
+ nextSequenceNumber = state.sequenceNumber + 1;
9055
+ })
9056
+ .next(() => nextSequenceNumber);
9057
+ }
8793
9058
  }
8794
9059
  /**
8795
9060
  * Helper to get a typed SimpleDbStore for the collectionParents
@@ -8797,6 +9062,24 @@ class IndexedDbIndexManager {
8797
9062
  */
8798
9063
  function collectionParentsStore(txn) {
8799
9064
  return getStore(txn, DbCollectionParent.store);
9065
+ }
9066
+ /**
9067
+ * Helper to get a typed SimpleDbStore for the index entry object store.
9068
+ */
9069
+ function indexEntriesStore(txn) {
9070
+ return getStore(txn, DbIndexEntry.store);
9071
+ }
9072
+ /**
9073
+ * Helper to get a typed SimpleDbStore for the index configuration object store.
9074
+ */
9075
+ function indexConfigurationStore(txn) {
9076
+ return getStore(txn, DbIndexConfiguration.store);
9077
+ }
9078
+ /**
9079
+ * Helper to get a typed SimpleDbStore for the index state object store.
9080
+ */
9081
+ function indexStateStore(txn) {
9082
+ return getStore(txn, DbIndexState.store);
8800
9083
  }
8801
9084
 
8802
9085
  /**
@@ -10266,12 +10549,10 @@ class RemoteDocumentChangeBuffer {
10266
10549
  * `newIndexedDbRemoteDocumentCache()`.
10267
10550
  */
10268
10551
  class IndexedDbRemoteDocumentCacheImpl {
10269
- /**
10270
- * @param serializer - The document serializer.
10271
- * @param indexManager - The query indexes that need to be maintained.
10272
- */
10273
- constructor(serializer, indexManager) {
10552
+ constructor(serializer) {
10274
10553
  this.serializer = serializer;
10554
+ }
10555
+ setIndexManager(indexManager) {
10275
10556
  this.indexManager = indexManager;
10276
10557
  }
10277
10558
  /**
@@ -10393,21 +10674,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10393
10674
  }
10394
10675
  });
10395
10676
  }
10396
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
10677
+ getAll(transaction, collection, sinceReadTime) {
10397
10678
  let results = mutableDocumentMap();
10398
- const immediateChildrenPathLength = query.path.length + 1;
10679
+ const immediateChildrenPathLength = collection.length + 1;
10399
10680
  const iterationOptions = {};
10400
10681
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10401
10682
  // Documents are ordered by key, so we can use a prefix scan to narrow
10402
10683
  // down the documents we need to match the query against.
10403
- const startKey = query.path.toArray();
10684
+ const startKey = collection.toArray();
10404
10685
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10405
10686
  }
10406
10687
  else {
10407
10688
  // Execute an index-free query and filter by read time. This is safe
10408
10689
  // since all document changes to queries that have a
10409
10690
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10410
- const collectionKey = query.path.toArray();
10691
+ const collectionKey = collection.toArray();
10411
10692
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10412
10693
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10413
10694
  /* open= */ true);
@@ -10424,12 +10705,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10424
10705
  return;
10425
10706
  }
10426
10707
  const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
10427
- if (!query.path.isPrefixOf(document.key.path)) {
10428
- control.done();
10429
- }
10430
- else if (queryMatches(query, document)) {
10708
+ if (collection.isPrefixOf(document.key.path)) {
10431
10709
  results = results.insert(document.key, document);
10432
10710
  }
10711
+ else {
10712
+ control.done();
10713
+ }
10433
10714
  })
10434
10715
  .next(() => results);
10435
10716
  }
@@ -10467,14 +10748,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10467
10748
  return MutableDocument.newInvalidDocument(documentKey);
10468
10749
  }
10469
10750
  }
10470
- /**
10471
- * Creates a new IndexedDbRemoteDocumentCache.
10472
- *
10473
- * @param serializer - The document serializer.
10474
- * @param indexManager - The query indexes that need to be maintained.
10475
- */
10476
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10477
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
10751
+ /** Creates a new IndexedDbRemoteDocumentCache. */
10752
+ function newIndexedDbRemoteDocumentCache(serializer) {
10753
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10478
10754
  }
10479
10755
  /**
10480
10756
  * Returns the set of documents that have changed since the specified read
@@ -10641,9 +10917,6 @@ class SchemaConverter {
10641
10917
  * and local feature development.
10642
10918
  */
10643
10919
  createOrUpgrade(db, txn, fromVersion, toVersion) {
10644
- hardAssert(fromVersion < toVersion &&
10645
- fromVersion >= 0 &&
10646
- toVersion <= SCHEMA_VERSION);
10647
10920
  const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
10648
10921
  if (fromVersion < 1 && toVersion >= 1) {
10649
10922
  createPrimaryClientStore(db);
@@ -10710,6 +10983,11 @@ class SchemaConverter {
10710
10983
  createNamedQueriesStore(db);
10711
10984
  });
10712
10985
  }
10986
+ if (fromVersion < 12 && toVersion >= 12) {
10987
+ p = p.next(() => {
10988
+ createFieldIndex(db);
10989
+ });
10990
+ }
10713
10991
  return p;
10714
10992
  }
10715
10993
  addDocumentGlobal(txn) {
@@ -10916,6 +11194,20 @@ function createNamedQueriesStore(db) {
10916
11194
  db.createObjectStore(DbNamedQuery.store, {
10917
11195
  keyPath: DbNamedQuery.keyPath
10918
11196
  });
11197
+ }
11198
+ function createFieldIndex(db) {
11199
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
11200
+ keyPath: DbIndexConfiguration.keyPath,
11201
+ autoIncrement: true
11202
+ });
11203
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
11204
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
11205
+ keyPath: DbIndexState.keyPath
11206
+ });
11207
+ indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
11208
+ db.createObjectStore(DbIndexEntry.store, {
11209
+ keyPath: DbIndexEntry.keyPath
11210
+ });
10919
11211
  }
10920
11212
 
10921
11213
  /**
@@ -11027,7 +11319,7 @@ class IndexedDbPersistence {
11027
11319
  * If set to true, forcefully obtains database access. Existing tabs will
11028
11320
  * no longer be able to access IndexedDB.
11029
11321
  */
11030
- forceOwningTab) {
11322
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11031
11323
  this.allowTabSynchronization = allowTabSynchronization;
11032
11324
  this.persistenceKey = persistenceKey;
11033
11325
  this.clientId = clientId;
@@ -11036,6 +11328,7 @@ class IndexedDbPersistence {
11036
11328
  this.document = document;
11037
11329
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11038
11330
  this.forceOwningTab = forceOwningTab;
11331
+ this.schemaVersion = schemaVersion;
11039
11332
  this.listenSequence = null;
11040
11333
  this._started = false;
11041
11334
  this.isPrimary = false;
@@ -11057,10 +11350,9 @@ class IndexedDbPersistence {
11057
11350
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11058
11351
  this.dbName = persistenceKey + MAIN_DATABASE;
11059
11352
  this.serializer = new LocalSerializer(serializer);
11060
- this.simpleDb = new SimpleDb(this.dbName, SCHEMA_VERSION, new SchemaConverter(this.serializer));
11353
+ this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
11061
11354
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11062
- this.indexManager = new IndexedDbIndexManager();
11063
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
11355
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11064
11356
  this.bundleCache = new IndexedDbBundleCache();
11065
11357
  if (this.window && this.window.localStorage) {
11066
11358
  this.webStorage = this.window.localStorage;
@@ -11396,8 +11688,8 @@ class IndexedDbPersistence {
11396
11688
  get started() {
11397
11689
  return this._started;
11398
11690
  }
11399
- getMutationQueue(user) {
11400
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
11691
+ getMutationQueue(user, indexManager) {
11692
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11401
11693
  }
11402
11694
  getTargetCache() {
11403
11695
  return this.targetCache;
@@ -11405,8 +11697,8 @@ class IndexedDbPersistence {
11405
11697
  getRemoteDocumentCache() {
11406
11698
  return this.remoteDocumentCache;
11407
11699
  }
11408
- getIndexManager() {
11409
- return this.indexManager;
11700
+ getIndexManager(user) {
11701
+ return new IndexedDbIndexManager(user);
11410
11702
  }
11411
11703
  getBundleCache() {
11412
11704
  return this.bundleCache;
@@ -11414,11 +11706,12 @@ class IndexedDbPersistence {
11414
11706
  runTransaction(action, mode, transactionOperation) {
11415
11707
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11416
11708
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
11709
+ const objectStores = getObjectStores(this.schemaVersion);
11417
11710
  let persistenceTransaction;
11418
11711
  // Do all transactions as readwrite against all object stores, since we
11419
11712
  // are the only reader/writer.
11420
11713
  return this.simpleDb
11421
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
11714
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11422
11715
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11423
11716
  ? this.listenSequence.next()
11424
11717
  : ListenSequence.INVALID);
@@ -11790,39 +12083,28 @@ class LocalDocumentsView {
11790
12083
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11791
12084
  // Query the remote documents and overlay mutations.
11792
12085
  let results;
11793
- let mutationBatches;
11794
12086
  return this.remoteDocumentCache
11795
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
12087
+ .getAll(transaction, query.path, sinceReadTime)
11796
12088
  .next(queryResults => {
11797
12089
  results = queryResults;
11798
12090
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11799
12091
  })
11800
- .next(matchingMutationBatches => {
11801
- mutationBatches = matchingMutationBatches;
11802
- // It is possible that a PatchMutation can make a document match a query, even if
11803
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11804
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11805
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11806
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11807
- // and lead to missing result for the query.
11808
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11809
- results = mergedDocuments;
11810
- for (const batch of mutationBatches) {
11811
- for (const mutation of batch.mutations) {
11812
- const key = mutation.key;
11813
- let document = results.get(key);
11814
- if (document == null) {
11815
- // Create invalid document to apply mutations on top of
11816
- document = MutableDocument.newInvalidDocument(key);
11817
- results = results.insert(key, document);
11818
- }
11819
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
11820
- if (!document.isFoundDocument()) {
11821
- results = results.remove(key);
11822
- }
12092
+ .next(mutationBatches => {
12093
+ for (const batch of mutationBatches) {
12094
+ for (const mutation of batch.mutations) {
12095
+ const key = mutation.key;
12096
+ let document = results.get(key);
12097
+ if (document == null) {
12098
+ // Create invalid document to apply mutations on top of
12099
+ document = MutableDocument.newInvalidDocument(key);
12100
+ results = results.insert(key, document);
12101
+ }
12102
+ mutationApplyToLocalView(mutation, document, batch.localWriteTime);
12103
+ if (!document.isFoundDocument()) {
12104
+ results = results.remove(key);
11823
12105
  }
11824
12106
  }
11825
- });
12107
+ }
11826
12108
  })
11827
12109
  .next(() => {
11828
12110
  // Finally, filter out any documents that don't actually match
@@ -11835,29 +12117,6 @@ class LocalDocumentsView {
11835
12117
  return results;
11836
12118
  });
11837
12119
  }
11838
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
11839
- let missingBaseDocEntriesForPatching = documentKeySet();
11840
- for (const batch of matchingMutationBatches) {
11841
- for (const mutation of batch.mutations) {
11842
- if (mutation instanceof PatchMutation &&
11843
- existingDocuments.get(mutation.key) === null) {
11844
- missingBaseDocEntriesForPatching =
11845
- missingBaseDocEntriesForPatching.add(mutation.key);
11846
- }
11847
- }
11848
- }
11849
- let mergedDocuments = existingDocuments;
11850
- return this.remoteDocumentCache
11851
- .getEntries(transaction, missingBaseDocEntriesForPatching)
11852
- .next(missingBaseDocs => {
11853
- missingBaseDocs.forEach((key, doc) => {
11854
- if (doc.isFoundDocument()) {
11855
- mergedDocuments = mergedDocuments.insert(key, doc);
11856
- }
11857
- });
11858
- return mergedDocuments;
11859
- });
11860
- }
11861
12120
  }
11862
12121
 
11863
12122
  /**
@@ -11916,11 +12175,18 @@ class LocalStoreImpl {
11916
12175
  * PORTING NOTE: This is only used for multi-tab synchronization.
11917
12176
  */
11918
12177
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
11919
- this.mutationQueue = persistence.getMutationQueue(initialUser);
11920
12178
  this.remoteDocuments = persistence.getRemoteDocumentCache();
11921
12179
  this.targetCache = persistence.getTargetCache();
11922
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
11923
12180
  this.bundleCache = persistence.getBundleCache();
12181
+ this.initializeUserComponents(initialUser);
12182
+ }
12183
+ initializeUserComponents(user) {
12184
+ // TODO(indexing): Add spec tests that test these components change after a
12185
+ // user change
12186
+ this.indexManager = this.persistence.getIndexManager(user);
12187
+ this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
12188
+ this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
12189
+ this.remoteDocuments.setIndexManager(this.indexManager);
11924
12190
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
11925
12191
  }
11926
12192
  collectGarbage(garbageCollector) {
@@ -11942,8 +12208,6 @@ persistence, queryEngine, initialUser, serializer) {
11942
12208
  // change.
11943
12209
  async function localStoreHandleUserChange(localStore, user) {
11944
12210
  const localStoreImpl = debugCast(localStore);
11945
- let newMutationQueue = localStoreImpl.mutationQueue;
11946
- let newLocalDocuments = localStoreImpl.localDocuments;
11947
12211
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
11948
12212
  // Swap out the mutation queue, grabbing the pending mutation batches
11949
12213
  // before and after.
@@ -11952,11 +12216,8 @@ async function localStoreHandleUserChange(localStore, user) {
11952
12216
  .getAllMutationBatches(txn)
11953
12217
  .next(promisedOldBatches => {
11954
12218
  oldBatches = promisedOldBatches;
11955
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
11956
- // Recreate our LocalDocumentsView using the new
11957
- // MutationQueue.
11958
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
11959
- return newMutationQueue.getAllMutationBatches(txn);
12219
+ localStoreImpl.initializeUserComponents(user);
12220
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
11960
12221
  })
11961
12222
  .next(newBatches => {
11962
12223
  const removedBatchIds = [];
@@ -11977,7 +12238,7 @@ async function localStoreHandleUserChange(localStore, user) {
11977
12238
  }
11978
12239
  // Return the set of all (potentially) changed documents and the list
11979
12240
  // of mutation batch IDs that were affected by change.
11980
- return newLocalDocuments
12241
+ return localStoreImpl.localDocuments
11981
12242
  .getDocuments(txn, changedKeys)
11982
12243
  .next(affectedDocuments => {
11983
12244
  return {
@@ -11988,9 +12249,6 @@ async function localStoreHandleUserChange(localStore, user) {
11988
12249
  });
11989
12250
  });
11990
12251
  });
11991
- localStoreImpl.mutationQueue = newMutationQueue;
11992
- localStoreImpl.localDocuments = newLocalDocuments;
11993
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
11994
12252
  return result;
11995
12253
  }
11996
12254
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -13056,14 +13314,16 @@ class MemoryRemoteDocumentCacheImpl {
13056
13314
  * expected to just return 0 to avoid unnecessarily doing the work of
13057
13315
  * calculating the size.
13058
13316
  */
13059
- constructor(indexManager, sizer) {
13060
- this.indexManager = indexManager;
13317
+ constructor(sizer) {
13061
13318
  this.sizer = sizer;
13062
13319
  /** Underlying cache of documents and their read times. */
13063
13320
  this.docs = documentEntryMap();
13064
13321
  /** Size of all cached documents. */
13065
13322
  this.size = 0;
13066
13323
  }
13324
+ setIndexManager(indexManager) {
13325
+ this.indexManager = indexManager;
13326
+ }
13067
13327
  /**
13068
13328
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13069
13329
  *
@@ -13111,21 +13371,22 @@ class MemoryRemoteDocumentCacheImpl {
13111
13371
  });
13112
13372
  return PersistencePromise.resolve(results);
13113
13373
  }
13114
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
13374
+ getAll(transaction, collectionPath, sinceReadTime) {
13115
13375
  let results = mutableDocumentMap();
13116
13376
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13117
13377
  // the documents we need to match the query against.
13118
- const prefix = new DocumentKey(query.path.child(''));
13378
+ const prefix = new DocumentKey(collectionPath.child(''));
13119
13379
  const iterator = this.docs.getIteratorFrom(prefix);
13120
13380
  while (iterator.hasNext()) {
13121
13381
  const { key, value: { document } } = iterator.getNext();
13122
- if (!query.path.isPrefixOf(key.path)) {
13382
+ if (!collectionPath.isPrefixOf(key.path)) {
13123
13383
  break;
13124
13384
  }
13125
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
13385
+ if (key.path.length > collectionPath.length + 1) {
13386
+ // Exclude entries from subcollections.
13126
13387
  continue;
13127
13388
  }
13128
- if (!queryMatches(query, document)) {
13389
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13129
13390
  continue;
13130
13391
  }
13131
13392
  results = results.insert(document.key, document.mutableCopy());
@@ -13147,13 +13408,12 @@ class MemoryRemoteDocumentCacheImpl {
13147
13408
  /**
13148
13409
  * Creates a new memory-only RemoteDocumentCache.
13149
13410
  *
13150
- * @param indexManager - A class that manages collection group indices.
13151
13411
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13152
13412
  * expected to just return 0 to avoid unnecessarily doing the work of
13153
13413
  * calculating the size.
13154
13414
  */
13155
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13156
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
13415
+ function newMemoryRemoteDocumentCache(sizer) {
13416
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13157
13417
  }
13158
13418
  /**
13159
13419
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13354,7 +13614,7 @@ class MemoryPersistence {
13354
13614
  this.targetCache = new MemoryTargetCache(this);
13355
13615
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13356
13616
  this.indexManager = new MemoryIndexManager();
13357
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
13617
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13358
13618
  this.serializer = new LocalSerializer(serializer);
13359
13619
  this.bundleCache = new MemoryBundleCache(this.serializer);
13360
13620
  }
@@ -13375,13 +13635,15 @@ class MemoryPersistence {
13375
13635
  setNetworkEnabled() {
13376
13636
  // No op.
13377
13637
  }
13378
- getIndexManager() {
13638
+ getIndexManager(user) {
13639
+ // We do not currently support indices for memory persistence, so we can
13640
+ // return the same shared instance of the memory index manager.
13379
13641
  return this.indexManager;
13380
13642
  }
13381
- getMutationQueue(user) {
13643
+ getMutationQueue(user, indexManager) {
13382
13644
  let queue = this.mutationQueues[user.toKey()];
13383
13645
  if (!queue) {
13384
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
13646
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13385
13647
  this.mutationQueues[user.toKey()] = queue;
13386
13648
  }
13387
13649
  return queue;
@@ -20344,12 +20606,12 @@ async function syncEngineListen(syncEngine, query) {
20344
20606
  }
20345
20607
  else {
20346
20608
  const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
20347
- const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20348
- targetId = targetData.targetId;
20349
- viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20350
20609
  if (syncEngineImpl.isPrimaryClient) {
20351
20610
  remoteStoreListen(syncEngineImpl.remoteStore, targetData);
20352
20611
  }
20612
+ const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20613
+ targetId = targetData.targetId;
20614
+ viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20353
20615
  }
20354
20616
  return viewSnapshot;
20355
20617
  }
@@ -24857,8 +25119,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
24857
25119
  this.metadata = metadata;
24858
25120
  }
24859
25121
  /**
24860
- * Property of the `DocumentSnapshot` that signals whether or not the data
24861
- * exists. True if the document exists.
25122
+ * Returns whether or not the data exists. True if the document exists.
24862
25123
  */
24863
25124
  exists() {
24864
25125
  return super.exists();
@@ -25232,51 +25493,53 @@ function limitToLast(limit) {
25232
25493
  return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
25233
25494
  }
25234
25495
  class QueryStartAtConstraint extends QueryConstraint {
25235
- constructor(type, _docOrFields, _before) {
25496
+ constructor(type, _docOrFields, _inclusive) {
25236
25497
  super();
25237
25498
  this.type = type;
25238
25499
  this._docOrFields = _docOrFields;
25239
- this._before = _before;
25500
+ this._inclusive = _inclusive;
25240
25501
  }
25241
25502
  _apply(query) {
25242
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
25503
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25243
25504
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
25244
25505
  }
25245
25506
  }
25246
25507
  function startAt(...docOrFields) {
25247
- return new QueryStartAtConstraint('startAt', docOrFields, /*before=*/ true);
25508
+ return new QueryStartAtConstraint('startAt', docOrFields,
25509
+ /*inclusive=*/ true);
25248
25510
  }
25249
25511
  function startAfter(...docOrFields) {
25250
25512
  return new QueryStartAtConstraint('startAfter', docOrFields,
25251
- /*before=*/ false);
25513
+ /*inclusive=*/ false);
25252
25514
  }
25253
25515
  class QueryEndAtConstraint extends QueryConstraint {
25254
- constructor(type, _docOrFields, _before) {
25516
+ constructor(type, _docOrFields, _inclusive) {
25255
25517
  super();
25256
25518
  this.type = type;
25257
25519
  this._docOrFields = _docOrFields;
25258
- this._before = _before;
25520
+ this._inclusive = _inclusive;
25259
25521
  }
25260
25522
  _apply(query) {
25261
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
25523
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25262
25524
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
25263
25525
  }
25264
25526
  }
25265
25527
  function endBefore(...docOrFields) {
25266
- return new QueryEndAtConstraint('endBefore', docOrFields, /*before=*/ true);
25528
+ return new QueryEndAtConstraint('endBefore', docOrFields,
25529
+ /*inclusive=*/ false);
25267
25530
  }
25268
25531
  function endAt(...docOrFields) {
25269
- return new QueryEndAtConstraint('endAt', docOrFields, /*before=*/ false);
25532
+ return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
25270
25533
  }
25271
25534
  /** Helper function to create a bound from a document or fields */
25272
- function newQueryBoundFromDocOrFields(query, methodName, docOrFields, before) {
25535
+ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
25273
25536
  docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
25274
25537
  if (docOrFields[0] instanceof DocumentSnapshot$1) {
25275
- return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, before);
25538
+ return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
25276
25539
  }
25277
25540
  else {
25278
25541
  const reader = newUserDataReader(query.firestore);
25279
- return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, before);
25542
+ return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
25280
25543
  }
25281
25544
  }
25282
25545
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
@@ -25334,7 +25597,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
25334
25597
  * of the query or if any of the fields in the order by are an uncommitted
25335
25598
  * server timestamp.
25336
25599
  */
25337
- function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25600
+ function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
25338
25601
  if (!doc) {
25339
25602
  throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
25340
25603
  `${methodName}().`);
@@ -25371,12 +25634,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25371
25634
  }
25372
25635
  }
25373
25636
  }
25374
- return new Bound(components, before);
25637
+ return new Bound(components, inclusive);
25375
25638
  }
25376
25639
  /**
25377
25640
  * Converts a list of field values to a `Bound` for the given query.
25378
25641
  */
25379
- function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, before) {
25642
+ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
25380
25643
  // Use explicit order by's because it has to match the query the user made
25381
25644
  const orderBy = query.explicitOrderBy;
25382
25645
  if (values.length > orderBy.length) {
@@ -25413,7 +25676,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
25413
25676
  components.push(wrapped);
25414
25677
  }
25415
25678
  }
25416
- return new Bound(components, before);
25679
+ return new Bound(components, inclusive);
25417
25680
  }
25418
25681
  /**
25419
25682
  * Parses the given `documentIdValue` into a `ReferenceValue`, throwing