@firebase/firestore 3.4.4-canary.4cec42313 → 3.4.4-canary.50de44c2e

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 (128) 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 +35 -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/src/model/target_index_matcher.d.ts +70 -0
  19. package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  20. package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  21. package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  22. package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  23. package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  24. package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  25. package/dist/firestore/test/util/helpers.d.ts +1 -1
  26. package/dist/index.d.ts +1 -2
  27. package/dist/index.esm2017.js +2967 -2808
  28. package/dist/index.esm2017.js.map +1 -1
  29. package/dist/index.esm5.js +2870 -2698
  30. package/dist/index.esm5.js.map +1 -1
  31. package/dist/index.node.cjs.js +397 -203
  32. package/dist/index.node.cjs.js.map +1 -1
  33. package/dist/index.node.mjs +397 -203
  34. package/dist/index.node.mjs.map +1 -1
  35. package/dist/index.rn.js +3215 -3060
  36. package/dist/index.rn.js.map +1 -1
  37. package/dist/internal.d.ts +178 -14
  38. package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
  39. package/dist/lite/firestore/src/core/target.d.ts +8 -4
  40. package/dist/lite/firestore/src/lite-api/query.d.ts +2 -2
  41. package/dist/lite/firestore/src/local/index_manager.d.ts +52 -0
  42. package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  43. package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  44. package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  45. package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +35 -7
  46. package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
  47. package/dist/lite/firestore/src/local/local_serializer.d.ts +7 -2
  48. package/dist/lite/firestore/src/local/memory_index_manager.d.ts +11 -0
  49. package/dist/lite/firestore/src/local/memory_persistence.d.ts +3 -2
  50. package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  51. package/dist/lite/firestore/src/local/persistence.d.ts +2 -2
  52. package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
  53. package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
  54. package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
  55. package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
  56. package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  57. package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  58. package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  59. package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  60. package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  61. package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  62. package/dist/lite/firestore/test/util/helpers.d.ts +1 -1
  63. package/dist/lite/index.browser.esm2017.js +1146 -1143
  64. package/dist/lite/index.browser.esm2017.js.map +1 -1
  65. package/dist/lite/index.browser.esm5.js +332 -331
  66. package/dist/lite/index.browser.esm5.js.map +1 -1
  67. package/dist/lite/index.node.cjs.js +36 -29
  68. package/dist/lite/index.node.cjs.js.map +1 -1
  69. package/dist/lite/index.node.mjs +36 -29
  70. package/dist/lite/index.node.mjs.map +1 -1
  71. package/dist/lite/index.rn.esm2017.js +301 -298
  72. package/dist/lite/index.rn.esm2017.js.map +1 -1
  73. package/dist/lite/internal.d.ts +2 -2
  74. package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +121 -121
  75. package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
  76. package/dist/lite/packages/firestore/src/core/target.d.ts +8 -4
  77. package/dist/lite/packages/firestore/src/lite-api/query.d.ts +2 -2
  78. package/dist/lite/packages/firestore/src/local/index_manager.d.ts +52 -0
  79. package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  80. package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  81. package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  82. package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +35 -7
  83. package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  84. package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +7 -2
  85. package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  86. package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +3 -2
  87. package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  88. package/dist/lite/packages/firestore/src/local/persistence.d.ts +2 -2
  89. package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  90. package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
  91. package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
  92. package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  93. package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  94. package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  95. package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  96. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  97. package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  98. package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  99. package/dist/lite/packages/firestore/test/util/helpers.d.ts +1 -1
  100. package/dist/lite/private.d.ts +2 -2
  101. package/dist/packages/firestore/dist/index.esm2017.d.ts +143 -143
  102. package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
  103. package/dist/packages/firestore/src/core/target.d.ts +8 -4
  104. package/dist/packages/firestore/src/lite-api/query.d.ts +2 -2
  105. package/dist/packages/firestore/src/local/index_manager.d.ts +52 -0
  106. package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +21 -2
  107. package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +4 -4
  108. package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
  109. package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +35 -7
  110. package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
  111. package/dist/packages/firestore/src/local/local_serializer.d.ts +7 -2
  112. package/dist/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
  113. package/dist/packages/firestore/src/local/memory_persistence.d.ts +3 -2
  114. package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
  115. package/dist/packages/firestore/src/local/persistence.d.ts +2 -2
  116. package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
  117. package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
  118. package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
  119. package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
  120. package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
  121. package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
  122. package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
  123. package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
  124. package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
  125. package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
  126. package/dist/packages/firestore/test/util/helpers.d.ts +1 -1
  127. package/dist/private.d.ts +178 -14
  128. package/package.json +9 -9
@@ -8,7 +8,7 @@ import { credentials, loadPackageDefinition, Metadata } from '@grpc/grpc-js';
8
8
  import { fromJSON } from '@grpc/proto-loader';
9
9
 
10
10
  const name = "@firebase/firestore";
11
- const version$1 = "3.4.4-canary.4cec42313";
11
+ const version$1 = "3.4.4-canary.50de44c2e";
12
12
 
13
13
  /**
14
14
  * @license
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
61
61
  User.FIRST_PARTY = new User('first-party-uid');
62
62
  User.MOCK_USER = new User('mock-user');
63
63
 
64
- const version = "9.6.5-canary.4cec42313";
64
+ const version = "9.6.6-canary.50de44c2e";
65
65
 
66
66
  /**
67
67
  * @license
@@ -1774,7 +1774,10 @@ DbNamedQuery.keyPath = 'name';
1774
1774
  /** An object representing the global configuration for a field index. */
