@firebase/firestore 3.4.4-canary.2dd99ae2d → 3.4.4-canary.3a8d4c1d1
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.
- package/dist/firestore/src/api/credentials.d.ts +1 -0
- package/dist/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/firestore/src/core/target.d.ts +8 -4
- package/dist/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/firestore/src/lite-api/query.d.ts +2 -2
- package/dist/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/firestore/src/local/index_manager.d.ts +52 -0
- package/dist/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
- package/dist/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/firestore/src/local/indexeddb_schema.d.ts +195 -7
- package/dist/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/firestore/src/local/memory_index_manager.d.ts +11 -0
- package/dist/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/firestore/src/local/persistence.d.ts +8 -2
- package/dist/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/firestore/src/model/document_key.d.ts +5 -0
- package/dist/firestore/src/model/field_index.d.ts +2 -2
- package/dist/firestore/src/model/overlay.d.ts +32 -0
- package/dist/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/firestore/test/util/helpers.d.ts +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.esm2017.js +4496 -3484
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4452 -3556
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1728 -287
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1728 -287
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +4508 -3496
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +253 -14
- package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/lite/firestore/src/core/target.d.ts +8 -4
- package/dist/lite/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/lite/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/lite/firestore/src/lite-api/query.d.ts +2 -2
- package/dist/lite/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/lite/firestore/src/local/index_manager.d.ts +52 -0
- package/dist/lite/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
- package/dist/lite/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +195 -7
- package/dist/lite/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/lite/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/lite/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/lite/firestore/src/local/memory_index_manager.d.ts +11 -0
- package/dist/lite/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/lite/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/lite/firestore/src/local/persistence.d.ts +8 -2
- package/dist/lite/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/lite/firestore/src/model/document_key.d.ts +5 -0
- package/dist/lite/firestore/src/model/field_index.d.ts +2 -2
- package/dist/lite/firestore/src/model/overlay.d.ts +32 -0
- package/dist/lite/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/lite/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/lite/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/lite/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/lite/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/lite/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/lite/firestore/test/util/helpers.d.ts +1 -1
- package/dist/lite/index.browser.esm2017.js +1155 -1143
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +355 -342
- package/dist/lite/index.browser.esm5.js.map +1 -1
- package/dist/lite/index.node.cjs.js +47 -29
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +47 -29
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +332 -320
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +7 -2
- package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +121 -121
- package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/lite/packages/firestore/src/core/target.d.ts +8 -4
- package/dist/lite/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/lite/packages/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/lite/packages/firestore/src/lite-api/query.d.ts +2 -2
- package/dist/lite/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/lite/packages/firestore/src/local/index_manager.d.ts +52 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
- package/dist/lite/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
- package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/lite/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
- package/dist/lite/packages/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/lite/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/lite/packages/firestore/src/local/persistence.d.ts +8 -2
- package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/lite/packages/firestore/src/model/document_key.d.ts +5 -0
- package/dist/lite/packages/firestore/src/model/field_index.d.ts +2 -2
- package/dist/lite/packages/firestore/src/model/overlay.d.ts +32 -0
- package/dist/lite/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/lite/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/lite/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/lite/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/lite/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/lite/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/lite/packages/firestore/test/util/helpers.d.ts +1 -1
- package/dist/lite/private.d.ts +2 -2
- package/dist/packages/firestore/dist/index.esm2017.d.ts +189 -186
- package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/packages/firestore/src/core/target.d.ts +8 -4
- package/dist/packages/firestore/src/index/index_byte_encoder.d.ts +18 -1
- package/dist/packages/firestore/src/index/index_entry.d.ts +26 -0
- package/dist/packages/firestore/src/lite-api/query.d.ts +2 -2
- package/dist/packages/firestore/src/local/document_overlay_cache.d.ts +72 -0
- package/dist/packages/firestore/src/local/index_manager.d.ts +52 -0
- package/dist/packages/firestore/src/local/indexeddb_document_overlay_cache.d.ts +45 -0
- package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +40 -2
- package/dist/packages/firestore/src/local/indexeddb_persistence.d.ts +6 -4
- package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +2 -8
- package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +195 -7
- package/dist/packages/firestore/src/local/local_documents_view.d.ts +0 -1
- package/dist/packages/firestore/src/local/local_serializer.d.ts +18 -2
- package/dist/packages/firestore/src/local/memory_document_overlay_cache.d.ts +37 -0
- package/dist/packages/firestore/src/local/memory_index_manager.d.ts +11 -0
- package/dist/packages/firestore/src/local/memory_persistence.d.ts +6 -2
- package/dist/packages/firestore/src/local/memory_remote_document_cache.d.ts +1 -3
- package/dist/packages/firestore/src/local/persistence.d.ts +8 -2
- package/dist/packages/firestore/src/local/remote_document_cache.d.ts +7 -9
- package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/packages/firestore/src/model/document_key.d.ts +5 -0
- package/dist/packages/firestore/src/model/field_index.d.ts +2 -2
- package/dist/packages/firestore/src/model/overlay.d.ts +32 -0
- package/dist/packages/firestore/src/model/target_index_matcher.d.ts +70 -0
- package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/packages/firestore/test/util/helpers.d.ts +1 -1
- package/dist/private.d.ts +248 -14
- package/package.json +9 -9
package/dist/index.node.cjs.js
CHANGED
|
@@ -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.
|
|
15
|
+
const version$1 = "3.4.4-canary.3a8d4c1d1";
|
|
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.
|
|
68
|
+
const version = "9.6.6-canary.3a8d4c1d1";
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* @license
|
|
@@ -670,13 +670,19 @@ class FirebaseAppCheckTokenProvider {
|
|
|
670
670
|
this.appCheckProvider = appCheckProvider;
|
|
671
671
|
this.forceRefresh = false;
|
|
672
672
|
this.appCheck = null;
|
|
673
|
+
this.latestAppCheckToken = null;
|
|
673
674
|
}
|
|
674
675
|
start(asyncQueue, changeListener) {
|
|
675
676
|
const onTokenChanged = tokenResult => {
|
|
676
677
|
if (tokenResult.error != null) {
|
|
677
678
|
logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
|
|
678
679
|
}
|
|
679
|
-
|
|
680
|
+
const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
|
|
681
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
682
|
+
logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
|
|
683
|
+
return tokenUpdated
|
|
684
|
+
? changeListener(tokenResult.token)
|
|
685
|
+
: Promise.resolve();
|
|
680
686
|
};
|
|
681
687
|
this.tokenListener = (tokenResult) => {
|
|
682
688
|
asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
|
|
@@ -711,6 +717,7 @@ class FirebaseAppCheckTokenProvider {
|
|
|
711
717
|
return this.appCheck.getToken(forceRefresh).then(tokenResult => {
|
|
712
718
|
if (tokenResult) {
|
|
713
719
|
hardAssert(typeof tokenResult.token === 'string');
|
|
720
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
714
721
|
return new AppCheckToken(tokenResult.token);
|
|
715
722
|
}
|
|
716
723
|
else {
|
|
@@ -1196,23 +1203,6 @@ function decodeResourcePath(path) {
|
|
|
1196
1203
|
}
|
|
1197
1204
|
return new ResourcePath(segments);
|
|
1198
1205
|
}
|
|
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
1206
|
/**
|
|
1217
1207
|
* Schema Version for the Web client:
|
|
1218
1208
|
* 1. Initial version including Mutation Queue, Query Cache, and Remote
|
|
@@ -1231,8 +1221,10 @@ function decodeResourcePath(path) {
|
|
|
1231
1221
|
* an auto-incrementing ID. This is required for Index-Free queries.
|
|
1232
1222
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
1233
1223
|
* 11. Add bundles and named_queries for bundle support.
|
|
1224
|
+
* 12. Add document overlays.
|
|
1225
|
+
* 13. Add indexing support.
|
|
1234
1226
|
*/
|
|
1235
|
-
const SCHEMA_VERSION =
|
|
1227
|
+
const SCHEMA_VERSION = 12;
|
|
1236
1228
|
/**
|
|
1237
1229
|
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
|
|
1238
1230
|
*/
|
|
@@ -1757,9 +1749,7 @@ class DbClientMetadata {
|
|
|
1757
1749
|
DbClientMetadata.store = 'clientMetadata';
|
|
1758
1750
|
/** Keys are automatically assigned via the clientId properties. */
|
|
1759
1751
|
DbClientMetadata.keyPath = 'clientId';
|
|
1760
|
-
/**
|
|
1761
|
-
* A object representing a bundle loaded by the SDK.
|
|
1762
|
-
*/
|
|
1752
|
+
/** An object representing a bundle loaded by the SDK. */
|
|
1763
1753
|
class DbBundle {
|
|
1764
1754
|
constructor(
|
|
1765
1755
|
/** The ID of the loaded bundle. */
|
|
@@ -1776,9 +1766,7 @@ class DbBundle {
|
|
|
1776
1766
|
/** Name of the IndexedDb object store. */
|
|
1777
1767
|
DbBundle.store = 'bundles';
|
|
1778
1768
|
DbBundle.keyPath = 'bundleId';
|
|
1779
|
-
/**
|
|
1780
|
-
* A object representing a named query loaded by the SDK via a bundle.
|
|
1781
|
-
*/
|
|
1769
|
+
/** An object representing a named query loaded by the SDK via a bundle. */
|
|
1782
1770
|
class DbNamedQuery {
|
|
1783
1771
|
constructor(
|
|
1784
1772
|
/** The name of the query. */
|
|
@@ -1795,6 +1783,158 @@ class DbNamedQuery {
|
|
|
1795
1783
|
/** Name of the IndexedDb object store. */
|
|
1796
1784
|
DbNamedQuery.store = 'namedQueries';
|
|
1797
1785
|
DbNamedQuery.keyPath = 'name';
|
|
1786
|
+
/** An object representing the global configuration for a field index. */
|
|
1787
|
+
class DbIndexConfiguration {
|
|
1788
|
+
constructor(
|
|
1789
|
+
/**
|
|
1790
|
+
* The index id for this entry. Undefined for indexes that are not yet
|
|
1791
|
+
* persisted.
|
|
1792
|
+
*/
|
|
1793
|
+
indexId,
|
|
1794
|
+
/** The collection group this index belongs to. */
|
|
1795
|
+
collectionGroup,
|
|
1796
|
+
/** The fields to index for this index. */
|
|
1797
|
+
fields) {
|
|
1798
|
+
this.indexId = indexId;
|
|
1799
|
+
this.collectionGroup = collectionGroup;
|
|
1800
|
+
this.fields = fields;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
/** Name of the IndexedDb object store. */
|
|
1804
|
+
DbIndexConfiguration.store = 'indexConfiguration';
|
|
1805
|
+
DbIndexConfiguration.keyPath = 'indexId';
|
|
1806
|
+
/**
|
|
1807
|
+
* An index that provides access to the index configurations by collection
|
|
1808
|
+
* group.
|
|
1809
|
+
*
|
|
1810
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1811
|
+
* not possible here as the Web client supports concurrent access to
|
|
1812
|
+
* persistence via multi-tab.
|
|
1813
|
+
*/
|
|
1814
|
+
DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
|
|
1815
|
+
DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
|
|
1816
|
+
/**
|
|
1817
|
+
* An object describing how up-to-date the index backfill is for each user and
|
|
1818
|
+
* index.
|
|
1819
|
+
*/
|
|
1820
|
+
class DbIndexState {
|
|
1821
|
+
constructor(
|
|
1822
|
+
/** The index id for this entry. */
|
|
1823
|
+
indexId,
|
|
1824
|
+
/** The user id for this entry. */
|
|
1825
|
+
uid,
|
|
1826
|
+
/**
|
|
1827
|
+
* A number that indicates when the index was last updated (relative to
|
|
1828
|
+
* other indexes).
|
|
1829
|
+
*/
|
|
1830
|
+
sequenceNumber,
|
|
1831
|
+
/**
|
|
1832
|
+
* The latest read time that has been indexed by Firestore for this field
|
|
1833
|
+
* index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
|
|
1834
|
+
*/
|
|
1835
|
+
readTime,
|
|
1836
|
+
/**
|
|
1837
|
+
* The last document that has been indexed for this field index. Empty if
|
|
1838
|
+
* no documents have been indexed.
|
|
1839
|
+
*/
|
|
1840
|
+
documentKey,
|
|
1841
|
+
/**
|
|
1842
|
+
* The largest mutation batch id that has been processed for this index. -1
|
|
1843
|
+
* if no mutations have been indexed.
|
|
1844
|
+
*/
|
|
1845
|
+
largestBatchId) {
|
|
1846
|
+
this.indexId = indexId;
|
|
1847
|
+
this.uid = uid;
|
|
1848
|
+
this.sequenceNumber = sequenceNumber;
|
|
1849
|
+
this.readTime = readTime;
|
|
1850
|
+
this.documentKey = documentKey;
|
|
1851
|
+
this.largestBatchId = largestBatchId;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
/** Name of the IndexedDb object store. */
|
|
1855
|
+
DbIndexState.store = 'indexState';
|
|
1856
|
+
DbIndexState.keyPath = ['indexId', 'uid'];
|
|
1857
|
+
/**
|
|
1858
|
+
* An index that provides access to documents in a collection sorted by last
|
|
1859
|
+
* update time. Used by the backfiller.
|
|
1860
|
+
*
|
|
1861
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1862
|
+
* not possible here as the Web client supports concurrent access to
|
|
1863
|
+
* persistence via multi-tab.
|
|
1864
|
+
*/
|
|
1865
|
+
DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
|
|
1866
|
+
DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
|
|
1867
|
+
/** An object that stores the encoded entries for all documents and fields. */
|
|
1868
|
+
class DbIndexEntry {
|
|
1869
|
+
constructor(
|
|
1870
|
+
/** The index id for this entry. */
|
|
1871
|
+
indexId,
|
|
1872
|
+
/** The user id for this entry. */
|
|
1873
|
+
uid,
|
|
1874
|
+
/** The encoded array index value for this entry. */
|
|
1875
|
+
arrayValue,
|
|
1876
|
+
/** The encoded directional value for equality and inequality filters. */
|
|
1877
|
+
directionalValue,
|
|
1878
|
+
/** The document key this entry points to. */
|
|
1879
|
+
documentKey) {
|
|
1880
|
+
this.indexId = indexId;
|
|
1881
|
+
this.uid = uid;
|
|
1882
|
+
this.arrayValue = arrayValue;
|
|
1883
|
+
this.directionalValue = directionalValue;
|
|
1884
|
+
this.documentKey = documentKey;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
/** Name of the IndexedDb object store. */
|
|
1888
|
+
DbIndexEntry.store = 'indexEntries';
|
|
1889
|
+
DbIndexEntry.keyPath = [
|
|
1890
|
+
'indexId',
|
|
1891
|
+
'uid',
|
|
1892
|
+
'arrayValue',
|
|
1893
|
+
'directionalValue',
|
|
1894
|
+
'documentKey'
|
|
1895
|
+
];
|
|
1896
|
+
DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
|
|
1897
|
+
DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
|
|
1898
|
+
/**
|
|
1899
|
+
* An object representing a document overlay.
|
|
1900
|
+
*/
|
|
1901
|
+
class DbDocumentOverlay {
|
|
1902
|
+
constructor(
|
|
1903
|
+
/** The user ID to whom this overlay belongs. */
|
|
1904
|
+
userId,
|
|
1905
|
+
/** The path to the collection that contains the document. */
|
|
1906
|
+
collectionPath,
|
|
1907
|
+
/** The ID (key) of the document within the collection. */
|
|
1908
|
+
documentId,
|
|
1909
|
+
/** The collection group to which the document belongs. */
|
|
1910
|
+
collectionGroup,
|
|
1911
|
+
/** The largest batch ID that's been applied for this overlay. */
|
|
1912
|
+
largestBatchId,
|
|
1913
|
+
/** The overlay mutation. */
|
|
1914
|
+
overlayMutation) {
|
|
1915
|
+
this.userId = userId;
|
|
1916
|
+
this.collectionPath = collectionPath;
|
|
1917
|
+
this.documentId = documentId;
|
|
1918
|
+
this.collectionGroup = collectionGroup;
|
|
1919
|
+
this.largestBatchId = largestBatchId;
|
|
1920
|
+
this.overlayMutation = overlayMutation;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
/** Name of the IndexedDb object store. */
|
|
1924
|
+
DbDocumentOverlay.store = 'documentOverlays';
|
|
1925
|
+
DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
|
|
1926
|
+
DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
|
|
1927
|
+
DbDocumentOverlay.collectionPathOverlayIndexPath = [
|
|
1928
|
+
'userId',
|
|
1929
|
+
'collectionPath',
|
|
1930
|
+
'largestBatchId'
|
|
1931
|
+
];
|
|
1932
|
+
DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
|
|
1933
|
+
DbDocumentOverlay.collectionGroupOverlayIndexPath = [
|
|
1934
|
+
'userId',
|
|
1935
|
+
'collectionGroup',
|
|
1936
|
+
'largestBatchId'
|
|
1937
|
+
];
|
|
1798
1938
|
// Visible for testing
|
|
1799
1939
|
const V1_STORES = [
|
|
1800
1940
|
DbMutationQueue.store,
|
|
@@ -1809,7 +1949,6 @@ const V1_STORES = [
|
|
|
1809
1949
|
// V2 is no longer usable (see comment at top of file)
|
|
1810
1950
|
// Visible for testing
|
|
1811
1951
|
const V3_STORES = V1_STORES;
|
|
1812
|
-
// Visible for testing
|
|
1813
1952
|
// Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
|
|
1814
1953
|
const V4_STORES = [...V3_STORES, DbClientMetadata.store];
|
|
1815
1954
|
// V5 does not change the set of stores.
|
|
@@ -1819,12 +1958,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
|
|
|
1819
1958
|
// V9 does not change the set of stores.
|
|
1820
1959
|
// V10 does not change the set of stores.
|
|
1821
1960
|
const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1961
|
+
const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
|
|
1962
|
+
const V13_STORES = [
|
|
1963
|
+
...V12_STORES,
|
|
1964
|
+
DbIndexConfiguration.store,
|
|
1965
|
+
DbIndexState.store,
|
|
1966
|
+
DbIndexEntry.store
|
|
1967
|
+
];
|
|
1968
|
+
/** Returns the object stores for the provided schema. */
|
|
1969
|
+
function getObjectStores(schemaVersion) {
|
|
1970
|
+
if (schemaVersion === 13) {
|
|
1971
|
+
return V13_STORES;
|
|
1972
|
+
}
|
|
1973
|
+
else if (schemaVersion === 12) {
|
|
1974
|
+
return V12_STORES;
|
|
1975
|
+
}
|
|
1976
|
+
else if (schemaVersion === 11) {
|
|
1977
|
+
return V11_STORES;
|
|
1978
|
+
}
|
|
1979
|
+
else {
|
|
1980
|
+
fail();
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1828
1983
|
|
|
1829
1984
|
/**
|
|
1830
1985
|
* @license
|
|
@@ -2065,7 +2220,7 @@ class PersistencePromise {
|
|
|
2065
2220
|
*/
|
|
2066
2221
|
// References to `window` are guarded by SimpleDb.isAvailable()
|
|
2067
2222
|
/* eslint-disable no-restricted-globals */
|
|
2068
|
-
const LOG_TAG$
|
|
2223
|
+
const LOG_TAG$h = 'SimpleDb';
|
|
2069
2224
|
/**
|
|
2070
2225
|
* The maximum number of retry attempts for an IndexedDb transaction that fails
|
|
2071
2226
|
* with a DOMException.
|
|
@@ -2116,11 +2271,20 @@ class SimpleDbTransaction {
|
|
|
2116
2271
|
this.completionDeferred.reject(error);
|
|
2117
2272
|
}
|
|
2118
2273
|
if (!this.aborted) {
|
|
2119
|
-
logDebug(LOG_TAG$
|
|
2274
|
+
logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
|
|
2120
2275
|
this.aborted = true;
|
|
2121
2276
|
this.transaction.abort();
|
|
2122
2277
|
}
|
|
2123
2278
|
}
|
|
2279
|
+
maybeCommit() {
|
|
2280
|
+
// If the browser supports V3 IndexedDB, we invoke commit() explicitly to
|
|
2281
|
+
// speed up index DB processing if the event loop remains blocks.
|
|
2282
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2283
|
+
const maybeV3IndexedDb = this.transaction;
|
|
2284
|
+
if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
|
|
2285
|
+
maybeV3IndexedDb.commit();
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2124
2288
|
/**
|
|
2125
2289
|
* Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
|
|
2126
2290
|
* operations performed on the SimpleDbStore happen within the context of this
|
|
@@ -2170,7 +2334,7 @@ class SimpleDb {
|
|
|
2170
2334
|
}
|
|
2171
2335
|
/** Deletes the specified database. */
|
|
2172
2336
|
static delete(name) {
|
|
2173
|
-
logDebug(LOG_TAG$
|
|
2337
|
+
logDebug(LOG_TAG$h, 'Removing database:', name);
|
|
2174
2338
|
return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
|
|
2175
2339
|
}
|
|
2176
2340
|
/** Returns true if IndexedDB is available in the current environment. */
|
|
@@ -2249,7 +2413,7 @@ class SimpleDb {
|
|
|
2249
2413
|
*/
|
|
2250
2414
|
async ensureDb(action) {
|
|
2251
2415
|
if (!this.db) {
|
|
2252
|
-
logDebug(LOG_TAG$
|
|
2416
|
+
logDebug(LOG_TAG$h, 'Opening database:', this.name);
|
|
2253
2417
|
this.db = await new Promise((resolve, reject) => {
|
|
2254
2418
|
// TODO(mikelehen): Investigate browser compatibility.
|
|
2255
2419
|
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
|
|
@@ -2285,12 +2449,12 @@ class SimpleDb {
|
|
|
2285
2449
|
}
|
|
2286
2450
|
};
|
|
2287
2451
|
request.onupgradeneeded = (event) => {
|
|
2288
|
-
logDebug(LOG_TAG$
|
|
2452
|
+
logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
|
|
2289
2453
|
const db = event.target.result;
|
|
2290
2454
|
this.schemaConverter
|
|
2291
2455
|
.createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
|
|
2292
2456
|
.next(() => {
|
|
2293
|
-
logDebug(LOG_TAG$
|
|
2457
|
+
logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
|
|
2294
2458
|
});
|
|
2295
2459
|
};
|
|
2296
2460
|
});
|
|
@@ -2317,6 +2481,10 @@ class SimpleDb {
|
|
|
2317
2481
|
this.db = await this.ensureDb(action);
|
|
2318
2482
|
const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
|
|
2319
2483
|
const transactionFnResult = transactionFn(transaction)
|
|
2484
|
+
.next(result => {
|
|
2485
|
+
transaction.maybeCommit();
|
|
2486
|
+
return result;
|
|
2487
|
+
})
|
|
2320
2488
|
.catch(error => {
|
|
2321
2489
|
// Abort the transaction if there was an error.
|
|
2322
2490
|
transaction.abort(error);
|
|
@@ -2344,7 +2512,7 @@ class SimpleDb {
|
|
|
2344
2512
|
// exception is wrapped in a generic error by our async/await handling.
|
|
2345
2513
|
const retryable = error.name !== 'FirebaseError' &&
|
|
2346
2514
|
attemptNumber < TRANSACTION_RETRY_COUNT;
|
|
2347
|
-
logDebug(LOG_TAG$
|
|
2515
|
+
logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
|
|
2348
2516
|
this.close();
|
|
2349
2517
|
if (!retryable) {
|
|
2350
2518
|
return Promise.reject(error);
|
|
@@ -2431,11 +2599,11 @@ class SimpleDbStore {
|
|
|
2431
2599
|
put(keyOrValue, value) {
|
|
2432
2600
|
let request;
|
|
2433
2601
|
if (value !== undefined) {
|
|
2434
|
-
logDebug(LOG_TAG$
|
|
2602
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
|
|
2435
2603
|
request = this.store.put(value, keyOrValue);
|
|
2436
2604
|
}
|
|
2437
2605
|
else {
|
|
2438
|
-
logDebug(LOG_TAG$
|
|
2606
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
|
|
2439
2607
|
request = this.store.put(keyOrValue);
|
|
2440
2608
|
}
|
|
2441
2609
|
return wrapRequest(request);
|
|
@@ -2448,7 +2616,7 @@ class SimpleDbStore {
|
|
|
2448
2616
|
* @returns The key of the value to add.
|
|
2449
2617
|
*/
|
|
2450
2618
|
add(value) {
|
|
2451
|
-
logDebug(LOG_TAG$
|
|
2619
|
+
logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
|
|
2452
2620
|
const request = this.store.add(value);
|
|
2453
2621
|
return wrapRequest(request);
|
|
2454
2622
|
}
|
|
@@ -2468,12 +2636,12 @@ class SimpleDbStore {
|
|
|
2468
2636
|
if (result === undefined) {
|
|
2469
2637
|
result = null;
|
|
2470
2638
|
}
|
|
2471
|
-
logDebug(LOG_TAG$
|
|
2639
|
+
logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
|
|
2472
2640
|
return result;
|
|
2473
2641
|
});
|
|
2474
2642
|
}
|
|
2475
2643
|
delete(key) {
|
|
2476
|
-
logDebug(LOG_TAG$
|
|
2644
|
+
logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
|
|
2477
2645
|
const request = this.store.delete(key);
|
|
2478
2646
|
return wrapRequest(request);
|
|
2479
2647
|
}
|
|
@@ -2484,21 +2652,37 @@ class SimpleDbStore {
|
|
|
2484
2652
|
* Returns the number of rows in the store.
|
|
2485
2653
|
*/
|
|
2486
2654
|
count() {
|
|
2487
|
-
logDebug(LOG_TAG$
|
|
2655
|
+
logDebug(LOG_TAG$h, 'COUNT', this.store.name);
|
|
2488
2656
|
const request = this.store.count();
|
|
2489
2657
|
return wrapRequest(request);
|
|
2490
2658
|
}
|
|
2491
2659
|
loadAll(indexOrRange, range) {
|
|
2492
|
-
const
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
return
|
|
2498
|
-
|
|
2660
|
+
const iterateOptions = this.options(indexOrRange, range);
|
|
2661
|
+
// Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
|
|
2662
|
+
// 20% faster. Unfortunately, getAll() does not support custom indices.
|
|
2663
|
+
if (!iterateOptions.index && typeof this.store.getAll === 'function') {
|
|
2664
|
+
const request = this.store.getAll(iterateOptions.range);
|
|
2665
|
+
return new PersistencePromise((resolve, reject) => {
|
|
2666
|
+
request.onerror = (event) => {
|
|
2667
|
+
reject(event.target.error);
|
|
2668
|
+
};
|
|
2669
|
+
request.onsuccess = (event) => {
|
|
2670
|
+
resolve(event.target.result);
|
|
2671
|
+
};
|
|
2672
|
+
});
|
|
2673
|
+
}
|
|
2674
|
+
else {
|
|
2675
|
+
const cursor = this.cursor(iterateOptions);
|
|
2676
|
+
const results = [];
|
|
2677
|
+
return this.iterateCursor(cursor, (key, value) => {
|
|
2678
|
+
results.push(value);
|
|
2679
|
+
}).next(() => {
|
|
2680
|
+
return results;
|
|
2681
|
+
});
|
|
2682
|
+
}
|
|
2499
2683
|
}
|
|
2500
2684
|
deleteAll(indexOrRange, range) {
|
|
2501
|
-
logDebug(LOG_TAG$
|
|
2685
|
+
logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
|
|
2502
2686
|
const options = this.options(indexOrRange, range);
|
|
2503
2687
|
options.keysOnly = false;
|
|
2504
2688
|
const cursor = this.cursor(options);
|
|
@@ -3521,11 +3705,22 @@ class DocumentKey {
|
|
|
3521
3705
|
static empty() {
|
|
3522
3706
|
return new DocumentKey(ResourcePath.emptyPath());
|
|
3523
3707
|
}
|
|
3708
|
+
get collectionGroup() {
|
|
3709
|
+
return this.path.popLast().lastSegment();
|
|
3710
|
+
}
|
|
3524
3711
|
/** Returns true if the document is in the specified collectionId. */
|
|
3525
3712
|
hasCollectionId(collectionId) {
|
|
3526
3713
|
return (this.path.length >= 2 &&
|
|
3527
3714
|
this.path.get(this.path.length - 2) === collectionId);
|
|
3528
3715
|
}
|
|
3716
|
+
/** Returns the collection group (i.e. the name of the parent collection) for this key. */
|
|
3717
|
+
getCollectionGroup() {
|
|
3718
|
+
return this.path.get(this.path.length - 2);
|
|
3719
|
+
}
|
|
3720
|
+
/** Returns the fully qualified path to the parent collection. */
|
|
3721
|
+
getCollectionPath() {
|
|
3722
|
+
return this.path.popLast();
|
|
3723
|
+
}
|
|
3529
3724
|
isEqual(other) {
|
|
3530
3725
|
return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
|
|
3531
3726
|
}
|
|
@@ -3565,6 +3760,13 @@ class DocumentKey {
|
|
|
3565
3760
|
* See the License for the specific language governing permissions and
|
|
3566
3761
|
* limitations under the License.
|
|
3567
3762
|
*/
|
|
3763
|
+
const MAX_VALUE = {
|
|
3764
|
+
mapValue: {
|
|
3765
|
+
fields: {
|
|
3766
|
+
'__type__': { stringValue: '__max___' }
|
|
3767
|
+
}
|
|
3768
|
+
}
|
|
3769
|
+
};
|
|
3568
3770
|
/** Extracts the backend's type order for the provided value. */
|
|
3569
3771
|
function typeOrder(value) {
|
|
3570
3772
|
if ('nullValue' in value) {
|
|
@@ -3974,6 +4176,10 @@ function deepClone(source) {
|
|
|
3974
4176
|
else {
|
|
3975
4177
|
return Object.assign({}, source);
|
|
3976
4178
|
}
|
|
4179
|
+
}
|
|
4180
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
4181
|
+
function isMaxValue(value) {
|
|
4182
|
+
return valueEquals(value, MAX_VALUE);
|
|
3977
4183
|
}
|
|
3978
4184
|
|
|
3979
4185
|
/**
|
|
@@ -4349,17 +4555,25 @@ class FieldIndex {
|
|
|
4349
4555
|
/** The collection ID this index applies to. */
|
|
4350
4556
|
collectionGroup,
|
|
4351
4557
|
/** The field segments for this index. */
|
|
4352
|
-
|
|
4558
|
+
fields,
|
|
4353
4559
|
/** Shows how up-to-date the index is for the current user. */
|
|
4354
4560
|
indexState) {
|
|
4355
4561
|
this.indexId = indexId;
|
|
4356
4562
|
this.collectionGroup = collectionGroup;
|
|
4357
|
-
this.
|
|
4563
|
+
this.fields = fields;
|
|
4358
4564
|
this.indexState = indexState;
|
|
4359
4565
|
}
|
|
4360
4566
|
}
|
|
4361
4567
|
/** An ID for an index that has not yet been added to persistence. */
|
|
4362
4568
|
FieldIndex.UNKNOWN_ID = -1;
|
|
4569
|
+
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
4570
|
+
function fieldIndexGetArraySegment(fieldIndex) {
|
|
4571
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
4572
|
+
}
|
|
4573
|
+
/** Returns all directional (ascending/descending) segments for this index. */
|
|
4574
|
+
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4575
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4576
|
+
}
|
|
4363
4577
|
/** An index component consisting of field path and index type. */
|
|
4364
4578
|
class IndexSegment {
|
|
4365
4579
|
constructor(
|
|
@@ -4464,27 +4678,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
|
|
|
4464
4678
|
function canonifyTarget(target) {
|
|
4465
4679
|
const targetImpl = debugCast(target);
|
|
4466
4680
|
if (targetImpl.memoizedCanonicalId === null) {
|
|
4467
|
-
let
|
|
4681
|
+
let str = targetImpl.path.canonicalString();
|
|
4468
4682
|
if (targetImpl.collectionGroup !== null) {
|
|
4469
|
-
|
|
4683
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
4470
4684
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4685
|
+
str += '|f:';
|
|
4686
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
4687
|
+
str += '|ob:';
|
|
4688
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
4475
4689
|
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
4476
|
-
|
|
4477
|
-
|
|
4690
|
+
str += '|l:';
|
|
4691
|
+
str += targetImpl.limit;
|
|
4478
4692
|
}
|
|
4479
4693
|
if (targetImpl.startAt) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4694
|
+
str += '|lb:';
|
|
4695
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
4696
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4482
4697
|
}
|
|
4483
4698
|
if (targetImpl.endAt) {
|
|
4484
|
-
|
|
4485
|
-
|
|
4699
|
+
str += '|ub:';
|
|
4700
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
4701
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4486
4702
|
}
|
|
4487
|
-
targetImpl.memoizedCanonicalId =
|
|
4703
|
+
targetImpl.memoizedCanonicalId = str;
|
|
4488
4704
|
}
|
|
4489
4705
|
return targetImpl.memoizedCanonicalId;
|
|
4490
4706
|
}
|
|
@@ -4507,10 +4723,14 @@ function stringifyTarget(target) {
|
|
|
4507
4723
|
.join(', ')}]`;
|
|
4508
4724
|
}
|
|
4509
4725
|
if (target.startAt) {
|
|
4510
|
-
str += ', startAt: '
|
|
4726
|
+
str += ', startAt: ';
|
|
4727
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
4728
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4511
4729
|
}
|
|
4512
4730
|
if (target.endAt) {
|
|
4513
|
-
str += ', endAt: '
|
|
4731
|
+
str += ', endAt: ';
|
|
4732
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
4733
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4514
4734
|
}
|
|
4515
4735
|
return `Target(${str})`;
|
|
4516
4736
|
}
|
|
@@ -4733,7 +4953,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4733
4953
|
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
4734
4954
|
}
|
|
4735
4955
|
}
|
|
4736
|
-
// TODO(indexing): Change Bound.before to "inclusive"
|
|
4737
4956
|
/**
|
|
4738
4957
|
* Represents a bound of a query.
|
|
4739
4958
|
*
|
|
@@ -4749,17 +4968,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4749
4968
|
* just after the provided values.
|
|
4750
4969
|
*/
|
|
4751
4970
|
class Bound {
|
|
4752
|
-
constructor(position,
|
|
4971
|
+
constructor(position, inclusive) {
|
|
4753
4972
|
this.position = position;
|
|
4754
|
-
this.
|
|
4973
|
+
this.inclusive = inclusive;
|
|
4755
4974
|
}
|
|
4756
4975
|
}
|
|
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
4976
|
/**
|
|
4764
4977
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
4765
4978
|
*/
|
|
@@ -4779,11 +4992,7 @@ function stringifyOrderBy(orderBy) {
|
|
|
4779
4992
|
function orderByEquals(left, right) {
|
|
4780
4993
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
4781
4994
|
}
|
|
4782
|
-
|
|
4783
|
-
* Returns true if a document sorts before a bound using the provided sort
|
|
4784
|
-
* order.
|
|
4785
|
-
*/
|
|
4786
|
-
function sortsBeforeDocument(bound, orderBy, doc) {
|
|
4995
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
4787
4996
|
let comparison = 0;
|
|
4788
4997
|
for (let i = 0; i < bound.position.length; i++) {
|
|
4789
4998
|
const orderByComponent = orderBy[i];
|
|
@@ -4802,7 +5011,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
|
|
|
4802
5011
|
break;
|
|
4803
5012
|
}
|
|
4804
5013
|
}
|
|
4805
|
-
return
|
|
5014
|
+
return comparison;
|
|
5015
|
+
}
|
|
5016
|
+
/**
|
|
5017
|
+
* Returns true if a document sorts after a bound using the provided sort
|
|
5018
|
+
* order.
|
|
5019
|
+
*/
|
|
5020
|
+
function boundSortsAfterDocument(bound, orderBy, doc) {
|
|
5021
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5022
|
+
return bound.inclusive ? comparison >= 0 : comparison > 0;
|
|
5023
|
+
}
|
|
5024
|
+
/**
|
|
5025
|
+
* Returns true if a document sorts before a bound using the provided sort
|
|
5026
|
+
* order.
|
|
5027
|
+
*/
|
|
5028
|
+
function boundSortsBeforeDocument(bound, orderBy, doc) {
|
|
5029
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5030
|
+
return bound.inclusive ? comparison <= 0 : comparison < 0;
|
|
4806
5031
|
}
|
|
4807
5032
|
function boundEquals(left, right) {
|
|
4808
5033
|
if (left === null) {
|
|
@@ -4811,7 +5036,7 @@ function boundEquals(left, right) {
|
|
|
4811
5036
|
else if (right === null) {
|
|
4812
5037
|
return false;
|
|
4813
5038
|
}
|
|
4814
|
-
if (left.
|
|
5039
|
+
if (left.inclusive !== right.inclusive ||
|
|
4815
5040
|
left.position.length !== right.position.length) {
|
|
4816
5041
|
return false;
|
|
4817
5042
|
}
|
|
@@ -5015,10 +5240,10 @@ function queryToTarget(query) {
|
|
|
5015
5240
|
}
|
|
5016
5241
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
5017
5242
|
const startAt = queryImpl.endAt
|
|
5018
|
-
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.
|
|
5243
|
+
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
|
|
5019
5244
|
: null;
|
|
5020
5245
|
const endAt = queryImpl.startAt
|
|
5021
|
-
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.
|
|
5246
|
+
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
|
|
5022
5247
|
: null;
|
|
5023
5248
|
// Now return as a LimitType.First query.
|
|
5024
5249
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
|
|
@@ -5106,11 +5331,11 @@ function queryMatchesFilters(query, doc) {
|
|
|
5106
5331
|
/** Makes sure a document is within the bounds, if provided. */
|
|
5107
5332
|
function queryMatchesBounds(query, doc) {
|
|
5108
5333
|
if (query.startAt &&
|
|
5109
|
-
!
|
|
5334
|
+
!boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
|
|
5110
5335
|
return false;
|
|
5111
5336
|
}
|
|
5112
5337
|
if (query.endAt &&
|
|
5113
|
-
|
|
5338
|
+
!boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
|
|
5114
5339
|
return false;
|
|
5115
5340
|
}
|
|
5116
5341
|
return true;
|
|
@@ -5765,6 +5990,72 @@ class SortedSetIterator {
|
|
|
5765
5990
|
hasNext() {
|
|
5766
5991
|
return this.iter.hasNext();
|
|
5767
5992
|
}
|
|
5993
|
+
}
|
|
5994
|
+
/**
|
|
5995
|
+
* Compares two sorted sets for equality using their natural ordering. The
|
|
5996
|
+
* method computes the intersection and invokes `onAdd` for every element that
|
|
5997
|
+
* is in `after` but not `before`. `onRemove` is invoked for every element in
|
|
5998
|
+
* `before` but missing from `after`.
|
|
5999
|
+
*
|
|
6000
|
+
* The method creates a copy of both `before` and `after` and runs in O(n log
|
|
6001
|
+
* n), where n is the size of the two lists.
|
|
6002
|
+
*
|
|
6003
|
+
* @param before - The elements that exist in the original set.
|
|
6004
|
+
* @param after - The elements to diff against the original set.
|
|
6005
|
+
* @param comparator - The comparator for the elements in before and after.
|
|
6006
|
+
* @param onAdd - A function to invoke for every element that is part of `
|
|
6007
|
+
* after` but not `before`.
|
|
6008
|
+
* @param onRemove - A function to invoke for every element that is part of
|
|
6009
|
+
* `before` but not `after`.
|
|
6010
|
+
*/
|
|
6011
|
+
function diffSortedSets(before, after, comparator, onAdd, onRemove) {
|
|
6012
|
+
const beforeIt = before.getIterator();
|
|
6013
|
+
const afterIt = after.getIterator();
|
|
6014
|
+
let beforeValue = advanceIterator(beforeIt);
|
|
6015
|
+
let afterValue = advanceIterator(afterIt);
|
|
6016
|
+
// Walk through the two sets at the same time, using the ordering defined by
|
|
6017
|
+
// `comparator`.
|
|
6018
|
+
while (beforeValue || afterValue) {
|
|
6019
|
+
let added = false;
|
|
6020
|
+
let removed = false;
|
|
6021
|
+
if (beforeValue && afterValue) {
|
|
6022
|
+
const cmp = comparator(beforeValue, afterValue);
|
|
6023
|
+
if (cmp < 0) {
|
|
6024
|
+
// The element was removed if the next element in our ordered
|
|
6025
|
+
// walkthrough is only in `before`.
|
|
6026
|
+
removed = true;
|
|
6027
|
+
}
|
|
6028
|
+
else if (cmp > 0) {
|
|
6029
|
+
// The element was added if the next element in our ordered walkthrough
|
|
6030
|
+
// is only in `after`.
|
|
6031
|
+
added = true;
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
else if (beforeValue != null) {
|
|
6035
|
+
removed = true;
|
|
6036
|
+
}
|
|
6037
|
+
else {
|
|
6038
|
+
added = true;
|
|
6039
|
+
}
|
|
6040
|
+
if (added) {
|
|
6041
|
+
onAdd(afterValue);
|
|
6042
|
+
afterValue = advanceIterator(afterIt);
|
|
6043
|
+
}
|
|
6044
|
+
else if (removed) {
|
|
6045
|
+
onRemove(beforeValue);
|
|
6046
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6047
|
+
}
|
|
6048
|
+
else {
|
|
6049
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6050
|
+
afterValue = advanceIterator(afterIt);
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
/**
|
|
6055
|
+
* Returns the next element from the iterator or `undefined` if none available.
|
|
6056
|
+
*/
|
|
6057
|
+
function advanceIterator(it) {
|
|
6058
|
+
return it.hasNext() ? it.getNext() : undefined;
|
|
5768
6059
|
}
|
|
5769
6060
|
|
|
5770
6061
|
/**
|
|
@@ -6603,6 +6894,47 @@ class MutationBatchResult {
|
|
|
6603
6894
|
}
|
|
6604
6895
|
}
|
|
6605
6896
|
|
|
6897
|
+
/**
|
|
6898
|
+
* @license
|
|
6899
|
+
* Copyright 2022 Google LLC
|
|
6900
|
+
*
|
|
6901
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6902
|
+
* you may not use this file except in compliance with the License.
|
|
6903
|
+
* You may obtain a copy of the License at
|
|
6904
|
+
*
|
|
6905
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
6906
|
+
*
|
|
6907
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
6908
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
6909
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6910
|
+
* See the License for the specific language governing permissions and
|
|
6911
|
+
* limitations under the License.
|
|
6912
|
+
*/
|
|
6913
|
+
/**
|
|
6914
|
+
* Representation of an overlay computed by Firestore.
|
|
6915
|
+
*
|
|
6916
|
+
* Holds information about a mutation and the largest batch id in Firestore when
|
|
6917
|
+
* the mutation was created.
|
|
6918
|
+
*/
|
|
6919
|
+
class Overlay {
|
|
6920
|
+
constructor(largestBatchId, mutation) {
|
|
6921
|
+
this.largestBatchId = largestBatchId;
|
|
6922
|
+
this.mutation = mutation;
|
|
6923
|
+
}
|
|
6924
|
+
getKey() {
|
|
6925
|
+
return this.mutation.key;
|
|
6926
|
+
}
|
|
6927
|
+
isEqual(other) {
|
|
6928
|
+
return other !== null && this.mutation === other.mutation;
|
|
6929
|
+
}
|
|
6930
|
+
toString() {
|
|
6931
|
+
return `Overlay{
|
|
6932
|
+
largestBatchId: ${this.largestBatchId},
|
|
6933
|
+
mutation: ${this.mutation.toString()}
|
|
6934
|
+
}`;
|
|
6935
|
+
}
|
|
6936
|
+
}
|
|
6937
|
+
|
|
6606
6938
|
/**
|
|
6607
6939
|
* @license
|
|
6608
6940
|
* Copyright 2017 Google LLC
|
|
@@ -7075,7 +7407,7 @@ class TargetState {
|
|
|
7075
7407
|
this._current = true;
|
|
7076
7408
|
}
|
|
7077
7409
|
}
|
|
7078
|
-
const LOG_TAG$
|
|
7410
|
+
const LOG_TAG$g = 'WatchChangeAggregator';
|
|
7079
7411
|
/**
|
|
7080
7412
|
* A helper class to accumulate watch changes into a RemoteEvent.
|
|
7081
7413
|
*/
|
|
@@ -7370,7 +7702,7 @@ class WatchChangeAggregator {
|
|
|
7370
7702
|
isActiveTarget(targetId) {
|
|
7371
7703
|
const targetActive = this.targetDataForActiveTarget(targetId) !== null;
|
|
7372
7704
|
if (!targetActive) {
|
|
7373
|
-
logDebug(LOG_TAG$
|
|
7705
|
+
logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
|
|
7374
7706
|
}
|
|
7375
7707
|
return targetActive;
|
|
7376
7708
|
}
|
|
@@ -7989,10 +8321,10 @@ function toQueryTarget(serializer, target) {
|
|
|
7989
8321
|
result.structuredQuery.limit = limit;
|
|
7990
8322
|
}
|
|
7991
8323
|
if (target.startAt) {
|
|
7992
|
-
result.structuredQuery.startAt =
|
|
8324
|
+
result.structuredQuery.startAt = toStartAtCursor(target.startAt);
|
|
7993
8325
|
}
|
|
7994
8326
|
if (target.endAt) {
|
|
7995
|
-
result.structuredQuery.endAt =
|
|
8327
|
+
result.structuredQuery.endAt = toEndAtCursor(target.endAt);
|
|
7996
8328
|
}
|
|
7997
8329
|
return result;
|
|
7998
8330
|
}
|
|
@@ -8025,11 +8357,11 @@ function convertQueryTargetToQuery(target) {
|
|
|
8025
8357
|
}
|
|
8026
8358
|
let startAt = null;
|
|
8027
8359
|
if (query.startAt) {
|
|
8028
|
-
startAt =
|
|
8360
|
+
startAt = fromStartAtCursor(query.startAt);
|
|
8029
8361
|
}
|
|
8030
8362
|
let endAt = null;
|
|
8031
8363
|
if (query.endAt) {
|
|
8032
|
-
endAt =
|
|
8364
|
+
endAt = fromEndAtCursor(query.endAt);
|
|
8033
8365
|
}
|
|
8034
8366
|
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
8035
8367
|
}
|
|
@@ -8120,16 +8452,27 @@ function toOrder(orderBys) {
|
|
|
8120
8452
|
function fromOrder(orderBys) {
|
|
8121
8453
|
return orderBys.map(order => fromPropertyOrder(order));
|
|
8122
8454
|
}
|
|
8123
|
-
function
|
|
8455
|
+
function toStartAtCursor(cursor) {
|
|
8456
|
+
return {
|
|
8457
|
+
before: cursor.inclusive,
|
|
8458
|
+
values: cursor.position
|
|
8459
|
+
};
|
|
8460
|
+
}
|
|
8461
|
+
function toEndAtCursor(cursor) {
|
|
8124
8462
|
return {
|
|
8125
|
-
before: cursor.
|
|
8463
|
+
before: !cursor.inclusive,
|
|
8126
8464
|
values: cursor.position
|
|
8127
8465
|
};
|
|
8128
8466
|
}
|
|
8129
|
-
function
|
|
8130
|
-
const
|
|
8467
|
+
function fromStartAtCursor(cursor) {
|
|
8468
|
+
const inclusive = !!cursor.before;
|
|
8131
8469
|
const position = cursor.values || [];
|
|
8132
|
-
return new Bound(position,
|
|
8470
|
+
return new Bound(position, inclusive);
|
|
8471
|
+
}
|
|
8472
|
+
function fromEndAtCursor(cursor) {
|
|
8473
|
+
const inclusive = !cursor.before;
|
|
8474
|
+
const position = cursor.values || [];
|
|
8475
|
+
return new Bound(position, inclusive);
|
|
8133
8476
|
}
|
|
8134
8477
|
// visible for testing
|
|
8135
8478
|
function toDirection(dir) {
|
|
@@ -8576,28 +8919,764 @@ function fromBundledQuery(bundledQuery) {
|
|
|
8576
8919
|
if (bundledQuery.limitType === 'LAST') {
|
|
8577
8920
|
return queryWithLimit(query, query.limit, "L" /* Last */);
|
|
8578
8921
|
}
|
|
8579
|
-
return query;
|
|
8922
|
+
return query;
|
|
8923
|
+
}
|
|
8924
|
+
/** Encodes a NamedQuery proto object to a NamedQuery model object. */
|
|
8925
|
+
function fromProtoNamedQuery(namedQuery) {
|
|
8926
|
+
return {
|
|
8927
|
+
name: namedQuery.name,
|
|
8928
|
+
query: fromBundledQuery(namedQuery.bundledQuery),
|
|
8929
|
+
readTime: fromVersion(namedQuery.readTime)
|
|
8930
|
+
};
|
|
8931
|
+
}
|
|
8932
|
+
/** Decodes a BundleMetadata proto into a BundleMetadata object. */
|
|
8933
|
+
function fromBundleMetadata(metadata) {
|
|
8934
|
+
return {
|
|
8935
|
+
id: metadata.id,
|
|
8936
|
+
version: metadata.version,
|
|
8937
|
+
createTime: fromVersion(metadata.createTime)
|
|
8938
|
+
};
|
|
8939
|
+
}
|
|
8940
|
+
/** Encodes a DbDocumentOverlay object to an Overlay model object. */
|
|
8941
|
+
function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
|
|
8942
|
+
return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
|
|
8943
|
+
}
|
|
8944
|
+
/** Decodes an Overlay model object into a DbDocumentOverlay object. */
|
|
8945
|
+
function toDbDocumentOverlay(localSerializer, userId, overlay) {
|
|
8946
|
+
const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
|
|
8947
|
+
return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
|
|
8948
|
+
}
|
|
8949
|
+
/**
|
|
8950
|
+
* Returns the DbDocumentOverlayKey corresponding to the given user and
|
|
8951
|
+
* document key.
|
|
8952
|
+
*/
|
|
8953
|
+
function toDbDocumentOverlayKey(userId, docKey) {
|
|
8954
|
+
const docId = docKey.path.lastSegment();
|
|
8955
|
+
const collectionPath = encodeResourcePath(docKey.path.popLast());
|
|
8956
|
+
return [userId, collectionPath, docId];
|
|
8957
|
+
}
|
|
8958
|
+
function toDbIndexConfiguration(index) {
|
|
8959
|
+
return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
|
|
8960
|
+
}
|
|
8961
|
+
function fromDbIndexConfiguration(index, state) {
|
|
8962
|
+
const decodedState = state
|
|
8963
|
+
? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
|
|
8964
|
+
: IndexState.empty();
|
|
8965
|
+
const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
|
|
8966
|
+
return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
|
|
8967
|
+
}
|
|
8968
|
+
function toDbIndexState(indexId, user, sequenceNumber, offset) {
|
|
8969
|
+
return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
|
|
8970
|
+
}
|
|
8971
|
+
|
|
8972
|
+
/**
|
|
8973
|
+
* @license
|
|
8974
|
+
* Copyright 2020 Google LLC
|
|
8975
|
+
*
|
|
8976
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8977
|
+
* you may not use this file except in compliance with the License.
|
|
8978
|
+
* You may obtain a copy of the License at
|
|
8979
|
+
*
|
|
8980
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8981
|
+
*
|
|
8982
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
8983
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
8984
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
8985
|
+
* See the License for the specific language governing permissions and
|
|
8986
|
+
* limitations under the License.
|
|
8987
|
+
*/
|
|
8988
|
+
class IndexedDbBundleCache {
|
|
8989
|
+
getBundleMetadata(transaction, bundleId) {
|
|
8990
|
+
return bundlesStore(transaction)
|
|
8991
|
+
.get(bundleId)
|
|
8992
|
+
.next(bundle => {
|
|
8993
|
+
if (bundle) {
|
|
8994
|
+
return fromDbBundle(bundle);
|
|
8995
|
+
}
|
|
8996
|
+
return undefined;
|
|
8997
|
+
});
|
|
8998
|
+
}
|
|
8999
|
+
saveBundleMetadata(transaction, bundleMetadata) {
|
|
9000
|
+
return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
|
|
9001
|
+
}
|
|
9002
|
+
getNamedQuery(transaction, queryName) {
|
|
9003
|
+
return namedQueriesStore(transaction)
|
|
9004
|
+
.get(queryName)
|
|
9005
|
+
.next(query => {
|
|
9006
|
+
if (query) {
|
|
9007
|
+
return fromDbNamedQuery(query);
|
|
9008
|
+
}
|
|
9009
|
+
return undefined;
|
|
9010
|
+
});
|
|
9011
|
+
}
|
|
9012
|
+
saveNamedQuery(transaction, query) {
|
|
9013
|
+
return namedQueriesStore(transaction).put(toDbNamedQuery(query));
|
|
9014
|
+
}
|
|
9015
|
+
}
|
|
9016
|
+
/**
|
|
9017
|
+
* Helper to get a typed SimpleDbStore for the bundles object store.
|
|
9018
|
+
*/
|
|
9019
|
+
function bundlesStore(txn) {
|
|
9020
|
+
return getStore(txn, DbBundle.store);
|
|
9021
|
+
}
|
|
9022
|
+
/**
|
|
9023
|
+
* Helper to get a typed SimpleDbStore for the namedQueries object store.
|
|
9024
|
+
*/
|
|
9025
|
+
function namedQueriesStore(txn) {
|
|
9026
|
+
return getStore(txn, DbNamedQuery.store);
|
|
9027
|
+
}
|
|
9028
|
+
|
|
9029
|
+
/**
|
|
9030
|
+
* @license
|
|
9031
|
+
* Copyright 2022 Google LLC
|
|
9032
|
+
*
|
|
9033
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9034
|
+
* you may not use this file except in compliance with the License.
|
|
9035
|
+
* You may obtain a copy of the License at
|
|
9036
|
+
*
|
|
9037
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9038
|
+
*
|
|
9039
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9040
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9041
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9042
|
+
* See the License for the specific language governing permissions and
|
|
9043
|
+
* limitations under the License.
|
|
9044
|
+
*/
|
|
9045
|
+
/**
|
|
9046
|
+
* Implementation of DocumentOverlayCache using IndexedDb.
|
|
9047
|
+
*/
|
|
9048
|
+
class IndexedDbDocumentOverlayCache {
|
|
9049
|
+
/**
|
|
9050
|
+
* @param serializer - The document serializer.
|
|
9051
|
+
* @param userId - The userId for which we are accessing overlays.
|
|
9052
|
+
*/
|
|
9053
|
+
constructor(serializer, userId) {
|
|
9054
|
+
this.serializer = serializer;
|
|
9055
|
+
this.userId = userId;
|
|
9056
|
+
}
|
|
9057
|
+
static forUser(serializer, user) {
|
|
9058
|
+
const userId = user.uid || '';
|
|
9059
|
+
return new IndexedDbDocumentOverlayCache(serializer, userId);
|
|
9060
|
+
}
|
|
9061
|
+
getOverlay(transaction, key) {
|
|
9062
|
+
return documentOverlayStore(transaction)
|
|
9063
|
+
.get(toDbDocumentOverlayKey(this.userId, key))
|
|
9064
|
+
.next(dbOverlay => {
|
|
9065
|
+
if (dbOverlay) {
|
|
9066
|
+
return fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9067
|
+
}
|
|
9068
|
+
return null;
|
|
9069
|
+
});
|
|
9070
|
+
}
|
|
9071
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
9072
|
+
const promises = [];
|
|
9073
|
+
overlays.forEach(mutation => {
|
|
9074
|
+
const overlay = new Overlay(largestBatchId, mutation);
|
|
9075
|
+
promises.push(this.saveOverlay(transaction, overlay));
|
|
9076
|
+
});
|
|
9077
|
+
return PersistencePromise.waitFor(promises);
|
|
9078
|
+
}
|
|
9079
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
9080
|
+
const collectionPaths = new Set();
|
|
9081
|
+
// Get the set of unique collection paths.
|
|
9082
|
+
documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
|
|
9083
|
+
const promises = [];
|
|
9084
|
+
collectionPaths.forEach(collectionPath => {
|
|
9085
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
|
|
9086
|
+
/*lowerOpen=*/ false,
|
|
9087
|
+
/*upperOpen=*/ true);
|
|
9088
|
+
promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
|
|
9089
|
+
});
|
|
9090
|
+
return PersistencePromise.waitFor(promises);
|
|
9091
|
+
}
|
|
9092
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
9093
|
+
const result = new Map();
|
|
9094
|
+
const collectionPath = encodeResourcePath(collection);
|
|
9095
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9096
|
+
// is not inclusive.
|
|
9097
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
|
|
9098
|
+
/*lowerOpen=*/ true);
|
|
9099
|
+
return documentOverlayStore(transaction)
|
|
9100
|
+
.loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
|
|
9101
|
+
.next(dbOverlays => {
|
|
9102
|
+
for (const dbOverlay of dbOverlays) {
|
|
9103
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9104
|
+
result.set(overlay.getKey(), overlay);
|
|
9105
|
+
}
|
|
9106
|
+
return result;
|
|
9107
|
+
});
|
|
9108
|
+
}
|
|
9109
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
9110
|
+
const result = new Map();
|
|
9111
|
+
let currentBatchId = undefined;
|
|
9112
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9113
|
+
// is not inclusive.
|
|
9114
|
+
const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
|
|
9115
|
+
/*lowerOpen=*/ true);
|
|
9116
|
+
return documentOverlayStore(transaction)
|
|
9117
|
+
.iterate({
|
|
9118
|
+
index: DbDocumentOverlay.collectionGroupOverlayIndex,
|
|
9119
|
+
range
|
|
9120
|
+
}, (_, dbOverlay, control) => {
|
|
9121
|
+
// We do not want to return partial batch overlays, even if the size
|
|
9122
|
+
// of the result set exceeds the given `count` argument. Therefore, we
|
|
9123
|
+
// continue to aggregate results even after the result size exceeds
|
|
9124
|
+
// `count` if there are more overlays from the `currentBatchId`.
|
|
9125
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9126
|
+
if (result.size < count ||
|
|
9127
|
+
overlay.largestBatchId === currentBatchId) {
|
|
9128
|
+
result.set(overlay.getKey(), overlay);
|
|
9129
|
+
currentBatchId = overlay.largestBatchId;
|
|
9130
|
+
}
|
|
9131
|
+
else {
|
|
9132
|
+
control.done();
|
|
9133
|
+
}
|
|
9134
|
+
})
|
|
9135
|
+
.next(() => result);
|
|
9136
|
+
}
|
|
9137
|
+
saveOverlay(transaction, overlay) {
|
|
9138
|
+
return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
|
|
9139
|
+
}
|
|
9140
|
+
}
|
|
9141
|
+
/**
|
|
9142
|
+
* Helper to get a typed SimpleDbStore for the document overlay object store.
|
|
9143
|
+
*/
|
|
9144
|
+
function documentOverlayStore(txn) {
|
|
9145
|
+
return getStore(txn, DbDocumentOverlay.store);
|
|
9146
|
+
}
|
|
9147
|
+
|
|
9148
|
+
/**
|
|
9149
|
+
* @license
|
|
9150
|
+
* Copyright 2021 Google LLC
|
|
9151
|
+
*
|
|
9152
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9153
|
+
* you may not use this file except in compliance with the License.
|
|
9154
|
+
* You may obtain a copy of the License at
|
|
9155
|
+
*
|
|
9156
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9157
|
+
*
|
|
9158
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9159
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9160
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9161
|
+
* See the License for the specific language governing permissions and
|
|
9162
|
+
* limitations under the License.
|
|
9163
|
+
*/
|
|
9164
|
+
// Note: This code is copied from the backend. Code that is not used by
|
|
9165
|
+
// Firestore was removed.
|
|
9166
|
+
const INDEX_TYPE_NULL = 5;
|
|
9167
|
+
const INDEX_TYPE_BOOLEAN = 10;
|
|
9168
|
+
const INDEX_TYPE_NAN = 13;
|
|
9169
|
+
const INDEX_TYPE_NUMBER = 15;
|
|
9170
|
+
const INDEX_TYPE_TIMESTAMP = 20;
|
|
9171
|
+
const INDEX_TYPE_STRING = 25;
|
|
9172
|
+
const INDEX_TYPE_BLOB = 30;
|
|
9173
|
+
const INDEX_TYPE_REFERENCE = 37;
|
|
9174
|
+
const INDEX_TYPE_GEOPOINT = 45;
|
|
9175
|
+
const INDEX_TYPE_ARRAY = 50;
|
|
9176
|
+
const INDEX_TYPE_MAP = 55;
|
|
9177
|
+
const INDEX_TYPE_REFERENCE_SEGMENT = 60;
|
|
9178
|
+
// A terminator that indicates that a truncatable value was not truncated.
|
|
9179
|
+
// This must be smaller than all other type labels.
|
|
9180
|
+
const NOT_TRUNCATED = 2;
|
|
9181
|
+
/** Firestore index value writer. */
|
|
9182
|
+
class FirestoreIndexValueWriter {
|
|
9183
|
+
constructor() { }
|
|
9184
|
+
// The write methods below short-circuit writing terminators for values
|
|
9185
|
+
// containing a (terminating) truncated value.
|
|
9186
|
+
//
|
|
9187
|
+
// As an example, consider the resulting encoding for:
|
|
9188
|
+
//
|
|
9189
|
+
// ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
|
|
9190
|
+
// ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
|
|
9191
|
+
// ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
|
|
9192
|
+
/** Writes an index value. */
|
|
9193
|
+
writeIndexValue(value, encoder) {
|
|
9194
|
+
this.writeIndexValueAux(value, encoder);
|
|
9195
|
+
// Write separator to split index values
|
|
9196
|
+
// (see go/firestore-storage-format#encodings).
|
|
9197
|
+
encoder.writeInfinity();
|
|
9198
|
+
}
|
|
9199
|
+
writeIndexValueAux(indexValue, encoder) {
|
|
9200
|
+
if ('nullValue' in indexValue) {
|
|
9201
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
|
|
9202
|
+
}
|
|
9203
|
+
else if ('booleanValue' in indexValue) {
|
|
9204
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
|
|
9205
|
+
encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
|
|
9206
|
+
}
|
|
9207
|
+
else if ('integerValue' in indexValue) {
|
|
9208
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9209
|
+
encoder.writeNumber(normalizeNumber(indexValue.integerValue));
|
|
9210
|
+
}
|
|
9211
|
+
else if ('doubleValue' in indexValue) {
|
|
9212
|
+
const n = normalizeNumber(indexValue.doubleValue);
|
|
9213
|
+
if (isNaN(n)) {
|
|
9214
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
|
|
9215
|
+
}
|
|
9216
|
+
else {
|
|
9217
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9218
|
+
if (isNegativeZero(n)) {
|
|
9219
|
+
// -0.0, 0 and 0.0 are all considered the same
|
|
9220
|
+
encoder.writeNumber(0.0);
|
|
9221
|
+
}
|
|
9222
|
+
else {
|
|
9223
|
+
encoder.writeNumber(n);
|
|
9224
|
+
}
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
else if ('timestampValue' in indexValue) {
|
|
9228
|
+
const timestamp = indexValue.timestampValue;
|
|
9229
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
|
|
9230
|
+
if (typeof timestamp === 'string') {
|
|
9231
|
+
encoder.writeString(timestamp);
|
|
9232
|
+
}
|
|
9233
|
+
else {
|
|
9234
|
+
encoder.writeString(`${timestamp.seconds || ''}`);
|
|
9235
|
+
encoder.writeNumber(timestamp.nanos || 0);
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
else if ('stringValue' in indexValue) {
|
|
9239
|
+
this.writeIndexString(indexValue.stringValue, encoder);
|
|
9240
|
+
this.writeTruncationMarker(encoder);
|
|
9241
|
+
}
|
|
9242
|
+
else if ('bytesValue' in indexValue) {
|
|
9243
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
|
|
9244
|
+
encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
|
|
9245
|
+
this.writeTruncationMarker(encoder);
|
|
9246
|
+
}
|
|
9247
|
+
else if ('referenceValue' in indexValue) {
|
|
9248
|
+
this.writeIndexEntityRef(indexValue.referenceValue, encoder);
|
|
9249
|
+
}
|
|
9250
|
+
else if ('geoPointValue' in indexValue) {
|
|
9251
|
+
const geoPoint = indexValue.geoPointValue;
|
|
9252
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
|
|
9253
|
+
encoder.writeNumber(geoPoint.latitude || 0);
|
|
9254
|
+
encoder.writeNumber(geoPoint.longitude || 0);
|
|
9255
|
+
}
|
|
9256
|
+
else if ('mapValue' in indexValue) {
|
|
9257
|
+
if (isMaxValue(indexValue)) {
|
|
9258
|
+
this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
|
|
9259
|
+
}
|
|
9260
|
+
else {
|
|
9261
|
+
this.writeIndexMap(indexValue.mapValue, encoder);
|
|
9262
|
+
this.writeTruncationMarker(encoder);
|
|
9263
|
+
}
|
|
9264
|
+
}
|
|
9265
|
+
else if ('arrayValue' in indexValue) {
|
|
9266
|
+
this.writeIndexArray(indexValue.arrayValue, encoder);
|
|
9267
|
+
this.writeTruncationMarker(encoder);
|
|
9268
|
+
}
|
|
9269
|
+
else {
|
|
9270
|
+
fail();
|
|
9271
|
+
}
|
|
9272
|
+
}
|
|
9273
|
+
writeIndexString(stringIndexValue, encoder) {
|
|
9274
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
|
|
9275
|
+
this.writeUnlabeledIndexString(stringIndexValue, encoder);
|
|
9276
|
+
}
|
|
9277
|
+
writeUnlabeledIndexString(stringIndexValue, encoder) {
|
|
9278
|
+
encoder.writeString(stringIndexValue);
|
|
9279
|
+
}
|
|
9280
|
+
writeIndexMap(mapIndexValue, encoder) {
|
|
9281
|
+
const map = mapIndexValue.fields || {};
|
|
9282
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
|
|
9283
|
+
for (const key of Object.keys(map)) {
|
|
9284
|
+
this.writeIndexString(key, encoder);
|
|
9285
|
+
this.writeIndexValueAux(map[key], encoder);
|
|
9286
|
+
}
|
|
9287
|
+
}
|
|
9288
|
+
writeIndexArray(arrayIndexValue, encoder) {
|
|
9289
|
+
const values = arrayIndexValue.values || [];
|
|
9290
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
|
|
9291
|
+
for (const element of values) {
|
|
9292
|
+
this.writeIndexValueAux(element, encoder);
|
|
9293
|
+
}
|
|
9294
|
+
}
|
|
9295
|
+
writeIndexEntityRef(referenceValue, encoder) {
|
|
9296
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
|
|
9297
|
+
const path = DocumentKey.fromName(referenceValue).path;
|
|
9298
|
+
path.forEach(segment => {
|
|
9299
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
|
|
9300
|
+
this.writeUnlabeledIndexString(segment, encoder);
|
|
9301
|
+
});
|
|
9302
|
+
}
|
|
9303
|
+
writeValueTypeLabel(encoder, typeOrder) {
|
|
9304
|
+
encoder.writeNumber(typeOrder);
|
|
9305
|
+
}
|
|
9306
|
+
writeTruncationMarker(encoder) {
|
|
9307
|
+
// While the SDK does not implement truncation, the truncation marker is
|
|
9308
|
+
// used to terminate all variable length values (which are strings, bytes,
|
|
9309
|
+
// references, arrays and maps).
|
|
9310
|
+
encoder.writeNumber(NOT_TRUNCATED);
|
|
9311
|
+
}
|
|
9312
|
+
}
|
|
9313
|
+
FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
|
|
9314
|
+
|
|
9315
|
+
/**
|
|
9316
|
+
* @license
|
|
9317
|
+
* Copyright 2021 Google LLC
|
|
9318
|
+
*
|
|
9319
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9320
|
+
* you may not use this file except in compliance with the License.
|
|
9321
|
+
* You may obtain a copy of the License at
|
|
9322
|
+
*
|
|
9323
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9324
|
+
*
|
|
9325
|
+
* Unless required by applicable law | agreed to in writing, software
|
|
9326
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9327
|
+
* WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
|
|
9328
|
+
* See the License for the specific language governing permissions and
|
|
9329
|
+
* limitations under the License.
|
|
9330
|
+
*/
|
|
9331
|
+
/** These constants are taken from the backend. */
|
|
9332
|
+
const MIN_SURROGATE = '\uD800';
|
|
9333
|
+
const MAX_SURROGATE = '\uDBFF';
|
|
9334
|
+
const ESCAPE1 = 0x00;
|
|
9335
|
+
const NULL_BYTE = 0xff; // Combined with ESCAPE1
|
|
9336
|
+
const SEPARATOR = 0x01; // Combined with ESCAPE1
|
|
9337
|
+
const ESCAPE2 = 0xff;
|
|
9338
|
+
const INFINITY = 0xff; // Combined with ESCAPE2
|
|
9339
|
+
const FF_BYTE = 0x00; // Combined with ESCAPE2
|
|
9340
|
+
const LONG_SIZE = 64;
|
|
9341
|
+
const BYTE_SIZE = 8;
|
|
9342
|
+
/**
|
|
9343
|
+
* The default size of the buffer. This is arbitrary, but likely larger than
|
|
9344
|
+
* most index values so that less copies of the underlying buffer will be made.
|
|
9345
|
+
* For large values, a single copy will made to double the buffer length.
|
|
9346
|
+
*/
|
|
9347
|
+
const DEFAULT_BUFFER_SIZE = 1024;
|
|
9348
|
+
/** Converts a JavaScript number to a byte array (using big endian encoding). */
|
|
9349
|
+
function doubleToLongBits(value) {
|
|
9350
|
+
const dv = new DataView(new ArrayBuffer(8));
|
|
9351
|
+
dv.setFloat64(0, value, /* littleEndian= */ false);
|
|
9352
|
+
return new Uint8Array(dv.buffer);
|
|
9353
|
+
}
|
|
9354
|
+
/**
|
|
9355
|
+
* Counts the number of zeros in a byte.
|
|
9356
|
+
*
|
|
9357
|
+
* Visible for testing.
|
|
9358
|
+
*/
|
|
9359
|
+
function numberOfLeadingZerosInByte(x) {
|
|
9360
|
+
if (x === 0) {
|
|
9361
|
+
return 8;
|
|
9362
|
+
}
|
|
9363
|
+
let zeros = 0;
|
|
9364
|
+
if (x >> 4 === 0) {
|
|
9365
|
+
// Test if the first four bits are zero.
|
|
9366
|
+
zeros += 4;
|
|
9367
|
+
x = x << 4;
|
|
9368
|
+
}
|
|
9369
|
+
if (x >> 6 === 0) {
|
|
9370
|
+
// Test if the first two (or next two) bits are zero.
|
|
9371
|
+
zeros += 2;
|
|
9372
|
+
x = x << 2;
|
|
9373
|
+
}
|
|
9374
|
+
if (x >> 7 === 0) {
|
|
9375
|
+
// Test if the remaining bit is zero.
|
|
9376
|
+
zeros += 1;
|
|
9377
|
+
}
|
|
9378
|
+
return zeros;
|
|
9379
|
+
}
|
|
9380
|
+
/** Counts the number of leading zeros in the given byte array. */
|
|
9381
|
+
function numberOfLeadingZeros(bytes) {
|
|
9382
|
+
let leadingZeros = 0;
|
|
9383
|
+
for (let i = 0; i < 8; ++i) {
|
|
9384
|
+
const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
|
|
9385
|
+
leadingZeros += zeros;
|
|
9386
|
+
if (zeros !== 8) {
|
|
9387
|
+
break;
|
|
9388
|
+
}
|
|
9389
|
+
}
|
|
9390
|
+
return leadingZeros;
|
|
9391
|
+
}
|
|
9392
|
+
/**
|
|
9393
|
+
* Returns the number of bytes required to store "value". Leading zero bytes
|
|
9394
|
+
* are skipped.
|
|
9395
|
+
*/
|
|
9396
|
+
function unsignedNumLength(value) {
|
|
9397
|
+
// This is just the number of bytes for the unsigned representation of the number.
|
|
9398
|
+
const numBits = LONG_SIZE - numberOfLeadingZeros(value);
|
|
9399
|
+
return Math.ceil(numBits / BYTE_SIZE);
|
|
9400
|
+
}
|
|
9401
|
+
/**
|
|
9402
|
+
* OrderedCodeWriter is a minimal-allocation implementation of the writing
|
|
9403
|
+
* behavior defined by the backend.
|
|
9404
|
+
*
|
|
9405
|
+
* The code is ported from its Java counterpart.
|
|
9406
|
+
*/
|
|
9407
|
+
class OrderedCodeWriter {
|
|
9408
|
+
constructor() {
|
|
9409
|
+
this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
|
|
9410
|
+
this.position = 0;
|
|
9411
|
+
}
|
|
9412
|
+
writeBytesAscending(value) {
|
|
9413
|
+
const it = value[Symbol.iterator]();
|
|
9414
|
+
let byte = it.next();
|
|
9415
|
+
while (!byte.done) {
|
|
9416
|
+
this.writeByteAscending(byte.value);
|
|
9417
|
+
byte = it.next();
|
|
9418
|
+
}
|
|
9419
|
+
this.writeSeparatorAscending();
|
|
9420
|
+
}
|
|
9421
|
+
writeBytesDescending(value) {
|
|
9422
|
+
const it = value[Symbol.iterator]();
|
|
9423
|
+
let byte = it.next();
|
|
9424
|
+
while (!byte.done) {
|
|
9425
|
+
this.writeByteDescending(byte.value);
|
|
9426
|
+
byte = it.next();
|
|
9427
|
+
}
|
|
9428
|
+
this.writeSeparatorDescending();
|
|
9429
|
+
}
|
|
9430
|
+
/** Writes utf8 bytes into this byte sequence, ascending. */
|
|
9431
|
+
writeUtf8Ascending(sequence) {
|
|
9432
|
+
for (const c of sequence) {
|
|
9433
|
+
const charCode = c.charCodeAt(0);
|
|
9434
|
+
if (charCode < 0x80) {
|
|
9435
|
+
this.writeByteAscending(charCode);
|
|
9436
|
+
}
|
|
9437
|
+
else if (charCode < 0x800) {
|
|
9438
|
+
this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
|
|
9439
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9440
|
+
}
|
|
9441
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9442
|
+
this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
|
|
9443
|
+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9444
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9445
|
+
}
|
|
9446
|
+
else {
|
|
9447
|
+
const codePoint = c.codePointAt(0);
|
|
9448
|
+
this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
|
|
9449
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9450
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9451
|
+
this.writeByteAscending(0x80 | (0x3f & codePoint));
|
|
9452
|
+
}
|
|
9453
|
+
}
|
|
9454
|
+
this.writeSeparatorAscending();
|
|
9455
|
+
}
|
|
9456
|
+
/** Writes utf8 bytes into this byte sequence, descending */
|
|
9457
|
+
writeUtf8Descending(sequence) {
|
|
9458
|
+
for (const c of sequence) {
|
|
9459
|
+
const charCode = c.charCodeAt(0);
|
|
9460
|
+
if (charCode < 0x80) {
|
|
9461
|
+
this.writeByteDescending(charCode);
|
|
9462
|
+
}
|
|
9463
|
+
else if (charCode < 0x800) {
|
|
9464
|
+
this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
|
|
9465
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9466
|
+
}
|
|
9467
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9468
|
+
this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
|
|
9469
|
+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9470
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9471
|
+
}
|
|
9472
|
+
else {
|
|
9473
|
+
const codePoint = c.codePointAt(0);
|
|
9474
|
+
this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
|
|
9475
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9476
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9477
|
+
this.writeByteDescending(0x80 | (0x3f & codePoint));
|
|
9478
|
+
}
|
|
9479
|
+
}
|
|
9480
|
+
this.writeSeparatorDescending();
|
|
9481
|
+
}
|
|
9482
|
+
writeNumberAscending(val) {
|
|
9483
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9484
|
+
// actual value in big-endian format with leading 0 bytes dropped.
|
|
9485
|
+
const value = this.toOrderedBits(val);
|
|
9486
|
+
const len = unsignedNumLength(value);
|
|
9487
|
+
this.ensureAvailable(1 + len);
|
|
9488
|
+
this.buffer[this.position++] = len & 0xff; // Write the length
|
|
9489
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9490
|
+
this.buffer[this.position++] = value[i] & 0xff;
|
|
9491
|
+
}
|
|
9492
|
+
}
|
|
9493
|
+
writeNumberDescending(val) {
|
|
9494
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9495
|
+
// inverted value in big-endian format with leading 0 bytes dropped.
|
|
9496
|
+
const value = this.toOrderedBits(val);
|
|
9497
|
+
const len = unsignedNumLength(value);
|
|
9498
|
+
this.ensureAvailable(1 + len);
|
|
9499
|
+
this.buffer[this.position++] = ~(len & 0xff); // Write the length
|
|
9500
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9501
|
+
this.buffer[this.position++] = ~(value[i] & 0xff);
|
|
9502
|
+
}
|
|
9503
|
+
}
|
|
9504
|
+
/**
|
|
9505
|
+
* Writes the "infinity" byte sequence that sorts after all other byte
|
|
9506
|
+
* sequences written in ascending order.
|
|
9507
|
+
*/
|
|
9508
|
+
writeInfinityAscending() {
|
|
9509
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9510
|
+
this.writeEscapedByteAscending(INFINITY);
|
|
9511
|
+
}
|
|
9512
|
+
/**
|
|
9513
|
+
* Writes the "infinity" byte sequence that sorts before all other byte
|
|
9514
|
+
* sequences written in descending order.
|
|
9515
|
+
*/
|
|
9516
|
+
writeInfinityDescending() {
|
|
9517
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9518
|
+
this.writeEscapedByteDescending(INFINITY);
|
|
9519
|
+
}
|
|
9520
|
+
/**
|
|
9521
|
+
* Resets the buffer such that it is the same as when it was newly
|
|
9522
|
+
* constructed.
|
|
9523
|
+
*/
|
|
9524
|
+
reset() {
|
|
9525
|
+
this.position = 0;
|
|
9526
|
+
}
|
|
9527
|
+
seed(encodedBytes) {
|
|
9528
|
+
this.ensureAvailable(encodedBytes.length);
|
|
9529
|
+
this.buffer.set(encodedBytes, this.position);
|
|
9530
|
+
this.position += encodedBytes.length;
|
|
9531
|
+
}
|
|
9532
|
+
/** Makes a copy of the encoded bytes in this buffer. */
|
|
9533
|
+
encodedBytes() {
|
|
9534
|
+
return this.buffer.slice(0, this.position);
|
|
9535
|
+
}
|
|
9536
|
+
/**
|
|
9537
|
+
* Encodes `val` into an encoding so that the order matches the IEEE 754
|
|
9538
|
+
* floating-point comparison results with the following exceptions:
|
|
9539
|
+
* -0.0 < 0.0
|
|
9540
|
+
* all non-NaN < NaN
|
|
9541
|
+
* NaN = NaN
|
|
9542
|
+
*/
|
|
9543
|
+
toOrderedBits(val) {
|
|
9544
|
+
const value = doubleToLongBits(val);
|
|
9545
|
+
// Check if the first bit is set. We use a bit mask since value[0] is
|
|
9546
|
+
// encoded as a number from 0 to 255.
|
|
9547
|
+
const isNegative = (value[0] & 0x80) !== 0;
|
|
9548
|
+
// Revert the two complement to get natural ordering
|
|
9549
|
+
value[0] ^= isNegative ? 0xff : 0x80;
|
|
9550
|
+
for (let i = 1; i < value.length; ++i) {
|
|
9551
|
+
value[i] ^= isNegative ? 0xff : 0x00;
|
|
9552
|
+
}
|
|
9553
|
+
return value;
|
|
9554
|
+
}
|
|
9555
|
+
/** Writes a single byte ascending to the buffer. */
|
|
9556
|
+
writeByteAscending(b) {
|
|
9557
|
+
const masked = b & 0xff;
|
|
9558
|
+
if (masked === ESCAPE1) {
|
|
9559
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9560
|
+
this.writeEscapedByteAscending(NULL_BYTE);
|
|
9561
|
+
}
|
|
9562
|
+
else if (masked === ESCAPE2) {
|
|
9563
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9564
|
+
this.writeEscapedByteAscending(FF_BYTE);
|
|
9565
|
+
}
|
|
9566
|
+
else {
|
|
9567
|
+
this.writeEscapedByteAscending(masked);
|
|
9568
|
+
}
|
|
9569
|
+
}
|
|
9570
|
+
/** Writes a single byte descending to the buffer. */
|
|
9571
|
+
writeByteDescending(b) {
|
|
9572
|
+
const masked = b & 0xff;
|
|
9573
|
+
if (masked === ESCAPE1) {
|
|
9574
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9575
|
+
this.writeEscapedByteDescending(NULL_BYTE);
|
|
9576
|
+
}
|
|
9577
|
+
else if (masked === ESCAPE2) {
|
|
9578
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9579
|
+
this.writeEscapedByteDescending(FF_BYTE);
|
|
9580
|
+
}
|
|
9581
|
+
else {
|
|
9582
|
+
this.writeEscapedByteDescending(b);
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
writeSeparatorAscending() {
|
|
9586
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9587
|
+
this.writeEscapedByteAscending(SEPARATOR);
|
|
9588
|
+
}
|
|
9589
|
+
writeSeparatorDescending() {
|
|
9590
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9591
|
+
this.writeEscapedByteDescending(SEPARATOR);
|
|
9592
|
+
}
|
|
9593
|
+
writeEscapedByteAscending(b) {
|
|
9594
|
+
this.ensureAvailable(1);
|
|
9595
|
+
this.buffer[this.position++] = b;
|
|
9596
|
+
}
|
|
9597
|
+
writeEscapedByteDescending(b) {
|
|
9598
|
+
this.ensureAvailable(1);
|
|
9599
|
+
this.buffer[this.position++] = ~b;
|
|
9600
|
+
}
|
|
9601
|
+
ensureAvailable(bytes) {
|
|
9602
|
+
const minCapacity = bytes + this.position;
|
|
9603
|
+
if (minCapacity <= this.buffer.length) {
|
|
9604
|
+
return;
|
|
9605
|
+
}
|
|
9606
|
+
// Try doubling.
|
|
9607
|
+
let newLength = this.buffer.length * 2;
|
|
9608
|
+
// Still not big enough? Just allocate the right size.
|
|
9609
|
+
if (newLength < minCapacity) {
|
|
9610
|
+
newLength = minCapacity;
|
|
9611
|
+
}
|
|
9612
|
+
// Create the new buffer.
|
|
9613
|
+
const newBuffer = new Uint8Array(newLength);
|
|
9614
|
+
newBuffer.set(this.buffer); // copy old data
|
|
9615
|
+
this.buffer = newBuffer;
|
|
9616
|
+
}
|
|
9617
|
+
}
|
|
9618
|
+
|
|
9619
|
+
class AscendingIndexByteEncoder {
|
|
9620
|
+
constructor(orderedCode) {
|
|
9621
|
+
this.orderedCode = orderedCode;
|
|
9622
|
+
}
|
|
9623
|
+
writeBytes(value) {
|
|
9624
|
+
this.orderedCode.writeBytesAscending(value);
|
|
9625
|
+
}
|
|
9626
|
+
writeString(value) {
|
|
9627
|
+
this.orderedCode.writeUtf8Ascending(value);
|
|
9628
|
+
}
|
|
9629
|
+
writeNumber(value) {
|
|
9630
|
+
this.orderedCode.writeNumberAscending(value);
|
|
9631
|
+
}
|
|
9632
|
+
writeInfinity() {
|
|
9633
|
+
this.orderedCode.writeInfinityAscending();
|
|
9634
|
+
}
|
|
8580
9635
|
}
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
}
|
|
9636
|
+
class DescendingIndexByteEncoder {
|
|
9637
|
+
constructor(orderedCode) {
|
|
9638
|
+
this.orderedCode = orderedCode;
|
|
9639
|
+
}
|
|
9640
|
+
writeBytes(value) {
|
|
9641
|
+
this.orderedCode.writeBytesDescending(value);
|
|
9642
|
+
}
|
|
9643
|
+
writeString(value) {
|
|
9644
|
+
this.orderedCode.writeUtf8Descending(value);
|
|
9645
|
+
}
|
|
9646
|
+
writeNumber(value) {
|
|
9647
|
+
this.orderedCode.writeNumberDescending(value);
|
|
9648
|
+
}
|
|
9649
|
+
writeInfinity() {
|
|
9650
|
+
this.orderedCode.writeInfinityDescending();
|
|
9651
|
+
}
|
|
8588
9652
|
}
|
|
8589
|
-
/**
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
9653
|
+
/**
|
|
9654
|
+
* Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
|
|
9655
|
+
* actual encoding.
|
|
9656
|
+
*/
|
|
9657
|
+
class IndexByteEncoder {
|
|
9658
|
+
constructor() {
|
|
9659
|
+
this.orderedCode = new OrderedCodeWriter();
|
|
9660
|
+
this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
|
|
9661
|
+
this.descending = new DescendingIndexByteEncoder(this.orderedCode);
|
|
9662
|
+
}
|
|
9663
|
+
seed(encodedBytes) {
|
|
9664
|
+
this.orderedCode.seed(encodedBytes);
|
|
9665
|
+
}
|
|
9666
|
+
forKind(kind) {
|
|
9667
|
+
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
9668
|
+
}
|
|
9669
|
+
encodedBytes() {
|
|
9670
|
+
return this.orderedCode.encodedBytes();
|
|
9671
|
+
}
|
|
9672
|
+
reset() {
|
|
9673
|
+
this.orderedCode.reset();
|
|
9674
|
+
}
|
|
8596
9675
|
}
|
|
8597
9676
|
|
|
8598
9677
|
/**
|
|
8599
9678
|
* @license
|
|
8600
|
-
* Copyright
|
|
9679
|
+
* Copyright 2022 Google LLC
|
|
8601
9680
|
*
|
|
8602
9681
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8603
9682
|
* you may not use this file except in compliance with the License.
|
|
@@ -8611,45 +9690,38 @@ function fromBundleMetadata(metadata) {
|
|
|
8611
9690
|
* See the License for the specific language governing permissions and
|
|
8612
9691
|
* limitations under the License.
|
|
8613
9692
|
*/
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
}
|
|
8622
|
-
return undefined;
|
|
8623
|
-
});
|
|
9693
|
+
/** Represents an index entry saved by the SDK in persisted storage. */
|
|
9694
|
+
class IndexEntry {
|
|
9695
|
+
constructor(indexId, documentKey, arrayValue, directionalValue) {
|
|
9696
|
+
this.indexId = indexId;
|
|
9697
|
+
this.documentKey = documentKey;
|
|
9698
|
+
this.arrayValue = arrayValue;
|
|
9699
|
+
this.directionalValue = directionalValue;
|
|
8624
9700
|
}
|
|
8625
|
-
|
|
8626
|
-
|
|
9701
|
+
}
|
|
9702
|
+
function indexEntryComparator(left, right) {
|
|
9703
|
+
let cmp = left.indexId - right.indexId;
|
|
9704
|
+
if (cmp !== 0) {
|
|
9705
|
+
return cmp;
|
|
8627
9706
|
}
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
.next(query => {
|
|
8632
|
-
if (query) {
|
|
8633
|
-
return fromDbNamedQuery(query);
|
|
8634
|
-
}
|
|
8635
|
-
return undefined;
|
|
8636
|
-
});
|
|
9707
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
9708
|
+
if (cmp !== 0) {
|
|
9709
|
+
return cmp;
|
|
8637
9710
|
}
|
|
8638
|
-
|
|
8639
|
-
|
|
9711
|
+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
|
|
9712
|
+
if (cmp !== 0) {
|
|
9713
|
+
return cmp;
|
|
8640
9714
|
}
|
|
9715
|
+
return compareByteArrays(left.directionalValue, right.directionalValue);
|
|
8641
9716
|
}
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
}
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
*/
|
|
8651
|
-
function namedQueriesStore(txn) {
|
|
8652
|
-
return getStore(txn, DbNamedQuery.store);
|
|
9717
|
+
function compareByteArrays(left, right) {
|
|
9718
|
+
for (let i = 0; i < left.length && i < right.length; ++i) {
|
|
9719
|
+
const compare = left[i] - right[i];
|
|
9720
|
+
if (compare !== 0) {
|
|
9721
|
+
return compare;
|
|
9722
|
+
}
|
|
9723
|
+
}
|
|
9724
|
+
return left.length - right.length;
|
|
8653
9725
|
}
|
|
8654
9726
|
|
|
8655
9727
|
/**
|
|
@@ -8682,6 +9754,38 @@ class MemoryIndexManager {
|
|
|
8682
9754
|
getCollectionParents(transaction, collectionId) {
|
|
8683
9755
|
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
8684
9756
|
}
|
|
9757
|
+
addFieldIndex(transaction, index) {
|
|
9758
|
+
// Field indices are not supported with memory persistence.
|
|
9759
|
+
return PersistencePromise.resolve();
|
|
9760
|
+
}
|
|
9761
|
+
deleteFieldIndex(transaction, index) {
|
|
9762
|
+
// Field indices are not supported with memory persistence.
|
|
9763
|
+
return PersistencePromise.resolve();
|
|
9764
|
+
}
|
|
9765
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9766
|
+
// Field indices are not supported with memory persistence.
|
|
9767
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9768
|
+
}
|
|
9769
|
+
getFieldIndex(transaction, target) {
|
|
9770
|
+
// Field indices are not supported with memory persistence.
|
|
9771
|
+
return PersistencePromise.resolve(null);
|
|
9772
|
+
}
|
|
9773
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9774
|
+
// Field indices are not supported with memory persistence.
|
|
9775
|
+
return PersistencePromise.resolve([]);
|
|
9776
|
+
}
|
|
9777
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9778
|
+
// Field indices are not supported with memory persistence.
|
|
9779
|
+
return PersistencePromise.resolve(null);
|
|
9780
|
+
}
|
|
9781
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9782
|
+
// Field indices are not supported with memory persistence.
|
|
9783
|
+
return PersistencePromise.resolve();
|
|
9784
|
+
}
|
|
9785
|
+
updateIndexEntries(transaction, documents) {
|
|
9786
|
+
// Field indices are not supported with memory persistence.
|
|
9787
|
+
return PersistencePromise.resolve();
|
|
9788
|
+
}
|
|
8685
9789
|
}
|
|
8686
9790
|
/**
|
|
8687
9791
|
* Internal implementation of the collection-parent index exposed by MemoryIndexManager.
|
|
@@ -8731,19 +9835,25 @@ class MemoryCollectionParentIndex {
|
|
|
8731
9835
|
* See the License for the specific language governing permissions and
|
|
8732
9836
|
* limitations under the License.
|
|
8733
9837
|
*/
|
|
9838
|
+
const LOG_TAG$f = 'IndexedDbIndexManager';
|
|
8734
9839
|
/**
|
|
8735
9840
|
* A persisted implementation of IndexManager.
|
|
9841
|
+
*
|
|
9842
|
+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
|
|
9843
|
+
* data as it supports multi-tab access.
|
|
8736
9844
|
*/
|
|
8737
9845
|
class IndexedDbIndexManager {
|
|
8738
|
-
constructor() {
|
|
9846
|
+
constructor(user) {
|
|
9847
|
+
this.user = user;
|
|
8739
9848
|
/**
|
|
8740
9849
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
8741
9850
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
8742
9851
|
*
|
|
8743
|
-
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
8744
|
-
* satisfy reads.
|
|
9852
|
+
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
9853
|
+
* used to satisfy reads.
|
|
8745
9854
|
*/
|
|
8746
9855
|
this.collectionParentsCache = new MemoryCollectionParentIndex();
|
|
9856
|
+
this.uid = user.uid || '';
|
|
8747
9857
|
}
|
|
8748
9858
|
/**
|
|
8749
9859
|
* Adds a new entry to the collection parent index.
|
|
@@ -8790,6 +9900,198 @@ class IndexedDbIndexManager {
|
|
|
8790
9900
|
return parentPaths;
|
|
8791
9901
|
});
|
|
8792
9902
|
}
|
|
9903
|
+
addFieldIndex(transaction, index) {
|
|
9904
|
+
// TODO(indexing): Verify that the auto-incrementing index ID works in
|
|
9905
|
+
// Safari & Firefox.
|
|
9906
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9907
|
+
const dbIndex = toDbIndexConfiguration(index);
|
|
9908
|
+
delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
|
|
9909
|
+
return indexes.add(dbIndex).next();
|
|
9910
|
+
}
|
|
9911
|
+
deleteFieldIndex(transaction, index) {
|
|
9912
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9913
|
+
const states = indexStateStore(transaction);
|
|
9914
|
+
const entries = indexEntriesStore(transaction);
|
|
9915
|
+
return indexes
|
|
9916
|
+
.delete(index.indexId)
|
|
9917
|
+
.next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9918
|
+
/*lowerOpen=*/ false,
|
|
9919
|
+
/*upperOpen=*/ true)))
|
|
9920
|
+
.next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9921
|
+
/*lowerOpen=*/ false,
|
|
9922
|
+
/*upperOpen=*/ true)));
|
|
9923
|
+
}
|
|
9924
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9925
|
+
// TODO(indexing): Implement
|
|
9926
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9927
|
+
}
|
|
9928
|
+
getFieldIndex(transaction, target) {
|
|
9929
|
+
// TODO(indexing): Implement
|
|
9930
|
+
return PersistencePromise.resolve(null);
|
|
9931
|
+
}
|
|
9932
|
+
/**
|
|
9933
|
+
* Returns the byte encoded form of the directional values in the field index.
|
|
9934
|
+
* Returns `null` if the document does not have all fields specified in the
|
|
9935
|
+
* index.
|
|
9936
|
+
*/
|
|
9937
|
+
encodeDirectionalElements(fieldIndex, document) {
|
|
9938
|
+
const encoder = new IndexByteEncoder();
|
|
9939
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
9940
|
+
const field = document.data.field(segment.fieldPath);
|
|
9941
|
+
if (field == null) {
|
|
9942
|
+
return null;
|
|
9943
|
+
}
|
|
9944
|
+
const directionalEncoder = encoder.forKind(segment.kind);
|
|
9945
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
|
|
9946
|
+
}
|
|
9947
|
+
return encoder.encodedBytes();
|
|
9948
|
+
}
|
|
9949
|
+
/** Encodes a single value to the ascending index format. */
|
|
9950
|
+
encodeSingleElement(value) {
|
|
9951
|
+
const encoder = new IndexByteEncoder();
|
|
9952
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
9953
|
+
return encoder.encodedBytes();
|
|
9954
|
+
}
|
|
9955
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9956
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9957
|
+
const states = indexStateStore(transaction);
|
|
9958
|
+
return (collectionGroup
|
|
9959
|
+
? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9960
|
+
: indexes.loadAll()).next(indexConfigs => {
|
|
9961
|
+
const result = [];
|
|
9962
|
+
return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
|
|
9963
|
+
return states
|
|
9964
|
+
.get([indexConfig.indexId, this.uid])
|
|
9965
|
+
.next(indexState => {
|
|
9966
|
+
result.push(fromDbIndexConfiguration(indexConfig, indexState));
|
|
9967
|
+
});
|
|
9968
|
+
}).next(() => result);
|
|
9969
|
+
});
|
|
9970
|
+
}
|
|
9971
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9972
|
+
return this.getFieldIndexes(transaction).next(indexes => {
|
|
9973
|
+
if (indexes.length === 0) {
|
|
9974
|
+
return null;
|
|
9975
|
+
}
|
|
9976
|
+
indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
|
|
9977
|
+
return indexes[0].collectionGroup;
|
|
9978
|
+
});
|
|
9979
|
+
}
|
|
9980
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9981
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9982
|
+
const states = indexStateStore(transaction);
|
|
9983
|
+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
|
|
9984
|
+
.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9985
|
+
.next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
|
|
9986
|
+
}
|
|
9987
|
+
updateIndexEntries(transaction, documents) {
|
|
9988
|
+
// Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
|
|
9989
|
+
// it could be used across different IndexedDB transactions. As any cached
|
|
9990
|
+
// data might be invalidated by other multi-tab clients, we can only trust
|
|
9991
|
+
// data within a single IndexedDB transaction. We therefore add a cache
|
|
9992
|
+
// here.
|
|
9993
|
+
const memoizedIndexes = new Map();
|
|
9994
|
+
return PersistencePromise.forEach(documents, (key, doc) => {
|
|
9995
|
+
const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
|
|
9996
|
+
const fieldIndexes = memoizedCollectionIndexes
|
|
9997
|
+
? PersistencePromise.resolve(memoizedCollectionIndexes)
|
|
9998
|
+
: this.getFieldIndexes(transaction, key.collectionGroup);
|
|
9999
|
+
return fieldIndexes.next(fieldIndexes => {
|
|
10000
|
+
memoizedIndexes.set(key.collectionGroup, fieldIndexes);
|
|
10001
|
+
return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
|
|
10002
|
+
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
10003
|
+
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10004
|
+
if (!existingEntries.isEqual(newEntries)) {
|
|
10005
|
+
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10006
|
+
}
|
|
10007
|
+
return PersistencePromise.resolve();
|
|
10008
|
+
});
|
|
10009
|
+
});
|
|
10010
|
+
});
|
|
10011
|
+
});
|
|
10012
|
+
}
|
|
10013
|
+
addIndexEntry(transaction, document, indexEntry) {
|
|
10014
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10015
|
+
return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
|
|
10016
|
+
}
|
|
10017
|
+
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10018
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10019
|
+
return indexEntries.delete([
|
|
10020
|
+
indexEntry.indexId,
|
|
10021
|
+
this.uid,
|
|
10022
|
+
indexEntry.arrayValue,
|
|
10023
|
+
indexEntry.directionalValue,
|
|
10024
|
+
encodeResourcePath(document.key.path)
|
|
10025
|
+
]);
|
|
10026
|
+
}
|
|
10027
|
+
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
10028
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10029
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10030
|
+
return indexEntries
|
|
10031
|
+
.iterate({
|
|
10032
|
+
index: DbIndexEntry.documentKeyIndex,
|
|
10033
|
+
range: IDBKeyRange.only([
|
|
10034
|
+
fieldIndex.indexId,
|
|
10035
|
+
this.uid,
|
|
10036
|
+
encodeResourcePath(documentKey.path)
|
|
10037
|
+
])
|
|
10038
|
+
}, (_, entry) => {
|
|
10039
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
10040
|
+
})
|
|
10041
|
+
.next(() => results);
|
|
10042
|
+
}
|
|
10043
|
+
/** Creates the index entries for the given document. */
|
|
10044
|
+
computeIndexEntries(document, fieldIndex) {
|
|
10045
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10046
|
+
const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
|
|
10047
|
+
if (directionalValue == null) {
|
|
10048
|
+
return results;
|
|
10049
|
+
}
|
|
10050
|
+
const arraySegment = fieldIndexGetArraySegment(fieldIndex);
|
|
10051
|
+
if (arraySegment != null) {
|
|
10052
|
+
const value = document.data.field(arraySegment.fieldPath);
|
|
10053
|
+
if (isArray(value)) {
|
|
10054
|
+
for (const arrayValue of value.arrayValue.values || []) {
|
|
10055
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
|
|
10056
|
+
}
|
|
10057
|
+
}
|
|
10058
|
+
}
|
|
10059
|
+
else {
|
|
10060
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
|
|
10061
|
+
}
|
|
10062
|
+
return results;
|
|
10063
|
+
}
|
|
10064
|
+
/**
|
|
10065
|
+
* Updates the index entries for the provided document by deleting entries
|
|
10066
|
+
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10067
|
+
* entries.
|
|
10068
|
+
*/
|
|
10069
|
+
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10070
|
+
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10071
|
+
const promises = [];
|
|
10072
|
+
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10073
|
+
/* onAdd= */ entry => {
|
|
10074
|
+
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10075
|
+
},
|
|
10076
|
+
/* onRemove= */ entry => {
|
|
10077
|
+
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10078
|
+
});
|
|
10079
|
+
return PersistencePromise.waitFor(promises);
|
|
10080
|
+
}
|
|
10081
|
+
getNextSequenceNumber(transaction) {
|
|
10082
|
+
let nextSequenceNumber = 1;
|
|
10083
|
+
const states = indexStateStore(transaction);
|
|
10084
|
+
return states
|
|
10085
|
+
.iterate({
|
|
10086
|
+
index: DbIndexState.sequenceNumberIndex,
|
|
10087
|
+
reverse: true,
|
|
10088
|
+
range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
|
|
10089
|
+
}, (_, state, controller) => {
|
|
10090
|
+
controller.done();
|
|
10091
|
+
nextSequenceNumber = state.sequenceNumber + 1;
|
|
10092
|
+
})
|
|
10093
|
+
.next(() => nextSequenceNumber);
|
|
10094
|
+
}
|
|
8793
10095
|
}
|
|
8794
10096
|
/**
|
|
8795
10097
|
* Helper to get a typed SimpleDbStore for the collectionParents
|
|
@@ -8797,6 +10099,24 @@ class IndexedDbIndexManager {
|
|
|
8797
10099
|
*/
|
|
8798
10100
|
function collectionParentsStore(txn) {
|
|
8799
10101
|
return getStore(txn, DbCollectionParent.store);
|
|
10102
|
+
}
|
|
10103
|
+
/**
|
|
10104
|
+
* Helper to get a typed SimpleDbStore for the index entry object store.
|
|
10105
|
+
*/
|
|
10106
|
+
function indexEntriesStore(txn) {
|
|
10107
|
+
return getStore(txn, DbIndexEntry.store);
|
|
10108
|
+
}
|
|
10109
|
+
/**
|
|
10110
|
+
* Helper to get a typed SimpleDbStore for the index configuration object store.
|
|
10111
|
+
*/
|
|
10112
|
+
function indexConfigurationStore(txn) {
|
|
10113
|
+
return getStore(txn, DbIndexConfiguration.store);
|
|
10114
|
+
}
|
|
10115
|
+
/**
|
|
10116
|
+
* Helper to get a typed SimpleDbStore for the index state object store.
|
|
10117
|
+
*/
|
|
10118
|
+
function indexStateStore(txn) {
|
|
10119
|
+
return getStore(txn, DbIndexState.store);
|
|
8800
10120
|
}
|
|
8801
10121
|
|
|
8802
10122
|
/**
|
|
@@ -10266,12 +11586,10 @@ class RemoteDocumentChangeBuffer {
|
|
|
10266
11586
|
* `newIndexedDbRemoteDocumentCache()`.
|
|
10267
11587
|
*/
|
|
10268
11588
|
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) {
|
|
11589
|
+
constructor(serializer) {
|
|
10274
11590
|
this.serializer = serializer;
|
|
11591
|
+
}
|
|
11592
|
+
setIndexManager(indexManager) {
|
|
10275
11593
|
this.indexManager = indexManager;
|
|
10276
11594
|
}
|
|
10277
11595
|
/**
|
|
@@ -10393,21 +11711,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10393
11711
|
}
|
|
10394
11712
|
});
|
|
10395
11713
|
}
|
|
10396
|
-
|
|
11714
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10397
11715
|
let results = mutableDocumentMap();
|
|
10398
|
-
const immediateChildrenPathLength =
|
|
11716
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
10399
11717
|
const iterationOptions = {};
|
|
10400
11718
|
if (sinceReadTime.isEqual(SnapshotVersion.min())) {
|
|
10401
11719
|
// Documents are ordered by key, so we can use a prefix scan to narrow
|
|
10402
11720
|
// down the documents we need to match the query against.
|
|
10403
|
-
const startKey =
|
|
11721
|
+
const startKey = collection.toArray();
|
|
10404
11722
|
iterationOptions.range = IDBKeyRange.lowerBound(startKey);
|
|
10405
11723
|
}
|
|
10406
11724
|
else {
|
|
10407
11725
|
// Execute an index-free query and filter by read time. This is safe
|
|
10408
11726
|
// since all document changes to queries that have a
|
|
10409
11727
|
// lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
|
|
10410
|
-
const collectionKey =
|
|
11728
|
+
const collectionKey = collection.toArray();
|
|
10411
11729
|
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
10412
11730
|
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
10413
11731
|
/* open= */ true);
|
|
@@ -10424,12 +11742,12 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10424
11742
|
return;
|
|
10425
11743
|
}
|
|
10426
11744
|
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
10427
|
-
if (
|
|
10428
|
-
control.done();
|
|
10429
|
-
}
|
|
10430
|
-
else if (queryMatches(query, document)) {
|
|
11745
|
+
if (collection.isPrefixOf(document.key.path)) {
|
|
10431
11746
|
results = results.insert(document.key, document);
|
|
10432
11747
|
}
|
|
11748
|
+
else {
|
|
11749
|
+
control.done();
|
|
11750
|
+
}
|
|
10433
11751
|
})
|
|
10434
11752
|
.next(() => results);
|
|
10435
11753
|
}
|
|
@@ -10467,14 +11785,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10467
11785
|
return MutableDocument.newInvalidDocument(documentKey);
|
|
10468
11786
|
}
|
|
10469
11787
|
}
|
|
10470
|
-
/**
|
|
10471
|
-
|
|
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);
|
|
11788
|
+
/** Creates a new IndexedDbRemoteDocumentCache. */
|
|
11789
|
+
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
11790
|
+
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
10478
11791
|
}
|
|
10479
11792
|
/**
|
|
10480
11793
|
* Returns the set of documents that have changed since the specified read
|
|
@@ -10641,9 +11954,6 @@ class SchemaConverter {
|
|
|
10641
11954
|
* and local feature development.
|
|
10642
11955
|
*/
|
|
10643
11956
|
createOrUpgrade(db, txn, fromVersion, toVersion) {
|
|
10644
|
-
hardAssert(fromVersion < toVersion &&
|
|
10645
|
-
fromVersion >= 0 &&
|
|
10646
|
-
toVersion <= SCHEMA_VERSION);
|
|
10647
11957
|
const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
|
|
10648
11958
|
if (fromVersion < 1 && toVersion >= 1) {
|
|
10649
11959
|
createPrimaryClientStore(db);
|
|
@@ -10710,6 +12020,16 @@ class SchemaConverter {
|
|
|
10710
12020
|
createNamedQueriesStore(db);
|
|
10711
12021
|
});
|
|
10712
12022
|
}
|
|
12023
|
+
if (fromVersion < 12 && toVersion >= 12) {
|
|
12024
|
+
p = p.next(() => {
|
|
12025
|
+
createDocumentOverlayStore(db);
|
|
12026
|
+
});
|
|
12027
|
+
}
|
|
12028
|
+
if (fromVersion < 13 && toVersion >= 13) {
|
|
12029
|
+
p = p.next(() => {
|
|
12030
|
+
createFieldIndex(db);
|
|
12031
|
+
});
|
|
12032
|
+
}
|
|
10713
12033
|
return p;
|
|
10714
12034
|
}
|
|
10715
12035
|
addDocumentGlobal(txn) {
|
|
@@ -10916,6 +12236,28 @@ function createNamedQueriesStore(db) {
|
|
|
10916
12236
|
db.createObjectStore(DbNamedQuery.store, {
|
|
10917
12237
|
keyPath: DbNamedQuery.keyPath
|
|
10918
12238
|
});
|
|
12239
|
+
}
|
|
12240
|
+
function createFieldIndex(db) {
|
|
12241
|
+
const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
|
|
12242
|
+
keyPath: DbIndexConfiguration.keyPath,
|
|
12243
|
+
autoIncrement: true
|
|
12244
|
+
});
|
|
12245
|
+
indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
|
|
12246
|
+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
|
|
12247
|
+
keyPath: DbIndexState.keyPath
|
|
12248
|
+
});
|
|
12249
|
+
indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
|
|
12250
|
+
const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
|
|
12251
|
+
keyPath: DbIndexEntry.keyPath
|
|
12252
|
+
});
|
|
12253
|
+
indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
|
|
12254
|
+
}
|
|
12255
|
+
function createDocumentOverlayStore(db) {
|
|
12256
|
+
const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
|
|
12257
|
+
keyPath: DbDocumentOverlay.keyPath
|
|
12258
|
+
});
|
|
12259
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
|
|
12260
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
|
|
10919
12261
|
}
|
|
10920
12262
|
|
|
10921
12263
|
/**
|
|
@@ -11027,7 +12369,7 @@ class IndexedDbPersistence {
|
|
|
11027
12369
|
* If set to true, forcefully obtains database access. Existing tabs will
|
|
11028
12370
|
* no longer be able to access IndexedDB.
|
|
11029
12371
|
*/
|
|
11030
|
-
forceOwningTab) {
|
|
12372
|
+
forceOwningTab, schemaVersion = SCHEMA_VERSION) {
|
|
11031
12373
|
this.allowTabSynchronization = allowTabSynchronization;
|
|
11032
12374
|
this.persistenceKey = persistenceKey;
|
|
11033
12375
|
this.clientId = clientId;
|
|
@@ -11036,6 +12378,7 @@ class IndexedDbPersistence {
|
|
|
11036
12378
|
this.document = document;
|
|
11037
12379
|
this.sequenceNumberSyncer = sequenceNumberSyncer;
|
|
11038
12380
|
this.forceOwningTab = forceOwningTab;
|
|
12381
|
+
this.schemaVersion = schemaVersion;
|
|
11039
12382
|
this.listenSequence = null;
|
|
11040
12383
|
this._started = false;
|
|
11041
12384
|
this.isPrimary = false;
|
|
@@ -11057,10 +12400,9 @@ class IndexedDbPersistence {
|
|
|
11057
12400
|
this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
|
|
11058
12401
|
this.dbName = persistenceKey + MAIN_DATABASE;
|
|
11059
12402
|
this.serializer = new LocalSerializer(serializer);
|
|
11060
|
-
this.simpleDb = new SimpleDb(this.dbName,
|
|
12403
|
+
this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
|
|
11061
12404
|
this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
|
|
11062
|
-
this.
|
|
11063
|
-
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
|
|
12405
|
+
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
|
|
11064
12406
|
this.bundleCache = new IndexedDbBundleCache();
|
|
11065
12407
|
if (this.window && this.window.localStorage) {
|
|
11066
12408
|
this.webStorage = this.window.localStorage;
|
|
@@ -11396,8 +12738,8 @@ class IndexedDbPersistence {
|
|
|
11396
12738
|
get started() {
|
|
11397
12739
|
return this._started;
|
|
11398
12740
|
}
|
|
11399
|
-
getMutationQueue(user) {
|
|
11400
|
-
return IndexedDbMutationQueue.forUser(user, this.serializer,
|
|
12741
|
+
getMutationQueue(user, indexManager) {
|
|
12742
|
+
return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
|
|
11401
12743
|
}
|
|
11402
12744
|
getTargetCache() {
|
|
11403
12745
|
return this.targetCache;
|
|
@@ -11405,8 +12747,11 @@ class IndexedDbPersistence {
|
|
|
11405
12747
|
getRemoteDocumentCache() {
|
|
11406
12748
|
return this.remoteDocumentCache;
|
|
11407
12749
|
}
|
|
11408
|
-
getIndexManager() {
|
|
11409
|
-
return
|
|
12750
|
+
getIndexManager(user) {
|
|
12751
|
+
return new IndexedDbIndexManager(user);
|
|
12752
|
+
}
|
|
12753
|
+
getDocumentOverlayCache(user) {
|
|
12754
|
+
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
11410
12755
|
}
|
|
11411
12756
|
getBundleCache() {
|
|
11412
12757
|
return this.bundleCache;
|
|
@@ -11414,11 +12759,12 @@ class IndexedDbPersistence {
|
|
|
11414
12759
|
runTransaction(action, mode, transactionOperation) {
|
|
11415
12760
|
logDebug(LOG_TAG$d, 'Starting transaction:', action);
|
|
11416
12761
|
const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
|
|
12762
|
+
const objectStores = getObjectStores(this.schemaVersion);
|
|
11417
12763
|
let persistenceTransaction;
|
|
11418
12764
|
// Do all transactions as readwrite against all object stores, since we
|
|
11419
12765
|
// are the only reader/writer.
|
|
11420
12766
|
return this.simpleDb
|
|
11421
|
-
.runTransaction(action, simpleDbMode,
|
|
12767
|
+
.runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
|
|
11422
12768
|
persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
|
|
11423
12769
|
? this.listenSequence.next()
|
|
11424
12770
|
: ListenSequence.INVALID);
|
|
@@ -11790,39 +13136,28 @@ class LocalDocumentsView {
|
|
|
11790
13136
|
getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
|
|
11791
13137
|
// Query the remote documents and overlay mutations.
|
|
11792
13138
|
let results;
|
|
11793
|
-
let mutationBatches;
|
|
11794
13139
|
return this.remoteDocumentCache
|
|
11795
|
-
.
|
|
13140
|
+
.getAll(transaction, query.path, sinceReadTime)
|
|
11796
13141
|
.next(queryResults => {
|
|
11797
13142
|
results = queryResults;
|
|
11798
13143
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
11799
13144
|
})
|
|
11800
|
-
.next(
|
|
11801
|
-
mutationBatches
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
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
|
-
}
|
|
13145
|
+
.next(mutationBatches => {
|
|
13146
|
+
for (const batch of mutationBatches) {
|
|
13147
|
+
for (const mutation of batch.mutations) {
|
|
13148
|
+
const key = mutation.key;
|
|
13149
|
+
let document = results.get(key);
|
|
13150
|
+
if (document == null) {
|
|
13151
|
+
// Create invalid document to apply mutations on top of
|
|
13152
|
+
document = MutableDocument.newInvalidDocument(key);
|
|
13153
|
+
results = results.insert(key, document);
|
|
13154
|
+
}
|
|
13155
|
+
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
13156
|
+
if (!document.isFoundDocument()) {
|
|
13157
|
+
results = results.remove(key);
|
|
11823
13158
|
}
|
|
11824
13159
|
}
|
|
11825
|
-
}
|
|
13160
|
+
}
|
|
11826
13161
|
})
|
|
11827
13162
|
.next(() => {
|
|
11828
13163
|
// Finally, filter out any documents that don't actually match
|
|
@@ -11835,29 +13170,6 @@ class LocalDocumentsView {
|
|
|
11835
13170
|
return results;
|
|
11836
13171
|
});
|
|
11837
13172
|
}
|
|
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
13173
|
}
|
|
11862
13174
|
|
|
11863
13175
|
/**
|
|
@@ -11916,11 +13228,18 @@ class LocalStoreImpl {
|
|
|
11916
13228
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
11917
13229
|
*/
|
|
11918
13230
|
this.lastDocumentChangeReadTime = SnapshotVersion.min();
|
|
11919
|
-
this.mutationQueue = persistence.getMutationQueue(initialUser);
|
|
11920
13231
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
11921
13232
|
this.targetCache = persistence.getTargetCache();
|
|
11922
|
-
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
|
|
11923
13233
|
this.bundleCache = persistence.getBundleCache();
|
|
13234
|
+
this.initializeUserComponents(initialUser);
|
|
13235
|
+
}
|
|
13236
|
+
initializeUserComponents(user) {
|
|
13237
|
+
// TODO(indexing): Add spec tests that test these components change after a
|
|
13238
|
+
// user change
|
|
13239
|
+
this.indexManager = this.persistence.getIndexManager(user);
|
|
13240
|
+
this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
|
|
13241
|
+
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
|
|
13242
|
+
this.remoteDocuments.setIndexManager(this.indexManager);
|
|
11924
13243
|
this.queryEngine.setLocalDocumentsView(this.localDocuments);
|
|
11925
13244
|
}
|
|
11926
13245
|
collectGarbage(garbageCollector) {
|
|
@@ -11942,8 +13261,6 @@ persistence, queryEngine, initialUser, serializer) {
|
|
|
11942
13261
|
// change.
|
|
11943
13262
|
async function localStoreHandleUserChange(localStore, user) {
|
|
11944
13263
|
const localStoreImpl = debugCast(localStore);
|
|
11945
|
-
let newMutationQueue = localStoreImpl.mutationQueue;
|
|
11946
|
-
let newLocalDocuments = localStoreImpl.localDocuments;
|
|
11947
13264
|
const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
|
|
11948
13265
|
// Swap out the mutation queue, grabbing the pending mutation batches
|
|
11949
13266
|
// before and after.
|
|
@@ -11952,11 +13269,8 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11952
13269
|
.getAllMutationBatches(txn)
|
|
11953
13270
|
.next(promisedOldBatches => {
|
|
11954
13271
|
oldBatches = promisedOldBatches;
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
// MutationQueue.
|
|
11958
|
-
newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
|
|
11959
|
-
return newMutationQueue.getAllMutationBatches(txn);
|
|
13272
|
+
localStoreImpl.initializeUserComponents(user);
|
|
13273
|
+
return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
|
|
11960
13274
|
})
|
|
11961
13275
|
.next(newBatches => {
|
|
11962
13276
|
const removedBatchIds = [];
|
|
@@ -11977,7 +13291,7 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11977
13291
|
}
|
|
11978
13292
|
// Return the set of all (potentially) changed documents and the list
|
|
11979
13293
|
// of mutation batch IDs that were affected by change.
|
|
11980
|
-
return
|
|
13294
|
+
return localStoreImpl.localDocuments
|
|
11981
13295
|
.getDocuments(txn, changedKeys)
|
|
11982
13296
|
.next(affectedDocuments => {
|
|
11983
13297
|
return {
|
|
@@ -11988,9 +13302,6 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11988
13302
|
});
|
|
11989
13303
|
});
|
|
11990
13304
|
});
|
|
11991
|
-
localStoreImpl.mutationQueue = newMutationQueue;
|
|
11992
|
-
localStoreImpl.localDocuments = newLocalDocuments;
|
|
11993
|
-
localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
|
|
11994
13305
|
return result;
|
|
11995
13306
|
}
|
|
11996
13307
|
/* Accepts locally generated Mutations and commit them to storage. */
|
|
@@ -12703,6 +14014,122 @@ class MemoryBundleCache {
|
|
|
12703
14014
|
}
|
|
12704
14015
|
}
|
|
12705
14016
|
|
|
14017
|
+
/**
|
|
14018
|
+
* @license
|
|
14019
|
+
* Copyright 2022 Google LLC
|
|
14020
|
+
*
|
|
14021
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14022
|
+
* you may not use this file except in compliance with the License.
|
|
14023
|
+
* You may obtain a copy of the License at
|
|
14024
|
+
*
|
|
14025
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14026
|
+
*
|
|
14027
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14028
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14029
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14030
|
+
* See the License for the specific language governing permissions and
|
|
14031
|
+
* limitations under the License.
|
|
14032
|
+
*/
|
|
14033
|
+
/**
|
|
14034
|
+
* An in-memory implementation of DocumentOverlayCache.
|
|
14035
|
+
*/
|
|
14036
|
+
class MemoryDocumentOverlayCache {
|
|
14037
|
+
constructor() {
|
|
14038
|
+
// A map sorted by DocumentKey, whose value is a pair of the largest batch id
|
|
14039
|
+
// for the overlay and the overlay itself.
|
|
14040
|
+
this.overlays = new SortedMap(DocumentKey.comparator);
|
|
14041
|
+
this.overlayByBatchId = new Map();
|
|
14042
|
+
}
|
|
14043
|
+
getOverlay(transaction, key) {
|
|
14044
|
+
return PersistencePromise.resolve(this.overlays.get(key));
|
|
14045
|
+
}
|
|
14046
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
14047
|
+
overlays.forEach(mutation => {
|
|
14048
|
+
this.saveOverlay(transaction, largestBatchId, mutation);
|
|
14049
|
+
});
|
|
14050
|
+
return PersistencePromise.resolve();
|
|
14051
|
+
}
|
|
14052
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
14053
|
+
const keys = this.overlayByBatchId.get(batchId);
|
|
14054
|
+
if (keys !== undefined) {
|
|
14055
|
+
keys.forEach(key => (this.overlays = this.overlays.remove(key)));
|
|
14056
|
+
this.overlayByBatchId.delete(batchId);
|
|
14057
|
+
}
|
|
14058
|
+
return PersistencePromise.resolve();
|
|
14059
|
+
}
|
|
14060
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
14061
|
+
const result = new Map();
|
|
14062
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
14063
|
+
const prefix = new DocumentKey(collection.child(''));
|
|
14064
|
+
const iter = this.overlays.getIteratorFrom(prefix);
|
|
14065
|
+
while (iter.hasNext()) {
|
|
14066
|
+
const entry = iter.getNext();
|
|
14067
|
+
const overlay = entry.value;
|
|
14068
|
+
const key = overlay.getKey();
|
|
14069
|
+
if (!collection.isPrefixOf(key.path)) {
|
|
14070
|
+
break;
|
|
14071
|
+
}
|
|
14072
|
+
// Documents from sub-collections
|
|
14073
|
+
if (key.path.length !== immediateChildrenPathLength) {
|
|
14074
|
+
continue;
|
|
14075
|
+
}
|
|
14076
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14077
|
+
result.set(overlay.getKey(), overlay);
|
|
14078
|
+
}
|
|
14079
|
+
}
|
|
14080
|
+
return PersistencePromise.resolve(result);
|
|
14081
|
+
}
|
|
14082
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
14083
|
+
let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
|
|
14084
|
+
const iter = this.overlays.getIterator();
|
|
14085
|
+
while (iter.hasNext()) {
|
|
14086
|
+
const entry = iter.getNext();
|
|
14087
|
+
const overlay = entry.value;
|
|
14088
|
+
const key = overlay.getKey();
|
|
14089
|
+
if (key.getCollectionGroup() !== collectionGroup) {
|
|
14090
|
+
continue;
|
|
14091
|
+
}
|
|
14092
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14093
|
+
let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
|
|
14094
|
+
if (overlaysForBatchId === null) {
|
|
14095
|
+
overlaysForBatchId = new Map();
|
|
14096
|
+
batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
|
|
14097
|
+
}
|
|
14098
|
+
overlaysForBatchId.set(overlay.getKey(), overlay);
|
|
14099
|
+
}
|
|
14100
|
+
}
|
|
14101
|
+
const result = new Map();
|
|
14102
|
+
const batchIter = batchIdToOverlays.getIterator();
|
|
14103
|
+
while (batchIter.hasNext()) {
|
|
14104
|
+
const entry = batchIter.getNext();
|
|
14105
|
+
const overlays = entry.value;
|
|
14106
|
+
overlays.forEach((overlay, key) => result.set(key, overlay));
|
|
14107
|
+
if (result.size >= count) {
|
|
14108
|
+
break;
|
|
14109
|
+
}
|
|
14110
|
+
}
|
|
14111
|
+
return PersistencePromise.resolve(result);
|
|
14112
|
+
}
|
|
14113
|
+
saveOverlay(transaction, largestBatchId, mutation) {
|
|
14114
|
+
if (mutation === null) {
|
|
14115
|
+
return;
|
|
14116
|
+
}
|
|
14117
|
+
// Remove the association of the overlay to its batch id.
|
|
14118
|
+
const existing = this.overlays.get(mutation.key);
|
|
14119
|
+
if (existing !== null) {
|
|
14120
|
+
this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
|
|
14121
|
+
}
|
|
14122
|
+
this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
|
|
14123
|
+
// Create the association of this overlay to the given largestBatchId.
|
|
14124
|
+
let batch = this.overlayByBatchId.get(largestBatchId);
|
|
14125
|
+
if (batch === undefined) {
|
|
14126
|
+
batch = new Set();
|
|
14127
|
+
this.overlayByBatchId.set(largestBatchId, batch);
|
|
14128
|
+
}
|
|
14129
|
+
batch.add(mutation.key);
|
|
14130
|
+
}
|
|
14131
|
+
}
|
|
14132
|
+
|
|
12706
14133
|
/**
|
|
12707
14134
|
* @license
|
|
12708
14135
|
* Copyright 2017 Google LLC
|
|
@@ -13056,14 +14483,16 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13056
14483
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13057
14484
|
* calculating the size.
|
|
13058
14485
|
*/
|
|
13059
|
-
constructor(
|
|
13060
|
-
this.indexManager = indexManager;
|
|
14486
|
+
constructor(sizer) {
|
|
13061
14487
|
this.sizer = sizer;
|
|
13062
14488
|
/** Underlying cache of documents and their read times. */
|
|
13063
14489
|
this.docs = documentEntryMap();
|
|
13064
14490
|
/** Size of all cached documents. */
|
|
13065
14491
|
this.size = 0;
|
|
13066
14492
|
}
|
|
14493
|
+
setIndexManager(indexManager) {
|
|
14494
|
+
this.indexManager = indexManager;
|
|
14495
|
+
}
|
|
13067
14496
|
/**
|
|
13068
14497
|
* Adds the supplied entry to the cache and updates the cache size as appropriate.
|
|
13069
14498
|
*
|
|
@@ -13111,21 +14540,22 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13111
14540
|
});
|
|
13112
14541
|
return PersistencePromise.resolve(results);
|
|
13113
14542
|
}
|
|
13114
|
-
|
|
14543
|
+
getAll(transaction, collectionPath, sinceReadTime) {
|
|
13115
14544
|
let results = mutableDocumentMap();
|
|
13116
14545
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
13117
14546
|
// the documents we need to match the query against.
|
|
13118
|
-
const prefix = new DocumentKey(
|
|
14547
|
+
const prefix = new DocumentKey(collectionPath.child(''));
|
|
13119
14548
|
const iterator = this.docs.getIteratorFrom(prefix);
|
|
13120
14549
|
while (iterator.hasNext()) {
|
|
13121
14550
|
const { key, value: { document } } = iterator.getNext();
|
|
13122
|
-
if (!
|
|
14551
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
13123
14552
|
break;
|
|
13124
14553
|
}
|
|
13125
|
-
if (
|
|
14554
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14555
|
+
// Exclude entries from subcollections.
|
|
13126
14556
|
continue;
|
|
13127
14557
|
}
|
|
13128
|
-
if (
|
|
14558
|
+
if (document.readTime.compareTo(sinceReadTime) <= 0) {
|
|
13129
14559
|
continue;
|
|
13130
14560
|
}
|
|
13131
14561
|
results = results.insert(document.key, document.mutableCopy());
|
|
@@ -13147,13 +14577,12 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13147
14577
|
/**
|
|
13148
14578
|
* Creates a new memory-only RemoteDocumentCache.
|
|
13149
14579
|
*
|
|
13150
|
-
* @param indexManager - A class that manages collection group indices.
|
|
13151
14580
|
* @param sizer - Used to assess the size of a document. For eager GC, this is
|
|
13152
14581
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13153
14582
|
* calculating the size.
|
|
13154
14583
|
*/
|
|
13155
|
-
function newMemoryRemoteDocumentCache(
|
|
13156
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
14584
|
+
function newMemoryRemoteDocumentCache(sizer) {
|
|
14585
|
+
return new MemoryRemoteDocumentCacheImpl(sizer);
|
|
13157
14586
|
}
|
|
13158
14587
|
/**
|
|
13159
14588
|
* Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
|
|
@@ -13347,6 +14776,7 @@ class MemoryPersistence {
|
|
|
13347
14776
|
*/
|
|
13348
14777
|
constructor(referenceDelegateFactory, serializer) {
|
|
13349
14778
|
this.mutationQueues = {};
|
|
14779
|
+
this.overlays = {};
|
|
13350
14780
|
this.listenSequence = new ListenSequence(0);
|
|
13351
14781
|
this._started = false;
|
|
13352
14782
|
this._started = true;
|
|
@@ -13354,7 +14784,7 @@ class MemoryPersistence {
|
|
|
13354
14784
|
this.targetCache = new MemoryTargetCache(this);
|
|
13355
14785
|
const sizer = (doc) => this.referenceDelegate.documentSize(doc);
|
|
13356
14786
|
this.indexManager = new MemoryIndexManager();
|
|
13357
|
-
this.remoteDocumentCache = newMemoryRemoteDocumentCache(
|
|
14787
|
+
this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
|
|
13358
14788
|
this.serializer = new LocalSerializer(serializer);
|
|
13359
14789
|
this.bundleCache = new MemoryBundleCache(this.serializer);
|
|
13360
14790
|
}
|
|
@@ -13375,13 +14805,23 @@ class MemoryPersistence {
|
|
|
13375
14805
|
setNetworkEnabled() {
|
|
13376
14806
|
// No op.
|
|
13377
14807
|
}
|
|
13378
|
-
getIndexManager() {
|
|
14808
|
+
getIndexManager(user) {
|
|
14809
|
+
// We do not currently support indices for memory persistence, so we can
|
|
14810
|
+
// return the same shared instance of the memory index manager.
|
|
13379
14811
|
return this.indexManager;
|
|
13380
14812
|
}
|
|
13381
|
-
|
|
14813
|
+
getDocumentOverlayCache(user) {
|
|
14814
|
+
let overlay = this.overlays[user.toKey()];
|
|
14815
|
+
if (!overlay) {
|
|
14816
|
+
overlay = new MemoryDocumentOverlayCache();
|
|
14817
|
+
this.overlays[user.toKey()] = overlay;
|
|
14818
|
+
}
|
|
14819
|
+
return overlay;
|
|
14820
|
+
}
|
|
14821
|
+
getMutationQueue(user, indexManager) {
|
|
13382
14822
|
let queue = this.mutationQueues[user.toKey()];
|
|
13383
14823
|
if (!queue) {
|
|
13384
|
-
queue = new MemoryMutationQueue(
|
|
14824
|
+
queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
|
|
13385
14825
|
this.mutationQueues[user.toKey()] = queue;
|
|
13386
14826
|
}
|
|
13387
14827
|
return queue;
|
|
@@ -20344,12 +21784,12 @@ async function syncEngineListen(syncEngine, query) {
|
|
|
20344
21784
|
}
|
|
20345
21785
|
else {
|
|
20346
21786
|
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
21787
|
if (syncEngineImpl.isPrimaryClient) {
|
|
20351
21788
|
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
|
|
20352
21789
|
}
|
|
21790
|
+
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
21791
|
+
targetId = targetData.targetId;
|
|
21792
|
+
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20353
21793
|
}
|
|
20354
21794
|
return viewSnapshot;
|
|
20355
21795
|
}
|
|
@@ -24857,8 +26297,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
|
|
|
24857
26297
|
this.metadata = metadata;
|
|
24858
26298
|
}
|
|
24859
26299
|
/**
|
|
24860
|
-
*
|
|
24861
|
-
* exists. True if the document exists.
|
|
26300
|
+
* Returns whether or not the data exists. True if the document exists.
|
|
24862
26301
|
*/
|
|
24863
26302
|
exists() {
|
|
24864
26303
|
return super.exists();
|
|
@@ -25232,51 +26671,53 @@ function limitToLast(limit) {
|
|
|
25232
26671
|
return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
|
|
25233
26672
|
}
|
|
25234
26673
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
25235
|
-
constructor(type, _docOrFields,
|
|
26674
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25236
26675
|
super();
|
|
25237
26676
|
this.type = type;
|
|
25238
26677
|
this._docOrFields = _docOrFields;
|
|
25239
|
-
this.
|
|
26678
|
+
this._inclusive = _inclusive;
|
|
25240
26679
|
}
|
|
25241
26680
|
_apply(query) {
|
|
25242
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26681
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25243
26682
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
25244
26683
|
}
|
|
25245
26684
|
}
|
|
25246
26685
|
function startAt(...docOrFields) {
|
|
25247
|
-
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26686
|
+
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26687
|
+
/*inclusive=*/ true);
|
|
25248
26688
|
}
|
|
25249
26689
|
function startAfter(...docOrFields) {
|
|
25250
26690
|
return new QueryStartAtConstraint('startAfter', docOrFields,
|
|
25251
|
-
/*
|
|
26691
|
+
/*inclusive=*/ false);
|
|
25252
26692
|
}
|
|
25253
26693
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
25254
|
-
constructor(type, _docOrFields,
|
|
26694
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25255
26695
|
super();
|
|
25256
26696
|
this.type = type;
|
|
25257
26697
|
this._docOrFields = _docOrFields;
|
|
25258
|
-
this.
|
|
26698
|
+
this._inclusive = _inclusive;
|
|
25259
26699
|
}
|
|
25260
26700
|
_apply(query) {
|
|
25261
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26701
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25262
26702
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
25263
26703
|
}
|
|
25264
26704
|
}
|
|
25265
26705
|
function endBefore(...docOrFields) {
|
|
25266
|
-
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26706
|
+
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26707
|
+
/*inclusive=*/ false);
|
|
25267
26708
|
}
|
|
25268
26709
|
function endAt(...docOrFields) {
|
|
25269
|
-
return new QueryEndAtConstraint('endAt', docOrFields, /*
|
|
26710
|
+
return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
|
|
25270
26711
|
}
|
|
25271
26712
|
/** Helper function to create a bound from a document or fields */
|
|
25272
|
-
function newQueryBoundFromDocOrFields(query, methodName, docOrFields,
|
|
26713
|
+
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
25273
26714
|
docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
|
|
25274
26715
|
if (docOrFields[0] instanceof DocumentSnapshot$1) {
|
|
25275
|
-
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document,
|
|
26716
|
+
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
|
|
25276
26717
|
}
|
|
25277
26718
|
else {
|
|
25278
26719
|
const reader = newUserDataReader(query.firestore);
|
|
25279
|
-
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields,
|
|
26720
|
+
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
|
|
25280
26721
|
}
|
|
25281
26722
|
}
|
|
25282
26723
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
@@ -25334,7 +26775,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
|
|
|
25334
26775
|
* of the query or if any of the fields in the order by are an uncommitted
|
|
25335
26776
|
* server timestamp.
|
|
25336
26777
|
*/
|
|
25337
|
-
function newQueryBoundFromDocument(query, databaseId, methodName, doc,
|
|
26778
|
+
function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
|
|
25338
26779
|
if (!doc) {
|
|
25339
26780
|
throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
|
|
25340
26781
|
`${methodName}().`);
|
|
@@ -25371,12 +26812,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
|
|
|
25371
26812
|
}
|
|
25372
26813
|
}
|
|
25373
26814
|
}
|
|
25374
|
-
return new Bound(components,
|
|
26815
|
+
return new Bound(components, inclusive);
|
|
25375
26816
|
}
|
|
25376
26817
|
/**
|
|
25377
26818
|
* Converts a list of field values to a `Bound` for the given query.
|
|
25378
26819
|
*/
|
|
25379
|
-
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values,
|
|
26820
|
+
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
|
|
25380
26821
|
// Use explicit order by's because it has to match the query the user made
|
|
25381
26822
|
const orderBy = query.explicitOrderBy;
|
|
25382
26823
|
if (values.length > orderBy.length) {
|
|
@@ -25413,7 +26854,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
|
|
|
25413
26854
|
components.push(wrapped);
|
|
25414
26855
|
}
|
|
25415
26856
|
}
|
|
25416
|
-
return new Bound(components,
|
|
26857
|
+
return new Bound(components, inclusive);
|
|
25417
26858
|
}
|
|
25418
26859
|
/**
|
|
25419
26860
|
* Parses the given `documentIdValue` into a `ReferenceValue`, throwing
|