@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.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import { credentials, loadPackageDefinition, Metadata } from '@grpc/grpc-js';
|
|
|
8
8
|
import { fromJSON } from '@grpc/proto-loader';
|
|
9
9
|
|
|
10
10
|
const name = "@firebase/firestore";
|
|
11
|
-
const version$1 = "3.4.4-canary.
|
|
11
|
+
const version$1 = "3.4.4-canary.3a8d4c1d1";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @license
|
|
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
61
61
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
62
62
|
User.MOCK_USER = new User('mock-user');
|
|
63
63
|
|
|
64
|
-
const version = "9.6.
|
|
64
|
+
const version = "9.6.6-canary.3a8d4c1d1";
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @license
|
|
@@ -666,13 +666,19 @@ class FirebaseAppCheckTokenProvider {
|
|
|
666
666
|
this.appCheckProvider = appCheckProvider;
|
|
667
667
|
this.forceRefresh = false;
|
|
668
668
|
this.appCheck = null;
|
|
669
|
+
this.latestAppCheckToken = null;
|
|
669
670
|
}
|
|
670
671
|
start(asyncQueue, changeListener) {
|
|
671
672
|
const onTokenChanged = tokenResult => {
|
|
672
673
|
if (tokenResult.error != null) {
|
|
673
674
|
logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
|
|
674
675
|
}
|
|
675
|
-
|
|
676
|
+
const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
|
|
677
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
678
|
+
logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
|
|
679
|
+
return tokenUpdated
|
|
680
|
+
? changeListener(tokenResult.token)
|
|
681
|
+
: Promise.resolve();
|
|
676
682
|
};
|
|
677
683
|
this.tokenListener = (tokenResult) => {
|
|
678
684
|
asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
|
|
@@ -707,6 +713,7 @@ class FirebaseAppCheckTokenProvider {
|
|
|
707
713
|
return this.appCheck.getToken(forceRefresh).then(tokenResult => {
|
|
708
714
|
if (tokenResult) {
|
|
709
715
|
hardAssert(typeof tokenResult.token === 'string');
|
|
716
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
710
717
|
return new AppCheckToken(tokenResult.token);
|
|
711
718
|
}
|
|
712
719
|
else {
|
|
@@ -1192,23 +1199,6 @@ function decodeResourcePath(path) {
|
|
|
1192
1199
|
}
|
|
1193
1200
|
return new ResourcePath(segments);
|
|
1194
1201
|
}
|
|
1195
|
-
|
|
1196
|
-
/**
|
|
1197
|
-
* @license
|
|
1198
|
-
* Copyright 2017 Google LLC
|
|
1199
|
-
*
|
|
1200
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1201
|
-
* you may not use this file except in compliance with the License.
|
|
1202
|
-
* You may obtain a copy of the License at
|
|
1203
|
-
*
|
|
1204
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1205
|
-
*
|
|
1206
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1207
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1208
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1209
|
-
* See the License for the specific language governing permissions and
|
|
1210
|
-
* limitations under the License.
|
|
1211
|
-
*/
|
|
1212
1202
|
/**
|
|
1213
1203
|
* Schema Version for the Web client:
|
|
1214
1204
|
* 1. Initial version including Mutation Queue, Query Cache, and Remote
|
|
@@ -1227,8 +1217,10 @@ function decodeResourcePath(path) {
|
|
|
1227
1217
|
* an auto-incrementing ID. This is required for Index-Free queries.
|
|
1228
1218
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
1229
1219
|
* 11. Add bundles and named_queries for bundle support.
|
|
1220
|
+
* 12. Add document overlays.
|
|
1221
|
+
* 13. Add indexing support.
|
|
1230
1222
|
*/
|
|
1231
|
-
const SCHEMA_VERSION =
|
|
1223
|
+
const SCHEMA_VERSION = 12;
|
|
1232
1224
|
/**
|
|
1233
1225
|
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
|
|
1234
1226
|
*/
|
|
@@ -1753,9 +1745,7 @@ class DbClientMetadata {
|
|
|
1753
1745
|
DbClientMetadata.store = 'clientMetadata';
|
|
1754
1746
|
/** Keys are automatically assigned via the clientId properties. */
|
|
1755
1747
|
DbClientMetadata.keyPath = 'clientId';
|
|
1756
|
-
/**
|
|
1757
|
-
* A object representing a bundle loaded by the SDK.
|
|
1758
|
-
*/
|
|
1748
|
+
/** An object representing a bundle loaded by the SDK. */
|
|
1759
1749
|
class DbBundle {
|
|
1760
1750
|
constructor(
|
|
1761
1751
|
/** The ID of the loaded bundle. */
|
|
@@ -1772,9 +1762,7 @@ class DbBundle {
|
|
|
1772
1762
|
/** Name of the IndexedDb object store. */
|
|
1773
1763
|
DbBundle.store = 'bundles';
|
|
1774
1764
|
DbBundle.keyPath = 'bundleId';
|
|
1775
|
-
/**
|
|
1776
|
-
* A object representing a named query loaded by the SDK via a bundle.
|
|
1777
|
-
*/
|
|
1765
|
+
/** An object representing a named query loaded by the SDK via a bundle. */
|
|
1778
1766
|
class DbNamedQuery {
|
|
1779
1767
|
constructor(
|
|
1780
1768
|
/** The name of the query. */
|
|
@@ -1791,6 +1779,158 @@ class DbNamedQuery {
|
|
|
1791
1779
|
/** Name of the IndexedDb object store. */
|
|
1792
1780
|
DbNamedQuery.store = 'namedQueries';
|
|
1793
1781
|
DbNamedQuery.keyPath = 'name';
|
|
1782
|
+
/** An object representing the global configuration for a field index. */
|
|
1783
|
+
class DbIndexConfiguration {
|
|
1784
|
+
constructor(
|
|
1785
|
+
/**
|
|
1786
|
+
* The index id for this entry. Undefined for indexes that are not yet
|
|
1787
|
+
* persisted.
|
|
1788
|
+
*/
|
|
1789
|
+
indexId,
|
|
1790
|
+
/** The collection group this index belongs to. */
|
|
1791
|
+
collectionGroup,
|
|
1792
|
+
/** The fields to index for this index. */
|
|
1793
|
+
fields) {
|
|
1794
|
+
this.indexId = indexId;
|
|
1795
|
+
this.collectionGroup = collectionGroup;
|
|
1796
|
+
this.fields = fields;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
/** Name of the IndexedDb object store. */
|
|
1800
|
+
DbIndexConfiguration.store = 'indexConfiguration';
|
|
1801
|
+
DbIndexConfiguration.keyPath = 'indexId';
|
|
1802
|
+
/**
|
|
1803
|
+
* An index that provides access to the index configurations by collection
|
|
1804
|
+
* group.
|
|
1805
|
+
*
|
|
1806
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1807
|
+
* not possible here as the Web client supports concurrent access to
|
|
1808
|
+
* persistence via multi-tab.
|
|
1809
|
+
*/
|
|
1810
|
+
DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
|
|
1811
|
+
DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
|
|
1812
|
+
/**
|
|
1813
|
+
* An object describing how up-to-date the index backfill is for each user and
|
|
1814
|
+
* index.
|
|
1815
|
+
*/
|
|
1816
|
+
class DbIndexState {
|
|
1817
|
+
constructor(
|
|
1818
|
+
/** The index id for this entry. */
|
|
1819
|
+
indexId,
|
|
1820
|
+
/** The user id for this entry. */
|
|
1821
|
+
uid,
|
|
1822
|
+
/**
|
|
1823
|
+
* A number that indicates when the index was last updated (relative to
|
|
1824
|
+
* other indexes).
|
|
1825
|
+
*/
|
|
1826
|
+
sequenceNumber,
|
|
1827
|
+
/**
|
|
1828
|
+
* The latest read time that has been indexed by Firestore for this field
|
|
1829
|
+
* index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
|
|
1830
|
+
*/
|
|
1831
|
+
readTime,
|
|
1832
|
+
/**
|
|
1833
|
+
* The last document that has been indexed for this field index. Empty if
|
|
1834
|
+
* no documents have been indexed.
|
|
1835
|
+
*/
|
|
1836
|
+
documentKey,
|
|
1837
|
+
/**
|
|
1838
|
+
* The largest mutation batch id that has been processed for this index. -1
|
|
1839
|
+
* if no mutations have been indexed.
|
|
1840
|
+
*/
|
|
1841
|
+
largestBatchId) {
|
|
1842
|
+
this.indexId = indexId;
|
|
1843
|
+
this.uid = uid;
|
|
1844
|
+
this.sequenceNumber = sequenceNumber;
|
|
1845
|
+
this.readTime = readTime;
|
|
1846
|
+
this.documentKey = documentKey;
|
|
1847
|
+
this.largestBatchId = largestBatchId;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
/** Name of the IndexedDb object store. */
|
|
1851
|
+
DbIndexState.store = 'indexState';
|
|
1852
|
+
DbIndexState.keyPath = ['indexId', 'uid'];
|
|
1853
|
+
/**
|
|
1854
|
+
* An index that provides access to documents in a collection sorted by last
|
|
1855
|
+
* update time. Used by the backfiller.
|
|
1856
|
+
*
|
|
1857
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1858
|
+
* not possible here as the Web client supports concurrent access to
|
|
1859
|
+
* persistence via multi-tab.
|
|
1860
|
+
*/
|
|
1861
|
+
DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
|
|
1862
|
+
DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
|
|
1863
|
+
/** An object that stores the encoded entries for all documents and fields. */
|
|
1864
|
+
class DbIndexEntry {
|
|
1865
|
+
constructor(
|
|
1866
|
+
/** The index id for this entry. */
|
|
1867
|
+
indexId,
|
|
1868
|
+
/** The user id for this entry. */
|
|
1869
|
+
uid,
|
|
1870
|
+
/** The encoded array index value for this entry. */
|
|
1871
|
+
arrayValue,
|
|
1872
|
+
/** The encoded directional value for equality and inequality filters. */
|
|
1873
|
+
directionalValue,
|
|
1874
|
+
/** The document key this entry points to. */
|
|
1875
|
+
documentKey) {
|
|
1876
|
+
this.indexId = indexId;
|
|
1877
|
+
this.uid = uid;
|
|
1878
|
+
this.arrayValue = arrayValue;
|
|
1879
|
+
this.directionalValue = directionalValue;
|
|
1880
|
+
this.documentKey = documentKey;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
/** Name of the IndexedDb object store. */
|
|
1884
|
+
DbIndexEntry.store = 'indexEntries';
|
|
1885
|
+
DbIndexEntry.keyPath = [
|
|
1886
|
+
'indexId',
|
|
1887
|
+
'uid',
|
|
1888
|
+
'arrayValue',
|
|
1889
|
+
'directionalValue',
|
|
1890
|
+
'documentKey'
|
|
1891
|
+
];
|
|
1892
|
+
DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
|
|
1893
|
+
DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
|
|
1894
|
+
/**
|
|
1895
|
+
* An object representing a document overlay.
|
|
1896
|
+
*/
|
|
1897
|
+
class DbDocumentOverlay {
|
|
1898
|
+
constructor(
|
|
1899
|
+
/** The user ID to whom this overlay belongs. */
|
|
1900
|
+
userId,
|
|
1901
|
+
/** The path to the collection that contains the document. */
|
|
1902
|
+
collectionPath,
|
|
1903
|
+
/** The ID (key) of the document within the collection. */
|
|
1904
|
+
documentId,
|
|
1905
|
+
/** The collection group to which the document belongs. */
|
|
1906
|
+
collectionGroup,
|
|
1907
|
+
/** The largest batch ID that's been applied for this overlay. */
|
|
1908
|
+
largestBatchId,
|
|
1909
|
+
/** The overlay mutation. */
|
|
1910
|
+
overlayMutation) {
|
|
1911
|
+
this.userId = userId;
|
|
1912
|
+
this.collectionPath = collectionPath;
|
|
1913
|
+
this.documentId = documentId;
|
|
1914
|
+
this.collectionGroup = collectionGroup;
|
|
1915
|
+
this.largestBatchId = largestBatchId;
|
|
1916
|
+
this.overlayMutation = overlayMutation;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
/** Name of the IndexedDb object store. */
|
|
1920
|
+
DbDocumentOverlay.store = 'documentOverlays';
|
|
1921
|
+
DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
|
|
1922
|
+
DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
|
|
1923
|
+
DbDocumentOverlay.collectionPathOverlayIndexPath = [
|
|
1924
|
+
'userId',
|
|
1925
|
+
'collectionPath',
|
|
1926
|
+
'largestBatchId'
|
|
1927
|
+
];
|
|
1928
|
+
DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
|
|
1929
|
+
DbDocumentOverlay.collectionGroupOverlayIndexPath = [
|
|
1930
|
+
'userId',
|
|
1931
|
+
'collectionGroup',
|
|
1932
|
+
'largestBatchId'
|
|
1933
|
+
];
|
|
1794
1934
|
// Visible for testing
|
|
1795
1935
|
const V1_STORES = [
|
|
1796
1936
|
DbMutationQueue.store,
|
|
@@ -1805,7 +1945,6 @@ const V1_STORES = [
|
|
|
1805
1945
|
// V2 is no longer usable (see comment at top of file)
|
|
1806
1946
|
// Visible for testing
|
|
1807
1947
|
const V3_STORES = V1_STORES;
|
|
1808
|
-
// Visible for testing
|
|
1809
1948
|
// Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
|
|
1810
1949
|
const V4_STORES = [...V3_STORES, DbClientMetadata.store];
|
|
1811
1950
|
// V5 does not change the set of stores.
|
|
@@ -1815,12 +1954,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
|
|
|
1815
1954
|
// V9 does not change the set of stores.
|
|
1816
1955
|
// V10 does not change the set of stores.
|
|
1817
1956
|
const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1957
|
+
const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
|
|
1958
|
+
const V13_STORES = [
|
|
1959
|
+
...V12_STORES,
|
|
1960
|
+
DbIndexConfiguration.store,
|
|
1961
|
+
DbIndexState.store,
|
|
1962
|
+
DbIndexEntry.store
|
|
1963
|
+
];
|
|
1964
|
+
/** Returns the object stores for the provided schema. */
|
|
1965
|
+
function getObjectStores(schemaVersion) {
|
|
1966
|
+
if (schemaVersion === 13) {
|
|
1967
|
+
return V13_STORES;
|
|
1968
|
+
}
|
|
1969
|
+
else if (schemaVersion === 12) {
|
|
1970
|
+
return V12_STORES;
|
|
1971
|
+
}
|
|
1972
|
+
else if (schemaVersion === 11) {
|
|
1973
|
+
return V11_STORES;
|
|
1974
|
+
}
|
|
1975
|
+
else {
|
|
1976
|
+
fail();
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1824
1979
|
|
|
1825
1980
|
/**
|
|
1826
1981
|
* @license
|
|
@@ -2061,7 +2216,7 @@ class PersistencePromise {
|
|
|
2061
2216
|
*/
|
|
2062
2217
|
// References to `window` are guarded by SimpleDb.isAvailable()
|
|
2063
2218
|
/* eslint-disable no-restricted-globals */
|
|
2064
|
-
const LOG_TAG$
|
|
2219
|
+
const LOG_TAG$h = 'SimpleDb';
|
|
2065
2220
|
/**
|
|
2066
2221
|
* The maximum number of retry attempts for an IndexedDb transaction that fails
|
|
2067
2222
|
* with a DOMException.
|
|
@@ -2112,11 +2267,20 @@ class SimpleDbTransaction {
|
|
|
2112
2267
|
this.completionDeferred.reject(error);
|
|
2113
2268
|
}
|
|
2114
2269
|
if (!this.aborted) {
|
|
2115
|
-
logDebug(LOG_TAG$
|
|
2270
|
+
logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
|
|
2116
2271
|
this.aborted = true;
|
|
2117
2272
|
this.transaction.abort();
|
|
2118
2273
|
}
|
|
2119
2274
|
}
|
|
2275
|
+
maybeCommit() {
|
|
2276
|
+
// If the browser supports V3 IndexedDB, we invoke commit() explicitly to
|
|
2277
|
+
// speed up index DB processing if the event loop remains blocks.
|
|
2278
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2279
|
+
const maybeV3IndexedDb = this.transaction;
|
|
2280
|
+
if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
|
|
2281
|
+
maybeV3IndexedDb.commit();
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2120
2284
|
/**
|
|
2121
2285
|
* Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
|
|
2122
2286
|
* operations performed on the SimpleDbStore happen within the context of this
|
|
@@ -2166,7 +2330,7 @@ class SimpleDb {
|
|
|
2166
2330
|
}
|
|
2167
2331
|
/** Deletes the specified database. */
|
|
2168
2332
|
static delete(name) {
|
|
2169
|
-
logDebug(LOG_TAG$
|
|
2333
|
+
logDebug(LOG_TAG$h, 'Removing database:', name);
|
|
2170
2334
|
return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
|
|
2171
2335
|
}
|
|
2172
2336
|
/** Returns true if IndexedDB is available in the current environment. */
|
|
@@ -2245,7 +2409,7 @@ class SimpleDb {
|
|
|
2245
2409
|
*/
|
|
2246
2410
|
async ensureDb(action) {
|
|
2247
2411
|
if (!this.db) {
|
|
2248
|
-
logDebug(LOG_TAG$
|
|
2412
|
+
logDebug(LOG_TAG$h, 'Opening database:', this.name);
|
|
2249
2413
|
this.db = await new Promise((resolve, reject) => {
|
|
2250
2414
|
// TODO(mikelehen): Investigate browser compatibility.
|
|
2251
2415
|
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
|
|
@@ -2281,12 +2445,12 @@ class SimpleDb {
|
|
|
2281
2445
|
}
|
|
2282
2446
|
};
|
|
2283
2447
|
request.onupgradeneeded = (event) => {
|
|
2284
|
-
logDebug(LOG_TAG$
|
|
2448
|
+
logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
|
|
2285
2449
|
const db = event.target.result;
|
|
2286
2450
|
this.schemaConverter
|
|
2287
2451
|
.createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
|
|
2288
2452
|
.next(() => {
|
|
2289
|
-
logDebug(LOG_TAG$
|
|
2453
|
+
logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
|
|
2290
2454
|
});
|
|
2291
2455
|
};
|
|
2292
2456
|
});
|
|
@@ -2313,6 +2477,10 @@ class SimpleDb {
|
|
|
2313
2477
|
this.db = await this.ensureDb(action);
|
|
2314
2478
|
const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
|
|
2315
2479
|
const transactionFnResult = transactionFn(transaction)
|
|
2480
|
+
.next(result => {
|
|
2481
|
+
transaction.maybeCommit();
|
|
2482
|
+
return result;
|
|
2483
|
+
})
|
|
2316
2484
|
.catch(error => {
|
|
2317
2485
|
// Abort the transaction if there was an error.
|
|
2318
2486
|
transaction.abort(error);
|
|
@@ -2340,7 +2508,7 @@ class SimpleDb {
|
|
|
2340
2508
|
// exception is wrapped in a generic error by our async/await handling.
|
|
2341
2509
|
const retryable = error.name !== 'FirebaseError' &&
|
|
2342
2510
|
attemptNumber < TRANSACTION_RETRY_COUNT;
|
|
2343
|
-
logDebug(LOG_TAG$
|
|
2511
|
+
logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
|
|
2344
2512
|
this.close();
|
|
2345
2513
|
if (!retryable) {
|
|
2346
2514
|
return Promise.reject(error);
|
|
@@ -2427,11 +2595,11 @@ class SimpleDbStore {
|
|
|
2427
2595
|
put(keyOrValue, value) {
|
|
2428
2596
|
let request;
|
|
2429
2597
|
if (value !== undefined) {
|
|
2430
|
-
logDebug(LOG_TAG$
|
|
2598
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
|
|
2431
2599
|
request = this.store.put(value, keyOrValue);
|
|
2432
2600
|
}
|
|
2433
2601
|
else {
|
|
2434
|
-
logDebug(LOG_TAG$
|
|
2602
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
|
|
2435
2603
|
request = this.store.put(keyOrValue);
|
|
2436
2604
|
}
|
|
2437
2605
|
return wrapRequest(request);
|
|
@@ -2444,7 +2612,7 @@ class SimpleDbStore {
|
|
|
2444
2612
|
* @returns The key of the value to add.
|
|
2445
2613
|
*/
|
|
2446
2614
|
add(value) {
|
|
2447
|
-
logDebug(LOG_TAG$
|
|
2615
|
+
logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
|
|
2448
2616
|
const request = this.store.add(value);
|
|
2449
2617
|
return wrapRequest(request);
|
|
2450
2618
|
}
|
|
@@ -2464,12 +2632,12 @@ class SimpleDbStore {
|
|
|
2464
2632
|
if (result === undefined) {
|
|
2465
2633
|
result = null;
|
|
2466
2634
|
}
|
|
2467
|
-
logDebug(LOG_TAG$
|
|
2635
|
+
logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
|
|
2468
2636
|
return result;
|
|
2469
2637
|
});
|
|
2470
2638
|
}
|
|
2471
2639
|
delete(key) {
|
|
2472
|
-
logDebug(LOG_TAG$
|
|
2640
|
+
logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
|
|
2473
2641
|
const request = this.store.delete(key);
|
|
2474
2642
|
return wrapRequest(request);
|
|
2475
2643
|
}
|
|
@@ -2480,21 +2648,37 @@ class SimpleDbStore {
|
|
|
2480
2648
|
* Returns the number of rows in the store.
|
|
2481
2649
|
*/
|
|
2482
2650
|
count() {
|
|
2483
|
-
logDebug(LOG_TAG$
|
|
2651
|
+
logDebug(LOG_TAG$h, 'COUNT', this.store.name);
|
|
2484
2652
|
const request = this.store.count();
|
|
2485
2653
|
return wrapRequest(request);
|
|
2486
2654
|
}
|
|
2487
2655
|
loadAll(indexOrRange, range) {
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
return
|
|
2494
|
-
|
|
2656
|
+
const iterateOptions = this.options(indexOrRange, range);
|
|
2657
|
+
// Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
|
|
2658
|
+
// 20% faster. Unfortunately, getAll() does not support custom indices.
|
|
2659
|
+
if (!iterateOptions.index && typeof this.store.getAll === 'function') {
|
|
2660
|
+
const request = this.store.getAll(iterateOptions.range);
|
|
2661
|
+
return new PersistencePromise((resolve, reject) => {
|
|
2662
|
+
request.onerror = (event) => {
|
|
2663
|
+
reject(event.target.error);
|
|
2664
|
+
};
|
|
2665
|
+
request.onsuccess = (event) => {
|
|
2666
|
+
resolve(event.target.result);
|
|
2667
|
+
};
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
else {
|
|
2671
|
+
const cursor = this.cursor(iterateOptions);
|
|
2672
|
+
const results = [];
|
|
2673
|
+
return this.iterateCursor(cursor, (key, value) => {
|
|
2674
|
+
results.push(value);
|
|
2675
|
+
}).next(() => {
|
|
2676
|
+
return results;
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2495
2679
|
}
|
|
2496
2680
|
deleteAll(indexOrRange, range) {
|
|
2497
|
-
logDebug(LOG_TAG$
|
|
2681
|
+
logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
|
|
2498
2682
|
const options = this.options(indexOrRange, range);
|
|
2499
2683
|
options.keysOnly = false;
|
|
2500
2684
|
const cursor = this.cursor(options);
|
|
@@ -3517,11 +3701,22 @@ class DocumentKey {
|
|
|
3517
3701
|
static empty() {
|
|
3518
3702
|
return new DocumentKey(ResourcePath.emptyPath());
|
|
3519
3703
|
}
|
|
3704
|
+
get collectionGroup() {
|
|
3705
|
+
return this.path.popLast().lastSegment();
|
|
3706
|
+
}
|
|
3520
3707
|
/** Returns true if the document is in the specified collectionId. */
|
|
3521
3708
|
hasCollectionId(collectionId) {
|
|
3522
3709
|
return (this.path.length >= 2 &&
|
|
3523
3710
|
this.path.get(this.path.length - 2) === collectionId);
|
|
3524
3711
|
}
|
|
3712
|
+
/** Returns the collection group (i.e. the name of the parent collection) for this key. */
|
|
3713
|
+
getCollectionGroup() {
|
|
3714
|
+
return this.path.get(this.path.length - 2);
|
|
3715
|
+
}
|
|
3716
|
+
/** Returns the fully qualified path to the parent collection. */
|
|
3717
|
+
getCollectionPath() {
|
|
3718
|
+
return this.path.popLast();
|
|
3719
|
+
}
|
|
3525
3720
|
isEqual(other) {
|
|
3526
3721
|
return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
|
|
3527
3722
|
}
|
|
@@ -3561,6 +3756,13 @@ class DocumentKey {
|
|
|
3561
3756
|
* See the License for the specific language governing permissions and
|
|
3562
3757
|
* limitations under the License.
|
|
3563
3758
|
*/
|
|
3759
|
+
const MAX_VALUE = {
|
|
3760
|
+
mapValue: {
|
|
3761
|
+
fields: {
|
|
3762
|
+
'__type__': { stringValue: '__max___' }
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3564
3766
|
/** Extracts the backend's type order for the provided value. */
|
|
3565
3767
|
function typeOrder(value) {
|
|
3566
3768
|
if ('nullValue' in value) {
|
|
@@ -3970,6 +4172,10 @@ function deepClone(source) {
|
|
|
3970
4172
|
else {
|
|
3971
4173
|
return Object.assign({}, source);
|
|
3972
4174
|
}
|
|
4175
|
+
}
|
|
4176
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
4177
|
+
function isMaxValue(value) {
|
|
4178
|
+
return valueEquals(value, MAX_VALUE);
|
|
3973
4179
|
}
|
|
3974
4180
|
|
|
3975
4181
|
/**
|
|
@@ -4345,17 +4551,25 @@ class FieldIndex {
|
|
|
4345
4551
|
/** The collection ID this index applies to. */
|
|
4346
4552
|
collectionGroup,
|
|
4347
4553
|
/** The field segments for this index. */
|
|
4348
|
-
|
|
4554
|
+
fields,
|
|
4349
4555
|
/** Shows how up-to-date the index is for the current user. */
|
|
4350
4556
|
indexState) {
|
|
4351
4557
|
this.indexId = indexId;
|
|
4352
4558
|
this.collectionGroup = collectionGroup;
|
|
4353
|
-
this.
|
|
4559
|
+
this.fields = fields;
|
|
4354
4560
|
this.indexState = indexState;
|
|
4355
4561
|
}
|
|
4356
4562
|
}
|
|
4357
4563
|
/** An ID for an index that has not yet been added to persistence. */
|
|
4358
4564
|
FieldIndex.UNKNOWN_ID = -1;
|
|
4565
|
+
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
4566
|
+
function fieldIndexGetArraySegment(fieldIndex) {
|
|
4567
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
4568
|
+
}
|
|
4569
|
+
/** Returns all directional (ascending/descending) segments for this index. */
|
|
4570
|
+
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4571
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4572
|
+
}
|
|
4359
4573
|
/** An index component consisting of field path and index type. */
|
|
4360
4574
|
class IndexSegment {
|
|
4361
4575
|
constructor(
|
|
@@ -4460,27 +4674,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
|
|
|
4460
4674
|
function canonifyTarget(target) {
|
|
4461
4675
|
const targetImpl = debugCast(target);
|
|
4462
4676
|
if (targetImpl.memoizedCanonicalId === null) {
|
|
4463
|
-
let
|
|
4677
|
+
let str = targetImpl.path.canonicalString();
|
|
4464
4678
|
if (targetImpl.collectionGroup !== null) {
|
|
4465
|
-
|
|
4679
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
4466
4680
|
}
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4681
|
+
str += '|f:';
|
|
4682
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
4683
|
+
str += '|ob:';
|
|
4684
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
4471
4685
|
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
4472
|
-
|
|
4473
|
-
|
|
4686
|
+
str += '|l:';
|
|
4687
|
+
str += targetImpl.limit;
|
|
4474
4688
|
}
|
|
4475
4689
|
if (targetImpl.startAt) {
|
|
4476
|
-
|
|
4477
|
-
|
|
4690
|
+
str += '|lb:';
|
|
4691
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
4692
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4478
4693
|
}
|
|
4479
4694
|
if (targetImpl.endAt) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4695
|
+
str += '|ub:';
|
|
4696
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
4697
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4482
4698
|
}
|
|
4483
|
-
targetImpl.memoizedCanonicalId =
|
|
4699
|
+
targetImpl.memoizedCanonicalId = str;
|
|
4484
4700
|
}
|
|
4485
4701
|
return targetImpl.memoizedCanonicalId;
|
|
4486
4702
|
}
|
|
@@ -4503,10 +4719,14 @@ function stringifyTarget(target) {
|
|
|
4503
4719
|
.join(', ')}]`;
|
|
4504
4720
|
}
|
|
4505
4721
|
if (target.startAt) {
|
|
4506
|
-
str += ', startAt: '
|
|
4722
|
+
str += ', startAt: ';
|
|
4723
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
4724
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4507
4725
|
}
|
|
4508
4726
|
if (target.endAt) {
|
|
4509
|
-
str += ', endAt: '
|
|
4727
|
+
str += ', endAt: ';
|
|
4728
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
4729
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4510
4730
|
}
|
|
4511
4731
|
return `Target(${str})`;
|
|
4512
4732
|
}
|
|
@@ -4729,7 +4949,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4729
4949
|
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
4730
4950
|
}
|
|
4731
4951
|
}
|
|
4732
|
-
// TODO(indexing): Change Bound.before to "inclusive"
|
|
4733
4952
|
/**
|
|
4734
4953
|
* Represents a bound of a query.
|
|
4735
4954
|
*
|
|
@@ -4745,17 +4964,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4745
4964
|
* just after the provided values.
|
|
4746
4965
|
*/
|
|
4747
4966
|
class Bound {
|
|
4748
|
-
constructor(position,
|
|
4967
|
+
constructor(position, inclusive) {
|
|
4749
4968
|
this.position = position;
|
|
4750
|
-
this.
|
|
4969
|
+
this.inclusive = inclusive;
|
|
4751
4970
|
}
|
|
4752
4971
|
}
|
|
4753
|
-
function canonifyBound(bound) {
|
|
4754
|
-
// TODO(b/29183165): Make this collision robust.
|
|
4755
|
-
return `${bound.before ? 'b' : 'a'}:${bound.position
|
|
4756
|
-
.map(p => canonicalId(p))
|
|
4757
|
-
.join(',')}`;
|
|
4758
|
-
}
|
|
4759
4972
|
/**
|
|
4760
4973
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
4761
4974
|
*/
|
|
@@ -4775,11 +4988,7 @@ function stringifyOrderBy(orderBy) {
|
|
|
4775
4988
|
function orderByEquals(left, right) {
|
|
4776
4989
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
4777
4990
|
}
|
|
4778
|
-
|
|
4779
|
-
* Returns true if a document sorts before a bound using the provided sort
|
|
4780
|
-
* order.
|
|
4781
|
-
*/
|
|
4782
|
-
function sortsBeforeDocument(bound, orderBy, doc) {
|
|
4991
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
4783
4992
|
let comparison = 0;
|
|
4784
4993
|
for (let i = 0; i < bound.position.length; i++) {
|
|
4785
4994
|
const orderByComponent = orderBy[i];
|
|
@@ -4798,7 +5007,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
|
|
|
4798
5007
|
break;
|
|
4799
5008
|
}
|
|
4800
5009
|
}
|
|
4801
|
-
return
|
|
5010
|
+
return comparison;
|
|
5011
|
+
}
|
|
5012
|
+
/**
|
|
5013
|
+
* Returns true if a document sorts after a bound using the provided sort
|
|
5014
|
+
* order.
|
|
5015
|
+
*/
|
|
5016
|
+
function boundSortsAfterDocument(bound, orderBy, doc) {
|
|
5017
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5018
|
+
return bound.inclusive ? comparison >= 0 : comparison > 0;
|
|
5019
|
+
}
|
|
5020
|
+
/**
|
|
5021
|
+
* Returns true if a document sorts before a bound using the provided sort
|
|
5022
|
+
* order.
|
|
5023
|
+
*/
|
|
5024
|
+
function boundSortsBeforeDocument(bound, orderBy, doc) {
|
|
5025
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5026
|
+
return bound.inclusive ? comparison <= 0 : comparison < 0;
|
|
4802
5027
|
}
|
|
4803
5028
|
function boundEquals(left, right) {
|
|
4804
5029
|
if (left === null) {
|
|
@@ -4807,7 +5032,7 @@ function boundEquals(left, right) {
|
|
|
4807
5032
|
else if (right === null) {
|
|
4808
5033
|
return false;
|
|
4809
5034
|
}
|
|
4810
|
-
if (left.
|
|
5035
|
+
if (left.inclusive !== right.inclusive ||
|
|
4811
5036
|
left.position.length !== right.position.length) {
|
|
4812
5037
|
return false;
|
|
4813
5038
|
}
|
|
@@ -5011,10 +5236,10 @@ function queryToTarget(query) {
|
|
|
5011
5236
|
}
|
|
5012
5237
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
5013
5238
|
const startAt = queryImpl.endAt
|
|
5014
|
-
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.
|
|
5239
|
+
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
|
|
5015
5240
|
: null;
|
|
5016
5241
|
const endAt = queryImpl.startAt
|
|
5017
|
-
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.
|
|
5242
|
+
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
|
|
5018
5243
|
: null;
|
|
5019
5244
|
// Now return as a LimitType.First query.
|
|
5020
5245
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
|
|
@@ -5102,11 +5327,11 @@ function queryMatchesFilters(query, doc) {
|
|
|
5102
5327
|
/** Makes sure a document is within the bounds, if provided. */
|
|
5103
5328
|
function queryMatchesBounds(query, doc) {
|
|
5104
5329
|
if (query.startAt &&
|
|
5105
|
-
!
|
|
5330
|
+
!boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
|
|
5106
5331
|
return false;
|
|
5107
5332
|
}
|
|
5108
5333
|
if (query.endAt &&
|
|
5109
|
-
|
|
5334
|
+
!boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
|
|
5110
5335
|
return false;
|
|
5111
5336
|
}
|
|
5112
5337
|
return true;
|
|
@@ -5761,6 +5986,72 @@ class SortedSetIterator {
|
|
|
5761
5986
|
hasNext() {
|
|
5762
5987
|
return this.iter.hasNext();
|
|
5763
5988
|
}
|
|
5989
|
+
}
|
|
5990
|
+
/**
|
|
5991
|
+
* Compares two sorted sets for equality using their natural ordering. The
|
|
5992
|
+
* method computes the intersection and invokes `onAdd` for every element that
|
|
5993
|
+
* is in `after` but not `before`. `onRemove` is invoked for every element in
|
|
5994
|
+
* `before` but missing from `after`.
|
|
5995
|
+
*
|
|
5996
|
+
* The method creates a copy of both `before` and `after` and runs in O(n log
|
|
5997
|
+
* n), where n is the size of the two lists.
|
|
5998
|
+
*
|
|
5999
|
+
* @param before - The elements that exist in the original set.
|
|
6000
|
+
* @param after - The elements to diff against the original set.
|
|
6001
|
+
* @param comparator - The comparator for the elements in before and after.
|
|
6002
|
+
* @param onAdd - A function to invoke for every element that is part of `
|
|
6003
|
+
* after` but not `before`.
|
|
6004
|
+
* @param onRemove - A function to invoke for every element that is part of
|
|
6005
|
+
* `before` but not `after`.
|
|
6006
|
+
*/
|
|
6007
|
+
function diffSortedSets(before, after, comparator, onAdd, onRemove) {
|
|
6008
|
+
const beforeIt = before.getIterator();
|
|
6009
|
+
const afterIt = after.getIterator();
|
|
6010
|
+
let beforeValue = advanceIterator(beforeIt);
|
|
6011
|
+
let afterValue = advanceIterator(afterIt);
|
|
6012
|
+
// Walk through the two sets at the same time, using the ordering defined by
|
|
6013
|
+
// `comparator`.
|
|
6014
|
+
while (beforeValue || afterValue) {
|
|
6015
|
+
let added = false;
|
|
6016
|
+
let removed = false;
|
|
6017
|
+
if (beforeValue && afterValue) {
|
|
6018
|
+
const cmp = comparator(beforeValue, afterValue);
|
|
6019
|
+
if (cmp < 0) {
|
|
6020
|
+
// The element was removed if the next element in our ordered
|
|
6021
|
+
// walkthrough is only in `before`.
|
|
6022
|
+
removed = true;
|
|
6023
|
+
}
|
|
6024
|
+
else if (cmp > 0) {
|
|
6025
|
+
// The element was added if the next element in our ordered walkthrough
|
|
6026
|
+
// is only in `after`.
|
|
6027
|
+
added = true;
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
else if (beforeValue != null) {
|
|
6031
|
+
removed = true;
|
|
6032
|
+
}
|
|
6033
|
+
else {
|
|
6034
|
+
added = true;
|
|
6035
|
+
}
|
|
6036
|
+
if (added) {
|
|
6037
|
+
onAdd(afterValue);
|
|
6038
|
+
afterValue = advanceIterator(afterIt);
|
|
6039
|
+
}
|
|
6040
|
+
else if (removed) {
|
|
6041
|
+
onRemove(beforeValue);
|
|
6042
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6043
|
+
}
|
|
6044
|
+
else {
|
|
6045
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6046
|
+
afterValue = advanceIterator(afterIt);
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
/**
|
|
6051
|
+
* Returns the next element from the iterator or `undefined` if none available.
|
|
6052
|
+
*/
|
|
6053
|
+
function advanceIterator(it) {
|
|
6054
|
+
return it.hasNext() ? it.getNext() : undefined;
|
|
5764
6055
|
}
|
|
5765
6056
|
|
|
5766
6057
|
/**
|
|
@@ -6599,6 +6890,47 @@ class MutationBatchResult {
|
|
|
6599
6890
|
}
|
|
6600
6891
|
}
|
|
6601
6892
|
|
|
6893
|
+
/**
|
|
6894
|
+
* @license
|
|
6895
|
+
* Copyright 2022 Google LLC
|
|
6896
|
+
*
|
|
6897
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6898
|
+
* you may not use this file except in compliance with the License.
|
|
6899
|
+
* You may obtain a copy of the License at
|
|
6900
|
+
*
|
|
6901
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
6902
|
+
*
|
|
6903
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
6904
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
6905
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6906
|
+
* See the License for the specific language governing permissions and
|
|
6907
|
+
* limitations under the License.
|
|
6908
|
+
*/
|
|
6909
|
+
/**
|
|
6910
|
+
* Representation of an overlay computed by Firestore.
|
|
6911
|
+
*
|
|
6912
|
+
* Holds information about a mutation and the largest batch id in Firestore when
|
|
6913
|
+
* the mutation was created.
|
|
6914
|
+
*/
|
|
6915
|
+
class Overlay {
|
|
6916
|
+
constructor(largestBatchId, mutation) {
|
|
6917
|
+
this.largestBatchId = largestBatchId;
|
|
6918
|
+
this.mutation = mutation;
|
|
6919
|
+
}
|
|
6920
|
+
getKey() {
|
|
6921
|
+
return this.mutation.key;
|
|
6922
|
+
}
|
|
6923
|
+
isEqual(other) {
|
|
6924
|
+
return other !== null && this.mutation === other.mutation;
|
|
6925
|
+
}
|
|
6926
|
+
toString() {
|
|
6927
|
+
return `Overlay{
|
|
6928
|
+
largestBatchId: ${this.largestBatchId},
|
|
6929
|
+
mutation: ${this.mutation.toString()}
|
|
6930
|
+
}`;
|
|
6931
|
+
}
|
|
6932
|
+
}
|
|
6933
|
+
|
|
6602
6934
|
/**
|
|
6603
6935
|
* @license
|
|
6604
6936
|
* Copyright 2017 Google LLC
|
|
@@ -7071,7 +7403,7 @@ class TargetState {
|
|
|
7071
7403
|
this._current = true;
|
|
7072
7404
|
}
|
|
7073
7405
|
}
|
|
7074
|
-
const LOG_TAG$
|
|
7406
|
+
const LOG_TAG$g = 'WatchChangeAggregator';
|
|
7075
7407
|
/**
|
|
7076
7408
|
* A helper class to accumulate watch changes into a RemoteEvent.
|
|
7077
7409
|
*/
|
|
@@ -7366,7 +7698,7 @@ class WatchChangeAggregator {
|
|
|
7366
7698
|
isActiveTarget(targetId) {
|
|
7367
7699
|
const targetActive = this.targetDataForActiveTarget(targetId) !== null;
|
|
7368
7700
|
if (!targetActive) {
|
|
7369
|
-
logDebug(LOG_TAG$
|
|
7701
|
+
logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
|
|
7370
7702
|
}
|
|
7371
7703
|
return targetActive;
|
|
7372
7704
|
}
|
|
@@ -7985,10 +8317,10 @@ function toQueryTarget(serializer, target) {
|
|
|
7985
8317
|
result.structuredQuery.limit = limit;
|
|
7986
8318
|
}
|
|
7987
8319
|
if (target.startAt) {
|
|
7988
|
-
result.structuredQuery.startAt =
|
|
8320
|
+
result.structuredQuery.startAt = toStartAtCursor(target.startAt);
|
|
7989
8321
|
}
|
|
7990
8322
|
if (target.endAt) {
|
|
7991
|
-
result.structuredQuery.endAt =
|
|
8323
|
+
result.structuredQuery.endAt = toEndAtCursor(target.endAt);
|
|
7992
8324
|
}
|
|
7993
8325
|
return result;
|
|
7994
8326
|
}
|
|
@@ -8021,11 +8353,11 @@ function convertQueryTargetToQuery(target) {
|
|
|
8021
8353
|
}
|
|
8022
8354
|
let startAt = null;
|
|
8023
8355
|
if (query.startAt) {
|
|
8024
|
-
startAt =
|
|
8356
|
+
startAt = fromStartAtCursor(query.startAt);
|
|
8025
8357
|
}
|
|
8026
8358
|
let endAt = null;
|
|
8027
8359
|
if (query.endAt) {
|
|
8028
|
-
endAt =
|
|
8360
|
+
endAt = fromEndAtCursor(query.endAt);
|
|
8029
8361
|
}
|
|
8030
8362
|
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
8031
8363
|
}
|
|
@@ -8116,16 +8448,27 @@ function toOrder(orderBys) {
|
|
|
8116
8448
|
function fromOrder(orderBys) {
|
|
8117
8449
|
return orderBys.map(order => fromPropertyOrder(order));
|
|
8118
8450
|
}
|
|
8119
|
-
function
|
|
8451
|
+
function toStartAtCursor(cursor) {
|
|
8452
|
+
return {
|
|
8453
|
+
before: cursor.inclusive,
|
|
8454
|
+
values: cursor.position
|
|
8455
|
+
};
|
|
8456
|
+
}
|
|
8457
|
+
function toEndAtCursor(cursor) {
|
|
8120
8458
|
return {
|
|
8121
|
-
before: cursor.
|
|
8459
|
+
before: !cursor.inclusive,
|
|
8122
8460
|
values: cursor.position
|
|
8123
8461
|
};
|
|
8124
8462
|
}
|
|
8125
|
-
function
|
|
8126
|
-
const
|
|
8463
|
+
function fromStartAtCursor(cursor) {
|
|
8464
|
+
const inclusive = !!cursor.before;
|
|
8127
8465
|
const position = cursor.values || [];
|
|
8128
|
-
return new Bound(position,
|
|
8466
|
+
return new Bound(position, inclusive);
|
|
8467
|
+
}
|
|
8468
|
+
function fromEndAtCursor(cursor) {
|
|
8469
|
+
const inclusive = !cursor.before;
|
|
8470
|
+
const position = cursor.values || [];
|
|
8471
|
+
return new Bound(position, inclusive);
|
|
8129
8472
|
}
|
|
8130
8473
|
// visible for testing
|
|
8131
8474
|
function toDirection(dir) {
|
|
@@ -8572,28 +8915,764 @@ function fromBundledQuery(bundledQuery) {
|
|
|
8572
8915
|
if (bundledQuery.limitType === 'LAST') {
|
|
8573
8916
|
return queryWithLimit(query, query.limit, "L" /* Last */);
|
|
8574
8917
|
}
|
|
8575
|
-
return query;
|
|
8918
|
+
return query;
|
|
8919
|
+
}
|
|
8920
|
+
/** Encodes a NamedQuery proto object to a NamedQuery model object. */
|
|
8921
|
+
function fromProtoNamedQuery(namedQuery) {
|
|
8922
|
+
return {
|
|
8923
|
+
name: namedQuery.name,
|
|
8924
|
+
query: fromBundledQuery(namedQuery.bundledQuery),
|
|
8925
|
+
readTime: fromVersion(namedQuery.readTime)
|
|
8926
|
+
};
|
|
8927
|
+
}
|
|
8928
|
+
/** Decodes a BundleMetadata proto into a BundleMetadata object. */
|
|
8929
|
+
function fromBundleMetadata(metadata) {
|
|
8930
|
+
return {
|
|
8931
|
+
id: metadata.id,
|
|
8932
|
+
version: metadata.version,
|
|
8933
|
+
createTime: fromVersion(metadata.createTime)
|
|
8934
|
+
};
|
|
8935
|
+
}
|
|
8936
|
+
/** Encodes a DbDocumentOverlay object to an Overlay model object. */
|
|
8937
|
+
function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
|
|
8938
|
+
return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
|
|
8939
|
+
}
|
|
8940
|
+
/** Decodes an Overlay model object into a DbDocumentOverlay object. */
|
|
8941
|
+
function toDbDocumentOverlay(localSerializer, userId, overlay) {
|
|
8942
|
+
const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
|
|
8943
|
+
return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
|
|
8944
|
+
}
|
|
8945
|
+
/**
|
|
8946
|
+
* Returns the DbDocumentOverlayKey corresponding to the given user and
|
|
8947
|
+
* document key.
|
|
8948
|
+
*/
|
|
8949
|
+
function toDbDocumentOverlayKey(userId, docKey) {
|
|
8950
|
+
const docId = docKey.path.lastSegment();
|
|
8951
|
+
const collectionPath = encodeResourcePath(docKey.path.popLast());
|
|
8952
|
+
return [userId, collectionPath, docId];
|
|
8953
|
+
}
|
|
8954
|
+
function toDbIndexConfiguration(index) {
|
|
8955
|
+
return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
|
|
8956
|
+
}
|
|
8957
|
+
function fromDbIndexConfiguration(index, state) {
|
|
8958
|
+
const decodedState = state
|
|
8959
|
+
? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
|
|
8960
|
+
: IndexState.empty();
|
|
8961
|
+
const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
|
|
8962
|
+
return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
|
|
8963
|
+
}
|
|
8964
|
+
function toDbIndexState(indexId, user, sequenceNumber, offset) {
|
|
8965
|
+
return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
|
|
8966
|
+
}
|
|
8967
|
+
|
|
8968
|
+
/**
|
|
8969
|
+
* @license
|
|
8970
|
+
* Copyright 2020 Google LLC
|
|
8971
|
+
*
|
|
8972
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8973
|
+
* you may not use this file except in compliance with the License.
|
|
8974
|
+
* You may obtain a copy of the License at
|
|
8975
|
+
*
|
|
8976
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8977
|
+
*
|
|
8978
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
8979
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
8980
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
8981
|
+
* See the License for the specific language governing permissions and
|
|
8982
|
+
* limitations under the License.
|
|
8983
|
+
*/
|
|
8984
|
+
class IndexedDbBundleCache {
|
|
8985
|
+
getBundleMetadata(transaction, bundleId) {
|
|
8986
|
+
return bundlesStore(transaction)
|
|
8987
|
+
.get(bundleId)
|
|
8988
|
+
.next(bundle => {
|
|
8989
|
+
if (bundle) {
|
|
8990
|
+
return fromDbBundle(bundle);
|
|
8991
|
+
}
|
|
8992
|
+
return undefined;
|
|
8993
|
+
});
|
|
8994
|
+
}
|
|
8995
|
+
saveBundleMetadata(transaction, bundleMetadata) {
|
|
8996
|
+
return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
|
|
8997
|
+
}
|
|
8998
|
+
getNamedQuery(transaction, queryName) {
|
|
8999
|
+
return namedQueriesStore(transaction)
|
|
9000
|
+
.get(queryName)
|
|
9001
|
+
.next(query => {
|
|
9002
|
+
if (query) {
|
|
9003
|
+
return fromDbNamedQuery(query);
|
|
9004
|
+
}
|
|
9005
|
+
return undefined;
|
|
9006
|
+
});
|
|
9007
|
+
}
|
|
9008
|
+
saveNamedQuery(transaction, query) {
|
|
9009
|
+
return namedQueriesStore(transaction).put(toDbNamedQuery(query));
|
|
9010
|
+
}
|
|
9011
|
+
}
|
|
9012
|
+
/**
|
|
9013
|
+
* Helper to get a typed SimpleDbStore for the bundles object store.
|
|
9014
|
+
*/
|
|
9015
|
+
function bundlesStore(txn) {
|
|
9016
|
+
return getStore(txn, DbBundle.store);
|
|
9017
|
+
}
|
|
9018
|
+
/**
|
|
9019
|
+
* Helper to get a typed SimpleDbStore for the namedQueries object store.
|
|
9020
|
+
*/
|
|
9021
|
+
function namedQueriesStore(txn) {
|
|
9022
|
+
return getStore(txn, DbNamedQuery.store);
|
|
9023
|
+
}
|
|
9024
|
+
|
|
9025
|
+
/**
|
|
9026
|
+
* @license
|
|
9027
|
+
* Copyright 2022 Google LLC
|
|
9028
|
+
*
|
|
9029
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9030
|
+
* you may not use this file except in compliance with the License.
|
|
9031
|
+
* You may obtain a copy of the License at
|
|
9032
|
+
*
|
|
9033
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9034
|
+
*
|
|
9035
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9036
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9037
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9038
|
+
* See the License for the specific language governing permissions and
|
|
9039
|
+
* limitations under the License.
|
|
9040
|
+
*/
|
|
9041
|
+
/**
|
|
9042
|
+
* Implementation of DocumentOverlayCache using IndexedDb.
|
|
9043
|
+
*/
|
|
9044
|
+
class IndexedDbDocumentOverlayCache {
|
|
9045
|
+
/**
|
|
9046
|
+
* @param serializer - The document serializer.
|
|
9047
|
+
* @param userId - The userId for which we are accessing overlays.
|
|
9048
|
+
*/
|
|
9049
|
+
constructor(serializer, userId) {
|
|
9050
|
+
this.serializer = serializer;
|
|
9051
|
+
this.userId = userId;
|
|
9052
|
+
}
|
|
9053
|
+
static forUser(serializer, user) {
|
|
9054
|
+
const userId = user.uid || '';
|
|
9055
|
+
return new IndexedDbDocumentOverlayCache(serializer, userId);
|
|
9056
|
+
}
|
|
9057
|
+
getOverlay(transaction, key) {
|
|
9058
|
+
return documentOverlayStore(transaction)
|
|
9059
|
+
.get(toDbDocumentOverlayKey(this.userId, key))
|
|
9060
|
+
.next(dbOverlay => {
|
|
9061
|
+
if (dbOverlay) {
|
|
9062
|
+
return fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9063
|
+
}
|
|
9064
|
+
return null;
|
|
9065
|
+
});
|
|
9066
|
+
}
|
|
9067
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
9068
|
+
const promises = [];
|
|
9069
|
+
overlays.forEach(mutation => {
|
|
9070
|
+
const overlay = new Overlay(largestBatchId, mutation);
|
|
9071
|
+
promises.push(this.saveOverlay(transaction, overlay));
|
|
9072
|
+
});
|
|
9073
|
+
return PersistencePromise.waitFor(promises);
|
|
9074
|
+
}
|
|
9075
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
9076
|
+
const collectionPaths = new Set();
|
|
9077
|
+
// Get the set of unique collection paths.
|
|
9078
|
+
documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
|
|
9079
|
+
const promises = [];
|
|
9080
|
+
collectionPaths.forEach(collectionPath => {
|
|
9081
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
|
|
9082
|
+
/*lowerOpen=*/ false,
|
|
9083
|
+
/*upperOpen=*/ true);
|
|
9084
|
+
promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
|
|
9085
|
+
});
|
|
9086
|
+
return PersistencePromise.waitFor(promises);
|
|
9087
|
+
}
|
|
9088
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
9089
|
+
const result = new Map();
|
|
9090
|
+
const collectionPath = encodeResourcePath(collection);
|
|
9091
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9092
|
+
// is not inclusive.
|
|
9093
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
|
|
9094
|
+
/*lowerOpen=*/ true);
|
|
9095
|
+
return documentOverlayStore(transaction)
|
|
9096
|
+
.loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
|
|
9097
|
+
.next(dbOverlays => {
|
|
9098
|
+
for (const dbOverlay of dbOverlays) {
|
|
9099
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9100
|
+
result.set(overlay.getKey(), overlay);
|
|
9101
|
+
}
|
|
9102
|
+
return result;
|
|
9103
|
+
});
|
|
9104
|
+
}
|
|
9105
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
9106
|
+
const result = new Map();
|
|
9107
|
+
let currentBatchId = undefined;
|
|
9108
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9109
|
+
// is not inclusive.
|
|
9110
|
+
const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
|
|
9111
|
+
/*lowerOpen=*/ true);
|
|
9112
|
+
return documentOverlayStore(transaction)
|
|
9113
|
+
.iterate({
|
|
9114
|
+
index: DbDocumentOverlay.collectionGroupOverlayIndex,
|
|
9115
|
+
range
|
|
9116
|
+
}, (_, dbOverlay, control) => {
|
|
9117
|
+
// We do not want to return partial batch overlays, even if the size
|
|
9118
|
+
// of the result set exceeds the given `count` argument. Therefore, we
|
|
9119
|
+
// continue to aggregate results even after the result size exceeds
|
|
9120
|
+
// `count` if there are more overlays from the `currentBatchId`.
|
|
9121
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9122
|
+
if (result.size < count ||
|
|
9123
|
+
overlay.largestBatchId === currentBatchId) {
|
|
9124
|
+
result.set(overlay.getKey(), overlay);
|
|
9125
|
+
currentBatchId = overlay.largestBatchId;
|
|
9126
|
+
}
|
|
9127
|
+
else {
|
|
9128
|
+
control.done();
|
|
9129
|
+
}
|
|
9130
|
+
})
|
|
9131
|
+
.next(() => result);
|
|
9132
|
+
}
|
|
9133
|
+
saveOverlay(transaction, overlay) {
|
|
9134
|
+
return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
|
|
9135
|
+
}
|
|
9136
|
+
}
|
|
9137
|
+
/**
|
|
9138
|
+
* Helper to get a typed SimpleDbStore for the document overlay object store.
|
|
9139
|
+
*/
|
|
9140
|
+
function documentOverlayStore(txn) {
|
|
9141
|
+
return getStore(txn, DbDocumentOverlay.store);
|
|
9142
|
+
}
|
|
9143
|
+
|
|
9144
|
+
/**
|
|
9145
|
+
* @license
|
|
9146
|
+
* Copyright 2021 Google LLC
|
|
9147
|
+
*
|
|
9148
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9149
|
+
* you may not use this file except in compliance with the License.
|
|
9150
|
+
* You may obtain a copy of the License at
|
|
9151
|
+
*
|
|
9152
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9153
|
+
*
|
|
9154
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9155
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9156
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9157
|
+
* See the License for the specific language governing permissions and
|
|
9158
|
+
* limitations under the License.
|
|
9159
|
+
*/
|
|
9160
|
+
// Note: This code is copied from the backend. Code that is not used by
|
|
9161
|
+
// Firestore was removed.
|
|
9162
|
+
const INDEX_TYPE_NULL = 5;
|
|
9163
|
+
const INDEX_TYPE_BOOLEAN = 10;
|
|
9164
|
+
const INDEX_TYPE_NAN = 13;
|
|
9165
|
+
const INDEX_TYPE_NUMBER = 15;
|
|
9166
|
+
const INDEX_TYPE_TIMESTAMP = 20;
|
|
9167
|
+
const INDEX_TYPE_STRING = 25;
|
|
9168
|
+
const INDEX_TYPE_BLOB = 30;
|
|
9169
|
+
const INDEX_TYPE_REFERENCE = 37;
|
|
9170
|
+
const INDEX_TYPE_GEOPOINT = 45;
|
|
9171
|
+
const INDEX_TYPE_ARRAY = 50;
|
|
9172
|
+
const INDEX_TYPE_MAP = 55;
|
|
9173
|
+
const INDEX_TYPE_REFERENCE_SEGMENT = 60;
|
|
9174
|
+
// A terminator that indicates that a truncatable value was not truncated.
|
|
9175
|
+
// This must be smaller than all other type labels.
|
|
9176
|
+
const NOT_TRUNCATED = 2;
|
|
9177
|
+
/** Firestore index value writer. */
|
|
9178
|
+
class FirestoreIndexValueWriter {
|
|
9179
|
+
constructor() { }
|
|
9180
|
+
// The write methods below short-circuit writing terminators for values
|
|
9181
|
+
// containing a (terminating) truncated value.
|
|
9182
|
+
//
|
|
9183
|
+
// As an example, consider the resulting encoding for:
|
|
9184
|
+
//
|
|
9185
|
+
// ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
|
|
9186
|
+
// ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
|
|
9187
|
+
// ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
|
|
9188
|
+
/** Writes an index value. */
|
|
9189
|
+
writeIndexValue(value, encoder) {
|
|
9190
|
+
this.writeIndexValueAux(value, encoder);
|
|
9191
|
+
// Write separator to split index values
|
|
9192
|
+
// (see go/firestore-storage-format#encodings).
|
|
9193
|
+
encoder.writeInfinity();
|
|
9194
|
+
}
|
|
9195
|
+
writeIndexValueAux(indexValue, encoder) {
|
|
9196
|
+
if ('nullValue' in indexValue) {
|
|
9197
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
|
|
9198
|
+
}
|
|
9199
|
+
else if ('booleanValue' in indexValue) {
|
|
9200
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
|
|
9201
|
+
encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
|
|
9202
|
+
}
|
|
9203
|
+
else if ('integerValue' in indexValue) {
|
|
9204
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9205
|
+
encoder.writeNumber(normalizeNumber(indexValue.integerValue));
|
|
9206
|
+
}
|
|
9207
|
+
else if ('doubleValue' in indexValue) {
|
|
9208
|
+
const n = normalizeNumber(indexValue.doubleValue);
|
|
9209
|
+
if (isNaN(n)) {
|
|
9210
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
|
|
9211
|
+
}
|
|
9212
|
+
else {
|
|
9213
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9214
|
+
if (isNegativeZero(n)) {
|
|
9215
|
+
// -0.0, 0 and 0.0 are all considered the same
|
|
9216
|
+
encoder.writeNumber(0.0);
|
|
9217
|
+
}
|
|
9218
|
+
else {
|
|
9219
|
+
encoder.writeNumber(n);
|
|
9220
|
+
}
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
else if ('timestampValue' in indexValue) {
|
|
9224
|
+
const timestamp = indexValue.timestampValue;
|
|
9225
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
|
|
9226
|
+
if (typeof timestamp === 'string') {
|
|
9227
|
+
encoder.writeString(timestamp);
|
|
9228
|
+
}
|
|
9229
|
+
else {
|
|
9230
|
+
encoder.writeString(`${timestamp.seconds || ''}`);
|
|
9231
|
+
encoder.writeNumber(timestamp.nanos || 0);
|
|
9232
|
+
}
|
|
9233
|
+
}
|
|
9234
|
+
else if ('stringValue' in indexValue) {
|
|
9235
|
+
this.writeIndexString(indexValue.stringValue, encoder);
|
|
9236
|
+
this.writeTruncationMarker(encoder);
|
|
9237
|
+
}
|
|
9238
|
+
else if ('bytesValue' in indexValue) {
|
|
9239
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
|
|
9240
|
+
encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
|
|
9241
|
+
this.writeTruncationMarker(encoder);
|
|
9242
|
+
}
|
|
9243
|
+
else if ('referenceValue' in indexValue) {
|
|
9244
|
+
this.writeIndexEntityRef(indexValue.referenceValue, encoder);
|
|
9245
|
+
}
|
|
9246
|
+
else if ('geoPointValue' in indexValue) {
|
|
9247
|
+
const geoPoint = indexValue.geoPointValue;
|
|
9248
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
|
|
9249
|
+
encoder.writeNumber(geoPoint.latitude || 0);
|
|
9250
|
+
encoder.writeNumber(geoPoint.longitude || 0);
|
|
9251
|
+
}
|
|
9252
|
+
else if ('mapValue' in indexValue) {
|
|
9253
|
+
if (isMaxValue(indexValue)) {
|
|
9254
|
+
this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
|
|
9255
|
+
}
|
|
9256
|
+
else {
|
|
9257
|
+
this.writeIndexMap(indexValue.mapValue, encoder);
|
|
9258
|
+
this.writeTruncationMarker(encoder);
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
else if ('arrayValue' in indexValue) {
|
|
9262
|
+
this.writeIndexArray(indexValue.arrayValue, encoder);
|
|
9263
|
+
this.writeTruncationMarker(encoder);
|
|
9264
|
+
}
|
|
9265
|
+
else {
|
|
9266
|
+
fail();
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
writeIndexString(stringIndexValue, encoder) {
|
|
9270
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
|
|
9271
|
+
this.writeUnlabeledIndexString(stringIndexValue, encoder);
|
|
9272
|
+
}
|
|
9273
|
+
writeUnlabeledIndexString(stringIndexValue, encoder) {
|
|
9274
|
+
encoder.writeString(stringIndexValue);
|
|
9275
|
+
}
|
|
9276
|
+
writeIndexMap(mapIndexValue, encoder) {
|
|
9277
|
+
const map = mapIndexValue.fields || {};
|
|
9278
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
|
|
9279
|
+
for (const key of Object.keys(map)) {
|
|
9280
|
+
this.writeIndexString(key, encoder);
|
|
9281
|
+
this.writeIndexValueAux(map[key], encoder);
|
|
9282
|
+
}
|
|
9283
|
+
}
|
|
9284
|
+
writeIndexArray(arrayIndexValue, encoder) {
|
|
9285
|
+
const values = arrayIndexValue.values || [];
|
|
9286
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
|
|
9287
|
+
for (const element of values) {
|
|
9288
|
+
this.writeIndexValueAux(element, encoder);
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
writeIndexEntityRef(referenceValue, encoder) {
|
|
9292
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
|
|
9293
|
+
const path = DocumentKey.fromName(referenceValue).path;
|
|
9294
|
+
path.forEach(segment => {
|
|
9295
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
|
|
9296
|
+
this.writeUnlabeledIndexString(segment, encoder);
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
writeValueTypeLabel(encoder, typeOrder) {
|
|
9300
|
+
encoder.writeNumber(typeOrder);
|
|
9301
|
+
}
|
|
9302
|
+
writeTruncationMarker(encoder) {
|
|
9303
|
+
// While the SDK does not implement truncation, the truncation marker is
|
|
9304
|
+
// used to terminate all variable length values (which are strings, bytes,
|
|
9305
|
+
// references, arrays and maps).
|
|
9306
|
+
encoder.writeNumber(NOT_TRUNCATED);
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9309
|
+
FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
|
|
9310
|
+
|
|
9311
|
+
/**
|
|
9312
|
+
* @license
|
|
9313
|
+
* Copyright 2021 Google LLC
|
|
9314
|
+
*
|
|
9315
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9316
|
+
* you may not use this file except in compliance with the License.
|
|
9317
|
+
* You may obtain a copy of the License at
|
|
9318
|
+
*
|
|
9319
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9320
|
+
*
|
|
9321
|
+
* Unless required by applicable law | agreed to in writing, software
|
|
9322
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9323
|
+
* WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
|
|
9324
|
+
* See the License for the specific language governing permissions and
|
|
9325
|
+
* limitations under the License.
|
|
9326
|
+
*/
|
|
9327
|
+
/** These constants are taken from the backend. */
|
|
9328
|
+
const MIN_SURROGATE = '\uD800';
|
|
9329
|
+
const MAX_SURROGATE = '\uDBFF';
|
|
9330
|
+
const ESCAPE1 = 0x00;
|
|
9331
|
+
const NULL_BYTE = 0xff; // Combined with ESCAPE1
|
|
9332
|
+
const SEPARATOR = 0x01; // Combined with ESCAPE1
|
|
9333
|
+
const ESCAPE2 = 0xff;
|
|
9334
|
+
const INFINITY = 0xff; // Combined with ESCAPE2
|
|
9335
|
+
const FF_BYTE = 0x00; // Combined with ESCAPE2
|
|
9336
|
+
const LONG_SIZE = 64;
|
|
9337
|
+
const BYTE_SIZE = 8;
|
|
9338
|
+
/**
|
|
9339
|
+
* The default size of the buffer. This is arbitrary, but likely larger than
|
|
9340
|
+
* most index values so that less copies of the underlying buffer will be made.
|
|
9341
|
+
* For large values, a single copy will made to double the buffer length.
|
|
9342
|
+
*/
|
|
9343
|
+
const DEFAULT_BUFFER_SIZE = 1024;
|
|
9344
|
+
/** Converts a JavaScript number to a byte array (using big endian encoding). */
|
|
9345
|
+
function doubleToLongBits(value) {
|
|
9346
|
+
const dv = new DataView(new ArrayBuffer(8));
|
|
9347
|
+
dv.setFloat64(0, value, /* littleEndian= */ false);
|
|
9348
|
+
return new Uint8Array(dv.buffer);
|
|
9349
|
+
}
|
|
9350
|
+
/**
|
|
9351
|
+
* Counts the number of zeros in a byte.
|
|
9352
|
+
*
|
|
9353
|
+
* Visible for testing.
|
|
9354
|
+
*/
|
|
9355
|
+
function numberOfLeadingZerosInByte(x) {
|
|
9356
|
+
if (x === 0) {
|
|
9357
|
+
return 8;
|
|
9358
|
+
}
|
|
9359
|
+
let zeros = 0;
|
|
9360
|
+
if (x >> 4 === 0) {
|
|
9361
|
+
// Test if the first four bits are zero.
|
|
9362
|
+
zeros += 4;
|
|
9363
|
+
x = x << 4;
|
|
9364
|
+
}
|
|
9365
|
+
if (x >> 6 === 0) {
|
|
9366
|
+
// Test if the first two (or next two) bits are zero.
|
|
9367
|
+
zeros += 2;
|
|
9368
|
+
x = x << 2;
|
|
9369
|
+
}
|
|
9370
|
+
if (x >> 7 === 0) {
|
|
9371
|
+
// Test if the remaining bit is zero.
|
|
9372
|
+
zeros += 1;
|
|
9373
|
+
}
|
|
9374
|
+
return zeros;
|
|
9375
|
+
}
|
|
9376
|
+
/** Counts the number of leading zeros in the given byte array. */
|
|
9377
|
+
function numberOfLeadingZeros(bytes) {
|
|
9378
|
+
let leadingZeros = 0;
|
|
9379
|
+
for (let i = 0; i < 8; ++i) {
|
|
9380
|
+
const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
|
|
9381
|
+
leadingZeros += zeros;
|
|
9382
|
+
if (zeros !== 8) {
|
|
9383
|
+
break;
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
return leadingZeros;
|
|
9387
|
+
}
|
|
9388
|
+
/**
|
|
9389
|
+
* Returns the number of bytes required to store "value". Leading zero bytes
|
|
9390
|
+
* are skipped.
|
|
9391
|
+
*/
|
|
9392
|
+
function unsignedNumLength(value) {
|
|
9393
|
+
// This is just the number of bytes for the unsigned representation of the number.
|
|
9394
|
+
const numBits = LONG_SIZE - numberOfLeadingZeros(value);
|
|
9395
|
+
return Math.ceil(numBits / BYTE_SIZE);
|
|
9396
|
+
}
|
|
9397
|
+
/**
|
|
9398
|
+
* OrderedCodeWriter is a minimal-allocation implementation of the writing
|
|
9399
|
+
* behavior defined by the backend.
|
|
9400
|
+
*
|
|
9401
|
+
* The code is ported from its Java counterpart.
|
|
9402
|
+
*/
|
|
9403
|
+
class OrderedCodeWriter {
|
|
9404
|
+
constructor() {
|
|
9405
|
+
this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
|
|
9406
|
+
this.position = 0;
|
|
9407
|
+
}
|
|
9408
|
+
writeBytesAscending(value) {
|
|
9409
|
+
const it = value[Symbol.iterator]();
|
|
9410
|
+
let byte = it.next();
|
|
9411
|
+
while (!byte.done) {
|
|
9412
|
+
this.writeByteAscending(byte.value);
|
|
9413
|
+
byte = it.next();
|
|
9414
|
+
}
|
|
9415
|
+
this.writeSeparatorAscending();
|
|
9416
|
+
}
|
|
9417
|
+
writeBytesDescending(value) {
|
|
9418
|
+
const it = value[Symbol.iterator]();
|
|
9419
|
+
let byte = it.next();
|
|
9420
|
+
while (!byte.done) {
|
|
9421
|
+
this.writeByteDescending(byte.value);
|
|
9422
|
+
byte = it.next();
|
|
9423
|
+
}
|
|
9424
|
+
this.writeSeparatorDescending();
|
|
9425
|
+
}
|
|
9426
|
+
/** Writes utf8 bytes into this byte sequence, ascending. */
|
|
9427
|
+
writeUtf8Ascending(sequence) {
|
|
9428
|
+
for (const c of sequence) {
|
|
9429
|
+
const charCode = c.charCodeAt(0);
|
|
9430
|
+
if (charCode < 0x80) {
|
|
9431
|
+
this.writeByteAscending(charCode);
|
|
9432
|
+
}
|
|
9433
|
+
else if (charCode < 0x800) {
|
|
9434
|
+
this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
|
|
9435
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9436
|
+
}
|
|
9437
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9438
|
+
this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
|
|
9439
|
+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9440
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9441
|
+
}
|
|
9442
|
+
else {
|
|
9443
|
+
const codePoint = c.codePointAt(0);
|
|
9444
|
+
this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
|
|
9445
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9446
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9447
|
+
this.writeByteAscending(0x80 | (0x3f & codePoint));
|
|
9448
|
+
}
|
|
9449
|
+
}
|
|
9450
|
+
this.writeSeparatorAscending();
|
|
9451
|
+
}
|
|
9452
|
+
/** Writes utf8 bytes into this byte sequence, descending */
|
|
9453
|
+
writeUtf8Descending(sequence) {
|
|
9454
|
+
for (const c of sequence) {
|
|
9455
|
+
const charCode = c.charCodeAt(0);
|
|
9456
|
+
if (charCode < 0x80) {
|
|
9457
|
+
this.writeByteDescending(charCode);
|
|
9458
|
+
}
|
|
9459
|
+
else if (charCode < 0x800) {
|
|
9460
|
+
this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
|
|
9461
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9462
|
+
}
|
|
9463
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9464
|
+
this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
|
|
9465
|
+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9466
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9467
|
+
}
|
|
9468
|
+
else {
|
|
9469
|
+
const codePoint = c.codePointAt(0);
|
|
9470
|
+
this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
|
|
9471
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9472
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9473
|
+
this.writeByteDescending(0x80 | (0x3f & codePoint));
|
|
9474
|
+
}
|
|
9475
|
+
}
|
|
9476
|
+
this.writeSeparatorDescending();
|
|
9477
|
+
}
|
|
9478
|
+
writeNumberAscending(val) {
|
|
9479
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9480
|
+
// actual value in big-endian format with leading 0 bytes dropped.
|
|
9481
|
+
const value = this.toOrderedBits(val);
|
|
9482
|
+
const len = unsignedNumLength(value);
|
|
9483
|
+
this.ensureAvailable(1 + len);
|
|
9484
|
+
this.buffer[this.position++] = len & 0xff; // Write the length
|
|
9485
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9486
|
+
this.buffer[this.position++] = value[i] & 0xff;
|
|
9487
|
+
}
|
|
9488
|
+
}
|
|
9489
|
+
writeNumberDescending(val) {
|
|
9490
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9491
|
+
// inverted value in big-endian format with leading 0 bytes dropped.
|
|
9492
|
+
const value = this.toOrderedBits(val);
|
|
9493
|
+
const len = unsignedNumLength(value);
|
|
9494
|
+
this.ensureAvailable(1 + len);
|
|
9495
|
+
this.buffer[this.position++] = ~(len & 0xff); // Write the length
|
|
9496
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9497
|
+
this.buffer[this.position++] = ~(value[i] & 0xff);
|
|
9498
|
+
}
|
|
9499
|
+
}
|
|
9500
|
+
/**
|
|
9501
|
+
* Writes the "infinity" byte sequence that sorts after all other byte
|
|
9502
|
+
* sequences written in ascending order.
|
|
9503
|
+
*/
|
|
9504
|
+
writeInfinityAscending() {
|
|
9505
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9506
|
+
this.writeEscapedByteAscending(INFINITY);
|
|
9507
|
+
}
|
|
9508
|
+
/**
|
|
9509
|
+
* Writes the "infinity" byte sequence that sorts before all other byte
|
|
9510
|
+
* sequences written in descending order.
|
|
9511
|
+
*/
|
|
9512
|
+
writeInfinityDescending() {
|
|
9513
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9514
|
+
this.writeEscapedByteDescending(INFINITY);
|
|
9515
|
+
}
|
|
9516
|
+
/**
|
|
9517
|
+
* Resets the buffer such that it is the same as when it was newly
|
|
9518
|
+
* constructed.
|
|
9519
|
+
*/
|
|
9520
|
+
reset() {
|
|
9521
|
+
this.position = 0;
|
|
9522
|
+
}
|
|
9523
|
+
seed(encodedBytes) {
|
|
9524
|
+
this.ensureAvailable(encodedBytes.length);
|
|
9525
|
+
this.buffer.set(encodedBytes, this.position);
|
|
9526
|
+
this.position += encodedBytes.length;
|
|
9527
|
+
}
|
|
9528
|
+
/** Makes a copy of the encoded bytes in this buffer. */
|
|
9529
|
+
encodedBytes() {
|
|
9530
|
+
return this.buffer.slice(0, this.position);
|
|
9531
|
+
}
|
|
9532
|
+
/**
|
|
9533
|
+
* Encodes `val` into an encoding so that the order matches the IEEE 754
|
|
9534
|
+
* floating-point comparison results with the following exceptions:
|
|
9535
|
+
* -0.0 < 0.0
|
|
9536
|
+
* all non-NaN < NaN
|
|
9537
|
+
* NaN = NaN
|
|
9538
|
+
*/
|
|
9539
|
+
toOrderedBits(val) {
|
|
9540
|
+
const value = doubleToLongBits(val);
|
|
9541
|
+
// Check if the first bit is set. We use a bit mask since value[0] is
|
|
9542
|
+
// encoded as a number from 0 to 255.
|
|
9543
|
+
const isNegative = (value[0] & 0x80) !== 0;
|
|
9544
|
+
// Revert the two complement to get natural ordering
|
|
9545
|
+
value[0] ^= isNegative ? 0xff : 0x80;
|
|
9546
|
+
for (let i = 1; i < value.length; ++i) {
|
|
9547
|
+
value[i] ^= isNegative ? 0xff : 0x00;
|
|
9548
|
+
}
|
|
9549
|
+
return value;
|
|
9550
|
+
}
|
|
9551
|
+
/** Writes a single byte ascending to the buffer. */
|
|
9552
|
+
writeByteAscending(b) {
|
|
9553
|
+
const masked = b & 0xff;
|
|
9554
|
+
if (masked === ESCAPE1) {
|
|
9555
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9556
|
+
this.writeEscapedByteAscending(NULL_BYTE);
|
|
9557
|
+
}
|
|
9558
|
+
else if (masked === ESCAPE2) {
|
|
9559
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9560
|
+
this.writeEscapedByteAscending(FF_BYTE);
|
|
9561
|
+
}
|
|
9562
|
+
else {
|
|
9563
|
+
this.writeEscapedByteAscending(masked);
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
9566
|
+
/** Writes a single byte descending to the buffer. */
|
|
9567
|
+
writeByteDescending(b) {
|
|
9568
|
+
const masked = b & 0xff;
|
|
9569
|
+
if (masked === ESCAPE1) {
|
|
9570
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9571
|
+
this.writeEscapedByteDescending(NULL_BYTE);
|
|
9572
|
+
}
|
|
9573
|
+
else if (masked === ESCAPE2) {
|
|
9574
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9575
|
+
this.writeEscapedByteDescending(FF_BYTE);
|
|
9576
|
+
}
|
|
9577
|
+
else {
|
|
9578
|
+
this.writeEscapedByteDescending(b);
|
|
9579
|
+
}
|
|
9580
|
+
}
|
|
9581
|
+
writeSeparatorAscending() {
|
|
9582
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9583
|
+
this.writeEscapedByteAscending(SEPARATOR);
|
|
9584
|
+
}
|
|
9585
|
+
writeSeparatorDescending() {
|
|
9586
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9587
|
+
this.writeEscapedByteDescending(SEPARATOR);
|
|
9588
|
+
}
|
|
9589
|
+
writeEscapedByteAscending(b) {
|
|
9590
|
+
this.ensureAvailable(1);
|
|
9591
|
+
this.buffer[this.position++] = b;
|
|
9592
|
+
}
|
|
9593
|
+
writeEscapedByteDescending(b) {
|
|
9594
|
+
this.ensureAvailable(1);
|
|
9595
|
+
this.buffer[this.position++] = ~b;
|
|
9596
|
+
}
|
|
9597
|
+
ensureAvailable(bytes) {
|
|
9598
|
+
const minCapacity = bytes + this.position;
|
|
9599
|
+
if (minCapacity <= this.buffer.length) {
|
|
9600
|
+
return;
|
|
9601
|
+
}
|
|
9602
|
+
// Try doubling.
|
|
9603
|
+
let newLength = this.buffer.length * 2;
|
|
9604
|
+
// Still not big enough? Just allocate the right size.
|
|
9605
|
+
if (newLength < minCapacity) {
|
|
9606
|
+
newLength = minCapacity;
|
|
9607
|
+
}
|
|
9608
|
+
// Create the new buffer.
|
|
9609
|
+
const newBuffer = new Uint8Array(newLength);
|
|
9610
|
+
newBuffer.set(this.buffer); // copy old data
|
|
9611
|
+
this.buffer = newBuffer;
|
|
9612
|
+
}
|
|
9613
|
+
}
|
|
9614
|
+
|
|
9615
|
+
class AscendingIndexByteEncoder {
|
|
9616
|
+
constructor(orderedCode) {
|
|
9617
|
+
this.orderedCode = orderedCode;
|
|
9618
|
+
}
|
|
9619
|
+
writeBytes(value) {
|
|
9620
|
+
this.orderedCode.writeBytesAscending(value);
|
|
9621
|
+
}
|
|
9622
|
+
writeString(value) {
|
|
9623
|
+
this.orderedCode.writeUtf8Ascending(value);
|
|
9624
|
+
}
|
|
9625
|
+
writeNumber(value) {
|
|
9626
|
+
this.orderedCode.writeNumberAscending(value);
|
|
9627
|
+
}
|
|
9628
|
+
writeInfinity() {
|
|
9629
|
+
this.orderedCode.writeInfinityAscending();
|
|
9630
|
+
}
|
|
8576
9631
|
}
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
}
|
|
9632
|
+
class DescendingIndexByteEncoder {
|
|
9633
|
+
constructor(orderedCode) {
|
|
9634
|
+
this.orderedCode = orderedCode;
|
|
9635
|
+
}
|
|
9636
|
+
writeBytes(value) {
|
|
9637
|
+
this.orderedCode.writeBytesDescending(value);
|
|
9638
|
+
}
|
|
9639
|
+
writeString(value) {
|
|
9640
|
+
this.orderedCode.writeUtf8Descending(value);
|
|
9641
|
+
}
|
|
9642
|
+
writeNumber(value) {
|
|
9643
|
+
this.orderedCode.writeNumberDescending(value);
|
|
9644
|
+
}
|
|
9645
|
+
writeInfinity() {
|
|
9646
|
+
this.orderedCode.writeInfinityDescending();
|
|
9647
|
+
}
|
|
8584
9648
|
}
|
|
8585
|
-
/**
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
9649
|
+
/**
|
|
9650
|
+
* Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
|
|
9651
|
+
* actual encoding.
|
|
9652
|
+
*/
|
|
9653
|
+
class IndexByteEncoder {
|
|
9654
|
+
constructor() {
|
|
9655
|
+
this.orderedCode = new OrderedCodeWriter();
|
|
9656
|
+
this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
|
|
9657
|
+
this.descending = new DescendingIndexByteEncoder(this.orderedCode);
|
|
9658
|
+
}
|
|
9659
|
+
seed(encodedBytes) {
|
|
9660
|
+
this.orderedCode.seed(encodedBytes);
|
|
9661
|
+
}
|
|
9662
|
+
forKind(kind) {
|
|
9663
|
+
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
9664
|
+
}
|
|
9665
|
+
encodedBytes() {
|
|
9666
|
+
return this.orderedCode.encodedBytes();
|
|
9667
|
+
}
|
|
9668
|
+
reset() {
|
|
9669
|
+
this.orderedCode.reset();
|
|
9670
|
+
}
|
|
8592
9671
|
}
|
|
8593
9672
|
|
|
8594
9673
|
/**
|
|
8595
9674
|
* @license
|
|
8596
|
-
* Copyright
|
|
9675
|
+
* Copyright 2022 Google LLC
|
|
8597
9676
|
*
|
|
8598
9677
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8599
9678
|
* you may not use this file except in compliance with the License.
|
|
@@ -8607,45 +9686,38 @@ function fromBundleMetadata(metadata) {
|
|
|
8607
9686
|
* See the License for the specific language governing permissions and
|
|
8608
9687
|
* limitations under the License.
|
|
8609
9688
|
*/
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
}
|
|
8618
|
-
return undefined;
|
|
8619
|
-
});
|
|
9689
|
+
/** Represents an index entry saved by the SDK in persisted storage. */
|
|
9690
|
+
class IndexEntry {
|
|
9691
|
+
constructor(indexId, documentKey, arrayValue, directionalValue) {
|
|
9692
|
+
this.indexId = indexId;
|
|
9693
|
+
this.documentKey = documentKey;
|
|
9694
|
+
this.arrayValue = arrayValue;
|
|
9695
|
+
this.directionalValue = directionalValue;
|
|
8620
9696
|
}
|
|
8621
|
-
|
|
8622
|
-
|
|
9697
|
+
}
|
|
9698
|
+
function indexEntryComparator(left, right) {
|
|
9699
|
+
let cmp = left.indexId - right.indexId;
|
|
9700
|
+
if (cmp !== 0) {
|
|
9701
|
+
return cmp;
|
|
8623
9702
|
}
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8627
|
-
.next(query => {
|
|
8628
|
-
if (query) {
|
|
8629
|
-
return fromDbNamedQuery(query);
|
|
8630
|
-
}
|
|
8631
|
-
return undefined;
|
|
8632
|
-
});
|
|
9703
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
9704
|
+
if (cmp !== 0) {
|
|
9705
|
+
return cmp;
|
|
8633
9706
|
}
|
|
8634
|
-
|
|
8635
|
-
|
|
9707
|
+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
|
|
9708
|
+
if (cmp !== 0) {
|
|
9709
|
+
return cmp;
|
|
8636
9710
|
}
|
|
9711
|
+
return compareByteArrays(left.directionalValue, right.directionalValue);
|
|
8637
9712
|
}
|
|
8638
|
-
|
|
8639
|
-
|
|
8640
|
-
|
|
8641
|
-
|
|
8642
|
-
|
|
8643
|
-
}
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
*/
|
|
8647
|
-
function namedQueriesStore(txn) {
|
|
8648
|
-
return getStore(txn, DbNamedQuery.store);
|
|
9713
|
+
function compareByteArrays(left, right) {
|
|
9714
|
+
for (let i = 0; i < left.length && i < right.length; ++i) {
|
|
9715
|
+
const compare = left[i] - right[i];
|
|
9716
|
+
if (compare !== 0) {
|
|
9717
|
+
return compare;
|
|
9718
|
+
}
|
|
9719
|
+
}
|
|
9720
|
+
return left.length - right.length;
|
|
8649
9721
|
}
|
|
8650
9722
|
|
|
8651
9723
|
/**
|
|
@@ -8678,6 +9750,38 @@ class MemoryIndexManager {
|
|
|
8678
9750
|
getCollectionParents(transaction, collectionId) {
|
|
8679
9751
|
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
8680
9752
|
}
|
|
9753
|
+
addFieldIndex(transaction, index) {
|
|
9754
|
+
// Field indices are not supported with memory persistence.
|
|
9755
|
+
return PersistencePromise.resolve();
|
|
9756
|
+
}
|
|
9757
|
+
deleteFieldIndex(transaction, index) {
|
|
9758
|
+
// Field indices are not supported with memory persistence.
|
|
9759
|
+
return PersistencePromise.resolve();
|
|
9760
|
+
}
|
|
9761
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9762
|
+
// Field indices are not supported with memory persistence.
|
|
9763
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9764
|
+
}
|
|
9765
|
+
getFieldIndex(transaction, target) {
|
|
9766
|
+
// Field indices are not supported with memory persistence.
|
|
9767
|
+
return PersistencePromise.resolve(null);
|
|
9768
|
+
}
|
|
9769
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9770
|
+
// Field indices are not supported with memory persistence.
|
|
9771
|
+
return PersistencePromise.resolve([]);
|
|
9772
|
+
}
|
|
9773
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9774
|
+
// Field indices are not supported with memory persistence.
|
|
9775
|
+
return PersistencePromise.resolve(null);
|
|
9776
|
+
}
|
|
9777
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9778
|
+
// Field indices are not supported with memory persistence.
|
|
9779
|
+
return PersistencePromise.resolve();
|
|
9780
|
+
}
|
|
9781
|
+
updateIndexEntries(transaction, documents) {
|
|
9782
|
+
// Field indices are not supported with memory persistence.
|
|
9783
|
+
return PersistencePromise.resolve();
|
|
9784
|
+
}
|
|
8681
9785
|
}
|
|
8682
9786
|
/**
|
|
8683
9787
|
* Internal implementation of the collection-parent index exposed by MemoryIndexManager.
|
|
@@ -8727,19 +9831,25 @@ class MemoryCollectionParentIndex {
|
|
|
8727
9831
|
* See the License for the specific language governing permissions and
|
|
8728
9832
|
* limitations under the License.
|
|
8729
9833
|
*/
|
|
9834
|
+
const LOG_TAG$f = 'IndexedDbIndexManager';
|
|
8730
9835
|
/**
|
|
8731
9836
|
* A persisted implementation of IndexManager.
|
|
9837
|
+
*
|
|
9838
|
+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
|
|
9839
|
+
* data as it supports multi-tab access.
|
|
8732
9840
|
*/
|
|
8733
9841
|
class IndexedDbIndexManager {
|
|
8734
|
-
constructor() {
|
|
9842
|
+
constructor(user) {
|
|
9843
|
+
this.user = user;
|
|
8735
9844
|
/**
|
|
8736
9845
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
8737
9846
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
8738
9847
|
*
|
|
8739
|
-
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
8740
|
-
* satisfy reads.
|
|
9848
|
+
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
9849
|
+
* used to satisfy reads.
|
|
8741
9850
|
*/
|
|
8742
9851
|
this.collectionParentsCache = new MemoryCollectionParentIndex();
|
|
9852
|
+
this.uid = user.uid || '';
|
|
8743
9853
|
}
|
|
8744
9854
|
/**
|
|
8745
9855
|
* Adds a new entry to the collection parent index.
|
|
@@ -8786,6 +9896,198 @@ class IndexedDbIndexManager {
|
|
|
8786
9896
|
return parentPaths;
|
|
8787
9897
|
});
|
|
8788
9898
|
}
|
|
9899
|
+
addFieldIndex(transaction, index) {
|
|
9900
|
+
// TODO(indexing): Verify that the auto-incrementing index ID works in
|
|
9901
|
+
// Safari & Firefox.
|
|
9902
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9903
|
+
const dbIndex = toDbIndexConfiguration(index);
|
|
9904
|
+
delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
|
|
9905
|
+
return indexes.add(dbIndex).next();
|
|
9906
|
+
}
|
|
9907
|
+
deleteFieldIndex(transaction, index) {
|
|
9908
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9909
|
+
const states = indexStateStore(transaction);
|
|
9910
|
+
const entries = indexEntriesStore(transaction);
|
|
9911
|
+
return indexes
|
|
9912
|
+
.delete(index.indexId)
|
|
9913
|
+
.next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9914
|
+
/*lowerOpen=*/ false,
|
|
9915
|
+
/*upperOpen=*/ true)))
|
|
9916
|
+
.next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9917
|
+
/*lowerOpen=*/ false,
|
|
9918
|
+
/*upperOpen=*/ true)));
|
|
9919
|
+
}
|
|
9920
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9921
|
+
// TODO(indexing): Implement
|
|
9922
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9923
|
+
}
|
|
9924
|
+
getFieldIndex(transaction, target) {
|
|
9925
|
+
// TODO(indexing): Implement
|
|
9926
|
+
return PersistencePromise.resolve(null);
|
|
9927
|
+
}
|
|
9928
|
+
/**
|
|
9929
|
+
* Returns the byte encoded form of the directional values in the field index.
|
|
9930
|
+
* Returns `null` if the document does not have all fields specified in the
|
|
9931
|
+
* index.
|
|
9932
|
+
*/
|
|
9933
|
+
encodeDirectionalElements(fieldIndex, document) {
|
|
9934
|
+
const encoder = new IndexByteEncoder();
|
|
9935
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
9936
|
+
const field = document.data.field(segment.fieldPath);
|
|
9937
|
+
if (field == null) {
|
|
9938
|
+
return null;
|
|
9939
|
+
}
|
|
9940
|
+
const directionalEncoder = encoder.forKind(segment.kind);
|
|
9941
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
|
|
9942
|
+
}
|
|
9943
|
+
return encoder.encodedBytes();
|
|
9944
|
+
}
|
|
9945
|
+
/** Encodes a single value to the ascending index format. */
|
|
9946
|
+
encodeSingleElement(value) {
|
|
9947
|
+
const encoder = new IndexByteEncoder();
|
|
9948
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
9949
|
+
return encoder.encodedBytes();
|
|
9950
|
+
}
|
|
9951
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9952
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9953
|
+
const states = indexStateStore(transaction);
|
|
9954
|
+
return (collectionGroup
|
|
9955
|
+
? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9956
|
+
: indexes.loadAll()).next(indexConfigs => {
|
|
9957
|
+
const result = [];
|
|
9958
|
+
return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
|
|
9959
|
+
return states
|
|
9960
|
+
.get([indexConfig.indexId, this.uid])
|
|
9961
|
+
.next(indexState => {
|
|
9962
|
+
result.push(fromDbIndexConfiguration(indexConfig, indexState));
|
|
9963
|
+
});
|
|
9964
|
+
}).next(() => result);
|
|
9965
|
+
});
|
|
9966
|
+
}
|
|
9967
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9968
|
+
return this.getFieldIndexes(transaction).next(indexes => {
|
|
9969
|
+
if (indexes.length === 0) {
|
|
9970
|
+
return null;
|
|
9971
|
+
}
|
|
9972
|
+
indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
|
|
9973
|
+
return indexes[0].collectionGroup;
|
|
9974
|
+
});
|
|
9975
|
+
}
|
|
9976
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9977
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9978
|
+
const states = indexStateStore(transaction);
|
|
9979
|
+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
|
|
9980
|
+
.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9981
|
+
.next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
|
|
9982
|
+
}
|
|
9983
|
+
updateIndexEntries(transaction, documents) {
|
|
9984
|
+
// Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
|
|
9985
|
+
// it could be used across different IndexedDB transactions. As any cached
|
|
9986
|
+
// data might be invalidated by other multi-tab clients, we can only trust
|
|
9987
|
+
// data within a single IndexedDB transaction. We therefore add a cache
|
|
9988
|
+
// here.
|
|
9989
|
+
const memoizedIndexes = new Map();
|
|
9990
|
+
return PersistencePromise.forEach(documents, (key, doc) => {
|
|
9991
|
+
const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
|
|
9992
|
+
const fieldIndexes = memoizedCollectionIndexes
|
|
9993
|
+
? PersistencePromise.resolve(memoizedCollectionIndexes)
|
|
9994
|
+
: this.getFieldIndexes(transaction, key.collectionGroup);
|
|
9995
|
+
return fieldIndexes.next(fieldIndexes => {
|
|
9996
|
+
memoizedIndexes.set(key.collectionGroup, fieldIndexes);
|
|
9997
|
+
return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
|
|
9998
|
+
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
9999
|
+
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10000
|
+
if (!existingEntries.isEqual(newEntries)) {
|
|
10001
|
+
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10002
|
+
}
|
|
10003
|
+
return PersistencePromise.resolve();
|
|
10004
|
+
});
|
|
10005
|
+
});
|
|
10006
|
+
});
|
|
10007
|
+
});
|
|
10008
|
+
}
|
|
10009
|
+
addIndexEntry(transaction, document, indexEntry) {
|
|
10010
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10011
|
+
return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
|
|
10012
|
+
}
|
|
10013
|
+
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10014
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10015
|
+
return indexEntries.delete([
|
|
10016
|
+
indexEntry.indexId,
|
|
10017
|
+
this.uid,
|
|
10018
|
+
indexEntry.arrayValue,
|
|
10019
|
+
indexEntry.directionalValue,
|
|
10020
|
+
encodeResourcePath(document.key.path)
|
|
10021
|
+
]);
|
|
10022
|
+
}
|
|
10023
|
+
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
10024
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10025
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10026
|
+
return indexEntries
|
|
10027
|
+
.iterate({
|
|
10028
|
+
index: DbIndexEntry.documentKeyIndex,
|
|
10029
|
+
range: IDBKeyRange.only([
|
|
10030
|
+
fieldIndex.indexId,
|
|
10031
|
+
this.uid,
|
|
10032
|
+
encodeResourcePath(documentKey.path)
|
|
10033
|
+
])
|
|
10034
|
+
}, (_, entry) => {
|
|
10035
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
10036
|
+
})
|
|
10037
|
+
.next(() => results);
|
|
10038
|
+
}
|
|
10039
|
+
/** Creates the index entries for the given document. */
|
|
10040
|
+
computeIndexEntries(document, fieldIndex) {
|
|
10041
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10042
|
+
const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
|
|
10043
|
+
if (directionalValue == null) {
|
|
10044
|
+
return results;
|
|
10045
|
+
}
|
|
10046
|
+
const arraySegment = fieldIndexGetArraySegment(fieldIndex);
|
|
10047
|
+
if (arraySegment != null) {
|
|
10048
|
+
const value = document.data.field(arraySegment.fieldPath);
|
|
10049
|
+
if (isArray(value)) {
|
|
10050
|
+
for (const arrayValue of value.arrayValue.values || []) {
|
|
10051
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
|
|
10052
|
+
}
|
|
10053
|
+
}
|
|
10054
|
+
}
|
|
10055
|
+
else {
|
|
10056
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
|
|
10057
|
+
}
|
|
10058
|
+
return results;
|
|
10059
|
+
}
|
|
10060
|
+
/**
|
|
10061
|
+
* Updates the index entries for the provided document by deleting entries
|
|
10062
|
+
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10063
|
+
* entries.
|
|
10064
|
+
*/
|
|
10065
|
+
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10066
|
+
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10067
|
+
const promises = [];
|
|
10068
|
+
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10069
|
+
/* onAdd= */ entry => {
|
|
10070
|
+
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10071
|
+
},
|
|
10072
|
+
/* onRemove= */ entry => {
|
|
10073
|
+
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10074
|
+
});
|
|
10075
|
+
return PersistencePromise.waitFor(promises);
|
|
10076
|
+
}
|
|
10077
|
+
getNextSequenceNumber(transaction) {
|
|
10078
|
+
let nextSequenceNumber = 1;
|
|
10079
|
+
const states = indexStateStore(transaction);
|
|
10080
|
+
return states
|
|
10081
|
+
.iterate({
|
|
10082
|
+
index: DbIndexState.sequenceNumberIndex,
|
|
10083
|
+
reverse: true,
|
|
10084
|
+
range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
|
|
10085
|
+
}, (_, state, controller) => {
|
|
10086
|
+
controller.done();
|
|
10087
|
+
nextSequenceNumber = state.sequenceNumber + 1;
|
|
10088
|
+
})
|
|
10089
|
+
.next(() => nextSequenceNumber);
|
|
10090
|
+
}
|
|
8789
10091
|
}
|
|
8790
10092
|
/**
|
|
8791
10093
|
* Helper to get a typed SimpleDbStore for the collectionParents
|
|
@@ -8793,6 +10095,24 @@ class IndexedDbIndexManager {
|
|
|
8793
10095
|
*/
|
|
8794
10096
|
function collectionParentsStore(txn) {
|
|
8795
10097
|
return getStore(txn, DbCollectionParent.store);
|
|
10098
|
+
}
|
|
10099
|
+
/**
|
|
10100
|
+
* Helper to get a typed SimpleDbStore for the index entry object store.
|
|
10101
|
+
*/
|
|
10102
|
+
function indexEntriesStore(txn) {
|
|
10103
|
+
return getStore(txn, DbIndexEntry.store);
|
|
10104
|
+
}
|
|
10105
|
+
/**
|
|
10106
|
+
* Helper to get a typed SimpleDbStore for the index configuration object store.
|
|
10107
|
+
*/
|
|
10108
|
+
function indexConfigurationStore(txn) {
|
|
10109
|
+
return getStore(txn, DbIndexConfiguration.store);
|
|
10110
|
+
}
|
|
10111
|
+
/**
|
|
10112
|
+
* Helper to get a typed SimpleDbStore for the index state object store.
|
|
10113
|
+
*/
|
|
10114
|
+
function indexStateStore(txn) {
|
|
10115
|
+
return getStore(txn, DbIndexState.store);
|
|
8796
10116
|
}
|
|
8797
10117
|
|
|
8798
10118
|
/**
|
|
@@ -10262,12 +11582,10 @@ class RemoteDocumentChangeBuffer {
|
|
|
10262
11582
|
* `newIndexedDbRemoteDocumentCache()`.
|
|
10263
11583
|
*/
|
|
10264
11584
|
class IndexedDbRemoteDocumentCacheImpl {
|
|
10265
|
-
|
|
10266
|
-
* @param serializer - The document serializer.
|
|
10267
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10268
|
-
*/
|
|
10269
|
-
constructor(serializer, indexManager) {
|
|
11585
|
+
constructor(serializer) {
|
|
10270
11586
|
this.serializer = serializer;
|
|
11587
|
+
}
|
|
11588
|
+
setIndexManager(indexManager) {
|
|
10271
11589
|
this.indexManager = indexManager;
|
|
10272
11590
|
}
|
|
10273
11591
|
/**
|
|
@@ -10389,21 +11707,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10389
11707
|
}
|
|
10390
11708
|
});
|
|
10391
11709
|
}
|
|
10392
|
-
|
|
11710
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10393
11711
|
let results = mutableDocumentMap();
|
|
10394
|
-
const immediateChildrenPathLength =
|
|
11712
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
10395
11713
|
const iterationOptions = {};
|
|
10396
11714
|
if (sinceReadTime.isEqual(SnapshotVersion.min())) {
|
|
10397
11715
|
// Documents are ordered by key, so we can use a prefix scan to narrow
|
|
10398
11716
|
// down the documents we need to match the query against.
|
|
10399
|
-
const startKey =
|
|
11717
|
+
const startKey = collection.toArray();
|
|
10400
11718
|
iterationOptions.range = IDBKeyRange.lowerBound(startKey);
|
|
10401
11719
|
}
|
|
10402
11720
|
else {
|
|
10403
11721
|
// Execute an index-free query and filter by read time. This is safe
|
|
10404
11722
|
// since all document changes to queries that have a
|
|
10405
11723
|
// lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
|
|
10406
|
-
const collectionKey =
|
|
11724
|
+
const collectionKey = collection.toArray();
|
|
10407
11725
|
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
10408
11726
|
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
10409
11727
|
/* open= */ true);
|
|
@@ -10420,12 +11738,12 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10420
11738
|
return;
|
|
10421
11739
|
}
|
|
10422
11740
|
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
10423
|
-
if (
|
|
10424
|
-
control.done();
|
|
10425
|
-
}
|
|
10426
|
-
else if (queryMatches(query, document)) {
|
|
11741
|
+
if (collection.isPrefixOf(document.key.path)) {
|
|
10427
11742
|
results = results.insert(document.key, document);
|
|
10428
11743
|
}
|
|
11744
|
+
else {
|
|
11745
|
+
control.done();
|
|
11746
|
+
}
|
|
10429
11747
|
})
|
|
10430
11748
|
.next(() => results);
|
|
10431
11749
|
}
|
|
@@ -10463,14 +11781,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10463
11781
|
return MutableDocument.newInvalidDocument(documentKey);
|
|
10464
11782
|
}
|
|
10465
11783
|
}
|
|
10466
|
-
/**
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
* @param serializer - The document serializer.
|
|
10470
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10471
|
-
*/
|
|
10472
|
-
function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
|
|
10473
|
-
return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
|
|
11784
|
+
/** Creates a new IndexedDbRemoteDocumentCache. */
|
|
11785
|
+
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
11786
|
+
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
10474
11787
|
}
|
|
10475
11788
|
/**
|
|
10476
11789
|
* Returns the set of documents that have changed since the specified read
|
|
@@ -10637,9 +11950,6 @@ class SchemaConverter {
|
|
|
10637
11950
|
* and local feature development.
|
|
10638
11951
|
*/
|
|
10639
11952
|
createOrUpgrade(db, txn, fromVersion, toVersion) {
|
|
10640
|
-
hardAssert(fromVersion < toVersion &&
|
|
10641
|
-
fromVersion >= 0 &&
|
|
10642
|
-
toVersion <= SCHEMA_VERSION);
|
|
10643
11953
|
const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
|
|
10644
11954
|
if (fromVersion < 1 && toVersion >= 1) {
|
|
10645
11955
|
createPrimaryClientStore(db);
|
|
@@ -10706,6 +12016,16 @@ class SchemaConverter {
|
|
|
10706
12016
|
createNamedQueriesStore(db);
|
|
10707
12017
|
});
|
|
10708
12018
|
}
|
|
12019
|
+
if (fromVersion < 12 && toVersion >= 12) {
|
|
12020
|
+
p = p.next(() => {
|
|
12021
|
+
createDocumentOverlayStore(db);
|
|
12022
|
+
});
|
|
12023
|
+
}
|
|
12024
|
+
if (fromVersion < 13 && toVersion >= 13) {
|
|
12025
|
+
p = p.next(() => {
|
|
12026
|
+
createFieldIndex(db);
|
|
12027
|
+
});
|
|
12028
|
+
}
|
|
10709
12029
|
return p;
|
|
10710
12030
|
}
|
|
10711
12031
|
addDocumentGlobal(txn) {
|
|
@@ -10912,6 +12232,28 @@ function createNamedQueriesStore(db) {
|
|
|
10912
12232
|
db.createObjectStore(DbNamedQuery.store, {
|
|
10913
12233
|
keyPath: DbNamedQuery.keyPath
|
|
10914
12234
|
});
|
|
12235
|
+
}
|
|
12236
|
+
function createFieldIndex(db) {
|
|
12237
|
+
const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
|
|
12238
|
+
keyPath: DbIndexConfiguration.keyPath,
|
|
12239
|
+
autoIncrement: true
|
|
12240
|
+
});
|
|
12241
|
+
indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
|
|
12242
|
+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
|
|
12243
|
+
keyPath: DbIndexState.keyPath
|
|
12244
|
+
});
|
|
12245
|
+
indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
|
|
12246
|
+
const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
|
|
12247
|
+
keyPath: DbIndexEntry.keyPath
|
|
12248
|
+
});
|
|
12249
|
+
indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
|
|
12250
|
+
}
|
|
12251
|
+
function createDocumentOverlayStore(db) {
|
|
12252
|
+
const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
|
|
12253
|
+
keyPath: DbDocumentOverlay.keyPath
|
|
12254
|
+
});
|
|
12255
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
|
|
12256
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
|
|
10915
12257
|
}
|
|
10916
12258
|
|
|
10917
12259
|
/**
|
|
@@ -11023,7 +12365,7 @@ class IndexedDbPersistence {
|
|
|
11023
12365
|
* If set to true, forcefully obtains database access. Existing tabs will
|
|
11024
12366
|
* no longer be able to access IndexedDB.
|
|
11025
12367
|
*/
|
|
11026
|
-
forceOwningTab) {
|
|
12368
|
+
forceOwningTab, schemaVersion = SCHEMA_VERSION) {
|
|
11027
12369
|
this.allowTabSynchronization = allowTabSynchronization;
|
|
11028
12370
|
this.persistenceKey = persistenceKey;
|
|
11029
12371
|
this.clientId = clientId;
|
|
@@ -11032,6 +12374,7 @@ class IndexedDbPersistence {
|
|
|
11032
12374
|
this.document = document;
|
|
11033
12375
|
this.sequenceNumberSyncer = sequenceNumberSyncer;
|
|
11034
12376
|
this.forceOwningTab = forceOwningTab;
|
|
12377
|
+
this.schemaVersion = schemaVersion;
|
|
11035
12378
|
this.listenSequence = null;
|
|
11036
12379
|
this._started = false;
|
|
11037
12380
|
this.isPrimary = false;
|
|
@@ -11053,10 +12396,9 @@ class IndexedDbPersistence {
|
|
|
11053
12396
|
this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
|
|
11054
12397
|
this.dbName = persistenceKey + MAIN_DATABASE;
|
|
11055
12398
|
this.serializer = new LocalSerializer(serializer);
|
|
11056
|
-
this.simpleDb = new SimpleDb(this.dbName,
|
|
12399
|
+
this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
|
|
11057
12400
|
this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
|
|
11058
|
-
this.
|
|
11059
|
-
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
|
|
12401
|
+
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
|
|
11060
12402
|
this.bundleCache = new IndexedDbBundleCache();
|
|
11061
12403
|
if (this.window && this.window.localStorage) {
|
|
11062
12404
|
this.webStorage = this.window.localStorage;
|
|
@@ -11392,8 +12734,8 @@ class IndexedDbPersistence {
|
|
|
11392
12734
|
get started() {
|
|
11393
12735
|
return this._started;
|
|
11394
12736
|
}
|
|
11395
|
-
getMutationQueue(user) {
|
|
11396
|
-
return IndexedDbMutationQueue.forUser(user, this.serializer,
|
|
12737
|
+
getMutationQueue(user, indexManager) {
|
|
12738
|
+
return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
|
|
11397
12739
|
}
|
|
11398
12740
|
getTargetCache() {
|
|
11399
12741
|
return this.targetCache;
|
|
@@ -11401,8 +12743,11 @@ class IndexedDbPersistence {
|
|
|
11401
12743
|
getRemoteDocumentCache() {
|
|
11402
12744
|
return this.remoteDocumentCache;
|
|
11403
12745
|
}
|
|
11404
|
-
getIndexManager() {
|
|
11405
|
-
return
|
|
12746
|
+
getIndexManager(user) {
|
|
12747
|
+
return new IndexedDbIndexManager(user);
|
|
12748
|
+
}
|
|
12749
|
+
getDocumentOverlayCache(user) {
|
|
12750
|
+
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
11406
12751
|
}
|
|
11407
12752
|
getBundleCache() {
|
|
11408
12753
|
return this.bundleCache;
|
|
@@ -11410,11 +12755,12 @@ class IndexedDbPersistence {
|
|
|
11410
12755
|
runTransaction(action, mode, transactionOperation) {
|
|
11411
12756
|
logDebug(LOG_TAG$d, 'Starting transaction:', action);
|
|
11412
12757
|
const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
|
|
12758
|
+
const objectStores = getObjectStores(this.schemaVersion);
|
|
11413
12759
|
let persistenceTransaction;
|
|
11414
12760
|
// Do all transactions as readwrite against all object stores, since we
|
|
11415
12761
|
// are the only reader/writer.
|
|
11416
12762
|
return this.simpleDb
|
|
11417
|
-
.runTransaction(action, simpleDbMode,
|
|
12763
|
+
.runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
|
|
11418
12764
|
persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
|
|
11419
12765
|
? this.listenSequence.next()
|
|
11420
12766
|
: ListenSequence.INVALID);
|
|
@@ -11786,39 +13132,28 @@ class LocalDocumentsView {
|
|
|
11786
13132
|
getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
|
|
11787
13133
|
// Query the remote documents and overlay mutations.
|
|
11788
13134
|
let results;
|
|
11789
|
-
let mutationBatches;
|
|
11790
13135
|
return this.remoteDocumentCache
|
|
11791
|
-
.
|
|
13136
|
+
.getAll(transaction, query.path, sinceReadTime)
|
|
11792
13137
|
.next(queryResults => {
|
|
11793
13138
|
results = queryResults;
|
|
11794
13139
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
11795
13140
|
})
|
|
11796
|
-
.next(
|
|
11797
|
-
mutationBatches
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
let document = results.get(key);
|
|
11810
|
-
if (document == null) {
|
|
11811
|
-
// Create invalid document to apply mutations on top of
|
|
11812
|
-
document = MutableDocument.newInvalidDocument(key);
|
|
11813
|
-
results = results.insert(key, document);
|
|
11814
|
-
}
|
|
11815
|
-
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
11816
|
-
if (!document.isFoundDocument()) {
|
|
11817
|
-
results = results.remove(key);
|
|
11818
|
-
}
|
|
13141
|
+
.next(mutationBatches => {
|
|
13142
|
+
for (const batch of mutationBatches) {
|
|
13143
|
+
for (const mutation of batch.mutations) {
|
|
13144
|
+
const key = mutation.key;
|
|
13145
|
+
let document = results.get(key);
|
|
13146
|
+
if (document == null) {
|
|
13147
|
+
// Create invalid document to apply mutations on top of
|
|
13148
|
+
document = MutableDocument.newInvalidDocument(key);
|
|
13149
|
+
results = results.insert(key, document);
|
|
13150
|
+
}
|
|
13151
|
+
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
13152
|
+
if (!document.isFoundDocument()) {
|
|
13153
|
+
results = results.remove(key);
|
|
11819
13154
|
}
|
|
11820
13155
|
}
|
|
11821
|
-
}
|
|
13156
|
+
}
|
|
11822
13157
|
})
|
|
11823
13158
|
.next(() => {
|
|
11824
13159
|
// Finally, filter out any documents that don't actually match
|
|
@@ -11831,29 +13166,6 @@ class LocalDocumentsView {
|
|
|
11831
13166
|
return results;
|
|
11832
13167
|
});
|
|
11833
13168
|
}
|
|
11834
|
-
addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
|
|
11835
|
-
let missingBaseDocEntriesForPatching = documentKeySet();
|
|
11836
|
-
for (const batch of matchingMutationBatches) {
|
|
11837
|
-
for (const mutation of batch.mutations) {
|
|
11838
|
-
if (mutation instanceof PatchMutation &&
|
|
11839
|
-
existingDocuments.get(mutation.key) === null) {
|
|
11840
|
-
missingBaseDocEntriesForPatching =
|
|
11841
|
-
missingBaseDocEntriesForPatching.add(mutation.key);
|
|
11842
|
-
}
|
|
11843
|
-
}
|
|
11844
|
-
}
|
|
11845
|
-
let mergedDocuments = existingDocuments;
|
|
11846
|
-
return this.remoteDocumentCache
|
|
11847
|
-
.getEntries(transaction, missingBaseDocEntriesForPatching)
|
|
11848
|
-
.next(missingBaseDocs => {
|
|
11849
|
-
missingBaseDocs.forEach((key, doc) => {
|
|
11850
|
-
if (doc.isFoundDocument()) {
|
|
11851
|
-
mergedDocuments = mergedDocuments.insert(key, doc);
|
|
11852
|
-
}
|
|
11853
|
-
});
|
|
11854
|
-
return mergedDocuments;
|
|
11855
|
-
});
|
|
11856
|
-
}
|
|
11857
13169
|
}
|
|
11858
13170
|
|
|
11859
13171
|
/**
|
|
@@ -11912,11 +13224,18 @@ class LocalStoreImpl {
|
|
|
11912
13224
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
11913
13225
|
*/
|
|
11914
13226
|
this.lastDocumentChangeReadTime = SnapshotVersion.min();
|
|
11915
|
-
this.mutationQueue = persistence.getMutationQueue(initialUser);
|
|
11916
13227
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
11917
13228
|
this.targetCache = persistence.getTargetCache();
|
|
11918
|
-
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
|
|
11919
13229
|
this.bundleCache = persistence.getBundleCache();
|
|
13230
|
+
this.initializeUserComponents(initialUser);
|
|
13231
|
+
}
|
|
13232
|
+
initializeUserComponents(user) {
|
|
13233
|
+
// TODO(indexing): Add spec tests that test these components change after a
|
|
13234
|
+
// user change
|
|
13235
|
+
this.indexManager = this.persistence.getIndexManager(user);
|
|
13236
|
+
this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
|
|
13237
|
+
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
|
|
13238
|
+
this.remoteDocuments.setIndexManager(this.indexManager);
|
|
11920
13239
|
this.queryEngine.setLocalDocumentsView(this.localDocuments);
|
|
11921
13240
|
}
|
|
11922
13241
|
collectGarbage(garbageCollector) {
|
|
@@ -11938,8 +13257,6 @@ persistence, queryEngine, initialUser, serializer) {
|
|
|
11938
13257
|
// change.
|
|
11939
13258
|
async function localStoreHandleUserChange(localStore, user) {
|
|
11940
13259
|
const localStoreImpl = debugCast(localStore);
|
|
11941
|
-
let newMutationQueue = localStoreImpl.mutationQueue;
|
|
11942
|
-
let newLocalDocuments = localStoreImpl.localDocuments;
|
|
11943
13260
|
const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
|
|
11944
13261
|
// Swap out the mutation queue, grabbing the pending mutation batches
|
|
11945
13262
|
// before and after.
|
|
@@ -11948,11 +13265,8 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11948
13265
|
.getAllMutationBatches(txn)
|
|
11949
13266
|
.next(promisedOldBatches => {
|
|
11950
13267
|
oldBatches = promisedOldBatches;
|
|
11951
|
-
|
|
11952
|
-
|
|
11953
|
-
// MutationQueue.
|
|
11954
|
-
newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
|
|
11955
|
-
return newMutationQueue.getAllMutationBatches(txn);
|
|
13268
|
+
localStoreImpl.initializeUserComponents(user);
|
|
13269
|
+
return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
|
|
11956
13270
|
})
|
|
11957
13271
|
.next(newBatches => {
|
|
11958
13272
|
const removedBatchIds = [];
|
|
@@ -11973,7 +13287,7 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11973
13287
|
}
|
|
11974
13288
|
// Return the set of all (potentially) changed documents and the list
|
|
11975
13289
|
// of mutation batch IDs that were affected by change.
|
|
11976
|
-
return
|
|
13290
|
+
return localStoreImpl.localDocuments
|
|
11977
13291
|
.getDocuments(txn, changedKeys)
|
|
11978
13292
|
.next(affectedDocuments => {
|
|
11979
13293
|
return {
|
|
@@ -11984,9 +13298,6 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11984
13298
|
});
|
|
11985
13299
|
});
|
|
11986
13300
|
});
|
|
11987
|
-
localStoreImpl.mutationQueue = newMutationQueue;
|
|
11988
|
-
localStoreImpl.localDocuments = newLocalDocuments;
|
|
11989
|
-
localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
|
|
11990
13301
|
return result;
|
|
11991
13302
|
}
|
|
11992
13303
|
/* Accepts locally generated Mutations and commit them to storage. */
|
|
@@ -12699,6 +14010,122 @@ class MemoryBundleCache {
|
|
|
12699
14010
|
}
|
|
12700
14011
|
}
|
|
12701
14012
|
|
|
14013
|
+
/**
|
|
14014
|
+
* @license
|
|
14015
|
+
* Copyright 2022 Google LLC
|
|
14016
|
+
*
|
|
14017
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14018
|
+
* you may not use this file except in compliance with the License.
|
|
14019
|
+
* You may obtain a copy of the License at
|
|
14020
|
+
*
|
|
14021
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14022
|
+
*
|
|
14023
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14024
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14025
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14026
|
+
* See the License for the specific language governing permissions and
|
|
14027
|
+
* limitations under the License.
|
|
14028
|
+
*/
|
|
14029
|
+
/**
|
|
14030
|
+
* An in-memory implementation of DocumentOverlayCache.
|
|
14031
|
+
*/
|
|
14032
|
+
class MemoryDocumentOverlayCache {
|
|
14033
|
+
constructor() {
|
|
14034
|
+
// A map sorted by DocumentKey, whose value is a pair of the largest batch id
|
|
14035
|
+
// for the overlay and the overlay itself.
|
|
14036
|
+
this.overlays = new SortedMap(DocumentKey.comparator);
|
|
14037
|
+
this.overlayByBatchId = new Map();
|
|
14038
|
+
}
|
|
14039
|
+
getOverlay(transaction, key) {
|
|
14040
|
+
return PersistencePromise.resolve(this.overlays.get(key));
|
|
14041
|
+
}
|
|
14042
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
14043
|
+
overlays.forEach(mutation => {
|
|
14044
|
+
this.saveOverlay(transaction, largestBatchId, mutation);
|
|
14045
|
+
});
|
|
14046
|
+
return PersistencePromise.resolve();
|
|
14047
|
+
}
|
|
14048
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
14049
|
+
const keys = this.overlayByBatchId.get(batchId);
|
|
14050
|
+
if (keys !== undefined) {
|
|
14051
|
+
keys.forEach(key => (this.overlays = this.overlays.remove(key)));
|
|
14052
|
+
this.overlayByBatchId.delete(batchId);
|
|
14053
|
+
}
|
|
14054
|
+
return PersistencePromise.resolve();
|
|
14055
|
+
}
|
|
14056
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
14057
|
+
const result = new Map();
|
|
14058
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
14059
|
+
const prefix = new DocumentKey(collection.child(''));
|
|
14060
|
+
const iter = this.overlays.getIteratorFrom(prefix);
|
|
14061
|
+
while (iter.hasNext()) {
|
|
14062
|
+
const entry = iter.getNext();
|
|
14063
|
+
const overlay = entry.value;
|
|
14064
|
+
const key = overlay.getKey();
|
|
14065
|
+
if (!collection.isPrefixOf(key.path)) {
|
|
14066
|
+
break;
|
|
14067
|
+
}
|
|
14068
|
+
// Documents from sub-collections
|
|
14069
|
+
if (key.path.length !== immediateChildrenPathLength) {
|
|
14070
|
+
continue;
|
|
14071
|
+
}
|
|
14072
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14073
|
+
result.set(overlay.getKey(), overlay);
|
|
14074
|
+
}
|
|
14075
|
+
}
|
|
14076
|
+
return PersistencePromise.resolve(result);
|
|
14077
|
+
}
|
|
14078
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
14079
|
+
let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
|
|
14080
|
+
const iter = this.overlays.getIterator();
|
|
14081
|
+
while (iter.hasNext()) {
|
|
14082
|
+
const entry = iter.getNext();
|
|
14083
|
+
const overlay = entry.value;
|
|
14084
|
+
const key = overlay.getKey();
|
|
14085
|
+
if (key.getCollectionGroup() !== collectionGroup) {
|
|
14086
|
+
continue;
|
|
14087
|
+
}
|
|
14088
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14089
|
+
let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
|
|
14090
|
+
if (overlaysForBatchId === null) {
|
|
14091
|
+
overlaysForBatchId = new Map();
|
|
14092
|
+
batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
|
|
14093
|
+
}
|
|
14094
|
+
overlaysForBatchId.set(overlay.getKey(), overlay);
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14097
|
+
const result = new Map();
|
|
14098
|
+
const batchIter = batchIdToOverlays.getIterator();
|
|
14099
|
+
while (batchIter.hasNext()) {
|
|
14100
|
+
const entry = batchIter.getNext();
|
|
14101
|
+
const overlays = entry.value;
|
|
14102
|
+
overlays.forEach((overlay, key) => result.set(key, overlay));
|
|
14103
|
+
if (result.size >= count) {
|
|
14104
|
+
break;
|
|
14105
|
+
}
|
|
14106
|
+
}
|
|
14107
|
+
return PersistencePromise.resolve(result);
|
|
14108
|
+
}
|
|
14109
|
+
saveOverlay(transaction, largestBatchId, mutation) {
|
|
14110
|
+
if (mutation === null) {
|
|
14111
|
+
return;
|
|
14112
|
+
}
|
|
14113
|
+
// Remove the association of the overlay to its batch id.
|
|
14114
|
+
const existing = this.overlays.get(mutation.key);
|
|
14115
|
+
if (existing !== null) {
|
|
14116
|
+
this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
|
|
14117
|
+
}
|
|
14118
|
+
this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
|
|
14119
|
+
// Create the association of this overlay to the given largestBatchId.
|
|
14120
|
+
let batch = this.overlayByBatchId.get(largestBatchId);
|
|
14121
|
+
if (batch === undefined) {
|
|
14122
|
+
batch = new Set();
|
|
14123
|
+
this.overlayByBatchId.set(largestBatchId, batch);
|
|
14124
|
+
}
|
|
14125
|
+
batch.add(mutation.key);
|
|
14126
|
+
}
|
|
14127
|
+
}
|
|
14128
|
+
|
|
12702
14129
|
/**
|
|
12703
14130
|
* @license
|
|
12704
14131
|
* Copyright 2017 Google LLC
|
|
@@ -13052,14 +14479,16 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13052
14479
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13053
14480
|
* calculating the size.
|
|
13054
14481
|
*/
|
|
13055
|
-
constructor(
|
|
13056
|
-
this.indexManager = indexManager;
|
|
14482
|
+
constructor(sizer) {
|
|
13057
14483
|
this.sizer = sizer;
|
|
13058
14484
|
/** Underlying cache of documents and their read times. */
|
|
13059
14485
|
this.docs = documentEntryMap();
|
|
13060
14486
|
/** Size of all cached documents. */
|
|
13061
14487
|
this.size = 0;
|
|
13062
14488
|
}
|
|
14489
|
+
setIndexManager(indexManager) {
|
|
14490
|
+
this.indexManager = indexManager;
|
|
14491
|
+
}
|
|
13063
14492
|
/**
|
|
13064
14493
|
* Adds the supplied entry to the cache and updates the cache size as appropriate.
|
|
13065
14494
|
*
|
|
@@ -13107,21 +14536,22 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13107
14536
|
});
|
|
13108
14537
|
return PersistencePromise.resolve(results);
|
|
13109
14538
|
}
|
|
13110
|
-
|
|
14539
|
+
getAll(transaction, collectionPath, sinceReadTime) {
|
|
13111
14540
|
let results = mutableDocumentMap();
|
|
13112
14541
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
13113
14542
|
// the documents we need to match the query against.
|
|
13114
|
-
const prefix = new DocumentKey(
|
|
14543
|
+
const prefix = new DocumentKey(collectionPath.child(''));
|
|
13115
14544
|
const iterator = this.docs.getIteratorFrom(prefix);
|
|
13116
14545
|
while (iterator.hasNext()) {
|
|
13117
14546
|
const { key, value: { document } } = iterator.getNext();
|
|
13118
|
-
if (!
|
|
14547
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
13119
14548
|
break;
|
|
13120
14549
|
}
|
|
13121
|
-
if (
|
|
14550
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14551
|
+
// Exclude entries from subcollections.
|
|
13122
14552
|
continue;
|
|
13123
14553
|
}
|
|
13124
|
-
if (
|
|
14554
|
+
if (document.readTime.compareTo(sinceReadTime) <= 0) {
|
|
13125
14555
|
continue;
|
|
13126
14556
|
}
|
|
13127
14557
|
results = results.insert(document.key, document.mutableCopy());
|
|
@@ -13143,13 +14573,12 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13143
14573
|
/**
|
|
13144
14574
|
* Creates a new memory-only RemoteDocumentCache.
|
|
13145
14575
|
*
|
|
13146
|
-
* @param indexManager - A class that manages collection group indices.
|
|
13147
14576
|
* @param sizer - Used to assess the size of a document. For eager GC, this is
|
|
13148
14577
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13149
14578
|
* calculating the size.
|
|
13150
14579
|
*/
|
|
13151
|
-
function newMemoryRemoteDocumentCache(
|
|
13152
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
14580
|
+
function newMemoryRemoteDocumentCache(sizer) {
|
|
14581
|
+
return new MemoryRemoteDocumentCacheImpl(sizer);
|
|
13153
14582
|
}
|
|
13154
14583
|
/**
|
|
13155
14584
|
* Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
|
|
@@ -13343,6 +14772,7 @@ class MemoryPersistence {
|
|
|
13343
14772
|
*/
|
|
13344
14773
|
constructor(referenceDelegateFactory, serializer) {
|
|
13345
14774
|
this.mutationQueues = {};
|
|
14775
|
+
this.overlays = {};
|
|
13346
14776
|
this.listenSequence = new ListenSequence(0);
|
|
13347
14777
|
this._started = false;
|
|
13348
14778
|
this._started = true;
|
|
@@ -13350,7 +14780,7 @@ class MemoryPersistence {
|
|
|
13350
14780
|
this.targetCache = new MemoryTargetCache(this);
|
|
13351
14781
|
const sizer = (doc) => this.referenceDelegate.documentSize(doc);
|
|
13352
14782
|
this.indexManager = new MemoryIndexManager();
|
|
13353
|
-
this.remoteDocumentCache = newMemoryRemoteDocumentCache(
|
|
14783
|
+
this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
|
|
13354
14784
|
this.serializer = new LocalSerializer(serializer);
|
|
13355
14785
|
this.bundleCache = new MemoryBundleCache(this.serializer);
|
|
13356
14786
|
}
|
|
@@ -13371,13 +14801,23 @@ class MemoryPersistence {
|
|
|
13371
14801
|
setNetworkEnabled() {
|
|
13372
14802
|
// No op.
|
|
13373
14803
|
}
|
|
13374
|
-
getIndexManager() {
|
|
14804
|
+
getIndexManager(user) {
|
|
14805
|
+
// We do not currently support indices for memory persistence, so we can
|
|
14806
|
+
// return the same shared instance of the memory index manager.
|
|
13375
14807
|
return this.indexManager;
|
|
13376
14808
|
}
|
|
13377
|
-
|
|
14809
|
+
getDocumentOverlayCache(user) {
|
|
14810
|
+
let overlay = this.overlays[user.toKey()];
|
|
14811
|
+
if (!overlay) {
|
|
14812
|
+
overlay = new MemoryDocumentOverlayCache();
|
|
14813
|
+
this.overlays[user.toKey()] = overlay;
|
|
14814
|
+
}
|
|
14815
|
+
return overlay;
|
|
14816
|
+
}
|
|
14817
|
+
getMutationQueue(user, indexManager) {
|
|
13378
14818
|
let queue = this.mutationQueues[user.toKey()];
|
|
13379
14819
|
if (!queue) {
|
|
13380
|
-
queue = new MemoryMutationQueue(
|
|
14820
|
+
queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
|
|
13381
14821
|
this.mutationQueues[user.toKey()] = queue;
|
|
13382
14822
|
}
|
|
13383
14823
|
return queue;
|
|
@@ -20340,12 +21780,12 @@ async function syncEngineListen(syncEngine, query) {
|
|
|
20340
21780
|
}
|
|
20341
21781
|
else {
|
|
20342
21782
|
const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
|
|
20343
|
-
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
20344
|
-
targetId = targetData.targetId;
|
|
20345
|
-
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20346
21783
|
if (syncEngineImpl.isPrimaryClient) {
|
|
20347
21784
|
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
|
|
20348
21785
|
}
|
|
21786
|
+
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
21787
|
+
targetId = targetData.targetId;
|
|
21788
|
+
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20349
21789
|
}
|
|
20350
21790
|
return viewSnapshot;
|
|
20351
21791
|
}
|
|
@@ -24853,8 +26293,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
|
|
|
24853
26293
|
this.metadata = metadata;
|
|
24854
26294
|
}
|
|
24855
26295
|
/**
|
|
24856
|
-
*
|
|
24857
|
-
* exists. True if the document exists.
|
|
26296
|
+
* Returns whether or not the data exists. True if the document exists.
|
|
24858
26297
|
*/
|
|
24859
26298
|
exists() {
|
|
24860
26299
|
return super.exists();
|
|
@@ -25228,51 +26667,53 @@ function limitToLast(limit) {
|
|
|
25228
26667
|
return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
|
|
25229
26668
|
}
|
|
25230
26669
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
25231
|
-
constructor(type, _docOrFields,
|
|
26670
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25232
26671
|
super();
|
|
25233
26672
|
this.type = type;
|
|
25234
26673
|
this._docOrFields = _docOrFields;
|
|
25235
|
-
this.
|
|
26674
|
+
this._inclusive = _inclusive;
|
|
25236
26675
|
}
|
|
25237
26676
|
_apply(query) {
|
|
25238
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26677
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25239
26678
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
25240
26679
|
}
|
|
25241
26680
|
}
|
|
25242
26681
|
function startAt(...docOrFields) {
|
|
25243
|
-
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26682
|
+
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26683
|
+
/*inclusive=*/ true);
|
|
25244
26684
|
}
|
|
25245
26685
|
function startAfter(...docOrFields) {
|
|
25246
26686
|
return new QueryStartAtConstraint('startAfter', docOrFields,
|
|
25247
|
-
/*
|
|
26687
|
+
/*inclusive=*/ false);
|
|
25248
26688
|
}
|
|
25249
26689
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
25250
|
-
constructor(type, _docOrFields,
|
|
26690
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25251
26691
|
super();
|
|
25252
26692
|
this.type = type;
|
|
25253
26693
|
this._docOrFields = _docOrFields;
|
|
25254
|
-
this.
|
|
26694
|
+
this._inclusive = _inclusive;
|
|
25255
26695
|
}
|
|
25256
26696
|
_apply(query) {
|
|
25257
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26697
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25258
26698
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
25259
26699
|
}
|
|
25260
26700
|
}
|
|
25261
26701
|
function endBefore(...docOrFields) {
|
|
25262
|
-
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26702
|
+
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26703
|
+
/*inclusive=*/ false);
|
|
25263
26704
|
}
|
|
25264
26705
|
function endAt(...docOrFields) {
|
|
25265
|
-
return new QueryEndAtConstraint('endAt', docOrFields, /*
|
|
26706
|
+
return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
|
|
25266
26707
|
}
|
|
25267
26708
|
/** Helper function to create a bound from a document or fields */
|
|
25268
|
-
function newQueryBoundFromDocOrFields(query, methodName, docOrFields,
|
|
26709
|
+
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
25269
26710
|
docOrFields[0] = getModularInstance(docOrFields[0]);
|
|
25270
26711
|
if (docOrFields[0] instanceof DocumentSnapshot$1) {
|
|
25271
|
-
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document,
|
|
26712
|
+
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
|
|
25272
26713
|
}
|
|
25273
26714
|
else {
|
|
25274
26715
|
const reader = newUserDataReader(query.firestore);
|
|
25275
|
-
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields,
|
|
26716
|
+
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
|
|
25276
26717
|
}
|
|
25277
26718
|
}
|
|
25278
26719
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
@@ -25330,7 +26771,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
|
|
|
25330
26771
|
* of the query or if any of the fields in the order by are an uncommitted
|
|
25331
26772
|
* server timestamp.
|
|
25332
26773
|
*/
|
|
25333
|
-
function newQueryBoundFromDocument(query, databaseId, methodName, doc,
|
|
26774
|
+
function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
|
|
25334
26775
|
if (!doc) {
|
|
25335
26776
|
throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
|
|
25336
26777
|
`${methodName}().`);
|
|
@@ -25367,12 +26808,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
|
|
|
25367
26808
|
}
|
|
25368
26809
|
}
|
|
25369
26810
|
}
|
|
25370
|
-
return new Bound(components,
|
|
26811
|
+
return new Bound(components, inclusive);
|
|
25371
26812
|
}
|
|
25372
26813
|
/**
|
|
25373
26814
|
* Converts a list of field values to a `Bound` for the given query.
|
|
25374
26815
|
*/
|
|
25375
|
-
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values,
|
|
26816
|
+
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
|
|
25376
26817
|
// Use explicit order by's because it has to match the query the user made
|
|
25377
26818
|
const orderBy = query.explicitOrderBy;
|
|
25378
26819
|
if (values.length > orderBy.length) {
|
|
@@ -25409,7 +26850,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
|
|
|
25409
26850
|
components.push(wrapped);
|
|
25410
26851
|
}
|
|
25411
26852
|
}
|
|
25412
|
-
return new Bound(components,
|
|
26853
|
+
return new Bound(components, inclusive);
|
|
25413
26854
|
}
|
|
25414
26855
|
/**
|
|
25415
26856
|
* Parses the given `documentIdValue` into a `ReferenceValue`, throwing
|