1775
1775
  class DbIndexConfiguration {
1776
1776
  constructor(
1777
- /** The index id for this entry. */
1777
+ /**
1778
+ * The index id for this entry. Undefined for indexes that are not yet
1779
+ * persisted.
1780
+ */
1778
1781
  indexId,
1779
1782
  /** The collection group this index belongs to. */
1780
1783
  collectionGroup,
@@ -1788,6 +1791,16 @@ class DbIndexConfiguration {
1788
1791
  /** Name of the IndexedDb object store. */
1789
1792
  DbIndexConfiguration.store = 'indexConfiguration';
1790
1793
  DbIndexConfiguration.keyPath = 'indexId';
1794
+ /**
1795
+ * An index that provides access to the index configurations by collection
1796
+ * group.
1797
+ *
1798
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1799
+ * not possible here as the Web client supports concurrent access to
1800
+ * persistence via multi-tab.
1801
+ */
1802
+ DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
1803
+ DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
1791
1804
  /**
1792
1805
  * An object describing how up-to-date the index backfill is for each user and
1793
1806
  * index.
@@ -1829,8 +1842,18 @@ class DbIndexState {
1829
1842
  /** Name of the IndexedDb object store. */
1830
1843
  DbIndexState.store = 'indexState';
1831
1844
  DbIndexState.keyPath = ['indexId', 'uid'];
1845
+ /**
1846
+ * An index that provides access to documents in a collection sorted by last
1847
+ * update time. Used by the backfiller.
1848
+ *
1849
+ * PORTING NOTE: iOS and Android maintain this index in-memory, but this is
1850
+ * not possible here as the Web client supports concurrent access to
1851
+ * persistence via multi-tab.
1852
+ */
1853
+ DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
1854
+ DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
1832
1855
  /** An object that stores the encoded entries for all documents and fields. */
1833
- class DbIndexEntries {
1856
+ class DbIndexEntry {
1834
1857
  constructor(
1835
1858
  /** The index id for this entry. */
1836
1859
  indexId,
@@ -1850,8 +1873,8 @@ class DbIndexEntries {
1850
1873
  }
1851
1874
  }
1852
1875
  /** Name of the IndexedDb object store. */
1853
- DbIndexEntries.store = 'indexEntries';
1854
- DbIndexEntries.keyPath = [
1876
+ DbIndexEntry.store = 'indexEntries';
1877
+ DbIndexEntry.keyPath = [
1855
1878
  'indexId',
1856
1879
  'uid',
1857
1880
  'arrayValue',
@@ -1881,18 +1904,24 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
1881
1904
  // V9 does not change the set of stores.
1882
1905
  // V10 does not change the set of stores.
1883
1906
  const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
1884
- [
1907
+ const V12_STORES = [
1885
1908
  ...V11_STORES,
1886
1909
  DbIndexConfiguration.store,
1887
1910
  DbIndexState.store,
1888
- DbIndexEntries.store
1911
+ DbIndexEntry.store
1889
1912
  ];
1890
- /**
1891
- * The list of all default IndexedDB stores used throughout the SDK. This is
1892
- * used when creating transactions so that access across all stores is done
1893
- * atomically.
1894
- */
1895
- const ALL_STORES = V11_STORES;
1913
+ /** Returns the object stores for the provided schema. */
1914
+ function getObjectStores(schemaVersion) {
1915
+ if (schemaVersion === 12) {
1916
+ return V12_STORES;
1917
+ }
1918
+ else if (schemaVersion === 11) {
1919
+ return V11_STORES;
1920
+ }
1921
+ else {
1922
+ fail();
1923
+ }
1924
+ }
1896
1925
 
1897
1926
  /**
1898
1927
  * @license
@@ -2189,6 +2218,15 @@ class SimpleDbTransaction {
2189
2218
  this.transaction.abort();
2190
2219
  }
2191
2220
  }
2221
+ maybeCommit() {
2222
+ // If the browser supports V3 IndexedDB, we invoke commit() explicitly to
2223
+ // speed up index DB processing if the event loop remains blocks.
2224
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2225
+ const maybeV3IndexedDb = this.transaction;
2226
+ if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
2227
+ maybeV3IndexedDb.commit();
2228
+ }
2229
+ }
2192
2230
  /**
2193
2231
  * Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
2194
2232
  * operations performed on the SimpleDbStore happen within the context of this
@@ -2385,6 +2423,10 @@ class SimpleDb {
2385
2423
  this.db = await this.ensureDb(action);
2386
2424
  const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
2387
2425
  const transactionFnResult = transactionFn(transaction)
2426
+ .next(result => {
2427
+ transaction.maybeCommit();
2428
+ return result;
2429
+ })
2388
2430
  .catch(error => {
2389
2431
  // Abort the transaction if there was an error.
2390
2432
  transaction.abort(error);
@@ -2557,13 +2599,29 @@ class SimpleDbStore {
2557
2599
  return wrapRequest(request);
2558
2600
  }
2559
2601
  loadAll(indexOrRange, range) {
2560
- const cursor = this.cursor(this.options(indexOrRange, range));
2561
- const results = [];
2562
- return this.iterateCursor(cursor, (key, value) => {
2563
- results.push(value);
2564
- }).next(() => {
2565
- return results;
2566
- });
2602
+ const iterateOptions = this.options(indexOrRange, range);
2603
+ // Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
2604
+ // 20% faster. Unfortunately, getAll() does not support custom indices.
2605
+ if (!iterateOptions.index && typeof this.store.getAll === 'function') {
2606
+ const request = this.store.getAll(iterateOptions.range);
2607
+ return new PersistencePromise((resolve, reject) => {
2608
+ request.onerror = (event) => {
2609
+ reject(event.target.error);
2610
+ };
2611
+ request.onsuccess = (event) => {
2612
+ resolve(event.target.result);
2613
+ };
2614
+ });
2615
+ }
2616
+ else {
2617
+ const cursor = this.cursor(iterateOptions);
2618
+ const results = [];
2619
+ return this.iterateCursor(cursor, (key, value) => {
2620
+ results.push(value);
2621
+ }).next(() => {
2622
+ return results;
2623
+ });
2624
+ }
2567
2625
  }
2568
2626
  deleteAll(indexOrRange, range) {
2569
2627
  logDebug(LOG_TAG$g, 'DELETE ALL', this.store.name);
@@ -4417,12 +4475,12 @@ class FieldIndex {
4417
4475
  /** The collection ID this index applies to. */
4418
4476
  collectionGroup,
4419
4477
  /** The field segments for this index. */
4420
- segments,
4478
+ fields,
4421
4479
  /** Shows how up-to-date the index is for the current user. */
4422
4480
  indexState) {
4423
4481
  this.indexId = indexId;
4424
4482
  this.collectionGroup = collectionGroup;
4425
- this.segments = segments;
4483
+ this.fields = fields;
4426
4484
  this.indexState = indexState;
4427
4485
  }
4428
4486
  }
@@ -4532,27 +4590,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
4532
4590
  function canonifyTarget(target) {
4533
4591
  const targetImpl = debugCast(target);
4534
4592
  if (targetImpl.memoizedCanonicalId === null) {
4535
- let canonicalId = targetImpl.path.canonicalString();
4593
+ let str = targetImpl.path.canonicalString();
4536
4594
  if (targetImpl.collectionGroup !== null) {
4537
- canonicalId += '|cg:' + targetImpl.collectionGroup;
4595
+ str += '|cg:' + targetImpl.collectionGroup;
4538
4596
  }
4539
- canonicalId += '|f:';
4540
- canonicalId += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4541
- canonicalId += '|ob:';
4542
- canonicalId += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4597
+ str += '|f:';
4598
+ str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
4599
+ str += '|ob:';
4600
+ str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
4543
4601
  if (!isNullOrUndefined(targetImpl.limit)) {
4544
- canonicalId += '|l:';
4545
- canonicalId += targetImpl.limit;
4602
+ str += '|l:';
4603
+ str += targetImpl.limit;
4546
4604
  }
4547
4605
  if (targetImpl.startAt) {
4548
- canonicalId += '|lb:';
4549
- canonicalId += canonifyBound(targetImpl.startAt);
4606
+ str += '|lb:';
4607
+ str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
4608
+ str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
4550
4609
  }
4551
4610
  if (targetImpl.endAt) {
4552
- canonicalId += '|ub:';
4553
- canonicalId += canonifyBound(targetImpl.endAt);
4611
+ str += '|ub:';
4612
+ str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
4613
+ str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
4554
4614
  }
4555
- targetImpl.memoizedCanonicalId = canonicalId;
4615
+ targetImpl.memoizedCanonicalId = str;
4556
4616
  }
4557
4617
  return targetImpl.memoizedCanonicalId;
4558
4618
  }
@@ -4575,10 +4635,14 @@ function stringifyTarget(target) {
4575
4635
  .join(', ')}]`;
4576
4636
  }
4577
4637
  if (target.startAt) {
4578
- str += ', startAt: ' + canonifyBound(target.startAt);
4638
+ str += ', startAt: ';
4639
+ str += target.startAt.inclusive ? 'b:' : 'a:';
4640
+ str += target.startAt.position.map(p => canonicalId(p)).join(',');
4579
4641
  }
4580
4642
  if (target.endAt) {
4581
- str += ', endAt: ' + canonifyBound(target.endAt);
4643
+ str += ', endAt: ';
4644
+ str += target.endAt.inclusive ? 'a:' : 'b:';
4645
+ str += target.endAt.position.map(p => canonicalId(p)).join(',');
4582
4646
  }
4583
4647
  return `Target(${str})`;
4584
4648
  }
@@ -4801,7 +4865,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
4801
4865
  return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
4802
4866
  }
4803
4867
  }
4804
- // TODO(indexing): Change Bound.before to "inclusive"
4805
4868
  /**
4806
4869
  * Represents a bound of a query.
4807
4870
  *
@@ -4817,17 +4880,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
4817
4880
  * just after the provided values.
4818
4881
  */
4819
4882
  class Bound {
4820
- constructor(position, before) {
4883
+ constructor(position, inclusive) {
4821
4884
  this.position = position;
4822
- this.before = before;
4885
+ this.inclusive = inclusive;
4823
4886
  }
4824
4887
  }
4825
- function canonifyBound(bound) {
4826
- // TODO(b/29183165): Make this collision robust.
4827
- return `${bound.before ? 'b' : 'a'}:${bound.position
4828
- .map(p => canonicalId(p))
4829
- .join(',')}`;
4830
- }
4831
4888
  /**
4832
4889
  * An ordering on a field, in some Direction. Direction defaults to ASCENDING.
4833
4890
  */
@@ -4847,11 +4904,7 @@ function stringifyOrderBy(orderBy) {
4847
4904
  function orderByEquals(left, right) {
4848
4905
  return left.dir === right.dir && left.field.isEqual(right.field);
4849
4906
  }
4850
- /**
4851
- * Returns true if a document sorts before a bound using the provided sort
4852
- * order.
4853
- */
4854
- function sortsBeforeDocument(bound, orderBy, doc) {
4907
+ function boundCompareToDocument(bound, orderBy, doc) {
4855
4908
  let comparison = 0;
4856
4909
  for (let i = 0; i < bound.position.length; i++) {
4857
4910
  const orderByComponent = orderBy[i];
@@ -4870,7 +4923,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
4870
4923
  break;
4871
4924
  }
4872
4925
  }
4873
- return bound.before ? comparison <= 0 : comparison < 0;
4926
+ return comparison;
4927
+ }
4928
+ /**
4929
+ * Returns true if a document sorts after a bound using the provided sort
4930
+ * order.
4931
+ */
4932
+ function boundSortsAfterDocument(bound, orderBy, doc) {
4933
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
4934
+ return bound.inclusive ? comparison >= 0 : comparison > 0;
4935
+ }
4936
+ /**
4937
+ * Returns true if a document sorts before a bound using the provided sort
4938
+ * order.
4939
+ */
4940
+ function boundSortsBeforeDocument(bound, orderBy, doc) {
4941
+ const comparison = boundCompareToDocument(bound, orderBy, doc);
4942
+ return bound.inclusive ? comparison <= 0 : comparison < 0;
4874
4943
  }
4875
4944
  function boundEquals(left, right) {
4876
4945
  if (left === null) {
@@ -4879,7 +4948,7 @@ function boundEquals(left, right) {
4879
4948
  else if (right === null) {
4880
4949
  return false;
4881
4950
  }
4882
- if (left.before !== right.before ||
4951
+ if (left.inclusive !== right.inclusive ||
4883
4952
  left.position.length !== right.position.length) {
4884
4953
  return false;
4885
4954
  }
@@ -5083,10 +5152,10 @@ function queryToTarget(query) {
5083
5152
  }
5084
5153
  // We need to swap the cursors to match the now-flipped query ordering.
5085
5154
  const startAt = queryImpl.endAt
5086
- ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.before)
5155
+ ? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
5087
5156
  : null;
5088
5157
  const endAt = queryImpl.startAt
5089
- ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.before)
5158
+ ? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
5090
5159
  : null;
5091
5160
  // Now return as a LimitType.First query.
5092
5161
  queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
@@ -5174,11 +5243,11 @@ function queryMatchesFilters(query, doc) {
5174
5243
  /** Makes sure a document is within the bounds, if provided. */
5175
5244
  function queryMatchesBounds(query, doc) {
5176
5245
  if (query.startAt &&
5177
- !sortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5246
+ !boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
5178
5247
  return false;
5179
5248
  }
5180
5249
  if (query.endAt &&
5181
- sortsBeforeDocument(query.endAt, queryOrderBy(query), doc)) {
5250
+ !boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
5182
5251
  return false;
5183
5252
  }
5184
5253
  return true;
@@ -8057,10 +8126,10 @@ function toQueryTarget(serializer, target) {
8057
8126
  result.structuredQuery.limit = limit;
8058
8127
  }
8059
8128
  if (target.startAt) {
8060
- result.structuredQuery.startAt = toCursor(target.startAt);
8129
+ result.structuredQuery.startAt = toStartAtCursor(target.startAt);
8061
8130
  }
8062
8131
  if (target.endAt) {
8063
- result.structuredQuery.endAt = toCursor(target.endAt);
8132
+ result.structuredQuery.endAt = toEndAtCursor(target.endAt);
8064
8133
  }
8065
8134
  return result;
8066
8135
  }
@@ -8093,11 +8162,11 @@ function convertQueryTargetToQuery(target) {
8093
8162
  }
8094
8163
  let startAt = null;
8095
8164
  if (query.startAt) {
8096
- startAt = fromCursor(query.startAt);
8165
+ startAt = fromStartAtCursor(query.startAt);
8097
8166
  }
8098
8167
  let endAt = null;
8099
8168
  if (query.endAt) {
8100
- endAt = fromCursor(query.endAt);
8169
+ endAt = fromEndAtCursor(query.endAt);
8101
8170
  }
8102
8171
  return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
8103
8172
  }
@@ -8188,16 +8257,27 @@ function toOrder(orderBys) {
8188
8257
  function fromOrder(orderBys) {
8189
8258
  return orderBys.map(order => fromPropertyOrder(order));
8190
8259
  }
8191
- function toCursor(cursor) {
8260
+ function toStartAtCursor(cursor) {
8192
8261
  return {
8193
- before: cursor.before,
8262
+ before: cursor.inclusive,
8194
8263
  values: cursor.position
8195
8264
  };
8196
8265
  }
8197
- function fromCursor(cursor) {
8198
- const before = !!cursor.before;
8266
+ function toEndAtCursor(cursor) {
8267
+ return {
8268
+ before: !cursor.inclusive,
8269
+ values: cursor.position
8270
+ };
8271
+ }
8272
+ function fromStartAtCursor(cursor) {
8273
+ const inclusive = !!cursor.before;
8274
+ const position = cursor.values || [];
8275
+ return new Bound(position, inclusive);
8276
+ }
8277
+ function fromEndAtCursor(cursor) {
8278
+ const inclusive = !cursor.before;
8199
8279
  const position = cursor.values || [];
8200
- return new Bound(position, before);
8280
+ return new Bound(position, inclusive);
8201
8281
  }
8202
8282
  // visible for testing
8203
8283
  function toDirection(dir) {
@@ -8661,6 +8741,19 @@ function fromBundleMetadata(metadata) {
8661
8741
  version: metadata.version,
8662
8742
  createTime: fromVersion(metadata.createTime)
8663
8743
  };
8744
+ }
8745
+ function toDbIndexConfiguration(index) {
8746
+ return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
8747
+ }
8748
+ function fromDbIndexConfiguration(index, state) {
8749
+ const decodedState = state
8750
+ ? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
8751
+ : IndexState.empty();
8752
+ const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
8753
+ return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
8754
+ }
8755
+ function toDbIndexState(indexId, user, sequenceNumber, offset) {
8756
+ return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
8664
8757
  }
8665
8758
 
8666
8759
  /**
@@ -8750,6 +8843,38 @@ class MemoryIndexManager {
8750
8843
  getCollectionParents(transaction, collectionId) {
8751
8844
  return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
8752
8845
  }
8846
+ addFieldIndex(transaction, index) {
8847
+ // Field indices are not supported with memory persistence.
8848
+ return PersistencePromise.resolve();
8849
+ }
8850
+ deleteFieldIndex(transaction, index) {
8851
+ // Field indices are not supported with memory persistence.
8852
+ return PersistencePromise.resolve();
8853
+ }
8854
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
8855
+ // Field indices are not supported with memory persistence.
8856
+ return PersistencePromise.resolve(documentKeySet());
8857
+ }
8858
+ getFieldIndex(transaction, target) {
8859
+ // Field indices are not supported with memory persistence.
8860
+ return PersistencePromise.resolve(null);
8861
+ }
8862
+ getFieldIndexes(transaction, collectionGroup) {
8863
+ // Field indices are not supported with memory persistence.
8864
+ return PersistencePromise.resolve([]);
8865
+ }
8866
+ getNextCollectionGroupToUpdate(transaction) {
8867
+ // Field indices are not supported with memory persistence.
8868
+ return PersistencePromise.resolve(null);
8869
+ }
8870
+ updateCollectionGroup(transaction, collectionGroup, offset) {
8871
+ // Field indices are not supported with memory persistence.
8872
+ return PersistencePromise.resolve();
8873
+ }
8874
+ updateIndexEntries(transaction, documents) {
8875
+ // Field indices are not supported with memory persistence.
8876
+ return PersistencePromise.resolve();
8877
+ }
8753
8878
  }
8754
8879
  /**
8755
8880
  * Internal implementation of the collection-parent index exposed by MemoryIndexManager.
@@ -8801,17 +8926,22 @@ class MemoryCollectionParentIndex {
8801
8926
  */
8802
8927
  /**
8803
8928
  * A persisted implementation of IndexManager.
8929
+ *
8930
+ * PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
8931
+ * data as it supports multi-tab access.
8804
8932
  */
8805
8933
  class IndexedDbIndexManager {
8806
- constructor() {
8934
+ constructor(user) {
8935
+ this.user = user;
8807
8936
  /**
8808
8937
  * An in-memory copy of the index entries we've already written since the SDK
8809
8938
  * launched. Used to avoid re-writing the same entry repeatedly.
8810
8939
  *
8811
- * This is *NOT* a complete cache of what's in persistence and so can never be used to
8812
- * satisfy reads.
8940
+ * This is *NOT* a complete cache of what's in persistence and so can never be
8941
+ * used to satisfy reads.
8813
8942
  */
8814
8943
  this.collectionParentsCache = new MemoryCollectionParentIndex();
8944
+ this.uid = user.uid || '';
8815
8945
  }
8816
8946
  /**
8817
8947
  * Adds a new entry to the collection parent index.
@@ -8858,6 +8988,85 @@ class IndexedDbIndexManager {
8858
8988
  return parentPaths;
8859
8989
  });
8860
8990
  }
8991
+ addFieldIndex(transaction, index) {
8992
+ // TODO(indexing): Verify that the auto-incrementing index ID works in
8993
+ // Safari & Firefox.
8994
+ const indexes = indexConfigurationStore(transaction);
8995
+ const dbIndex = toDbIndexConfiguration(index);
8996
+ delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
8997
+ return indexes.add(dbIndex).next();
8998
+ }
8999
+ deleteFieldIndex(transaction, index) {
9000
+ const indexes = indexConfigurationStore(transaction);
9001
+ const states = indexStateStore(transaction);
9002
+ const entries = indexEntriesStore(transaction);
9003
+ return indexes
9004
+ .delete(index.indexId)
9005
+ .next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9006
+ /*lowerOpen=*/ false,
9007
+ /*upperOpen=*/ true)))
9008
+ .next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
9009
+ /*lowerOpen=*/ false,
9010
+ /*upperOpen=*/ true)));
9011
+ }
9012
+ getDocumentsMatchingTarget(transaction, fieldIndex, target) {
9013
+ // TODO(indexing): Implement
9014
+ return PersistencePromise.resolve(documentKeySet());
9015
+ }
9016
+ getFieldIndex(transaction, target) {
9017
+ // TODO(indexing): Implement
9018
+ return PersistencePromise.resolve(null);
9019
+ }
9020
+ getFieldIndexes(transaction, collectionGroup) {
9021
+ const indexes = indexConfigurationStore(transaction);
9022
+ const states = indexStateStore(transaction);
9023
+ return (collectionGroup
9024
+ ? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9025
+ : indexes.loadAll()).next(indexConfigs => {
9026
+ const result = [];
9027
+ return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
9028
+ return states
9029
+ .get([indexConfig.indexId, this.uid])
9030
+ .next(indexState => {
9031
+ result.push(fromDbIndexConfiguration(indexConfig, indexState));
9032
+ });
9033
+ }).next(() => result);
9034
+ });
9035
+ }
9036
+ getNextCollectionGroupToUpdate(transaction) {
9037
+ return this.getFieldIndexes(transaction).next(indexes => {
9038
+ if (indexes.length === 0) {
9039
+ return null;
9040
+ }
9041
+ indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
9042
+ return indexes[0].collectionGroup;
9043
+ });
9044
+ }
9045
+ updateCollectionGroup(transaction, collectionGroup, offset) {
9046
+ const indexes = indexConfigurationStore(transaction);
9047
+ const states = indexStateStore(transaction);
9048
+ return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
9049
+ .loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
9050
+ .next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
9051
+ }
9052
+ updateIndexEntries(transaction, documents) {
9053
+ // TODO(indexing): Implement
9054
+ return PersistencePromise.resolve();
9055
+ }
9056
+ getNextSequenceNumber(transaction) {
9057
+ let nextSequenceNumber = 1;
9058
+ const states = indexStateStore(transaction);
9059
+ return states
9060
+ .iterate({
9061
+ index: DbIndexState.sequenceNumberIndex,
9062
+ reverse: true,
9063
+ range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
9064
+ }, (_, state, controller) => {
9065
+ controller.done();
9066
+ nextSequenceNumber = state.sequenceNumber + 1;
9067
+ })
9068
+ .next(() => nextSequenceNumber);
9069
+ }
8861
9070
  }
8862
9071
  /**
8863
9072
  * Helper to get a typed SimpleDbStore for the collectionParents
@@ -8865,6 +9074,24 @@ class IndexedDbIndexManager {
8865
9074
  */
8866
9075
  function collectionParentsStore(txn) {
8867
9076
  return getStore(txn, DbCollectionParent.store);
9077
+ }
9078
+ /**
9079
+ * Helper to get a typed SimpleDbStore for the index entry object store.
9080
+ */
9081
+ function indexEntriesStore(txn) {
9082
+ return getStore(txn, DbIndexEntry.store);
9083
+ }
9084
+ /**
9085
+ * Helper to get a typed SimpleDbStore for the index configuration object store.
9086
+ */
9087
+ function indexConfigurationStore(txn) {
9088
+ return getStore(txn, DbIndexConfiguration.store);
9089
+ }
9090
+ /**
9091
+ * Helper to get a typed SimpleDbStore for the index state object store.
9092
+ */
9093
+ function indexStateStore(txn) {
9094
+ return getStore(txn, DbIndexState.store);
8868
9095
  }
8869
9096
 
8870
9097
  /**
@@ -10334,12 +10561,10 @@ class RemoteDocumentChangeBuffer {
10334
10561
  * `newIndexedDbRemoteDocumentCache()`.
10335
10562
  */
10336
10563
  class IndexedDbRemoteDocumentCacheImpl {
10337
- /**
10338
- * @param serializer - The document serializer.
10339
- * @param indexManager - The query indexes that need to be maintained.
10340
- */
10341
- constructor(serializer, indexManager) {
10564
+ constructor(serializer) {
10342
10565
  this.serializer = serializer;
10566
+ }
10567
+ setIndexManager(indexManager) {
10343
10568
  this.indexManager = indexManager;
10344
10569
  }
10345
10570
  /**
@@ -10461,21 +10686,21 @@ class IndexedDbRemoteDocumentCacheImpl {
10461
10686
  }
10462
10687
  });
10463
10688
  }
10464
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
10689
+ getAll(transaction, collection, sinceReadTime) {
10465
10690
  let results = mutableDocumentMap();
10466
- const immediateChildrenPathLength = query.path.length + 1;
10691
+ const immediateChildrenPathLength = collection.length + 1;
10467
10692
  const iterationOptions = {};
10468
10693
  if (sinceReadTime.isEqual(SnapshotVersion.min())) {
10469
10694
  // Documents are ordered by key, so we can use a prefix scan to narrow
10470
10695
  // down the documents we need to match the query against.
10471
- const startKey = query.path.toArray();
10696
+ const startKey = collection.toArray();
10472
10697
  iterationOptions.range = IDBKeyRange.lowerBound(startKey);
10473
10698
  }
10474
10699
  else {
10475
10700
  // Execute an index-free query and filter by read time. This is safe
10476
10701
  // since all document changes to queries that have a
10477
10702
  // lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
10478
- const collectionKey = query.path.toArray();
10703
+ const collectionKey = collection.toArray();
10479
10704
  const readTimeKey = toDbTimestampKey(sinceReadTime);
10480
10705
  iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
10481
10706
  /* open= */ true);
@@ -10492,12 +10717,12 @@ class IndexedDbRemoteDocumentCacheImpl {
10492
10717
  return;
10493
10718
  }
10494
10719
  const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
10495
- if (!query.path.isPrefixOf(document.key.path)) {
10496
- control.done();
10497
- }
10498
- else if (queryMatches(query, document)) {
10720
+ if (collection.isPrefixOf(document.key.path)) {
10499
10721
  results = results.insert(document.key, document);
10500
10722
  }
10723
+ else {
10724
+ control.done();
10725
+ }
10501
10726
  })
10502
10727
  .next(() => results);
10503
10728
  }
@@ -10535,14 +10760,9 @@ class IndexedDbRemoteDocumentCacheImpl {
10535
10760
  return MutableDocument.newInvalidDocument(documentKey);
10536
10761
  }
10537
10762
  }
10538
- /**
10539
- * Creates a new IndexedDbRemoteDocumentCache.
10540
- *
10541
- * @param serializer - The document serializer.
10542
- * @param indexManager - The query indexes that need to be maintained.
10543
- */
10544
- function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
10545
- return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
10763
+ /** Creates a new IndexedDbRemoteDocumentCache. */
10764
+ function newIndexedDbRemoteDocumentCache(serializer) {
10765
+ return new IndexedDbRemoteDocumentCacheImpl(serializer);
10546
10766
  }
10547
10767
  /**
10548
10768
  * Returns the set of documents that have changed since the specified read
@@ -10988,14 +11208,17 @@ function createNamedQueriesStore(db) {
10988
11208
  });
10989
11209
  }
10990
11210
  function createFieldIndex(db) {
10991
- db.createObjectStore(DbIndexConfiguration.store, {
10992
- keyPath: DbIndexConfiguration.keyPath
11211
+ const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
11212
+ keyPath: DbIndexConfiguration.keyPath,
11213
+ autoIncrement: true
10993
11214
  });
10994
- db.createObjectStore(DbIndexState.store, {
11215
+ indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
11216
+ const indexStateStore = db.createObjectStore(DbIndexState.store, {
10995
11217
  keyPath: DbIndexState.keyPath
10996
11218
  });
10997
- db.createObjectStore(DbIndexEntries.store, {
10998
- keyPath: DbIndexEntries.keyPath
11219
+ indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
11220
+ db.createObjectStore(DbIndexEntry.store, {
11221
+ keyPath: DbIndexEntry.keyPath
10999
11222
  });
11000
11223
  }
11001
11224
 
@@ -11108,7 +11331,7 @@ class IndexedDbPersistence {
11108
11331
  * If set to true, forcefully obtains database access. Existing tabs will
11109
11332
  * no longer be able to access IndexedDB.
11110
11333
  */
11111
- forceOwningTab) {
11334
+ forceOwningTab, schemaVersion = SCHEMA_VERSION) {
11112
11335
  this.allowTabSynchronization = allowTabSynchronization;
11113
11336
  this.persistenceKey = persistenceKey;
11114
11337
  this.clientId = clientId;
@@ -11117,6 +11340,7 @@ class IndexedDbPersistence {
11117
11340
  this.document = document;
11118
11341
  this.sequenceNumberSyncer = sequenceNumberSyncer;
11119
11342
  this.forceOwningTab = forceOwningTab;
11343
+ this.schemaVersion = schemaVersion;
11120
11344
  this.listenSequence = null;
11121
11345
  this._started = false;
11122
11346
  this.isPrimary = false;
@@ -11138,10 +11362,9 @@ class IndexedDbPersistence {
11138
11362
  this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
11139
11363
  this.dbName = persistenceKey + MAIN_DATABASE;
11140
11364
  this.serializer = new LocalSerializer(serializer);
11141
- this.simpleDb = new SimpleDb(this.dbName, SCHEMA_VERSION, new SchemaConverter(this.serializer));
11365
+ this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
11142
11366
  this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
11143
- this.indexManager = new IndexedDbIndexManager();
11144
- this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
11367
+ this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
11145
11368
  this.bundleCache = new IndexedDbBundleCache();
11146
11369
  if (this.window && this.window.localStorage) {
11147
11370
  this.webStorage = this.window.localStorage;
@@ -11477,8 +11700,8 @@ class IndexedDbPersistence {
11477
11700
  get started() {
11478
11701
  return this._started;
11479
11702
  }
11480
- getMutationQueue(user) {
11481
- return IndexedDbMutationQueue.forUser(user, this.serializer, this.indexManager, this.referenceDelegate);
11703
+ getMutationQueue(user, indexManager) {
11704
+ return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
11482
11705
  }
11483
11706
  getTargetCache() {
11484
11707
  return this.targetCache;
@@ -11486,8 +11709,8 @@ class IndexedDbPersistence {
11486
11709
  getRemoteDocumentCache() {
11487
11710
  return this.remoteDocumentCache;
11488
11711
  }
11489
- getIndexManager() {
11490
- return this.indexManager;
11712
+ getIndexManager(user) {
11713
+ return new IndexedDbIndexManager(user);
11491
11714
  }
11492
11715
  getBundleCache() {
11493
11716
  return this.bundleCache;
@@ -11495,11 +11718,12 @@ class IndexedDbPersistence {
11495
11718
  runTransaction(action, mode, transactionOperation) {
11496
11719
  logDebug(LOG_TAG$d, 'Starting transaction:', action);
11497
11720
  const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
11721
+ const objectStores = getObjectStores(this.schemaVersion);
11498
11722
  let persistenceTransaction;
11499
11723
  // Do all transactions as readwrite against all object stores, since we
11500
11724
  // are the only reader/writer.
11501
11725
  return this.simpleDb
11502
- .runTransaction(action, simpleDbMode, ALL_STORES, simpleDbTxn => {
11726
+ .runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
11503
11727
  persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
11504
11728
  ? this.listenSequence.next()
11505
11729
  : ListenSequence.INVALID);
@@ -11871,39 +12095,28 @@ class LocalDocumentsView {
11871
12095
  getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
11872
12096
  // Query the remote documents and overlay mutations.
11873
12097
  let results;
11874
- let mutationBatches;
11875
12098
  return this.remoteDocumentCache
11876
- .getDocumentsMatchingQuery(transaction, query, sinceReadTime)
12099
+ .getAll(transaction, query.path, sinceReadTime)
11877
12100
  .next(queryResults => {
11878
12101
  results = queryResults;
11879
12102
  return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
11880
12103
  })
11881
- .next(matchingMutationBatches => {
11882
- mutationBatches = matchingMutationBatches;
11883
- // It is possible that a PatchMutation can make a document match a query, even if
11884
- // the version in the RemoteDocumentCache is not a match yet (waiting for server
11885
- // to ack). To handle this, we find all document keys affected by the PatchMutations
11886
- // that are not in `result` yet, and back fill them via `remoteDocumentCache.getEntries`,
11887
- // otherwise those `PatchMutations` will be ignored because no base document can be found,
11888
- // and lead to missing result for the query.
11889
- return this.addMissingBaseDocuments(transaction, mutationBatches, results).next(mergedDocuments => {
11890
- results = mergedDocuments;
11891
- for (const batch of mutationBatches) {
11892
- for (const mutation of batch.mutations) {
11893
- const key = mutation.key;
11894
- let document = results.get(key);
11895
- if (document == null) {
11896
- // Create invalid document to apply mutations on top of
11897
- document = MutableDocument.newInvalidDocument(key);
11898
- results = results.insert(key, document);
11899
- }
11900
- mutationApplyToLocalView(mutation, document, batch.localWriteTime);
11901
- if (!document.isFoundDocument()) {
11902
- results = results.remove(key);
11903
- }
12104
+ .next(mutationBatches => {
12105
+ for (const batch of mutationBatches) {
12106
+ for (const mutation of batch.mutations) {
12107
+ const key = mutation.key;
12108
+ let document = results.get(key);
12109
+ if (document == null) {
12110
+ // Create invalid document to apply mutations on top of
12111
+ document = MutableDocument.newInvalidDocument(key);
12112
+ results = results.insert(key, document);
12113
+ }
12114
+ mutationApplyToLocalView(mutation, document, batch.localWriteTime);
12115
+ if (!document.isFoundDocument()) {
12116
+ results = results.remove(key);
11904
12117
  }
11905
12118
  }
11906
- });
12119
+ }
11907
12120
  })
11908
12121
  .next(() => {
11909
12122
  // Finally, filter out any documents that don't actually match
@@ -11916,29 +12129,6 @@ class LocalDocumentsView {
11916
12129
  return results;
11917
12130
  });
11918
12131
  }
11919
- addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
11920
- let missingBaseDocEntriesForPatching = documentKeySet();
11921
- for (const batch of matchingMutationBatches) {
11922
- for (const mutation of batch.mutations) {
11923
- if (mutation instanceof PatchMutation &&
11924
- existingDocuments.get(mutation.key) === null) {
11925
- missingBaseDocEntriesForPatching =
11926
- missingBaseDocEntriesForPatching.add(mutation.key);
11927
- }
11928
- }
11929
- }
11930
- let mergedDocuments = existingDocuments;
11931
- return this.remoteDocumentCache
11932
- .getEntries(transaction, missingBaseDocEntriesForPatching)
11933
- .next(missingBaseDocs => {
11934
- missingBaseDocs.forEach((key, doc) => {
11935
- if (doc.isFoundDocument()) {
11936
- mergedDocuments = mergedDocuments.insert(key, doc);
11937
- }
11938
- });
11939
- return mergedDocuments;
11940
- });
11941
- }
11942
12132
  }
11943
12133
 
11944
12134
  /**
@@ -11997,11 +12187,18 @@ class LocalStoreImpl {
11997
12187
  * PORTING NOTE: This is only used for multi-tab synchronization.
11998
12188
  */
11999
12189
  this.lastDocumentChangeReadTime = SnapshotVersion.min();
12000
- this.mutationQueue = persistence.getMutationQueue(initialUser);
12001
12190
  this.remoteDocuments = persistence.getRemoteDocumentCache();
12002
12191
  this.targetCache = persistence.getTargetCache();
12003
- this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
12004
12192
  this.bundleCache = persistence.getBundleCache();
12193
+ this.initializeUserComponents(initialUser);
12194
+ }
12195
+ initializeUserComponents(user) {
12196
+ // TODO(indexing): Add spec tests that test these components change after a
12197
+ // user change
12198
+ this.indexManager = this.persistence.getIndexManager(user);
12199
+ this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
12200
+ this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
12201
+ this.remoteDocuments.setIndexManager(this.indexManager);
12005
12202
  this.queryEngine.setLocalDocumentsView(this.localDocuments);
12006
12203
  }
12007
12204
  collectGarbage(garbageCollector) {
@@ -12023,8 +12220,6 @@ persistence, queryEngine, initialUser, serializer) {
12023
12220
  // change.
12024
12221
  async function localStoreHandleUserChange(localStore, user) {
12025
12222
  const localStoreImpl = debugCast(localStore);
12026
- let newMutationQueue = localStoreImpl.mutationQueue;
12027
- let newLocalDocuments = localStoreImpl.localDocuments;
12028
12223
  const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
12029
12224
  // Swap out the mutation queue, grabbing the pending mutation batches
12030
12225
  // before and after.
@@ -12033,11 +12228,8 @@ async function localStoreHandleUserChange(localStore, user) {
12033
12228
  .getAllMutationBatches(txn)
12034
12229
  .next(promisedOldBatches => {
12035
12230
  oldBatches = promisedOldBatches;
12036
- newMutationQueue = localStoreImpl.persistence.getMutationQueue(user);
12037
- // Recreate our LocalDocumentsView using the new
12038
- // MutationQueue.
12039
- newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
12040
- return newMutationQueue.getAllMutationBatches(txn);
12231
+ localStoreImpl.initializeUserComponents(user);
12232
+ return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
12041
12233
  })
12042
12234
  .next(newBatches => {
12043
12235
  const removedBatchIds = [];
@@ -12058,7 +12250,7 @@ async function localStoreHandleUserChange(localStore, user) {
12058
12250
  }
12059
12251
  // Return the set of all (potentially) changed documents and the list
12060
12252
  // of mutation batch IDs that were affected by change.
12061
- return newLocalDocuments
12253
+ return localStoreImpl.localDocuments
12062
12254
  .getDocuments(txn, changedKeys)
12063
12255
  .next(affectedDocuments => {
12064
12256
  return {
@@ -12069,9 +12261,6 @@ async function localStoreHandleUserChange(localStore, user) {
12069
12261
  });
12070
12262
  });
12071
12263
  });
12072
- localStoreImpl.mutationQueue = newMutationQueue;
12073
- localStoreImpl.localDocuments = newLocalDocuments;
12074
- localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
12075
12264
  return result;
12076
12265
  }
12077
12266
  /* Accepts locally generated Mutations and commit them to storage. */
@@ -13137,14 +13326,16 @@ class MemoryRemoteDocumentCacheImpl {
13137
13326
  * expected to just return 0 to avoid unnecessarily doing the work of
13138
13327
  * calculating the size.
13139
13328
  */
13140
- constructor(indexManager, sizer) {
13141
- this.indexManager = indexManager;
13329
+ constructor(sizer) {
13142
13330
  this.sizer = sizer;
13143
13331
  /** Underlying cache of documents and their read times. */
13144
13332
  this.docs = documentEntryMap();
13145
13333
  /** Size of all cached documents. */
13146
13334
  this.size = 0;
13147
13335
  }
13336
+ setIndexManager(indexManager) {
13337
+ this.indexManager = indexManager;
13338
+ }
13148
13339
  /**
13149
13340
  * Adds the supplied entry to the cache and updates the cache size as appropriate.
13150
13341
  *
@@ -13192,21 +13383,22 @@ class MemoryRemoteDocumentCacheImpl {
13192
13383
  });
13193
13384
  return PersistencePromise.resolve(results);
13194
13385
  }
13195
- getDocumentsMatchingQuery(transaction, query, sinceReadTime) {
13386
+ getAll(transaction, collectionPath, sinceReadTime) {
13196
13387
  let results = mutableDocumentMap();
13197
13388
  // Documents are ordered by key, so we can use a prefix scan to narrow down
13198
13389
  // the documents we need to match the query against.
13199
- const prefix = new DocumentKey(query.path.child(''));
13390
+ const prefix = new DocumentKey(collectionPath.child(''));
13200
13391
  const iterator = this.docs.getIteratorFrom(prefix);
13201
13392
  while (iterator.hasNext()) {
13202
13393
  const { key, value: { document } } = iterator.getNext();
13203
- if (!query.path.isPrefixOf(key.path)) {
13394
+ if (!collectionPath.isPrefixOf(key.path)) {
13204
13395
  break;
13205
13396
  }
13206
- if (document.readTime.compareTo(sinceReadTime) <= 0) {
13397
+ if (key.path.length > collectionPath.length + 1) {
13398
+ // Exclude entries from subcollections.
13207
13399
  continue;
13208
13400
  }
13209
- if (!queryMatches(query, document)) {
13401
+ if (document.readTime.compareTo(sinceReadTime) <= 0) {
13210
13402
  continue;
13211
13403
  }
13212
13404
  results = results.insert(document.key, document.mutableCopy());
@@ -13228,13 +13420,12 @@ class MemoryRemoteDocumentCacheImpl {
13228
13420
  /**
13229
13421
  * Creates a new memory-only RemoteDocumentCache.
13230
13422
  *
13231
- * @param indexManager - A class that manages collection group indices.
13232
13423
  * @param sizer - Used to assess the size of a document. For eager GC, this is
13233
13424
  * expected to just return 0 to avoid unnecessarily doing the work of
13234
13425
  * calculating the size.
13235
13426
  */
13236
- function newMemoryRemoteDocumentCache(indexManager, sizer) {
13237
- return new MemoryRemoteDocumentCacheImpl(indexManager, sizer);
13427
+ function newMemoryRemoteDocumentCache(sizer) {
13428
+ return new MemoryRemoteDocumentCacheImpl(sizer);
13238
13429
  }
13239
13430
  /**
13240
13431
  * Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
@@ -13435,7 +13626,7 @@ class MemoryPersistence {
13435
13626
  this.targetCache = new MemoryTargetCache(this);
13436
13627
  const sizer = (doc) => this.referenceDelegate.documentSize(doc);
13437
13628
  this.indexManager = new MemoryIndexManager();
13438
- this.remoteDocumentCache = newMemoryRemoteDocumentCache(this.indexManager, sizer);
13629
+ this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
13439
13630
  this.serializer = new LocalSerializer(serializer);
13440
13631
  this.bundleCache = new MemoryBundleCache(this.serializer);
13441
13632
  }
@@ -13456,13 +13647,15 @@ class MemoryPersistence {
13456
13647
  setNetworkEnabled() {
13457
13648
  // No op.
13458
13649
  }
13459
- getIndexManager() {
13650
+ getIndexManager(user) {
13651
+ // We do not currently support indices for memory persistence, so we can
13652
+ // return the same shared instance of the memory index manager.
13460
13653
  return this.indexManager;
13461
13654
  }
13462
- getMutationQueue(user) {
13655
+ getMutationQueue(user, indexManager) {
13463
13656
  let queue = this.mutationQueues[user.toKey()];
13464
13657
  if (!queue) {
13465
- queue = new MemoryMutationQueue(this.indexManager, this.referenceDelegate);
13658
+ queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
13466
13659
  this.mutationQueues[user.toKey()] = queue;
13467
13660
  }
13468
13661
  return queue;
@@ -20425,12 +20618,12 @@ async function syncEngineListen(syncEngine, query) {
20425
20618
  }
20426
20619
  else {
20427
20620
  const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
20428
- const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20429
- targetId = targetData.targetId;
20430
- viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20431
20621
  if (syncEngineImpl.isPrimaryClient) {
20432
20622
  remoteStoreListen(syncEngineImpl.remoteStore, targetData);
20433
20623
  }
20624
+ const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
20625
+ targetId = targetData.targetId;
20626
+ viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
20434
20627
  }
20435
20628
  return viewSnapshot;
20436
20629
  }
@@ -24938,8 +25131,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
24938
25131
  this.metadata = metadata;
24939
25132
  }
24940
25133
  /**
24941
- * Property of the `DocumentSnapshot` that signals whether or not the data
24942
- * exists. True if the document exists.
25134
+ * Returns whether or not the data exists. True if the document exists.
24943
25135
  */
24944
25136
  exists() {
24945
25137
  return super.exists();
@@ -25313,51 +25505,53 @@ function limitToLast(limit) {
25313
25505
  return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
25314
25506
  }
25315
25507
  class QueryStartAtConstraint extends QueryConstraint {
25316
- constructor(type, _docOrFields, _before) {
25508
+ constructor(type, _docOrFields, _inclusive) {
25317
25509
  super();
25318
25510
  this.type = type;
25319
25511
  this._docOrFields = _docOrFields;
25320
- this._before = _before;
25512
+ this._inclusive = _inclusive;
25321
25513
  }
25322
25514
  _apply(query) {
25323
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
25515
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25324
25516
  return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
25325
25517
  }
25326
25518
  }
25327
25519
  function startAt(...docOrFields) {
25328
- return new QueryStartAtConstraint('startAt', docOrFields, /*before=*/ true);
25520
+ return new QueryStartAtConstraint('startAt', docOrFields,
25521
+ /*inclusive=*/ true);
25329
25522
  }
25330
25523
  function startAfter(...docOrFields) {
25331
25524
  return new QueryStartAtConstraint('startAfter', docOrFields,
25332
- /*before=*/ false);
25525
+ /*inclusive=*/ false);
25333
25526
  }
25334
25527
  class QueryEndAtConstraint extends QueryConstraint {
25335
- constructor(type, _docOrFields, _before) {
25528
+ constructor(type, _docOrFields, _inclusive) {
25336
25529
  super();
25337
25530
  this.type = type;
25338
25531
  this._docOrFields = _docOrFields;
25339
- this._before = _before;
25532
+ this._inclusive = _inclusive;
25340
25533
  }
25341
25534
  _apply(query) {
25342
- const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._before);
25535
+ const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
25343
25536
  return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
25344
25537
  }
25345
25538
  }
25346
25539
  function endBefore(...docOrFields) {
25347
- return new QueryEndAtConstraint('endBefore', docOrFields, /*before=*/ true);
25540
+ return new QueryEndAtConstraint('endBefore', docOrFields,
25541
+ /*inclusive=*/ false);
25348
25542
  }
25349
25543
  function endAt(...docOrFields) {
25350
- return new QueryEndAtConstraint('endAt', docOrFields, /*before=*/ false);
25544
+ return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
25351
25545
  }
25352
25546
  /** Helper function to create a bound from a document or fields */
25353
- function newQueryBoundFromDocOrFields(query, methodName, docOrFields, before) {
25547
+ function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
25354
25548
  docOrFields[0] = getModularInstance(docOrFields[0]);
25355
25549
  if (docOrFields[0] instanceof DocumentSnapshot$1) {
25356
- return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, before);
25550
+ return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
25357
25551
  }
25358
25552
  else {
25359
25553
  const reader = newUserDataReader(query.firestore);
25360
- return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, before);
25554
+ return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
25361
25555
  }
25362
25556
  }
25363
25557
  function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
@@ -25415,7 +25609,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
25415
25609
  * of the query or if any of the fields in the order by are an uncommitted
25416
25610
  * server timestamp.
25417
25611
  */
25418
- function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25612
+ function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
25419
25613
  if (!doc) {
25420
25614
  throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
25421
25615
  `${methodName}().`);
@@ -25452,12 +25646,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
25452
25646
  }
25453
25647
  }
25454
25648
  }
25455
- return new Bound(components, before);
25649
+ return new Bound(components, inclusive);
25456
25650
  }
25457
25651
  /**
25458
25652
  * Converts a list of field values to a `Bound` for the given query.
25459
25653
  */
25460
- function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, before) {
25654
+ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
25461
25655
  // Use explicit order by's because it has to match the query the user made
25462
25656
  const orderBy = query.explicitOrderBy;
25463
25657
  if (values.length > orderBy.length) {
@@ -25494,7 +25688,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
25494
25688
  components.push(wrapped);
25495
25689
  }
25496
25690
  }
25497
- return new Bound(components, before);
25691
+ return new Bound(components, inclusive);
25498
25692
  }
25499
25693
  /**
25500
25694
  * Parses the given `documentIdValue` into a `ReferenceValue`, throwing