@firebase/firestore 3.4.4-canary.2dd99ae2d → 3.4.4-canary.307d019c7
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/platform/node/grpc_connection.d.ts +2 -2
- package/dist/firestore/src/platform/node/load_protos.d.ts +2 -2
- 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 +1757 -295
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1737 -296
- 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/platform/node/grpc_connection.d.ts +2 -2
- package/dist/lite/firestore/src/platform/node/load_protos.d.ts +2 -2
- 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/platform/node/grpc_connection.d.ts +2 -2
- package/dist/lite/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
- 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/platform/node/grpc_connection.d.ts +2 -2
- package/dist/packages/firestore/src/platform/node/load_protos.d.ts +2 -2
- package/dist/packages/firestore/src/util/sorted_set.d.ts +18 -0
- package/dist/packages/firestore/test/unit/local/counting_query_engine.d.ts +3 -3
- package/dist/packages/firestore/test/unit/local/document_overlay_cache.test.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/persistence_test_helpers.d.ts +1 -0
- package/dist/packages/firestore/test/unit/local/test_document_overlay_cache.d.ts +38 -0
- package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +11 -0
- package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +4 -2
- package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/packages/firestore/test/util/helpers.d.ts +1 -1
- package/dist/private.d.ts +248 -14
- package/package.json +9 -9
package/dist/index.node.cjs.js
CHANGED
|
@@ -8,11 +8,32 @@ var logger = require('@firebase/logger');
|
|
|
8
8
|
var util = require('util');
|
|
9
9
|
var util$1 = require('@firebase/util');
|
|
10
10
|
var crypto = require('crypto');
|
|
11
|
-
var
|
|
11
|
+
var grpc = require('@grpc/grpc-js');
|
|
12
12
|
var protoLoader = require('@grpc/proto-loader');
|
|
13
13
|
|
|
14
|
+
function _interopNamespace(e) {
|
|
15
|
+
if (e && e.__esModule) return e;
|
|
16
|
+
var n = Object.create(null);
|
|
17
|
+
if (e) {
|
|
18
|
+
Object.keys(e).forEach(function (k) {
|
|
19
|
+
if (k !== 'default') {
|
|
20
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
21
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () { return e[k]; }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
n["default"] = e;
|
|
29
|
+
return Object.freeze(n);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
|
|
33
|
+
var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
|
|
34
|
+
|
|
14
35
|
const name = "@firebase/firestore";
|
|
15
|
-
const version$1 = "3.4.4-canary.
|
|
36
|
+
const version$1 = "3.4.4-canary.307d019c7";
|
|
16
37
|
|
|
17
38
|
/**
|
|
18
39
|
* @license
|
|
@@ -65,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
65
86
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
66
87
|
User.MOCK_USER = new User('mock-user');
|
|
67
88
|
|
|
68
|
-
const version = "9.6.
|
|
89
|
+
const version = "9.6.6-canary.307d019c7";
|
|
69
90
|
|
|
70
91
|
/**
|
|
71
92
|
* @license
|
|
@@ -670,13 +691,19 @@ class FirebaseAppCheckTokenProvider {
|
|
|
670
691
|
this.appCheckProvider = appCheckProvider;
|
|
671
692
|
this.forceRefresh = false;
|
|
672
693
|
this.appCheck = null;
|
|
694
|
+
this.latestAppCheckToken = null;
|
|
673
695
|
}
|
|
674
696
|
start(asyncQueue, changeListener) {
|
|
675
697
|
const onTokenChanged = tokenResult => {
|
|
676
698
|
if (tokenResult.error != null) {
|
|
677
699
|
logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
|
|
678
700
|
}
|
|
679
|
-
|
|
701
|
+
const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
|
|
702
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
703
|
+
logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
|
|
704
|
+
return tokenUpdated
|
|
705
|
+
? changeListener(tokenResult.token)
|
|
706
|
+
: Promise.resolve();
|
|
680
707
|
};
|
|
681
708
|
this.tokenListener = (tokenResult) => {
|
|
682
709
|
asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
|
|
@@ -711,6 +738,7 @@ class FirebaseAppCheckTokenProvider {
|
|
|
711
738
|
return this.appCheck.getToken(forceRefresh).then(tokenResult => {
|
|
712
739
|
if (tokenResult) {
|
|
713
740
|
hardAssert(typeof tokenResult.token === 'string');
|
|
741
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
714
742
|
return new AppCheckToken(tokenResult.token);
|
|
715
743
|
}
|
|
716
744
|
else {
|
|
@@ -1196,23 +1224,6 @@ function decodeResourcePath(path) {
|
|
|
1196
1224
|
}
|
|
1197
1225
|
return new ResourcePath(segments);
|
|
1198
1226
|
}
|
|
1199
|
-
|
|
1200
|
-
/**
|
|
1201
|
-
* @license
|
|
1202
|
-
* Copyright 2017 Google LLC
|
|
1203
|
-
*
|
|
1204
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1205
|
-
* you may not use this file except in compliance with the License.
|
|
1206
|
-
* You may obtain a copy of the License at
|
|
1207
|
-
*
|
|
1208
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1209
|
-
*
|
|
1210
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1211
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1212
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1213
|
-
* See the License for the specific language governing permissions and
|
|
1214
|
-
* limitations under the License.
|
|
1215
|
-
*/
|
|
1216
1227
|
/**
|
|
1217
1228
|
* Schema Version for the Web client:
|
|
1218
1229
|
* 1. Initial version including Mutation Queue, Query Cache, and Remote
|
|
@@ -1231,8 +1242,10 @@ function decodeResourcePath(path) {
|
|
|
1231
1242
|
* an auto-incrementing ID. This is required for Index-Free queries.
|
|
1232
1243
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
1233
1244
|
* 11. Add bundles and named_queries for bundle support.
|
|
1245
|
+
* 12. Add document overlays.
|
|
1246
|
+
* 13. Add indexing support.
|
|
1234
1247
|
*/
|
|
1235
|
-
const SCHEMA_VERSION =
|
|
1248
|
+
const SCHEMA_VERSION = 12;
|
|
1236
1249
|
/**
|
|
1237
1250
|
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
|
|
1238
1251
|
*/
|
|
@@ -1757,9 +1770,7 @@ class DbClientMetadata {
|
|
|
1757
1770
|
DbClientMetadata.store = 'clientMetadata';
|
|
1758
1771
|
/** Keys are automatically assigned via the clientId properties. */
|
|
1759
1772
|
DbClientMetadata.keyPath = 'clientId';
|
|
1760
|
-
/**
|
|
1761
|
-
* A object representing a bundle loaded by the SDK.
|
|
1762
|
-
*/
|
|
1773
|
+
/** An object representing a bundle loaded by the SDK. */
|
|
1763
1774
|
class DbBundle {
|
|
1764
1775
|
constructor(
|
|
1765
1776
|
/** The ID of the loaded bundle. */
|
|
@@ -1776,9 +1787,7 @@ class DbBundle {
|
|
|
1776
1787
|
/** Name of the IndexedDb object store. */
|
|
1777
1788
|
DbBundle.store = 'bundles';
|
|
1778
1789
|
DbBundle.keyPath = 'bundleId';
|
|
1779
|
-
/**
|
|
1780
|
-
* A object representing a named query loaded by the SDK via a bundle.
|
|
1781
|
-
*/
|
|
1790
|
+
/** An object representing a named query loaded by the SDK via a bundle. */
|
|
1782
1791
|
class DbNamedQuery {
|
|
1783
1792
|
constructor(
|
|
1784
1793
|
/** The name of the query. */
|
|
@@ -1795,6 +1804,158 @@ class DbNamedQuery {
|
|
|
1795
1804
|
/** Name of the IndexedDb object store. */
|
|
1796
1805
|
DbNamedQuery.store = 'namedQueries';
|
|
1797
1806
|
DbNamedQuery.keyPath = 'name';
|
|
1807
|
+
/** An object representing the global configuration for a field index. */
|
|
1808
|
+
class DbIndexConfiguration {
|
|
1809
|
+
constructor(
|
|
1810
|
+
/**
|
|
1811
|
+
* The index id for this entry. Undefined for indexes that are not yet
|
|
1812
|
+
* persisted.
|
|
1813
|
+
*/
|
|
1814
|
+
indexId,
|
|
1815
|
+
/** The collection group this index belongs to. */
|
|
1816
|
+
collectionGroup,
|
|
1817
|
+
/** The fields to index for this index. */
|
|
1818
|
+
fields) {
|
|
1819
|
+
this.indexId = indexId;
|
|
1820
|
+
this.collectionGroup = collectionGroup;
|
|
1821
|
+
this.fields = fields;
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
/** Name of the IndexedDb object store. */
|
|
1825
|
+
DbIndexConfiguration.store = 'indexConfiguration';
|
|
1826
|
+
DbIndexConfiguration.keyPath = 'indexId';
|
|
1827
|
+
/**
|
|
1828
|
+
* An index that provides access to the index configurations by collection
|
|
1829
|
+
* group.
|
|
1830
|
+
*
|
|
1831
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1832
|
+
* not possible here as the Web client supports concurrent access to
|
|
1833
|
+
* persistence via multi-tab.
|
|
1834
|
+
*/
|
|
1835
|
+
DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
|
|
1836
|
+
DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
|
|
1837
|
+
/**
|
|
1838
|
+
* An object describing how up-to-date the index backfill is for each user and
|
|
1839
|
+
* index.
|
|
1840
|
+
*/
|
|
1841
|
+
class DbIndexState {
|
|
1842
|
+
constructor(
|
|
1843
|
+
/** The index id for this entry. */
|
|
1844
|
+
indexId,
|
|
1845
|
+
/** The user id for this entry. */
|
|
1846
|
+
uid,
|
|
1847
|
+
/**
|
|
1848
|
+
* A number that indicates when the index was last updated (relative to
|
|
1849
|
+
* other indexes).
|
|
1850
|
+
*/
|
|
1851
|
+
sequenceNumber,
|
|
1852
|
+
/**
|
|
1853
|
+
* The latest read time that has been indexed by Firestore for this field
|
|
1854
|
+
* index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
|
|
1855
|
+
*/
|
|
1856
|
+
readTime,
|
|
1857
|
+
/**
|
|
1858
|
+
* The last document that has been indexed for this field index. Empty if
|
|
1859
|
+
* no documents have been indexed.
|
|
1860
|
+
*/
|
|
1861
|
+
documentKey,
|
|
1862
|
+
/**
|
|
1863
|
+
* The largest mutation batch id that has been processed for this index. -1
|
|
1864
|
+
* if no mutations have been indexed.
|
|
1865
|
+
*/
|
|
1866
|
+
largestBatchId) {
|
|
1867
|
+
this.indexId = indexId;
|
|
1868
|
+
this.uid = uid;
|
|
1869
|
+
this.sequenceNumber = sequenceNumber;
|
|
1870
|
+
this.readTime = readTime;
|
|
1871
|
+
this.documentKey = documentKey;
|
|
1872
|
+
this.largestBatchId = largestBatchId;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
/** Name of the IndexedDb object store. */
|
|
1876
|
+
DbIndexState.store = 'indexState';
|
|
1877
|
+
DbIndexState.keyPath = ['indexId', 'uid'];
|
|
1878
|
+
/**
|
|
1879
|
+
* An index that provides access to documents in a collection sorted by last
|
|
1880
|
+
* update time. Used by the backfiller.
|
|
1881
|
+
*
|
|
1882
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1883
|
+
* not possible here as the Web client supports concurrent access to
|
|
1884
|
+
* persistence via multi-tab.
|
|
1885
|
+
*/
|
|
1886
|
+
DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
|
|
1887
|
+
DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
|
|
1888
|
+
/** An object that stores the encoded entries for all documents and fields. */
|
|
1889
|
+
class DbIndexEntry {
|
|
1890
|
+
constructor(
|
|
1891
|
+
/** The index id for this entry. */
|
|
1892
|
+
indexId,
|
|
1893
|
+
/** The user id for this entry. */
|
|
1894
|
+
uid,
|
|
1895
|
+
/** The encoded array index value for this entry. */
|
|
1896
|
+
arrayValue,
|
|
1897
|
+
/** The encoded directional value for equality and inequality filters. */
|
|
1898
|
+
directionalValue,
|
|
1899
|
+
/** The document key this entry points to. */
|
|
1900
|
+
documentKey) {
|
|
1901
|
+
this.indexId = indexId;
|
|
1902
|
+
this.uid = uid;
|
|
1903
|
+
this.arrayValue = arrayValue;
|
|
1904
|
+
this.directionalValue = directionalValue;
|
|
1905
|
+
this.documentKey = documentKey;
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
/** Name of the IndexedDb object store. */
|
|
1909
|
+
DbIndexEntry.store = 'indexEntries';
|
|
1910
|
+
DbIndexEntry.keyPath = [
|
|
1911
|
+
'indexId',
|
|
1912
|
+
'uid',
|
|
1913
|
+
'arrayValue',
|
|
1914
|
+
'directionalValue',
|
|
1915
|
+
'documentKey'
|
|
1916
|
+
];
|
|
1917
|
+
DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
|
|
1918
|
+
DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
|
|
1919
|
+
/**
|
|
1920
|
+
* An object representing a document overlay.
|
|
1921
|
+
*/
|
|
1922
|
+
class DbDocumentOverlay {
|
|
1923
|
+
constructor(
|
|
1924
|
+
/** The user ID to whom this overlay belongs. */
|
|
1925
|
+
userId,
|
|
1926
|
+
/** The path to the collection that contains the document. */
|
|
1927
|
+
collectionPath,
|
|
1928
|
+
/** The ID (key) of the document within the collection. */
|
|
1929
|
+
documentId,
|
|
1930
|
+
/** The collection group to which the document belongs. */
|
|
1931
|
+
collectionGroup,
|
|
1932
|
+
/** The largest batch ID that's been applied for this overlay. */
|
|
1933
|
+
largestBatchId,
|
|
1934
|
+
/** The overlay mutation. */
|
|
1935
|
+
overlayMutation) {
|
|
1936
|
+
this.userId = userId;
|
|
1937
|
+
this.collectionPath = collectionPath;
|
|
1938
|
+
this.documentId = documentId;
|
|
1939
|
+
this.collectionGroup = collectionGroup;
|
|
1940
|
+
this.largestBatchId = largestBatchId;
|
|
1941
|
+
this.overlayMutation = overlayMutation;
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
/** Name of the IndexedDb object store. */
|
|
1945
|
+
DbDocumentOverlay.store = 'documentOverlays';
|
|
1946
|
+
DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
|
|
1947
|
+
DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
|
|
1948
|
+
DbDocumentOverlay.collectionPathOverlayIndexPath = [
|
|
1949
|
+
'userId',
|
|
1950
|
+
'collectionPath',
|
|
1951
|
+
'largestBatchId'
|
|
1952
|
+
];
|
|
1953
|
+
DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
|
|
1954
|
+
DbDocumentOverlay.collectionGroupOverlayIndexPath = [
|
|
1955
|
+
'userId',
|
|
1956
|
+
'collectionGroup',
|
|
1957
|
+
'largestBatchId'
|
|
1958
|
+
];
|
|
1798
1959
|
// Visible for testing
|
|
1799
1960
|
const V1_STORES = [
|
|
1800
1961
|
DbMutationQueue.store,
|
|
@@ -1809,7 +1970,6 @@ const V1_STORES = [
|
|
|
1809
1970
|
// V2 is no longer usable (see comment at top of file)
|
|
1810
1971
|
// Visible for testing
|
|
1811
1972
|
const V3_STORES = V1_STORES;
|
|
1812
|
-
// Visible for testing
|
|
1813
1973
|
// Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
|
|
1814
1974
|
const V4_STORES = [...V3_STORES, DbClientMetadata.store];
|
|
1815
1975
|
// V5 does not change the set of stores.
|
|
@@ -1819,12 +1979,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
|
|
|
1819
1979
|
// V9 does not change the set of stores.
|
|
1820
1980
|
// V10 does not change the set of stores.
|
|
1821
1981
|
const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1982
|
+
const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
|
|
1983
|
+
const V13_STORES = [
|
|
1984
|
+
...V12_STORES,
|
|
1985
|
+
DbIndexConfiguration.store,
|
|
1986
|
+
DbIndexState.store,
|
|
1987
|
+
DbIndexEntry.store
|
|
1988
|
+
];
|
|
1989
|
+
/** Returns the object stores for the provided schema. */
|
|
1990
|
+
function getObjectStores(schemaVersion) {
|
|
1991
|
+
if (schemaVersion === 13) {
|
|
1992
|
+
return V13_STORES;
|
|
1993
|
+
}
|
|
1994
|
+
else if (schemaVersion === 12) {
|
|
1995
|
+
return V12_STORES;
|
|
1996
|
+
}
|
|
1997
|
+
else if (schemaVersion === 11) {
|
|
1998
|
+
return V11_STORES;
|
|
1999
|
+
}
|
|
2000
|
+
else {
|
|
2001
|
+
fail();
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
1828
2004
|
|
|
1829
2005
|
/**
|
|
1830
2006
|
* @license
|
|
@@ -2065,7 +2241,7 @@ class PersistencePromise {
|
|
|
2065
2241
|
*/
|
|
2066
2242
|
// References to `window` are guarded by SimpleDb.isAvailable()
|
|
2067
2243
|
/* eslint-disable no-restricted-globals */
|
|
2068
|
-
const LOG_TAG$
|
|
2244
|
+
const LOG_TAG$h = 'SimpleDb';
|
|
2069
2245
|
/**
|
|
2070
2246
|
* The maximum number of retry attempts for an IndexedDb transaction that fails
|
|
2071
2247
|
* with a DOMException.
|
|
@@ -2116,11 +2292,20 @@ class SimpleDbTransaction {
|
|
|
2116
2292
|
this.completionDeferred.reject(error);
|
|
2117
2293
|
}
|
|
2118
2294
|
if (!this.aborted) {
|
|
2119
|
-
logDebug(LOG_TAG$
|
|
2295
|
+
logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
|
|
2120
2296
|
this.aborted = true;
|
|
2121
2297
|
this.transaction.abort();
|
|
2122
2298
|
}
|
|
2123
2299
|
}
|
|
2300
|
+
maybeCommit() {
|
|
2301
|
+
// If the browser supports V3 IndexedDB, we invoke commit() explicitly to
|
|
2302
|
+
// speed up index DB processing if the event loop remains blocks.
|
|
2303
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2304
|
+
const maybeV3IndexedDb = this.transaction;
|
|
2305
|
+
if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
|
|
2306
|
+
maybeV3IndexedDb.commit();
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2124
2309
|
/**
|
|
2125
2310
|
* Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
|
|
2126
2311
|
* operations performed on the SimpleDbStore happen within the context of this
|
|
@@ -2170,7 +2355,7 @@ class SimpleDb {
|
|
|
2170
2355
|
}
|
|
2171
2356
|
/** Deletes the specified database. */
|
|
2172
2357
|
static delete(name) {
|
|
2173
|
-
logDebug(LOG_TAG$
|
|
2358
|
+
logDebug(LOG_TAG$h, 'Removing database:', name);
|
|
2174
2359
|
return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
|
|
2175
2360
|
}
|
|
2176
2361
|
/** Returns true if IndexedDB is available in the current environment. */
|
|
@@ -2249,7 +2434,7 @@ class SimpleDb {
|
|
|
2249
2434
|
*/
|
|
2250
2435
|
async ensureDb(action) {
|
|
2251
2436
|
if (!this.db) {
|
|
2252
|
-
logDebug(LOG_TAG$
|
|
2437
|
+
logDebug(LOG_TAG$h, 'Opening database:', this.name);
|
|
2253
2438
|
this.db = await new Promise((resolve, reject) => {
|
|
2254
2439
|
// TODO(mikelehen): Investigate browser compatibility.
|
|
2255
2440
|
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
|
|
@@ -2285,12 +2470,12 @@ class SimpleDb {
|
|
|
2285
2470
|
}
|
|
2286
2471
|
};
|
|
2287
2472
|
request.onupgradeneeded = (event) => {
|
|
2288
|
-
logDebug(LOG_TAG$
|
|
2473
|
+
logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
|
|
2289
2474
|
const db = event.target.result;
|
|
2290
2475
|
this.schemaConverter
|
|
2291
2476
|
.createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
|
|
2292
2477
|
.next(() => {
|
|
2293
|
-
logDebug(LOG_TAG$
|
|
2478
|
+
logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
|
|
2294
2479
|
});
|
|
2295
2480
|
};
|
|
2296
2481
|
});
|
|
@@ -2317,6 +2502,10 @@ class SimpleDb {
|
|
|
2317
2502
|
this.db = await this.ensureDb(action);
|
|
2318
2503
|
const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
|
|
2319
2504
|
const transactionFnResult = transactionFn(transaction)
|
|
2505
|
+
.next(result => {
|
|
2506
|
+
transaction.maybeCommit();
|
|
2507
|
+
return result;
|
|
2508
|
+
})
|
|
2320
2509
|
.catch(error => {
|
|
2321
2510
|
// Abort the transaction if there was an error.
|
|
2322
2511
|
transaction.abort(error);
|
|
@@ -2344,7 +2533,7 @@ class SimpleDb {
|
|
|
2344
2533
|
// exception is wrapped in a generic error by our async/await handling.
|
|
2345
2534
|
const retryable = error.name !== 'FirebaseError' &&
|
|
2346
2535
|
attemptNumber < TRANSACTION_RETRY_COUNT;
|
|
2347
|
-
logDebug(LOG_TAG$
|
|
2536
|
+
logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
|
|
2348
2537
|
this.close();
|
|
2349
2538
|
if (!retryable) {
|
|
2350
2539
|
return Promise.reject(error);
|
|
@@ -2431,11 +2620,11 @@ class SimpleDbStore {
|
|
|
2431
2620
|
put(keyOrValue, value) {
|
|
2432
2621
|
let request;
|
|
2433
2622
|
if (value !== undefined) {
|
|
2434
|
-
logDebug(LOG_TAG$
|
|
2623
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
|
|
2435
2624
|
request = this.store.put(value, keyOrValue);
|
|
2436
2625
|
}
|
|
2437
2626
|
else {
|
|
2438
|
-
logDebug(LOG_TAG$
|
|
2627
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
|
|
2439
2628
|
request = this.store.put(keyOrValue);
|
|
2440
2629
|
}
|
|
2441
2630
|
return wrapRequest(request);
|
|
@@ -2448,7 +2637,7 @@ class SimpleDbStore {
|
|
|
2448
2637
|
* @returns The key of the value to add.
|
|
2449
2638
|
*/
|
|
2450
2639
|
add(value) {
|
|
2451
|
-
logDebug(LOG_TAG$
|
|
2640
|
+
logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
|
|
2452
2641
|
const request = this.store.add(value);
|
|
2453
2642
|
return wrapRequest(request);
|
|
2454
2643
|
}
|
|
@@ -2468,12 +2657,12 @@ class SimpleDbStore {
|
|
|
2468
2657
|
if (result === undefined) {
|
|
2469
2658
|
result = null;
|
|
2470
2659
|
}
|
|
2471
|
-
logDebug(LOG_TAG$
|
|
2660
|
+
logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
|
|
2472
2661
|
return result;
|
|
2473
2662
|
});
|
|
2474
2663
|
}
|
|
2475
2664
|
delete(key) {
|
|
2476
|
-
logDebug(LOG_TAG$
|
|
2665
|
+
logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
|
|
2477
2666
|
const request = this.store.delete(key);
|
|
2478
2667
|
return wrapRequest(request);
|
|
2479
2668
|
}
|
|
@@ -2484,21 +2673,37 @@ class SimpleDbStore {
|
|
|
2484
2673
|
* Returns the number of rows in the store.
|
|
2485
2674
|
*/
|
|
2486
2675
|
count() {
|
|
2487
|
-
logDebug(LOG_TAG$
|
|
2676
|
+
logDebug(LOG_TAG$h, 'COUNT', this.store.name);
|
|
2488
2677
|
const request = this.store.count();
|
|
2489
2678
|
return wrapRequest(request);
|
|
2490
2679
|
}
|
|
2491
2680
|
loadAll(indexOrRange, range) {
|
|
2492
|
-
const
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
return
|
|
2498
|
-
|
|
2681
|
+
const iterateOptions = this.options(indexOrRange, range);
|
|
2682
|
+
// Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
|
|
2683
|
+
// 20% faster. Unfortunately, getAll() does not support custom indices.
|
|
2684
|
+
if (!iterateOptions.index && typeof this.store.getAll === 'function') {
|
|
2685
|
+
const request = this.store.getAll(iterateOptions.range);
|
|
2686
|
+
return new PersistencePromise((resolve, reject) => {
|
|
2687
|
+
request.onerror = (event) => {
|
|
2688
|
+
reject(event.target.error);
|
|
2689
|
+
};
|
|
2690
|
+
request.onsuccess = (event) => {
|
|
2691
|
+
resolve(event.target.result);
|
|
2692
|
+
};
|
|
2693
|
+
});
|
|
2694
|
+
}
|
|
2695
|
+
else {
|
|
2696
|
+
const cursor = this.cursor(iterateOptions);
|
|
2697
|
+
const results = [];
|
|
2698
|
+
return this.iterateCursor(cursor, (key, value) => {
|
|
2699
|
+
results.push(value);
|
|
2700
|
+
}).next(() => {
|
|
2701
|
+
return results;
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2499
2704
|
}
|
|
2500
2705
|
deleteAll(indexOrRange, range) {
|
|
2501
|
-
logDebug(LOG_TAG$
|
|
2706
|
+
logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
|
|
2502
2707
|
const options = this.options(indexOrRange, range);
|
|
2503
2708
|
options.keysOnly = false;
|
|
2504
2709
|
const cursor = this.cursor(options);
|
|
@@ -3521,11 +3726,22 @@ class DocumentKey {
|
|
|
3521
3726
|
static empty() {
|
|
3522
3727
|
return new DocumentKey(ResourcePath.emptyPath());
|
|
3523
3728
|
}
|
|
3729
|
+
get collectionGroup() {
|
|
3730
|
+
return this.path.popLast().lastSegment();
|
|
3731
|
+
}
|
|
3524
3732
|
/** Returns true if the document is in the specified collectionId. */
|
|
3525
3733
|
hasCollectionId(collectionId) {
|
|
3526
3734
|
return (this.path.length >= 2 &&
|
|
3527
3735
|
this.path.get(this.path.length - 2) === collectionId);
|
|
3528
3736
|
}
|
|
3737
|
+
/** Returns the collection group (i.e. the name of the parent collection) for this key. */
|
|
3738
|
+
getCollectionGroup() {
|
|
3739
|
+
return this.path.get(this.path.length - 2);
|
|
3740
|
+
}
|
|
3741
|
+
/** Returns the fully qualified path to the parent collection. */
|
|
3742
|
+
getCollectionPath() {
|
|
3743
|
+
return this.path.popLast();
|
|
3744
|
+
}
|
|
3529
3745
|
isEqual(other) {
|
|
3530
3746
|
return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
|
|
3531
3747
|
}
|
|
@@ -3565,6 +3781,13 @@ class DocumentKey {
|
|
|
3565
3781
|
* See the License for the specific language governing permissions and
|
|
3566
3782
|
* limitations under the License.
|
|
3567
3783
|
*/
|
|
3784
|
+
const MAX_VALUE = {
|
|
3785
|
+
mapValue: {
|
|
3786
|
+
fields: {
|
|
3787
|
+
'__type__': { stringValue: '__max___' }
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
};
|
|
3568
3791
|
/** Extracts the backend's type order for the provided value. */
|
|
3569
3792
|
function typeOrder(value) {
|
|
3570
3793
|
if ('nullValue' in value) {
|
|
@@ -3974,6 +4197,10 @@ function deepClone(source) {
|
|
|
3974
4197
|
else {
|
|
3975
4198
|
return Object.assign({}, source);
|
|
3976
4199
|
}
|
|
4200
|
+
}
|
|
4201
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
4202
|
+
function isMaxValue(value) {
|
|
4203
|
+
return valueEquals(value, MAX_VALUE);
|
|
3977
4204
|
}
|
|
3978
4205
|
|
|
3979
4206
|
/**
|
|
@@ -4349,17 +4576,25 @@ class FieldIndex {
|
|
|
4349
4576
|
/** The collection ID this index applies to. */
|
|
4350
4577
|
collectionGroup,
|
|
4351
4578
|
/** The field segments for this index. */
|
|
4352
|
-
|
|
4579
|
+
fields,
|
|
4353
4580
|
/** Shows how up-to-date the index is for the current user. */
|
|
4354
4581
|
indexState) {
|
|
4355
4582
|
this.indexId = indexId;
|
|
4356
4583
|
this.collectionGroup = collectionGroup;
|
|
4357
|
-
this.
|
|
4584
|
+
this.fields = fields;
|
|
4358
4585
|
this.indexState = indexState;
|
|
4359
4586
|
}
|
|
4360
4587
|
}
|
|
4361
4588
|
/** An ID for an index that has not yet been added to persistence. */
|
|
4362
4589
|
FieldIndex.UNKNOWN_ID = -1;
|
|
4590
|
+
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
4591
|
+
function fieldIndexGetArraySegment(fieldIndex) {
|
|
4592
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
4593
|
+
}
|
|
4594
|
+
/** Returns all directional (ascending/descending) segments for this index. */
|
|
4595
|
+
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4596
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4597
|
+
}
|
|
4363
4598
|
/** An index component consisting of field path and index type. */
|
|
4364
4599
|
class IndexSegment {
|
|
4365
4600
|
constructor(
|
|
@@ -4464,27 +4699,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
|
|
|
4464
4699
|
function canonifyTarget(target) {
|
|
4465
4700
|
const targetImpl = debugCast(target);
|
|
4466
4701
|
if (targetImpl.memoizedCanonicalId === null) {
|
|
4467
|
-
let
|
|
4702
|
+
let str = targetImpl.path.canonicalString();
|
|
4468
4703
|
if (targetImpl.collectionGroup !== null) {
|
|
4469
|
-
|
|
4704
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
4470
4705
|
}
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4706
|
+
str += '|f:';
|
|
4707
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
4708
|
+
str += '|ob:';
|
|
4709
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
4475
4710
|
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
4476
|
-
|
|
4477
|
-
|
|
4711
|
+
str += '|l:';
|
|
4712
|
+
str += targetImpl.limit;
|
|
4478
4713
|
}
|
|
4479
4714
|
if (targetImpl.startAt) {
|
|
4480
|
-
|
|
4481
|
-
|
|
4715
|
+
str += '|lb:';
|
|
4716
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
4717
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4482
4718
|
}
|
|
4483
4719
|
if (targetImpl.endAt) {
|
|
4484
|
-
|
|
4485
|
-
|
|
4720
|
+
str += '|ub:';
|
|
4721
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
4722
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4486
4723
|
}
|
|
4487
|
-
targetImpl.memoizedCanonicalId =
|
|
4724
|
+
targetImpl.memoizedCanonicalId = str;
|
|
4488
4725
|
}
|
|
4489
4726
|
return targetImpl.memoizedCanonicalId;
|
|
4490
4727
|
}
|
|
@@ -4507,10 +4744,14 @@ function stringifyTarget(target) {
|
|
|
4507
4744
|
.join(', ')}]`;
|
|
4508
4745
|
}
|
|
4509
4746
|
if (target.startAt) {
|
|
4510
|
-
str += ', startAt: '
|
|
4747
|
+
str += ', startAt: ';
|
|
4748
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
4749
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4511
4750
|
}
|
|
4512
4751
|
if (target.endAt) {
|
|
4513
|
-
str += ', endAt: '
|
|
4752
|
+
str += ', endAt: ';
|
|
4753
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
4754
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4514
4755
|
}
|
|
4515
4756
|
return `Target(${str})`;
|
|
4516
4757
|
}
|
|
@@ -4733,7 +4974,6 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4733
4974
|
return other.arrayValue.values.some(val => arrayValueContains(this.value.arrayValue, val));
|
|
4734
4975
|
}
|
|
4735
4976
|
}
|
|
4736
|
-
// TODO(indexing): Change Bound.before to "inclusive"
|
|
4737
4977
|
/**
|
|
4738
4978
|
* Represents a bound of a query.
|
|
4739
4979
|
*
|
|
@@ -4749,17 +4989,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4749
4989
|
* just after the provided values.
|
|
4750
4990
|
*/
|
|
4751
4991
|
class Bound {
|
|
4752
|
-
constructor(position,
|
|
4992
|
+
constructor(position, inclusive) {
|
|
4753
4993
|
this.position = position;
|
|
4754
|
-
this.
|
|
4994
|
+
this.inclusive = inclusive;
|
|
4755
4995
|
}
|
|
4756
4996
|
}
|
|
4757
|
-
function canonifyBound(bound) {
|
|
4758
|
-
// TODO(b/29183165): Make this collision robust.
|
|
4759
|
-
return `${bound.before ? 'b' : 'a'}:${bound.position
|
|
4760
|
-
.map(p => canonicalId(p))
|
|
4761
|
-
.join(',')}`;
|
|
4762
|
-
}
|
|
4763
4997
|
/**
|
|
4764
4998
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
4765
4999
|
*/
|
|
@@ -4779,11 +5013,7 @@ function stringifyOrderBy(orderBy) {
|
|
|
4779
5013
|
function orderByEquals(left, right) {
|
|
4780
5014
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
4781
5015
|
}
|
|
4782
|
-
|
|
4783
|
-
* Returns true if a document sorts before a bound using the provided sort
|
|
4784
|
-
* order.
|
|
4785
|
-
*/
|
|
4786
|
-
function sortsBeforeDocument(bound, orderBy, doc) {
|
|
5016
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
4787
5017
|
let comparison = 0;
|
|
4788
5018
|
for (let i = 0; i < bound.position.length; i++) {
|
|
4789
5019
|
const orderByComponent = orderBy[i];
|
|
@@ -4802,7 +5032,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
|
|
|
4802
5032
|
break;
|
|
4803
5033
|
}
|
|
4804
5034
|
}
|
|
4805
|
-
return
|
|
5035
|
+
return comparison;
|
|
5036
|
+
}
|
|
5037
|
+
/**
|
|
5038
|
+
* Returns true if a document sorts after a bound using the provided sort
|
|
5039
|
+
* order.
|
|
5040
|
+
*/
|
|
5041
|
+
function boundSortsAfterDocument(bound, orderBy, doc) {
|
|
5042
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5043
|
+
return bound.inclusive ? comparison >= 0 : comparison > 0;
|
|
5044
|
+
}
|
|
5045
|
+
/**
|
|
5046
|
+
* Returns true if a document sorts before a bound using the provided sort
|
|
5047
|
+
* order.
|
|
5048
|
+
*/
|
|
5049
|
+
function boundSortsBeforeDocument(bound, orderBy, doc) {
|
|
5050
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5051
|
+
return bound.inclusive ? comparison <= 0 : comparison < 0;
|
|
4806
5052
|
}
|
|
4807
5053
|
function boundEquals(left, right) {
|
|
4808
5054
|
if (left === null) {
|
|
@@ -4811,7 +5057,7 @@ function boundEquals(left, right) {
|
|
|
4811
5057
|
else if (right === null) {
|
|
4812
5058
|
return false;
|
|
4813
5059
|
}
|
|
4814
|
-
if (left.
|
|
5060
|
+
if (left.inclusive !== right.inclusive ||
|
|
4815
5061
|
left.position.length !== right.position.length) {
|
|
4816
5062
|
return false;
|
|
4817
5063
|
}
|
|
@@ -5015,10 +5261,10 @@ function queryToTarget(query) {
|
|
|
5015
5261
|
}
|
|
5016
5262
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
5017
5263
|
const startAt = queryImpl.endAt
|
|
5018
|
-
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.
|
|
5264
|
+
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
|
|
5019
5265
|
: null;
|
|
5020
5266
|
const endAt = queryImpl.startAt
|
|
5021
|
-
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.
|
|
5267
|
+
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
|
|
5022
5268
|
: null;
|
|
5023
5269
|
// Now return as a LimitType.First query.
|
|
5024
5270
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
|
|
@@ -5106,11 +5352,11 @@ function queryMatchesFilters(query, doc) {
|
|
|
5106
5352
|
/** Makes sure a document is within the bounds, if provided. */
|
|
5107
5353
|
function queryMatchesBounds(query, doc) {
|
|
5108
5354
|
if (query.startAt &&
|
|
5109
|
-
!
|
|
5355
|
+
!boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
|
|
5110
5356
|
return false;
|
|
5111
5357
|
}
|
|
5112
5358
|
if (query.endAt &&
|
|
5113
|
-
|
|
5359
|
+
!boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
|
|
5114
5360
|
return false;
|
|
5115
5361
|
}
|
|
5116
5362
|
return true;
|
|
@@ -5765,6 +6011,72 @@ class SortedSetIterator {
|
|
|
5765
6011
|
hasNext() {
|
|
5766
6012
|
return this.iter.hasNext();
|
|
5767
6013
|
}
|
|
6014
|
+
}
|
|
6015
|
+
/**
|
|
6016
|
+
* Compares two sorted sets for equality using their natural ordering. The
|
|
6017
|
+
* method computes the intersection and invokes `onAdd` for every element that
|
|
6018
|
+
* is in `after` but not `before`. `onRemove` is invoked for every element in
|
|
6019
|
+
* `before` but missing from `after`.
|
|
6020
|
+
*
|
|
6021
|
+
* The method creates a copy of both `before` and `after` and runs in O(n log
|
|
6022
|
+
* n), where n is the size of the two lists.
|
|
6023
|
+
*
|
|
6024
|
+
* @param before - The elements that exist in the original set.
|
|
6025
|
+
* @param after - The elements to diff against the original set.
|
|
6026
|
+
* @param comparator - The comparator for the elements in before and after.
|
|
6027
|
+
* @param onAdd - A function to invoke for every element that is part of `
|
|
6028
|
+
* after` but not `before`.
|
|
6029
|
+
* @param onRemove - A function to invoke for every element that is part of
|
|
6030
|
+
* `before` but not `after`.
|
|
6031
|
+
*/
|
|
6032
|
+
function diffSortedSets(before, after, comparator, onAdd, onRemove) {
|
|
6033
|
+
const beforeIt = before.getIterator();
|
|
6034
|
+
const afterIt = after.getIterator();
|
|
6035
|
+
let beforeValue = advanceIterator(beforeIt);
|
|
6036
|
+
let afterValue = advanceIterator(afterIt);
|
|
6037
|
+
// Walk through the two sets at the same time, using the ordering defined by
|
|
6038
|
+
// `comparator`.
|
|
6039
|
+
while (beforeValue || afterValue) {
|
|
6040
|
+
let added = false;
|
|
6041
|
+
let removed = false;
|
|
6042
|
+
if (beforeValue && afterValue) {
|
|
6043
|
+
const cmp = comparator(beforeValue, afterValue);
|
|
6044
|
+
if (cmp < 0) {
|
|
6045
|
+
// The element was removed if the next element in our ordered
|
|
6046
|
+
// walkthrough is only in `before`.
|
|
6047
|
+
removed = true;
|
|
6048
|
+
}
|
|
6049
|
+
else if (cmp > 0) {
|
|
6050
|
+
// The element was added if the next element in our ordered walkthrough
|
|
6051
|
+
// is only in `after`.
|
|
6052
|
+
added = true;
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
else if (beforeValue != null) {
|
|
6056
|
+
removed = true;
|
|
6057
|
+
}
|
|
6058
|
+
else {
|
|
6059
|
+
added = true;
|
|
6060
|
+
}
|
|
6061
|
+
if (added) {
|
|
6062
|
+
onAdd(afterValue);
|
|
6063
|
+
afterValue = advanceIterator(afterIt);
|
|
6064
|
+
}
|
|
6065
|
+
else if (removed) {
|
|
6066
|
+
onRemove(beforeValue);
|
|
6067
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6068
|
+
}
|
|
6069
|
+
else {
|
|
6070
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6071
|
+
afterValue = advanceIterator(afterIt);
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
/**
|
|
6076
|
+
* Returns the next element from the iterator or `undefined` if none available.
|
|
6077
|
+
*/
|
|
6078
|
+
function advanceIterator(it) {
|
|
6079
|
+
return it.hasNext() ? it.getNext() : undefined;
|
|
5768
6080
|
}
|
|
5769
6081
|
|
|
5770
6082
|
/**
|
|
@@ -6603,6 +6915,47 @@ class MutationBatchResult {
|
|
|
6603
6915
|
}
|
|
6604
6916
|
}
|
|
6605
6917
|
|
|
6918
|
+
/**
|
|
6919
|
+
* @license
|
|
6920
|
+
* Copyright 2022 Google LLC
|
|
6921
|
+
*
|
|
6922
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6923
|
+
* you may not use this file except in compliance with the License.
|
|
6924
|
+
* You may obtain a copy of the License at
|
|
6925
|
+
*
|
|
6926
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
6927
|
+
*
|
|
6928
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
6929
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
6930
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6931
|
+
* See the License for the specific language governing permissions and
|
|
6932
|
+
* limitations under the License.
|
|
6933
|
+
*/
|
|
6934
|
+
/**
|
|
6935
|
+
* Representation of an overlay computed by Firestore.
|
|
6936
|
+
*
|
|
6937
|
+
* Holds information about a mutation and the largest batch id in Firestore when
|
|
6938
|
+
* the mutation was created.
|
|
6939
|
+
*/
|
|
6940
|
+
class Overlay {
|
|
6941
|
+
constructor(largestBatchId, mutation) {
|
|
6942
|
+
this.largestBatchId = largestBatchId;
|
|
6943
|
+
this.mutation = mutation;
|
|
6944
|
+
}
|
|
6945
|
+
getKey() {
|
|
6946
|
+
return this.mutation.key;
|
|
6947
|
+
}
|
|
6948
|
+
isEqual(other) {
|
|
6949
|
+
return other !== null && this.mutation === other.mutation;
|
|
6950
|
+
}
|
|
6951
|
+
toString() {
|
|
6952
|
+
return `Overlay{
|
|
6953
|
+
largestBatchId: ${this.largestBatchId},
|
|
6954
|
+
mutation: ${this.mutation.toString()}
|
|
6955
|
+
}`;
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
|
|
6606
6959
|
/**
|
|
6607
6960
|
* @license
|
|
6608
6961
|
* Copyright 2017 Google LLC
|
|
@@ -7075,7 +7428,7 @@ class TargetState {
|
|
|
7075
7428
|
this._current = true;
|
|
7076
7429
|
}
|
|
7077
7430
|
}
|
|
7078
|
-
const LOG_TAG$
|
|
7431
|
+
const LOG_TAG$g = 'WatchChangeAggregator';
|
|
7079
7432
|
/**
|
|
7080
7433
|
* A helper class to accumulate watch changes into a RemoteEvent.
|
|
7081
7434
|
*/
|
|
@@ -7370,7 +7723,7 @@ class WatchChangeAggregator {
|
|
|
7370
7723
|
isActiveTarget(targetId) {
|
|
7371
7724
|
const targetActive = this.targetDataForActiveTarget(targetId) !== null;
|
|
7372
7725
|
if (!targetActive) {
|
|
7373
|
-
logDebug(LOG_TAG$
|
|
7726
|
+
logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
|
|
7374
7727
|
}
|
|
7375
7728
|
return targetActive;
|
|
7376
7729
|
}
|
|
@@ -7989,10 +8342,10 @@ function toQueryTarget(serializer, target) {
|
|
|
7989
8342
|
result.structuredQuery.limit = limit;
|
|
7990
8343
|
}
|
|
7991
8344
|
if (target.startAt) {
|
|
7992
|
-
result.structuredQuery.startAt =
|
|
8345
|
+
result.structuredQuery.startAt = toStartAtCursor(target.startAt);
|
|
7993
8346
|
}
|
|
7994
8347
|
if (target.endAt) {
|
|
7995
|
-
result.structuredQuery.endAt =
|
|
8348
|
+
result.structuredQuery.endAt = toEndAtCursor(target.endAt);
|
|
7996
8349
|
}
|
|
7997
8350
|
return result;
|
|
7998
8351
|
}
|
|
@@ -8025,11 +8378,11 @@ function convertQueryTargetToQuery(target) {
|
|
|
8025
8378
|
}
|
|
8026
8379
|
let startAt = null;
|
|
8027
8380
|
if (query.startAt) {
|
|
8028
|
-
startAt =
|
|
8381
|
+
startAt = fromStartAtCursor(query.startAt);
|
|
8029
8382
|
}
|
|
8030
8383
|
let endAt = null;
|
|
8031
8384
|
if (query.endAt) {
|
|
8032
|
-
endAt =
|
|
8385
|
+
endAt = fromEndAtCursor(query.endAt);
|
|
8033
8386
|
}
|
|
8034
8387
|
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
8035
8388
|
}
|
|
@@ -8120,16 +8473,27 @@ function toOrder(orderBys) {
|
|
|
8120
8473
|
function fromOrder(orderBys) {
|
|
8121
8474
|
return orderBys.map(order => fromPropertyOrder(order));
|
|
8122
8475
|
}
|
|
8123
|
-
function
|
|
8476
|
+
function toStartAtCursor(cursor) {
|
|
8477
|
+
return {
|
|
8478
|
+
before: cursor.inclusive,
|
|
8479
|
+
values: cursor.position
|
|
8480
|
+
};
|
|
8481
|
+
}
|
|
8482
|
+
function toEndAtCursor(cursor) {
|
|
8124
8483
|
return {
|
|
8125
|
-
before: cursor.
|
|
8484
|
+
before: !cursor.inclusive,
|
|
8126
8485
|
values: cursor.position
|
|
8127
8486
|
};
|
|
8128
8487
|
}
|
|
8129
|
-
function
|
|
8130
|
-
const
|
|
8488
|
+
function fromStartAtCursor(cursor) {
|
|
8489
|
+
const inclusive = !!cursor.before;
|
|
8490
|
+
const position = cursor.values || [];
|
|
8491
|
+
return new Bound(position, inclusive);
|
|
8492
|
+
}
|
|
8493
|
+
function fromEndAtCursor(cursor) {
|
|
8494
|
+
const inclusive = !cursor.before;
|
|
8131
8495
|
const position = cursor.values || [];
|
|
8132
|
-
return new Bound(position,
|
|
8496
|
+
return new Bound(position, inclusive);
|
|
8133
8497
|
}
|
|
8134
8498
|
// visible for testing
|
|
8135
8499
|
function toDirection(dir) {
|
|
@@ -8576,28 +8940,764 @@ function fromBundledQuery(bundledQuery) {
|
|
|
8576
8940
|
if (bundledQuery.limitType === 'LAST') {
|
|
8577
8941
|
return queryWithLimit(query, query.limit, "L" /* Last */);
|
|
8578
8942
|
}
|
|
8579
|
-
return query;
|
|
8943
|
+
return query;
|
|
8944
|
+
}
|
|
8945
|
+
/** Encodes a NamedQuery proto object to a NamedQuery model object. */
|
|
8946
|
+
function fromProtoNamedQuery(namedQuery) {
|
|
8947
|
+
return {
|
|
8948
|
+
name: namedQuery.name,
|
|
8949
|
+
query: fromBundledQuery(namedQuery.bundledQuery),
|
|
8950
|
+
readTime: fromVersion(namedQuery.readTime)
|
|
8951
|
+
};
|
|
8952
|
+
}
|
|
8953
|
+
/** Decodes a BundleMetadata proto into a BundleMetadata object. */
|
|
8954
|
+
function fromBundleMetadata(metadata) {
|
|
8955
|
+
return {
|
|
8956
|
+
id: metadata.id,
|
|
8957
|
+
version: metadata.version,
|
|
8958
|
+
createTime: fromVersion(metadata.createTime)
|
|
8959
|
+
};
|
|
8960
|
+
}
|
|
8961
|
+
/** Encodes a DbDocumentOverlay object to an Overlay model object. */
|
|
8962
|
+
function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
|
|
8963
|
+
return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
|
|
8964
|
+
}
|
|
8965
|
+
/** Decodes an Overlay model object into a DbDocumentOverlay object. */
|
|
8966
|
+
function toDbDocumentOverlay(localSerializer, userId, overlay) {
|
|
8967
|
+
const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
|
|
8968
|
+
return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
|
|
8969
|
+
}
|
|
8970
|
+
/**
|
|
8971
|
+
* Returns the DbDocumentOverlayKey corresponding to the given user and
|
|
8972
|
+
* document key.
|
|
8973
|
+
*/
|
|
8974
|
+
function toDbDocumentOverlayKey(userId, docKey) {
|
|
8975
|
+
const docId = docKey.path.lastSegment();
|
|
8976
|
+
const collectionPath = encodeResourcePath(docKey.path.popLast());
|
|
8977
|
+
return [userId, collectionPath, docId];
|
|
8978
|
+
}
|
|
8979
|
+
function toDbIndexConfiguration(index) {
|
|
8980
|
+
return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
|
|
8981
|
+
}
|
|
8982
|
+
function fromDbIndexConfiguration(index, state) {
|
|
8983
|
+
const decodedState = state
|
|
8984
|
+
? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
|
|
8985
|
+
: IndexState.empty();
|
|
8986
|
+
const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
|
|
8987
|
+
return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
|
|
8988
|
+
}
|
|
8989
|
+
function toDbIndexState(indexId, user, sequenceNumber, offset) {
|
|
8990
|
+
return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
|
|
8991
|
+
}
|
|
8992
|
+
|
|
8993
|
+
/**
|
|
8994
|
+
* @license
|
|
8995
|
+
* Copyright 2020 Google LLC
|
|
8996
|
+
*
|
|
8997
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8998
|
+
* you may not use this file except in compliance with the License.
|
|
8999
|
+
* You may obtain a copy of the License at
|
|
9000
|
+
*
|
|
9001
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9002
|
+
*
|
|
9003
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9004
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9005
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9006
|
+
* See the License for the specific language governing permissions and
|
|
9007
|
+
* limitations under the License.
|
|
9008
|
+
*/
|
|
9009
|
+
class IndexedDbBundleCache {
|
|
9010
|
+
getBundleMetadata(transaction, bundleId) {
|
|
9011
|
+
return bundlesStore(transaction)
|
|
9012
|
+
.get(bundleId)
|
|
9013
|
+
.next(bundle => {
|
|
9014
|
+
if (bundle) {
|
|
9015
|
+
return fromDbBundle(bundle);
|
|
9016
|
+
}
|
|
9017
|
+
return undefined;
|
|
9018
|
+
});
|
|
9019
|
+
}
|
|
9020
|
+
saveBundleMetadata(transaction, bundleMetadata) {
|
|
9021
|
+
return bundlesStore(transaction).put(toDbBundle(bundleMetadata));
|
|
9022
|
+
}
|
|
9023
|
+
getNamedQuery(transaction, queryName) {
|
|
9024
|
+
return namedQueriesStore(transaction)
|
|
9025
|
+
.get(queryName)
|
|
9026
|
+
.next(query => {
|
|
9027
|
+
if (query) {
|
|
9028
|
+
return fromDbNamedQuery(query);
|
|
9029
|
+
}
|
|
9030
|
+
return undefined;
|
|
9031
|
+
});
|
|
9032
|
+
}
|
|
9033
|
+
saveNamedQuery(transaction, query) {
|
|
9034
|
+
return namedQueriesStore(transaction).put(toDbNamedQuery(query));
|
|
9035
|
+
}
|
|
9036
|
+
}
|
|
9037
|
+
/**
|
|
9038
|
+
* Helper to get a typed SimpleDbStore for the bundles object store.
|
|
9039
|
+
*/
|
|
9040
|
+
function bundlesStore(txn) {
|
|
9041
|
+
return getStore(txn, DbBundle.store);
|
|
9042
|
+
}
|
|
9043
|
+
/**
|
|
9044
|
+
* Helper to get a typed SimpleDbStore for the namedQueries object store.
|
|
9045
|
+
*/
|
|
9046
|
+
function namedQueriesStore(txn) {
|
|
9047
|
+
return getStore(txn, DbNamedQuery.store);
|
|
9048
|
+
}
|
|
9049
|
+
|
|
9050
|
+
/**
|
|
9051
|
+
* @license
|
|
9052
|
+
* Copyright 2022 Google LLC
|
|
9053
|
+
*
|
|
9054
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9055
|
+
* you may not use this file except in compliance with the License.
|
|
9056
|
+
* You may obtain a copy of the License at
|
|
9057
|
+
*
|
|
9058
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9059
|
+
*
|
|
9060
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9061
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9062
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9063
|
+
* See the License for the specific language governing permissions and
|
|
9064
|
+
* limitations under the License.
|
|
9065
|
+
*/
|
|
9066
|
+
/**
|
|
9067
|
+
* Implementation of DocumentOverlayCache using IndexedDb.
|
|
9068
|
+
*/
|
|
9069
|
+
class IndexedDbDocumentOverlayCache {
|
|
9070
|
+
/**
|
|
9071
|
+
* @param serializer - The document serializer.
|
|
9072
|
+
* @param userId - The userId for which we are accessing overlays.
|
|
9073
|
+
*/
|
|
9074
|
+
constructor(serializer, userId) {
|
|
9075
|
+
this.serializer = serializer;
|
|
9076
|
+
this.userId = userId;
|
|
9077
|
+
}
|
|
9078
|
+
static forUser(serializer, user) {
|
|
9079
|
+
const userId = user.uid || '';
|
|
9080
|
+
return new IndexedDbDocumentOverlayCache(serializer, userId);
|
|
9081
|
+
}
|
|
9082
|
+
getOverlay(transaction, key) {
|
|
9083
|
+
return documentOverlayStore(transaction)
|
|
9084
|
+
.get(toDbDocumentOverlayKey(this.userId, key))
|
|
9085
|
+
.next(dbOverlay => {
|
|
9086
|
+
if (dbOverlay) {
|
|
9087
|
+
return fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9088
|
+
}
|
|
9089
|
+
return null;
|
|
9090
|
+
});
|
|
9091
|
+
}
|
|
9092
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
9093
|
+
const promises = [];
|
|
9094
|
+
overlays.forEach(mutation => {
|
|
9095
|
+
const overlay = new Overlay(largestBatchId, mutation);
|
|
9096
|
+
promises.push(this.saveOverlay(transaction, overlay));
|
|
9097
|
+
});
|
|
9098
|
+
return PersistencePromise.waitFor(promises);
|
|
9099
|
+
}
|
|
9100
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
9101
|
+
const collectionPaths = new Set();
|
|
9102
|
+
// Get the set of unique collection paths.
|
|
9103
|
+
documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
|
|
9104
|
+
const promises = [];
|
|
9105
|
+
collectionPaths.forEach(collectionPath => {
|
|
9106
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
|
|
9107
|
+
/*lowerOpen=*/ false,
|
|
9108
|
+
/*upperOpen=*/ true);
|
|
9109
|
+
promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
|
|
9110
|
+
});
|
|
9111
|
+
return PersistencePromise.waitFor(promises);
|
|
9112
|
+
}
|
|
9113
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
9114
|
+
const result = new Map();
|
|
9115
|
+
const collectionPath = encodeResourcePath(collection);
|
|
9116
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9117
|
+
// is not inclusive.
|
|
9118
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
|
|
9119
|
+
/*lowerOpen=*/ true);
|
|
9120
|
+
return documentOverlayStore(transaction)
|
|
9121
|
+
.loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
|
|
9122
|
+
.next(dbOverlays => {
|
|
9123
|
+
for (const dbOverlay of dbOverlays) {
|
|
9124
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9125
|
+
result.set(overlay.getKey(), overlay);
|
|
9126
|
+
}
|
|
9127
|
+
return result;
|
|
9128
|
+
});
|
|
9129
|
+
}
|
|
9130
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
9131
|
+
const result = new Map();
|
|
9132
|
+
let currentBatchId = undefined;
|
|
9133
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9134
|
+
// is not inclusive.
|
|
9135
|
+
const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
|
|
9136
|
+
/*lowerOpen=*/ true);
|
|
9137
|
+
return documentOverlayStore(transaction)
|
|
9138
|
+
.iterate({
|
|
9139
|
+
index: DbDocumentOverlay.collectionGroupOverlayIndex,
|
|
9140
|
+
range
|
|
9141
|
+
}, (_, dbOverlay, control) => {
|
|
9142
|
+
// We do not want to return partial batch overlays, even if the size
|
|
9143
|
+
// of the result set exceeds the given `count` argument. Therefore, we
|
|
9144
|
+
// continue to aggregate results even after the result size exceeds
|
|
9145
|
+
// `count` if there are more overlays from the `currentBatchId`.
|
|
9146
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9147
|
+
if (result.size < count ||
|
|
9148
|
+
overlay.largestBatchId === currentBatchId) {
|
|
9149
|
+
result.set(overlay.getKey(), overlay);
|
|
9150
|
+
currentBatchId = overlay.largestBatchId;
|
|
9151
|
+
}
|
|
9152
|
+
else {
|
|
9153
|
+
control.done();
|
|
9154
|
+
}
|
|
9155
|
+
})
|
|
9156
|
+
.next(() => result);
|
|
9157
|
+
}
|
|
9158
|
+
saveOverlay(transaction, overlay) {
|
|
9159
|
+
return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
|
|
9160
|
+
}
|
|
9161
|
+
}
|
|
9162
|
+
/**
|
|
9163
|
+
* Helper to get a typed SimpleDbStore for the document overlay object store.
|
|
9164
|
+
*/
|
|
9165
|
+
function documentOverlayStore(txn) {
|
|
9166
|
+
return getStore(txn, DbDocumentOverlay.store);
|
|
9167
|
+
}
|
|
9168
|
+
|
|
9169
|
+
/**
|
|
9170
|
+
* @license
|
|
9171
|
+
* Copyright 2021 Google LLC
|
|
9172
|
+
*
|
|
9173
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9174
|
+
* you may not use this file except in compliance with the License.
|
|
9175
|
+
* You may obtain a copy of the License at
|
|
9176
|
+
*
|
|
9177
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9178
|
+
*
|
|
9179
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9180
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9181
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9182
|
+
* See the License for the specific language governing permissions and
|
|
9183
|
+
* limitations under the License.
|
|
9184
|
+
*/
|
|
9185
|
+
// Note: This code is copied from the backend. Code that is not used by
|
|
9186
|
+
// Firestore was removed.
|
|
9187
|
+
const INDEX_TYPE_NULL = 5;
|
|
9188
|
+
const INDEX_TYPE_BOOLEAN = 10;
|
|
9189
|
+
const INDEX_TYPE_NAN = 13;
|
|
9190
|
+
const INDEX_TYPE_NUMBER = 15;
|
|
9191
|
+
const INDEX_TYPE_TIMESTAMP = 20;
|
|
9192
|
+
const INDEX_TYPE_STRING = 25;
|
|
9193
|
+
const INDEX_TYPE_BLOB = 30;
|
|
9194
|
+
const INDEX_TYPE_REFERENCE = 37;
|
|
9195
|
+
const INDEX_TYPE_GEOPOINT = 45;
|
|
9196
|
+
const INDEX_TYPE_ARRAY = 50;
|
|
9197
|
+
const INDEX_TYPE_MAP = 55;
|
|
9198
|
+
const INDEX_TYPE_REFERENCE_SEGMENT = 60;
|
|
9199
|
+
// A terminator that indicates that a truncatable value was not truncated.
|
|
9200
|
+
// This must be smaller than all other type labels.
|
|
9201
|
+
const NOT_TRUNCATED = 2;
|
|
9202
|
+
/** Firestore index value writer. */
|
|
9203
|
+
class FirestoreIndexValueWriter {
|
|
9204
|
+
constructor() { }
|
|
9205
|
+
// The write methods below short-circuit writing terminators for values
|
|
9206
|
+
// containing a (terminating) truncated value.
|
|
9207
|
+
//
|
|
9208
|
+
// As an example, consider the resulting encoding for:
|
|
9209
|
+
//
|
|
9210
|
+
// ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
|
|
9211
|
+
// ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
|
|
9212
|
+
// ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
|
|
9213
|
+
/** Writes an index value. */
|
|
9214
|
+
writeIndexValue(value, encoder) {
|
|
9215
|
+
this.writeIndexValueAux(value, encoder);
|
|
9216
|
+
// Write separator to split index values
|
|
9217
|
+
// (see go/firestore-storage-format#encodings).
|
|
9218
|
+
encoder.writeInfinity();
|
|
9219
|
+
}
|
|
9220
|
+
writeIndexValueAux(indexValue, encoder) {
|
|
9221
|
+
if ('nullValue' in indexValue) {
|
|
9222
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
|
|
9223
|
+
}
|
|
9224
|
+
else if ('booleanValue' in indexValue) {
|
|
9225
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
|
|
9226
|
+
encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
|
|
9227
|
+
}
|
|
9228
|
+
else if ('integerValue' in indexValue) {
|
|
9229
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9230
|
+
encoder.writeNumber(normalizeNumber(indexValue.integerValue));
|
|
9231
|
+
}
|
|
9232
|
+
else if ('doubleValue' in indexValue) {
|
|
9233
|
+
const n = normalizeNumber(indexValue.doubleValue);
|
|
9234
|
+
if (isNaN(n)) {
|
|
9235
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
|
|
9236
|
+
}
|
|
9237
|
+
else {
|
|
9238
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9239
|
+
if (isNegativeZero(n)) {
|
|
9240
|
+
// -0.0, 0 and 0.0 are all considered the same
|
|
9241
|
+
encoder.writeNumber(0.0);
|
|
9242
|
+
}
|
|
9243
|
+
else {
|
|
9244
|
+
encoder.writeNumber(n);
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
9247
|
+
}
|
|
9248
|
+
else if ('timestampValue' in indexValue) {
|
|
9249
|
+
const timestamp = indexValue.timestampValue;
|
|
9250
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
|
|
9251
|
+
if (typeof timestamp === 'string') {
|
|
9252
|
+
encoder.writeString(timestamp);
|
|
9253
|
+
}
|
|
9254
|
+
else {
|
|
9255
|
+
encoder.writeString(`${timestamp.seconds || ''}`);
|
|
9256
|
+
encoder.writeNumber(timestamp.nanos || 0);
|
|
9257
|
+
}
|
|
9258
|
+
}
|
|
9259
|
+
else if ('stringValue' in indexValue) {
|
|
9260
|
+
this.writeIndexString(indexValue.stringValue, encoder);
|
|
9261
|
+
this.writeTruncationMarker(encoder);
|
|
9262
|
+
}
|
|
9263
|
+
else if ('bytesValue' in indexValue) {
|
|
9264
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
|
|
9265
|
+
encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
|
|
9266
|
+
this.writeTruncationMarker(encoder);
|
|
9267
|
+
}
|
|
9268
|
+
else if ('referenceValue' in indexValue) {
|
|
9269
|
+
this.writeIndexEntityRef(indexValue.referenceValue, encoder);
|
|
9270
|
+
}
|
|
9271
|
+
else if ('geoPointValue' in indexValue) {
|
|
9272
|
+
const geoPoint = indexValue.geoPointValue;
|
|
9273
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
|
|
9274
|
+
encoder.writeNumber(geoPoint.latitude || 0);
|
|
9275
|
+
encoder.writeNumber(geoPoint.longitude || 0);
|
|
9276
|
+
}
|
|
9277
|
+
else if ('mapValue' in indexValue) {
|
|
9278
|
+
if (isMaxValue(indexValue)) {
|
|
9279
|
+
this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
|
|
9280
|
+
}
|
|
9281
|
+
else {
|
|
9282
|
+
this.writeIndexMap(indexValue.mapValue, encoder);
|
|
9283
|
+
this.writeTruncationMarker(encoder);
|
|
9284
|
+
}
|
|
9285
|
+
}
|
|
9286
|
+
else if ('arrayValue' in indexValue) {
|
|
9287
|
+
this.writeIndexArray(indexValue.arrayValue, encoder);
|
|
9288
|
+
this.writeTruncationMarker(encoder);
|
|
9289
|
+
}
|
|
9290
|
+
else {
|
|
9291
|
+
fail();
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
writeIndexString(stringIndexValue, encoder) {
|
|
9295
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
|
|
9296
|
+
this.writeUnlabeledIndexString(stringIndexValue, encoder);
|
|
9297
|
+
}
|
|
9298
|
+
writeUnlabeledIndexString(stringIndexValue, encoder) {
|
|
9299
|
+
encoder.writeString(stringIndexValue);
|
|
9300
|
+
}
|
|
9301
|
+
writeIndexMap(mapIndexValue, encoder) {
|
|
9302
|
+
const map = mapIndexValue.fields || {};
|
|
9303
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
|
|
9304
|
+
for (const key of Object.keys(map)) {
|
|
9305
|
+
this.writeIndexString(key, encoder);
|
|
9306
|
+
this.writeIndexValueAux(map[key], encoder);
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9309
|
+
writeIndexArray(arrayIndexValue, encoder) {
|
|
9310
|
+
const values = arrayIndexValue.values || [];
|
|
9311
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
|
|
9312
|
+
for (const element of values) {
|
|
9313
|
+
this.writeIndexValueAux(element, encoder);
|
|
9314
|
+
}
|
|
9315
|
+
}
|
|
9316
|
+
writeIndexEntityRef(referenceValue, encoder) {
|
|
9317
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
|
|
9318
|
+
const path = DocumentKey.fromName(referenceValue).path;
|
|
9319
|
+
path.forEach(segment => {
|
|
9320
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
|
|
9321
|
+
this.writeUnlabeledIndexString(segment, encoder);
|
|
9322
|
+
});
|
|
9323
|
+
}
|
|
9324
|
+
writeValueTypeLabel(encoder, typeOrder) {
|
|
9325
|
+
encoder.writeNumber(typeOrder);
|
|
9326
|
+
}
|
|
9327
|
+
writeTruncationMarker(encoder) {
|
|
9328
|
+
// While the SDK does not implement truncation, the truncation marker is
|
|
9329
|
+
// used to terminate all variable length values (which are strings, bytes,
|
|
9330
|
+
// references, arrays and maps).
|
|
9331
|
+
encoder.writeNumber(NOT_TRUNCATED);
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9334
|
+
FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
|
|
9335
|
+
|
|
9336
|
+
/**
|
|
9337
|
+
* @license
|
|
9338
|
+
* Copyright 2021 Google LLC
|
|
9339
|
+
*
|
|
9340
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9341
|
+
* you may not use this file except in compliance with the License.
|
|
9342
|
+
* You may obtain a copy of the License at
|
|
9343
|
+
*
|
|
9344
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9345
|
+
*
|
|
9346
|
+
* Unless required by applicable law | agreed to in writing, software
|
|
9347
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9348
|
+
* WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
|
|
9349
|
+
* See the License for the specific language governing permissions and
|
|
9350
|
+
* limitations under the License.
|
|
9351
|
+
*/
|
|
9352
|
+
/** These constants are taken from the backend. */
|
|
9353
|
+
const MIN_SURROGATE = '\uD800';
|
|
9354
|
+
const MAX_SURROGATE = '\uDBFF';
|
|
9355
|
+
const ESCAPE1 = 0x00;
|
|
9356
|
+
const NULL_BYTE = 0xff; // Combined with ESCAPE1
|
|
9357
|
+
const SEPARATOR = 0x01; // Combined with ESCAPE1
|
|
9358
|
+
const ESCAPE2 = 0xff;
|
|
9359
|
+
const INFINITY = 0xff; // Combined with ESCAPE2
|
|
9360
|
+
const FF_BYTE = 0x00; // Combined with ESCAPE2
|
|
9361
|
+
const LONG_SIZE = 64;
|
|
9362
|
+
const BYTE_SIZE = 8;
|
|
9363
|
+
/**
|
|
9364
|
+
* The default size of the buffer. This is arbitrary, but likely larger than
|
|
9365
|
+
* most index values so that less copies of the underlying buffer will be made.
|
|
9366
|
+
* For large values, a single copy will made to double the buffer length.
|
|
9367
|
+
*/
|
|
9368
|
+
const DEFAULT_BUFFER_SIZE = 1024;
|
|
9369
|
+
/** Converts a JavaScript number to a byte array (using big endian encoding). */
|
|
9370
|
+
function doubleToLongBits(value) {
|
|
9371
|
+
const dv = new DataView(new ArrayBuffer(8));
|
|
9372
|
+
dv.setFloat64(0, value, /* littleEndian= */ false);
|
|
9373
|
+
return new Uint8Array(dv.buffer);
|
|
9374
|
+
}
|
|
9375
|
+
/**
|
|
9376
|
+
* Counts the number of zeros in a byte.
|
|
9377
|
+
*
|
|
9378
|
+
* Visible for testing.
|
|
9379
|
+
*/
|
|
9380
|
+
function numberOfLeadingZerosInByte(x) {
|
|
9381
|
+
if (x === 0) {
|
|
9382
|
+
return 8;
|
|
9383
|
+
}
|
|
9384
|
+
let zeros = 0;
|
|
9385
|
+
if (x >> 4 === 0) {
|
|
9386
|
+
// Test if the first four bits are zero.
|
|
9387
|
+
zeros += 4;
|
|
9388
|
+
x = x << 4;
|
|
9389
|
+
}
|
|
9390
|
+
if (x >> 6 === 0) {
|
|
9391
|
+
// Test if the first two (or next two) bits are zero.
|
|
9392
|
+
zeros += 2;
|
|
9393
|
+
x = x << 2;
|
|
9394
|
+
}
|
|
9395
|
+
if (x >> 7 === 0) {
|
|
9396
|
+
// Test if the remaining bit is zero.
|
|
9397
|
+
zeros += 1;
|
|
9398
|
+
}
|
|
9399
|
+
return zeros;
|
|
9400
|
+
}
|
|
9401
|
+
/** Counts the number of leading zeros in the given byte array. */
|
|
9402
|
+
function numberOfLeadingZeros(bytes) {
|
|
9403
|
+
let leadingZeros = 0;
|
|
9404
|
+
for (let i = 0; i < 8; ++i) {
|
|
9405
|
+
const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
|
|
9406
|
+
leadingZeros += zeros;
|
|
9407
|
+
if (zeros !== 8) {
|
|
9408
|
+
break;
|
|
9409
|
+
}
|
|
9410
|
+
}
|
|
9411
|
+
return leadingZeros;
|
|
9412
|
+
}
|
|
9413
|
+
/**
|
|
9414
|
+
* Returns the number of bytes required to store "value". Leading zero bytes
|
|
9415
|
+
* are skipped.
|
|
9416
|
+
*/
|
|
9417
|
+
function unsignedNumLength(value) {
|
|
9418
|
+
// This is just the number of bytes for the unsigned representation of the number.
|
|
9419
|
+
const numBits = LONG_SIZE - numberOfLeadingZeros(value);
|
|
9420
|
+
return Math.ceil(numBits / BYTE_SIZE);
|
|
9421
|
+
}
|
|
9422
|
+
/**
|
|
9423
|
+
* OrderedCodeWriter is a minimal-allocation implementation of the writing
|
|
9424
|
+
* behavior defined by the backend.
|
|
9425
|
+
*
|
|
9426
|
+
* The code is ported from its Java counterpart.
|
|
9427
|
+
*/
|
|
9428
|
+
class OrderedCodeWriter {
|
|
9429
|
+
constructor() {
|
|
9430
|
+
this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
|
|
9431
|
+
this.position = 0;
|
|
9432
|
+
}
|
|
9433
|
+
writeBytesAscending(value) {
|
|
9434
|
+
const it = value[Symbol.iterator]();
|
|
9435
|
+
let byte = it.next();
|
|
9436
|
+
while (!byte.done) {
|
|
9437
|
+
this.writeByteAscending(byte.value);
|
|
9438
|
+
byte = it.next();
|
|
9439
|
+
}
|
|
9440
|
+
this.writeSeparatorAscending();
|
|
9441
|
+
}
|
|
9442
|
+
writeBytesDescending(value) {
|
|
9443
|
+
const it = value[Symbol.iterator]();
|
|
9444
|
+
let byte = it.next();
|
|
9445
|
+
while (!byte.done) {
|
|
9446
|
+
this.writeByteDescending(byte.value);
|
|
9447
|
+
byte = it.next();
|
|
9448
|
+
}
|
|
9449
|
+
this.writeSeparatorDescending();
|
|
9450
|
+
}
|
|
9451
|
+
/** Writes utf8 bytes into this byte sequence, ascending. */
|
|
9452
|
+
writeUtf8Ascending(sequence) {
|
|
9453
|
+
for (const c of sequence) {
|
|
9454
|
+
const charCode = c.charCodeAt(0);
|
|
9455
|
+
if (charCode < 0x80) {
|
|
9456
|
+
this.writeByteAscending(charCode);
|
|
9457
|
+
}
|
|
9458
|
+
else if (charCode < 0x800) {
|
|
9459
|
+
this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
|
|
9460
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9461
|
+
}
|
|
9462
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9463
|
+
this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
|
|
9464
|
+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9465
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9466
|
+
}
|
|
9467
|
+
else {
|
|
9468
|
+
const codePoint = c.codePointAt(0);
|
|
9469
|
+
this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
|
|
9470
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9471
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9472
|
+
this.writeByteAscending(0x80 | (0x3f & codePoint));
|
|
9473
|
+
}
|
|
9474
|
+
}
|
|
9475
|
+
this.writeSeparatorAscending();
|
|
9476
|
+
}
|
|
9477
|
+
/** Writes utf8 bytes into this byte sequence, descending */
|
|
9478
|
+
writeUtf8Descending(sequence) {
|
|
9479
|
+
for (const c of sequence) {
|
|
9480
|
+
const charCode = c.charCodeAt(0);
|
|
9481
|
+
if (charCode < 0x80) {
|
|
9482
|
+
this.writeByteDescending(charCode);
|
|
9483
|
+
}
|
|
9484
|
+
else if (charCode < 0x800) {
|
|
9485
|
+
this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
|
|
9486
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9487
|
+
}
|
|
9488
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9489
|
+
this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
|
|
9490
|
+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9491
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9492
|
+
}
|
|
9493
|
+
else {
|
|
9494
|
+
const codePoint = c.codePointAt(0);
|
|
9495
|
+
this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
|
|
9496
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9497
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9498
|
+
this.writeByteDescending(0x80 | (0x3f & codePoint));
|
|
9499
|
+
}
|
|
9500
|
+
}
|
|
9501
|
+
this.writeSeparatorDescending();
|
|
9502
|
+
}
|
|
9503
|
+
writeNumberAscending(val) {
|
|
9504
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9505
|
+
// actual value in big-endian format with leading 0 bytes dropped.
|
|
9506
|
+
const value = this.toOrderedBits(val);
|
|
9507
|
+
const len = unsignedNumLength(value);
|
|
9508
|
+
this.ensureAvailable(1 + len);
|
|
9509
|
+
this.buffer[this.position++] = len & 0xff; // Write the length
|
|
9510
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9511
|
+
this.buffer[this.position++] = value[i] & 0xff;
|
|
9512
|
+
}
|
|
9513
|
+
}
|
|
9514
|
+
writeNumberDescending(val) {
|
|
9515
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9516
|
+
// inverted value in big-endian format with leading 0 bytes dropped.
|
|
9517
|
+
const value = this.toOrderedBits(val);
|
|
9518
|
+
const len = unsignedNumLength(value);
|
|
9519
|
+
this.ensureAvailable(1 + len);
|
|
9520
|
+
this.buffer[this.position++] = ~(len & 0xff); // Write the length
|
|
9521
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9522
|
+
this.buffer[this.position++] = ~(value[i] & 0xff);
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9525
|
+
/**
|
|
9526
|
+
* Writes the "infinity" byte sequence that sorts after all other byte
|
|
9527
|
+
* sequences written in ascending order.
|
|
9528
|
+
*/
|
|
9529
|
+
writeInfinityAscending() {
|
|
9530
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9531
|
+
this.writeEscapedByteAscending(INFINITY);
|
|
9532
|
+
}
|
|
9533
|
+
/**
|
|
9534
|
+
* Writes the "infinity" byte sequence that sorts before all other byte
|
|
9535
|
+
* sequences written in descending order.
|
|
9536
|
+
*/
|
|
9537
|
+
writeInfinityDescending() {
|
|
9538
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9539
|
+
this.writeEscapedByteDescending(INFINITY);
|
|
9540
|
+
}
|
|
9541
|
+
/**
|
|
9542
|
+
* Resets the buffer such that it is the same as when it was newly
|
|
9543
|
+
* constructed.
|
|
9544
|
+
*/
|
|
9545
|
+
reset() {
|
|
9546
|
+
this.position = 0;
|
|
9547
|
+
}
|
|
9548
|
+
seed(encodedBytes) {
|
|
9549
|
+
this.ensureAvailable(encodedBytes.length);
|
|
9550
|
+
this.buffer.set(encodedBytes, this.position);
|
|
9551
|
+
this.position += encodedBytes.length;
|
|
9552
|
+
}
|
|
9553
|
+
/** Makes a copy of the encoded bytes in this buffer. */
|
|
9554
|
+
encodedBytes() {
|
|
9555
|
+
return this.buffer.slice(0, this.position);
|
|
9556
|
+
}
|
|
9557
|
+
/**
|
|
9558
|
+
* Encodes `val` into an encoding so that the order matches the IEEE 754
|
|
9559
|
+
* floating-point comparison results with the following exceptions:
|
|
9560
|
+
* -0.0 < 0.0
|
|
9561
|
+
* all non-NaN < NaN
|
|
9562
|
+
* NaN = NaN
|
|
9563
|
+
*/
|
|
9564
|
+
toOrderedBits(val) {
|
|
9565
|
+
const value = doubleToLongBits(val);
|
|
9566
|
+
// Check if the first bit is set. We use a bit mask since value[0] is
|
|
9567
|
+
// encoded as a number from 0 to 255.
|
|
9568
|
+
const isNegative = (value[0] & 0x80) !== 0;
|
|
9569
|
+
// Revert the two complement to get natural ordering
|
|
9570
|
+
value[0] ^= isNegative ? 0xff : 0x80;
|
|
9571
|
+
for (let i = 1; i < value.length; ++i) {
|
|
9572
|
+
value[i] ^= isNegative ? 0xff : 0x00;
|
|
9573
|
+
}
|
|
9574
|
+
return value;
|
|
9575
|
+
}
|
|
9576
|
+
/** Writes a single byte ascending to the buffer. */
|
|
9577
|
+
writeByteAscending(b) {
|
|
9578
|
+
const masked = b & 0xff;
|
|
9579
|
+
if (masked === ESCAPE1) {
|
|
9580
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9581
|
+
this.writeEscapedByteAscending(NULL_BYTE);
|
|
9582
|
+
}
|
|
9583
|
+
else if (masked === ESCAPE2) {
|
|
9584
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9585
|
+
this.writeEscapedByteAscending(FF_BYTE);
|
|
9586
|
+
}
|
|
9587
|
+
else {
|
|
9588
|
+
this.writeEscapedByteAscending(masked);
|
|
9589
|
+
}
|
|
9590
|
+
}
|
|
9591
|
+
/** Writes a single byte descending to the buffer. */
|
|
9592
|
+
writeByteDescending(b) {
|
|
9593
|
+
const masked = b & 0xff;
|
|
9594
|
+
if (masked === ESCAPE1) {
|
|
9595
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9596
|
+
this.writeEscapedByteDescending(NULL_BYTE);
|
|
9597
|
+
}
|
|
9598
|
+
else if (masked === ESCAPE2) {
|
|
9599
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9600
|
+
this.writeEscapedByteDescending(FF_BYTE);
|
|
9601
|
+
}
|
|
9602
|
+
else {
|
|
9603
|
+
this.writeEscapedByteDescending(b);
|
|
9604
|
+
}
|
|
9605
|
+
}
|
|
9606
|
+
writeSeparatorAscending() {
|
|
9607
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9608
|
+
this.writeEscapedByteAscending(SEPARATOR);
|
|
9609
|
+
}
|
|
9610
|
+
writeSeparatorDescending() {
|
|
9611
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9612
|
+
this.writeEscapedByteDescending(SEPARATOR);
|
|
9613
|
+
}
|
|
9614
|
+
writeEscapedByteAscending(b) {
|
|
9615
|
+
this.ensureAvailable(1);
|
|
9616
|
+
this.buffer[this.position++] = b;
|
|
9617
|
+
}
|
|
9618
|
+
writeEscapedByteDescending(b) {
|
|
9619
|
+
this.ensureAvailable(1);
|
|
9620
|
+
this.buffer[this.position++] = ~b;
|
|
9621
|
+
}
|
|
9622
|
+
ensureAvailable(bytes) {
|
|
9623
|
+
const minCapacity = bytes + this.position;
|
|
9624
|
+
if (minCapacity <= this.buffer.length) {
|
|
9625
|
+
return;
|
|
9626
|
+
}
|
|
9627
|
+
// Try doubling.
|
|
9628
|
+
let newLength = this.buffer.length * 2;
|
|
9629
|
+
// Still not big enough? Just allocate the right size.
|
|
9630
|
+
if (newLength < minCapacity) {
|
|
9631
|
+
newLength = minCapacity;
|
|
9632
|
+
}
|
|
9633
|
+
// Create the new buffer.
|
|
9634
|
+
const newBuffer = new Uint8Array(newLength);
|
|
9635
|
+
newBuffer.set(this.buffer); // copy old data
|
|
9636
|
+
this.buffer = newBuffer;
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
|
|
9640
|
+
class AscendingIndexByteEncoder {
|
|
9641
|
+
constructor(orderedCode) {
|
|
9642
|
+
this.orderedCode = orderedCode;
|
|
9643
|
+
}
|
|
9644
|
+
writeBytes(value) {
|
|
9645
|
+
this.orderedCode.writeBytesAscending(value);
|
|
9646
|
+
}
|
|
9647
|
+
writeString(value) {
|
|
9648
|
+
this.orderedCode.writeUtf8Ascending(value);
|
|
9649
|
+
}
|
|
9650
|
+
writeNumber(value) {
|
|
9651
|
+
this.orderedCode.writeNumberAscending(value);
|
|
9652
|
+
}
|
|
9653
|
+
writeInfinity() {
|
|
9654
|
+
this.orderedCode.writeInfinityAscending();
|
|
9655
|
+
}
|
|
8580
9656
|
}
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
}
|
|
9657
|
+
class DescendingIndexByteEncoder {
|
|
9658
|
+
constructor(orderedCode) {
|
|
9659
|
+
this.orderedCode = orderedCode;
|
|
9660
|
+
}
|
|
9661
|
+
writeBytes(value) {
|
|
9662
|
+
this.orderedCode.writeBytesDescending(value);
|
|
9663
|
+
}
|
|
9664
|
+
writeString(value) {
|
|
9665
|
+
this.orderedCode.writeUtf8Descending(value);
|
|
9666
|
+
}
|
|
9667
|
+
writeNumber(value) {
|
|
9668
|
+
this.orderedCode.writeNumberDescending(value);
|
|
9669
|
+
}
|
|
9670
|
+
writeInfinity() {
|
|
9671
|
+
this.orderedCode.writeInfinityDescending();
|
|
9672
|
+
}
|
|
8588
9673
|
}
|
|
8589
|
-
/**
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
9674
|
+
/**
|
|
9675
|
+
* Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
|
|
9676
|
+
* actual encoding.
|
|
9677
|
+
*/
|
|
9678
|
+
class IndexByteEncoder {
|
|
9679
|
+
constructor() {
|
|
9680
|
+
this.orderedCode = new OrderedCodeWriter();
|
|
9681
|
+
this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
|
|
9682
|
+
this.descending = new DescendingIndexByteEncoder(this.orderedCode);
|
|
9683
|
+
}
|
|
9684
|
+
seed(encodedBytes) {
|
|
9685
|
+
this.orderedCode.seed(encodedBytes);
|
|
9686
|
+
}
|
|
9687
|
+
forKind(kind) {
|
|
9688
|
+
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
9689
|
+
}
|
|
9690
|
+
encodedBytes() {
|
|
9691
|
+
return this.orderedCode.encodedBytes();
|
|
9692
|
+
}
|
|
9693
|
+
reset() {
|
|
9694
|
+
this.orderedCode.reset();
|
|
9695
|
+
}
|
|
8596
9696
|
}
|
|
8597
9697
|
|
|
8598
9698
|
/**
|
|
8599
9699
|
* @license
|
|
8600
|
-
* Copyright
|
|
9700
|
+
* Copyright 2022 Google LLC
|
|
8601
9701
|
*
|
|
8602
9702
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
8603
9703
|
* you may not use this file except in compliance with the License.
|
|
@@ -8611,45 +9711,38 @@ function fromBundleMetadata(metadata) {
|
|
|
8611
9711
|
* See the License for the specific language governing permissions and
|
|
8612
9712
|
* limitations under the License.
|
|
8613
9713
|
*/
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
}
|
|
8622
|
-
return undefined;
|
|
8623
|
-
});
|
|
9714
|
+
/** Represents an index entry saved by the SDK in persisted storage. */
|
|
9715
|
+
class IndexEntry {
|
|
9716
|
+
constructor(indexId, documentKey, arrayValue, directionalValue) {
|
|
9717
|
+
this.indexId = indexId;
|
|
9718
|
+
this.documentKey = documentKey;
|
|
9719
|
+
this.arrayValue = arrayValue;
|
|
9720
|
+
this.directionalValue = directionalValue;
|
|
8624
9721
|
}
|
|
8625
|
-
|
|
8626
|
-
|
|
9722
|
+
}
|
|
9723
|
+
function indexEntryComparator(left, right) {
|
|
9724
|
+
let cmp = left.indexId - right.indexId;
|
|
9725
|
+
if (cmp !== 0) {
|
|
9726
|
+
return cmp;
|
|
8627
9727
|
}
|
|
8628
|
-
|
|
8629
|
-
|
|
8630
|
-
|
|
8631
|
-
.next(query => {
|
|
8632
|
-
if (query) {
|
|
8633
|
-
return fromDbNamedQuery(query);
|
|
8634
|
-
}
|
|
8635
|
-
return undefined;
|
|
8636
|
-
});
|
|
9728
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
9729
|
+
if (cmp !== 0) {
|
|
9730
|
+
return cmp;
|
|
8637
9731
|
}
|
|
8638
|
-
|
|
8639
|
-
|
|
9732
|
+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
|
|
9733
|
+
if (cmp !== 0) {
|
|
9734
|
+
return cmp;
|
|
8640
9735
|
}
|
|
9736
|
+
return compareByteArrays(left.directionalValue, right.directionalValue);
|
|
8641
9737
|
}
|
|
8642
|
-
|
|
8643
|
-
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
}
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
*/
|
|
8651
|
-
function namedQueriesStore(txn) {
|
|
8652
|
-
return getStore(txn, DbNamedQuery.store);
|
|
9738
|
+
function compareByteArrays(left, right) {
|
|
9739
|
+
for (let i = 0; i < left.length && i < right.length; ++i) {
|
|
9740
|
+
const compare = left[i] - right[i];
|
|
9741
|
+
if (compare !== 0) {
|
|
9742
|
+
return compare;
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
return left.length - right.length;
|
|
8653
9746
|
}
|
|
8654
9747
|
|
|
8655
9748
|
/**
|
|
@@ -8682,6 +9775,38 @@ class MemoryIndexManager {
|
|
|
8682
9775
|
getCollectionParents(transaction, collectionId) {
|
|
8683
9776
|
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
8684
9777
|
}
|
|
9778
|
+
addFieldIndex(transaction, index) {
|
|
9779
|
+
// Field indices are not supported with memory persistence.
|
|
9780
|
+
return PersistencePromise.resolve();
|
|
9781
|
+
}
|
|
9782
|
+
deleteFieldIndex(transaction, index) {
|
|
9783
|
+
// Field indices are not supported with memory persistence.
|
|
9784
|
+
return PersistencePromise.resolve();
|
|
9785
|
+
}
|
|
9786
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9787
|
+
// Field indices are not supported with memory persistence.
|
|
9788
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9789
|
+
}
|
|
9790
|
+
getFieldIndex(transaction, target) {
|
|
9791
|
+
// Field indices are not supported with memory persistence.
|
|
9792
|
+
return PersistencePromise.resolve(null);
|
|
9793
|
+
}
|
|
9794
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9795
|
+
// Field indices are not supported with memory persistence.
|
|
9796
|
+
return PersistencePromise.resolve([]);
|
|
9797
|
+
}
|
|
9798
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9799
|
+
// Field indices are not supported with memory persistence.
|
|
9800
|
+
return PersistencePromise.resolve(null);
|
|
9801
|
+
}
|
|
9802
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9803
|
+
// Field indices are not supported with memory persistence.
|
|
9804
|
+
return PersistencePromise.resolve();
|
|
9805
|
+
}
|
|
9806
|
+
updateIndexEntries(transaction, documents) {
|
|
9807
|
+
// Field indices are not supported with memory persistence.
|
|
9808
|
+
return PersistencePromise.resolve();
|
|
9809
|
+
}
|
|
8685
9810
|
}
|
|
8686
9811
|
/**
|
|
8687
9812
|
* Internal implementation of the collection-parent index exposed by MemoryIndexManager.
|
|
@@ -8731,19 +9856,25 @@ class MemoryCollectionParentIndex {
|
|
|
8731
9856
|
* See the License for the specific language governing permissions and
|
|
8732
9857
|
* limitations under the License.
|
|
8733
9858
|
*/
|
|
9859
|
+
const LOG_TAG$f = 'IndexedDbIndexManager';
|
|
8734
9860
|
/**
|
|
8735
9861
|
* A persisted implementation of IndexManager.
|
|
9862
|
+
*
|
|
9863
|
+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
|
|
9864
|
+
* data as it supports multi-tab access.
|
|
8736
9865
|
*/
|
|
8737
9866
|
class IndexedDbIndexManager {
|
|
8738
|
-
constructor() {
|
|
9867
|
+
constructor(user) {
|
|
9868
|
+
this.user = user;
|
|
8739
9869
|
/**
|
|
8740
9870
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
8741
9871
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
8742
9872
|
*
|
|
8743
|
-
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
8744
|
-
* satisfy reads.
|
|
9873
|
+
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
9874
|
+
* used to satisfy reads.
|
|
8745
9875
|
*/
|
|
8746
9876
|
this.collectionParentsCache = new MemoryCollectionParentIndex();
|
|
9877
|
+
this.uid = user.uid || '';
|
|
8747
9878
|
}
|
|
8748
9879
|
/**
|
|
8749
9880
|
* Adds a new entry to the collection parent index.
|
|
@@ -8790,6 +9921,198 @@ class IndexedDbIndexManager {
|
|
|
8790
9921
|
return parentPaths;
|
|
8791
9922
|
});
|
|
8792
9923
|
}
|
|
9924
|
+
addFieldIndex(transaction, index) {
|
|
9925
|
+
// TODO(indexing): Verify that the auto-incrementing index ID works in
|
|
9926
|
+
// Safari & Firefox.
|
|
9927
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9928
|
+
const dbIndex = toDbIndexConfiguration(index);
|
|
9929
|
+
delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
|
|
9930
|
+
return indexes.add(dbIndex).next();
|
|
9931
|
+
}
|
|
9932
|
+
deleteFieldIndex(transaction, index) {
|
|
9933
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9934
|
+
const states = indexStateStore(transaction);
|
|
9935
|
+
const entries = indexEntriesStore(transaction);
|
|
9936
|
+
return indexes
|
|
9937
|
+
.delete(index.indexId)
|
|
9938
|
+
.next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9939
|
+
/*lowerOpen=*/ false,
|
|
9940
|
+
/*upperOpen=*/ true)))
|
|
9941
|
+
.next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9942
|
+
/*lowerOpen=*/ false,
|
|
9943
|
+
/*upperOpen=*/ true)));
|
|
9944
|
+
}
|
|
9945
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9946
|
+
// TODO(indexing): Implement
|
|
9947
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9948
|
+
}
|
|
9949
|
+
getFieldIndex(transaction, target) {
|
|
9950
|
+
// TODO(indexing): Implement
|
|
9951
|
+
return PersistencePromise.resolve(null);
|
|
9952
|
+
}
|
|
9953
|
+
/**
|
|
9954
|
+
* Returns the byte encoded form of the directional values in the field index.
|
|
9955
|
+
* Returns `null` if the document does not have all fields specified in the
|
|
9956
|
+
* index.
|
|
9957
|
+
*/
|
|
9958
|
+
encodeDirectionalElements(fieldIndex, document) {
|
|
9959
|
+
const encoder = new IndexByteEncoder();
|
|
9960
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
9961
|
+
const field = document.data.field(segment.fieldPath);
|
|
9962
|
+
if (field == null) {
|
|
9963
|
+
return null;
|
|
9964
|
+
}
|
|
9965
|
+
const directionalEncoder = encoder.forKind(segment.kind);
|
|
9966
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
|
|
9967
|
+
}
|
|
9968
|
+
return encoder.encodedBytes();
|
|
9969
|
+
}
|
|
9970
|
+
/** Encodes a single value to the ascending index format. */
|
|
9971
|
+
encodeSingleElement(value) {
|
|
9972
|
+
const encoder = new IndexByteEncoder();
|
|
9973
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
9974
|
+
return encoder.encodedBytes();
|
|
9975
|
+
}
|
|
9976
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9977
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9978
|
+
const states = indexStateStore(transaction);
|
|
9979
|
+
return (collectionGroup
|
|
9980
|
+
? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9981
|
+
: indexes.loadAll()).next(indexConfigs => {
|
|
9982
|
+
const result = [];
|
|
9983
|
+
return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
|
|
9984
|
+
return states
|
|
9985
|
+
.get([indexConfig.indexId, this.uid])
|
|
9986
|
+
.next(indexState => {
|
|
9987
|
+
result.push(fromDbIndexConfiguration(indexConfig, indexState));
|
|
9988
|
+
});
|
|
9989
|
+
}).next(() => result);
|
|
9990
|
+
});
|
|
9991
|
+
}
|
|
9992
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9993
|
+
return this.getFieldIndexes(transaction).next(indexes => {
|
|
9994
|
+
if (indexes.length === 0) {
|
|
9995
|
+
return null;
|
|
9996
|
+
}
|
|
9997
|
+
indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
|
|
9998
|
+
return indexes[0].collectionGroup;
|
|
9999
|
+
});
|
|
10000
|
+
}
|
|
10001
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
10002
|
+
const indexes = indexConfigurationStore(transaction);
|
|
10003
|
+
const states = indexStateStore(transaction);
|
|
10004
|
+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
|
|
10005
|
+
.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
10006
|
+
.next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
|
|
10007
|
+
}
|
|
10008
|
+
updateIndexEntries(transaction, documents) {
|
|
10009
|
+
// Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
|
|
10010
|
+
// it could be used across different IndexedDB transactions. As any cached
|
|
10011
|
+
// data might be invalidated by other multi-tab clients, we can only trust
|
|
10012
|
+
// data within a single IndexedDB transaction. We therefore add a cache
|
|
10013
|
+
// here.
|
|
10014
|
+
const memoizedIndexes = new Map();
|
|
10015
|
+
return PersistencePromise.forEach(documents, (key, doc) => {
|
|
10016
|
+
const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
|
|
10017
|
+
const fieldIndexes = memoizedCollectionIndexes
|
|
10018
|
+
? PersistencePromise.resolve(memoizedCollectionIndexes)
|
|
10019
|
+
: this.getFieldIndexes(transaction, key.collectionGroup);
|
|
10020
|
+
return fieldIndexes.next(fieldIndexes => {
|
|
10021
|
+
memoizedIndexes.set(key.collectionGroup, fieldIndexes);
|
|
10022
|
+
return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
|
|
10023
|
+
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
10024
|
+
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10025
|
+
if (!existingEntries.isEqual(newEntries)) {
|
|
10026
|
+
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10027
|
+
}
|
|
10028
|
+
return PersistencePromise.resolve();
|
|
10029
|
+
});
|
|
10030
|
+
});
|
|
10031
|
+
});
|
|
10032
|
+
});
|
|
10033
|
+
}
|
|
10034
|
+
addIndexEntry(transaction, document, indexEntry) {
|
|
10035
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10036
|
+
return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
|
|
10037
|
+
}
|
|
10038
|
+
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10039
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10040
|
+
return indexEntries.delete([
|
|
10041
|
+
indexEntry.indexId,
|
|
10042
|
+
this.uid,
|
|
10043
|
+
indexEntry.arrayValue,
|
|
10044
|
+
indexEntry.directionalValue,
|
|
10045
|
+
encodeResourcePath(document.key.path)
|
|
10046
|
+
]);
|
|
10047
|
+
}
|
|
10048
|
+
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
10049
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10050
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10051
|
+
return indexEntries
|
|
10052
|
+
.iterate({
|
|
10053
|
+
index: DbIndexEntry.documentKeyIndex,
|
|
10054
|
+
range: IDBKeyRange.only([
|
|
10055
|
+
fieldIndex.indexId,
|
|
10056
|
+
this.uid,
|
|
10057
|
+
encodeResourcePath(documentKey.path)
|
|
10058
|
+
])
|
|
10059
|
+
}, (_, entry) => {
|
|
10060
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
10061
|
+
})
|
|
10062
|
+
.next(() => results);
|
|
10063
|
+
}
|
|
10064
|
+
/** Creates the index entries for the given document. */
|
|
10065
|
+
computeIndexEntries(document, fieldIndex) {
|
|
10066
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10067
|
+
const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
|
|
10068
|
+
if (directionalValue == null) {
|
|
10069
|
+
return results;
|
|
10070
|
+
}
|
|
10071
|
+
const arraySegment = fieldIndexGetArraySegment(fieldIndex);
|
|
10072
|
+
if (arraySegment != null) {
|
|
10073
|
+
const value = document.data.field(arraySegment.fieldPath);
|
|
10074
|
+
if (isArray(value)) {
|
|
10075
|
+
for (const arrayValue of value.arrayValue.values || []) {
|
|
10076
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
|
|
10077
|
+
}
|
|
10078
|
+
}
|
|
10079
|
+
}
|
|
10080
|
+
else {
|
|
10081
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
|
|
10082
|
+
}
|
|
10083
|
+
return results;
|
|
10084
|
+
}
|
|
10085
|
+
/**
|
|
10086
|
+
* Updates the index entries for the provided document by deleting entries
|
|
10087
|
+
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10088
|
+
* entries.
|
|
10089
|
+
*/
|
|
10090
|
+
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10091
|
+
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10092
|
+
const promises = [];
|
|
10093
|
+
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10094
|
+
/* onAdd= */ entry => {
|
|
10095
|
+
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10096
|
+
},
|
|
10097
|
+
/* onRemove= */ entry => {
|
|
10098
|
+
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10099
|
+
});
|
|
10100
|
+
return PersistencePromise.waitFor(promises);
|
|
10101
|
+
}
|
|
10102
|
+
getNextSequenceNumber(transaction) {
|
|
10103
|
+
let nextSequenceNumber = 1;
|
|
10104
|
+
const states = indexStateStore(transaction);
|
|
10105
|
+
return states
|
|
10106
|
+
.iterate({
|
|
10107
|
+
index: DbIndexState.sequenceNumberIndex,
|
|
10108
|
+
reverse: true,
|
|
10109
|
+
range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
|
|
10110
|
+
}, (_, state, controller) => {
|
|
10111
|
+
controller.done();
|
|
10112
|
+
nextSequenceNumber = state.sequenceNumber + 1;
|
|
10113
|
+
})
|
|
10114
|
+
.next(() => nextSequenceNumber);
|
|
10115
|
+
}
|
|
8793
10116
|
}
|
|
8794
10117
|
/**
|
|
8795
10118
|
* Helper to get a typed SimpleDbStore for the collectionParents
|
|
@@ -8797,6 +10120,24 @@ class IndexedDbIndexManager {
|
|
|
8797
10120
|
*/
|
|
8798
10121
|
function collectionParentsStore(txn) {
|
|
8799
10122
|
return getStore(txn, DbCollectionParent.store);
|
|
10123
|
+
}
|
|
10124
|
+
/**
|
|
10125
|
+
* Helper to get a typed SimpleDbStore for the index entry object store.
|
|
10126
|
+
*/
|
|
10127
|
+
function indexEntriesStore(txn) {
|
|
10128
|
+
return getStore(txn, DbIndexEntry.store);
|
|
10129
|
+
}
|
|
10130
|
+
/**
|
|
10131
|
+
* Helper to get a typed SimpleDbStore for the index configuration object store.
|
|
10132
|
+
*/
|
|
10133
|
+
function indexConfigurationStore(txn) {
|
|
10134
|
+
return getStore(txn, DbIndexConfiguration.store);
|
|
10135
|
+
}
|
|
10136
|
+
/**
|
|
10137
|
+
* Helper to get a typed SimpleDbStore for the index state object store.
|
|
10138
|
+
*/
|
|
10139
|
+
function indexStateStore(txn) {
|
|
10140
|
+
return getStore(txn, DbIndexState.store);
|
|
8800
10141
|
}
|
|
8801
10142
|
|
|
8802
10143
|
/**
|
|
@@ -10266,12 +11607,10 @@ class RemoteDocumentChangeBuffer {
|
|
|
10266
11607
|
* `newIndexedDbRemoteDocumentCache()`.
|
|
10267
11608
|
*/
|
|
10268
11609
|
class IndexedDbRemoteDocumentCacheImpl {
|
|
10269
|
-
|
|
10270
|
-
* @param serializer - The document serializer.
|
|
10271
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10272
|
-
*/
|
|
10273
|
-
constructor(serializer, indexManager) {
|
|
11610
|
+
constructor(serializer) {
|
|
10274
11611
|
this.serializer = serializer;
|
|
11612
|
+
}
|
|
11613
|
+
setIndexManager(indexManager) {
|
|
10275
11614
|
this.indexManager = indexManager;
|
|
10276
11615
|
}
|
|
10277
11616
|
/**
|
|
@@ -10393,21 +11732,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10393
11732
|
}
|
|
10394
11733
|
});
|
|
10395
11734
|
}
|
|
10396
|
-
|
|
11735
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10397
11736
|
let results = mutableDocumentMap();
|
|
10398
|
-
const immediateChildrenPathLength =
|
|
11737
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
10399
11738
|
const iterationOptions = {};
|
|
10400
11739
|
if (sinceReadTime.isEqual(SnapshotVersion.min())) {
|
|
10401
11740
|
// Documents are ordered by key, so we can use a prefix scan to narrow
|
|
10402
11741
|
// down the documents we need to match the query against.
|
|
10403
|
-
const startKey =
|
|
11742
|
+
const startKey = collection.toArray();
|
|
10404
11743
|
iterationOptions.range = IDBKeyRange.lowerBound(startKey);
|
|
10405
11744
|
}
|
|
10406
11745
|
else {
|
|
10407
11746
|
// Execute an index-free query and filter by read time. This is safe
|
|
10408
11747
|
// since all document changes to queries that have a
|
|
10409
11748
|
// lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
|
|
10410
|
-
const collectionKey =
|
|
11749
|
+
const collectionKey = collection.toArray();
|
|
10411
11750
|
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
10412
11751
|
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
10413
11752
|
/* open= */ true);
|
|
@@ -10424,12 +11763,12 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10424
11763
|
return;
|
|
10425
11764
|
}
|
|
10426
11765
|
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
10427
|
-
if (
|
|
10428
|
-
control.done();
|
|
10429
|
-
}
|
|
10430
|
-
else if (queryMatches(query, document)) {
|
|
11766
|
+
if (collection.isPrefixOf(document.key.path)) {
|
|
10431
11767
|
results = results.insert(document.key, document);
|
|
10432
11768
|
}
|
|
11769
|
+
else {
|
|
11770
|
+
control.done();
|
|
11771
|
+
}
|
|
10433
11772
|
})
|
|
10434
11773
|
.next(() => results);
|
|
10435
11774
|
}
|
|
@@ -10467,14 +11806,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10467
11806
|
return MutableDocument.newInvalidDocument(documentKey);
|
|
10468
11807
|
}
|
|
10469
11808
|
}
|
|
10470
|
-
/**
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
* @param serializer - The document serializer.
|
|
10474
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10475
|
-
*/
|
|
10476
|
-
function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
|
|
10477
|
-
return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
|
|
11809
|
+
/** Creates a new IndexedDbRemoteDocumentCache. */
|
|
11810
|
+
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
11811
|
+
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
10478
11812
|
}
|
|
10479
11813
|
/**
|
|
10480
11814
|
* Returns the set of documents that have changed since the specified read
|
|
@@ -10641,9 +11975,6 @@ class SchemaConverter {
|
|
|
10641
11975
|
* and local feature development.
|
|
10642
11976
|
*/
|
|
10643
11977
|
createOrUpgrade(db, txn, fromVersion, toVersion) {
|
|
10644
|
-
hardAssert(fromVersion < toVersion &&
|
|
10645
|
-
fromVersion >= 0 &&
|
|
10646
|
-
toVersion <= SCHEMA_VERSION);
|
|
10647
11978
|
const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
|
|
10648
11979
|
if (fromVersion < 1 && toVersion >= 1) {
|
|
10649
11980
|
createPrimaryClientStore(db);
|
|
@@ -10710,6 +12041,16 @@ class SchemaConverter {
|
|
|
10710
12041
|
createNamedQueriesStore(db);
|
|
10711
12042
|
});
|
|
10712
12043
|
}
|
|
12044
|
+
if (fromVersion < 12 && toVersion >= 12) {
|
|
12045
|
+
p = p.next(() => {
|
|
12046
|
+
createDocumentOverlayStore(db);
|
|
12047
|
+
});
|
|
12048
|
+
}
|
|
12049
|
+
if (fromVersion < 13 && toVersion >= 13) {
|
|
12050
|
+
p = p.next(() => {
|
|
12051
|
+
createFieldIndex(db);
|
|
12052
|
+
});
|
|
12053
|
+
}
|
|
10713
12054
|
return p;
|
|
10714
12055
|
}
|
|
10715
12056
|
addDocumentGlobal(txn) {
|
|
@@ -10916,6 +12257,28 @@ function createNamedQueriesStore(db) {
|
|
|
10916
12257
|
db.createObjectStore(DbNamedQuery.store, {
|
|
10917
12258
|
keyPath: DbNamedQuery.keyPath
|
|
10918
12259
|
});
|
|
12260
|
+
}
|
|
12261
|
+
function createFieldIndex(db) {
|
|
12262
|
+
const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
|
|
12263
|
+
keyPath: DbIndexConfiguration.keyPath,
|
|
12264
|
+
autoIncrement: true
|
|
12265
|
+
});
|
|
12266
|
+
indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
|
|
12267
|
+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
|
|
12268
|
+
keyPath: DbIndexState.keyPath
|
|
12269
|
+
});
|
|
12270
|
+
indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
|
|
12271
|
+
const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
|
|
12272
|
+
keyPath: DbIndexEntry.keyPath
|
|
12273
|
+
});
|
|
12274
|
+
indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
|
|
12275
|
+
}
|
|
12276
|
+
function createDocumentOverlayStore(db) {
|
|
12277
|
+
const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
|
|
12278
|
+
keyPath: DbDocumentOverlay.keyPath
|
|
12279
|
+
});
|
|
12280
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
|
|
12281
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
|
|
10919
12282
|
}
|
|
10920
12283
|
|
|
10921
12284
|
/**
|
|
@@ -11027,7 +12390,7 @@ class IndexedDbPersistence {
|
|
|
11027
12390
|
* If set to true, forcefully obtains database access. Existing tabs will
|
|
11028
12391
|
* no longer be able to access IndexedDB.
|
|
11029
12392
|
*/
|
|
11030
|
-
forceOwningTab) {
|
|
12393
|
+
forceOwningTab, schemaVersion = SCHEMA_VERSION) {
|
|
11031
12394
|
this.allowTabSynchronization = allowTabSynchronization;
|
|
11032
12395
|
this.persistenceKey = persistenceKey;
|
|
11033
12396
|
this.clientId = clientId;
|
|
@@ -11036,6 +12399,7 @@ class IndexedDbPersistence {
|
|
|
11036
12399
|
this.document = document;
|
|
11037
12400
|
this.sequenceNumberSyncer = sequenceNumberSyncer;
|
|
11038
12401
|
this.forceOwningTab = forceOwningTab;
|
|
12402
|
+
this.schemaVersion = schemaVersion;
|
|
11039
12403
|
this.listenSequence = null;
|
|
11040
12404
|
this._started = false;
|
|
11041
12405
|
this.isPrimary = false;
|
|
@@ -11057,10 +12421,9 @@ class IndexedDbPersistence {
|
|
|
11057
12421
|
this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
|
|
11058
12422
|
this.dbName = persistenceKey + MAIN_DATABASE;
|
|
11059
12423
|
this.serializer = new LocalSerializer(serializer);
|
|
11060
|
-
this.simpleDb = new SimpleDb(this.dbName,
|
|
12424
|
+
this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
|
|
11061
12425
|
this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
|
|
11062
|
-
this.
|
|
11063
|
-
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
|
|
12426
|
+
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
|
|
11064
12427
|
this.bundleCache = new IndexedDbBundleCache();
|
|
11065
12428
|
if (this.window && this.window.localStorage) {
|
|
11066
12429
|
this.webStorage = this.window.localStorage;
|
|
@@ -11396,8 +12759,8 @@ class IndexedDbPersistence {
|
|
|
11396
12759
|
get started() {
|
|
11397
12760
|
return this._started;
|
|
11398
12761
|
}
|
|
11399
|
-
getMutationQueue(user) {
|
|
11400
|
-
return IndexedDbMutationQueue.forUser(user, this.serializer,
|
|
12762
|
+
getMutationQueue(user, indexManager) {
|
|
12763
|
+
return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
|
|
11401
12764
|
}
|
|
11402
12765
|
getTargetCache() {
|
|
11403
12766
|
return this.targetCache;
|
|
@@ -11405,8 +12768,11 @@ class IndexedDbPersistence {
|
|
|
11405
12768
|
getRemoteDocumentCache() {
|
|
11406
12769
|
return this.remoteDocumentCache;
|
|
11407
12770
|
}
|
|
11408
|
-
getIndexManager() {
|
|
11409
|
-
return
|
|
12771
|
+
getIndexManager(user) {
|
|
12772
|
+
return new IndexedDbIndexManager(user);
|
|
12773
|
+
}
|
|
12774
|
+
getDocumentOverlayCache(user) {
|
|
12775
|
+
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
11410
12776
|
}
|
|
11411
12777
|
getBundleCache() {
|
|
11412
12778
|
return this.bundleCache;
|
|
@@ -11414,11 +12780,12 @@ class IndexedDbPersistence {
|
|
|
11414
12780
|
runTransaction(action, mode, transactionOperation) {
|
|
11415
12781
|
logDebug(LOG_TAG$d, 'Starting transaction:', action);
|
|
11416
12782
|
const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
|
|
12783
|
+
const objectStores = getObjectStores(this.schemaVersion);
|
|
11417
12784
|
let persistenceTransaction;
|
|
11418
12785
|
// Do all transactions as readwrite against all object stores, since we
|
|
11419
12786
|
// are the only reader/writer.
|
|
11420
12787
|
return this.simpleDb
|
|
11421
|
-
.runTransaction(action, simpleDbMode,
|
|
12788
|
+
.runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
|
|
11422
12789
|
persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
|
|
11423
12790
|
? this.listenSequence.next()
|
|
11424
12791
|
: ListenSequence.INVALID);
|
|
@@ -11790,39 +13157,28 @@ class LocalDocumentsView {
|
|
|
11790
13157
|
getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
|
|
11791
13158
|
// Query the remote documents and overlay mutations.
|
|
11792
13159
|
let results;
|
|
11793
|
-
let mutationBatches;
|
|
11794
13160
|
return this.remoteDocumentCache
|
|
11795
|
-
.
|
|
13161
|
+
.getAll(transaction, query.path, sinceReadTime)
|
|
11796
13162
|
.next(queryResults => {
|
|
11797
13163
|
results = queryResults;
|
|
11798
13164
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
11799
13165
|
})
|
|
11800
|
-
.next(
|
|
11801
|
-
mutationBatches
|
|
11802
|
-
|
|
11803
|
-
|
|
11804
|
-
|
|
11805
|
-
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
|
|
11810
|
-
|
|
11811
|
-
|
|
11812
|
-
|
|
11813
|
-
let document = results.get(key);
|
|
11814
|
-
if (document == null) {
|
|
11815
|
-
// Create invalid document to apply mutations on top of
|
|
11816
|
-
document = MutableDocument.newInvalidDocument(key);
|
|
11817
|
-
results = results.insert(key, document);
|
|
11818
|
-
}
|
|
11819
|
-
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
11820
|
-
if (!document.isFoundDocument()) {
|
|
11821
|
-
results = results.remove(key);
|
|
11822
|
-
}
|
|
13166
|
+
.next(mutationBatches => {
|
|
13167
|
+
for (const batch of mutationBatches) {
|
|
13168
|
+
for (const mutation of batch.mutations) {
|
|
13169
|
+
const key = mutation.key;
|
|
13170
|
+
let document = results.get(key);
|
|
13171
|
+
if (document == null) {
|
|
13172
|
+
// Create invalid document to apply mutations on top of
|
|
13173
|
+
document = MutableDocument.newInvalidDocument(key);
|
|
13174
|
+
results = results.insert(key, document);
|
|
13175
|
+
}
|
|
13176
|
+
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
13177
|
+
if (!document.isFoundDocument()) {
|
|
13178
|
+
results = results.remove(key);
|
|
11823
13179
|
}
|
|
11824
13180
|
}
|
|
11825
|
-
}
|
|
13181
|
+
}
|
|
11826
13182
|
})
|
|
11827
13183
|
.next(() => {
|
|
11828
13184
|
// Finally, filter out any documents that don't actually match
|
|
@@ -11835,29 +13191,6 @@ class LocalDocumentsView {
|
|
|
11835
13191
|
return results;
|
|
11836
13192
|
});
|
|
11837
13193
|
}
|
|
11838
|
-
addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
|
|
11839
|
-
let missingBaseDocEntriesForPatching = documentKeySet();
|
|
11840
|
-
for (const batch of matchingMutationBatches) {
|
|
11841
|
-
for (const mutation of batch.mutations) {
|
|
11842
|
-
if (mutation instanceof PatchMutation &&
|
|
11843
|
-
existingDocuments.get(mutation.key) === null) {
|
|
11844
|
-
missingBaseDocEntriesForPatching =
|
|
11845
|
-
missingBaseDocEntriesForPatching.add(mutation.key);
|
|
11846
|
-
}
|
|
11847
|
-
}
|
|
11848
|
-
}
|
|
11849
|
-
let mergedDocuments = existingDocuments;
|
|
11850
|
-
return this.remoteDocumentCache
|
|
11851
|
-
.getEntries(transaction, missingBaseDocEntriesForPatching)
|
|
11852
|
-
.next(missingBaseDocs => {
|
|
11853
|
-
missingBaseDocs.forEach((key, doc) => {
|
|
11854
|
-
if (doc.isFoundDocument()) {
|
|
11855
|
-
mergedDocuments = mergedDocuments.insert(key, doc);
|
|
11856
|
-
}
|
|
11857
|
-
});
|
|
11858
|
-
return mergedDocuments;
|
|
11859
|
-
});
|
|
11860
|
-
}
|
|
11861
13194
|
}
|
|
11862
13195
|
|
|
11863
13196
|
/**
|
|
@@ -11916,11 +13249,18 @@ class LocalStoreImpl {
|
|
|
11916
13249
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
11917
13250
|
*/
|
|
11918
13251
|
this.lastDocumentChangeReadTime = SnapshotVersion.min();
|
|
11919
|
-
this.mutationQueue = persistence.getMutationQueue(initialUser);
|
|
11920
13252
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
11921
13253
|
this.targetCache = persistence.getTargetCache();
|
|
11922
|
-
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
|
|
11923
13254
|
this.bundleCache = persistence.getBundleCache();
|
|
13255
|
+
this.initializeUserComponents(initialUser);
|
|
13256
|
+
}
|
|
13257
|
+
initializeUserComponents(user) {
|
|
13258
|
+
// TODO(indexing): Add spec tests that test these components change after a
|
|
13259
|
+
// user change
|
|
13260
|
+
this.indexManager = this.persistence.getIndexManager(user);
|
|
13261
|
+
this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
|
|
13262
|
+
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
|
|
13263
|
+
this.remoteDocuments.setIndexManager(this.indexManager);
|
|
11924
13264
|
this.queryEngine.setLocalDocumentsView(this.localDocuments);
|
|
11925
13265
|
}
|
|
11926
13266
|
collectGarbage(garbageCollector) {
|
|
@@ -11942,8 +13282,6 @@ persistence, queryEngine, initialUser, serializer) {
|
|
|
11942
13282
|
// change.
|
|
11943
13283
|
async function localStoreHandleUserChange(localStore, user) {
|
|
11944
13284
|
const localStoreImpl = debugCast(localStore);
|
|
11945
|
-
let newMutationQueue = localStoreImpl.mutationQueue;
|
|
11946
|
-
let newLocalDocuments = localStoreImpl.localDocuments;
|
|
11947
13285
|
const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
|
|
11948
13286
|
// Swap out the mutation queue, grabbing the pending mutation batches
|
|
11949
13287
|
// before and after.
|
|
@@ -11952,11 +13290,8 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11952
13290
|
.getAllMutationBatches(txn)
|
|
11953
13291
|
.next(promisedOldBatches => {
|
|
11954
13292
|
oldBatches = promisedOldBatches;
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
// MutationQueue.
|
|
11958
|
-
newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
|
|
11959
|
-
return newMutationQueue.getAllMutationBatches(txn);
|
|
13293
|
+
localStoreImpl.initializeUserComponents(user);
|
|
13294
|
+
return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
|
|
11960
13295
|
})
|
|
11961
13296
|
.next(newBatches => {
|
|
11962
13297
|
const removedBatchIds = [];
|
|
@@ -11977,7 +13312,7 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11977
13312
|
}
|
|
11978
13313
|
// Return the set of all (potentially) changed documents and the list
|
|
11979
13314
|
// of mutation batch IDs that were affected by change.
|
|
11980
|
-
return
|
|
13315
|
+
return localStoreImpl.localDocuments
|
|
11981
13316
|
.getDocuments(txn, changedKeys)
|
|
11982
13317
|
.next(affectedDocuments => {
|
|
11983
13318
|
return {
|
|
@@ -11988,9 +13323,6 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11988
13323
|
});
|
|
11989
13324
|
});
|
|
11990
13325
|
});
|
|
11991
|
-
localStoreImpl.mutationQueue = newMutationQueue;
|
|
11992
|
-
localStoreImpl.localDocuments = newLocalDocuments;
|
|
11993
|
-
localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
|
|
11994
13326
|
return result;
|
|
11995
13327
|
}
|
|
11996
13328
|
/* Accepts locally generated Mutations and commit them to storage. */
|
|
@@ -12703,6 +14035,122 @@ class MemoryBundleCache {
|
|
|
12703
14035
|
}
|
|
12704
14036
|
}
|
|
12705
14037
|
|
|
14038
|
+
/**
|
|
14039
|
+
* @license
|
|
14040
|
+
* Copyright 2022 Google LLC
|
|
14041
|
+
*
|
|
14042
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14043
|
+
* you may not use this file except in compliance with the License.
|
|
14044
|
+
* You may obtain a copy of the License at
|
|
14045
|
+
*
|
|
14046
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14047
|
+
*
|
|
14048
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14049
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14050
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14051
|
+
* See the License for the specific language governing permissions and
|
|
14052
|
+
* limitations under the License.
|
|
14053
|
+
*/
|
|
14054
|
+
/**
|
|
14055
|
+
* An in-memory implementation of DocumentOverlayCache.
|
|
14056
|
+
*/
|
|
14057
|
+
class MemoryDocumentOverlayCache {
|
|
14058
|
+
constructor() {
|
|
14059
|
+
// A map sorted by DocumentKey, whose value is a pair of the largest batch id
|
|
14060
|
+
// for the overlay and the overlay itself.
|
|
14061
|
+
this.overlays = new SortedMap(DocumentKey.comparator);
|
|
14062
|
+
this.overlayByBatchId = new Map();
|
|
14063
|
+
}
|
|
14064
|
+
getOverlay(transaction, key) {
|
|
14065
|
+
return PersistencePromise.resolve(this.overlays.get(key));
|
|
14066
|
+
}
|
|
14067
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
14068
|
+
overlays.forEach(mutation => {
|
|
14069
|
+
this.saveOverlay(transaction, largestBatchId, mutation);
|
|
14070
|
+
});
|
|
14071
|
+
return PersistencePromise.resolve();
|
|
14072
|
+
}
|
|
14073
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
14074
|
+
const keys = this.overlayByBatchId.get(batchId);
|
|
14075
|
+
if (keys !== undefined) {
|
|
14076
|
+
keys.forEach(key => (this.overlays = this.overlays.remove(key)));
|
|
14077
|
+
this.overlayByBatchId.delete(batchId);
|
|
14078
|
+
}
|
|
14079
|
+
return PersistencePromise.resolve();
|
|
14080
|
+
}
|
|
14081
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
14082
|
+
const result = new Map();
|
|
14083
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
14084
|
+
const prefix = new DocumentKey(collection.child(''));
|
|
14085
|
+
const iter = this.overlays.getIteratorFrom(prefix);
|
|
14086
|
+
while (iter.hasNext()) {
|
|
14087
|
+
const entry = iter.getNext();
|
|
14088
|
+
const overlay = entry.value;
|
|
14089
|
+
const key = overlay.getKey();
|
|
14090
|
+
if (!collection.isPrefixOf(key.path)) {
|
|
14091
|
+
break;
|
|
14092
|
+
}
|
|
14093
|
+
// Documents from sub-collections
|
|
14094
|
+
if (key.path.length !== immediateChildrenPathLength) {
|
|
14095
|
+
continue;
|
|
14096
|
+
}
|
|
14097
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14098
|
+
result.set(overlay.getKey(), overlay);
|
|
14099
|
+
}
|
|
14100
|
+
}
|
|
14101
|
+
return PersistencePromise.resolve(result);
|
|
14102
|
+
}
|
|
14103
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
14104
|
+
let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
|
|
14105
|
+
const iter = this.overlays.getIterator();
|
|
14106
|
+
while (iter.hasNext()) {
|
|
14107
|
+
const entry = iter.getNext();
|
|
14108
|
+
const overlay = entry.value;
|
|
14109
|
+
const key = overlay.getKey();
|
|
14110
|
+
if (key.getCollectionGroup() !== collectionGroup) {
|
|
14111
|
+
continue;
|
|
14112
|
+
}
|
|
14113
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14114
|
+
let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
|
|
14115
|
+
if (overlaysForBatchId === null) {
|
|
14116
|
+
overlaysForBatchId = new Map();
|
|
14117
|
+
batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
|
|
14118
|
+
}
|
|
14119
|
+
overlaysForBatchId.set(overlay.getKey(), overlay);
|
|
14120
|
+
}
|
|
14121
|
+
}
|
|
14122
|
+
const result = new Map();
|
|
14123
|
+
const batchIter = batchIdToOverlays.getIterator();
|
|
14124
|
+
while (batchIter.hasNext()) {
|
|
14125
|
+
const entry = batchIter.getNext();
|
|
14126
|
+
const overlays = entry.value;
|
|
14127
|
+
overlays.forEach((overlay, key) => result.set(key, overlay));
|
|
14128
|
+
if (result.size >= count) {
|
|
14129
|
+
break;
|
|
14130
|
+
}
|
|
14131
|
+
}
|
|
14132
|
+
return PersistencePromise.resolve(result);
|
|
14133
|
+
}
|
|
14134
|
+
saveOverlay(transaction, largestBatchId, mutation) {
|
|
14135
|
+
if (mutation === null) {
|
|
14136
|
+
return;
|
|
14137
|
+
}
|
|
14138
|
+
// Remove the association of the overlay to its batch id.
|
|
14139
|
+
const existing = this.overlays.get(mutation.key);
|
|
14140
|
+
if (existing !== null) {
|
|
14141
|
+
this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
|
|
14142
|
+
}
|
|
14143
|
+
this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
|
|
14144
|
+
// Create the association of this overlay to the given largestBatchId.
|
|
14145
|
+
let batch = this.overlayByBatchId.get(largestBatchId);
|
|
14146
|
+
if (batch === undefined) {
|
|
14147
|
+
batch = new Set();
|
|
14148
|
+
this.overlayByBatchId.set(largestBatchId, batch);
|
|
14149
|
+
}
|
|
14150
|
+
batch.add(mutation.key);
|
|
14151
|
+
}
|
|
14152
|
+
}
|
|
14153
|
+
|
|
12706
14154
|
/**
|
|
12707
14155
|
* @license
|
|
12708
14156
|
* Copyright 2017 Google LLC
|
|
@@ -13056,14 +14504,16 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13056
14504
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13057
14505
|
* calculating the size.
|
|
13058
14506
|
*/
|
|
13059
|
-
constructor(
|
|
13060
|
-
this.indexManager = indexManager;
|
|
14507
|
+
constructor(sizer) {
|
|
13061
14508
|
this.sizer = sizer;
|
|
13062
14509
|
/** Underlying cache of documents and their read times. */
|
|
13063
14510
|
this.docs = documentEntryMap();
|
|
13064
14511
|
/** Size of all cached documents. */
|
|
13065
14512
|
this.size = 0;
|
|
13066
14513
|
}
|
|
14514
|
+
setIndexManager(indexManager) {
|
|
14515
|
+
this.indexManager = indexManager;
|
|
14516
|
+
}
|
|
13067
14517
|
/**
|
|
13068
14518
|
* Adds the supplied entry to the cache and updates the cache size as appropriate.
|
|
13069
14519
|
*
|
|
@@ -13111,21 +14561,22 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13111
14561
|
});
|
|
13112
14562
|
return PersistencePromise.resolve(results);
|
|
13113
14563
|
}
|
|
13114
|
-
|
|
14564
|
+
getAll(transaction, collectionPath, sinceReadTime) {
|
|
13115
14565
|
let results = mutableDocumentMap();
|
|
13116
14566
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
13117
14567
|
// the documents we need to match the query against.
|
|
13118
|
-
const prefix = new DocumentKey(
|
|
14568
|
+
const prefix = new DocumentKey(collectionPath.child(''));
|
|
13119
14569
|
const iterator = this.docs.getIteratorFrom(prefix);
|
|
13120
14570
|
while (iterator.hasNext()) {
|
|
13121
14571
|
const { key, value: { document } } = iterator.getNext();
|
|
13122
|
-
if (!
|
|
14572
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
13123
14573
|
break;
|
|
13124
14574
|
}
|
|
13125
|
-
if (
|
|
14575
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14576
|
+
// Exclude entries from subcollections.
|
|
13126
14577
|
continue;
|
|
13127
14578
|
}
|
|
13128
|
-
if (
|
|
14579
|
+
if (document.readTime.compareTo(sinceReadTime) <= 0) {
|
|
13129
14580
|
continue;
|
|
13130
14581
|
}
|
|
13131
14582
|
results = results.insert(document.key, document.mutableCopy());
|
|
@@ -13147,13 +14598,12 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
13147
14598
|
/**
|
|
13148
14599
|
* Creates a new memory-only RemoteDocumentCache.
|
|
13149
14600
|
*
|
|
13150
|
-
* @param indexManager - A class that manages collection group indices.
|
|
13151
14601
|
* @param sizer - Used to assess the size of a document. For eager GC, this is
|
|
13152
14602
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
13153
14603
|
* calculating the size.
|
|
13154
14604
|
*/
|
|
13155
|
-
function newMemoryRemoteDocumentCache(
|
|
13156
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
14605
|
+
function newMemoryRemoteDocumentCache(sizer) {
|
|
14606
|
+
return new MemoryRemoteDocumentCacheImpl(sizer);
|
|
13157
14607
|
}
|
|
13158
14608
|
/**
|
|
13159
14609
|
* Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
|
|
@@ -13347,6 +14797,7 @@ class MemoryPersistence {
|
|
|
13347
14797
|
*/
|
|
13348
14798
|
constructor(referenceDelegateFactory, serializer) {
|
|
13349
14799
|
this.mutationQueues = {};
|
|
14800
|
+
this.overlays = {};
|
|
13350
14801
|
this.listenSequence = new ListenSequence(0);
|
|
13351
14802
|
this._started = false;
|
|
13352
14803
|
this._started = true;
|
|
@@ -13354,7 +14805,7 @@ class MemoryPersistence {
|
|
|
13354
14805
|
this.targetCache = new MemoryTargetCache(this);
|
|
13355
14806
|
const sizer = (doc) => this.referenceDelegate.documentSize(doc);
|
|
13356
14807
|
this.indexManager = new MemoryIndexManager();
|
|
13357
|
-
this.remoteDocumentCache = newMemoryRemoteDocumentCache(
|
|
14808
|
+
this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
|
|
13358
14809
|
this.serializer = new LocalSerializer(serializer);
|
|
13359
14810
|
this.bundleCache = new MemoryBundleCache(this.serializer);
|
|
13360
14811
|
}
|
|
@@ -13375,13 +14826,23 @@ class MemoryPersistence {
|
|
|
13375
14826
|
setNetworkEnabled() {
|
|
13376
14827
|
// No op.
|
|
13377
14828
|
}
|
|
13378
|
-
getIndexManager() {
|
|
14829
|
+
getIndexManager(user) {
|
|
14830
|
+
// We do not currently support indices for memory persistence, so we can
|
|
14831
|
+
// return the same shared instance of the memory index manager.
|
|
13379
14832
|
return this.indexManager;
|
|
13380
14833
|
}
|
|
13381
|
-
|
|
14834
|
+
getDocumentOverlayCache(user) {
|
|
14835
|
+
let overlay = this.overlays[user.toKey()];
|
|
14836
|
+
if (!overlay) {
|
|
14837
|
+
overlay = new MemoryDocumentOverlayCache();
|
|
14838
|
+
this.overlays[user.toKey()] = overlay;
|
|
14839
|
+
}
|
|
14840
|
+
return overlay;
|
|
14841
|
+
}
|
|
14842
|
+
getMutationQueue(user, indexManager) {
|
|
13382
14843
|
let queue = this.mutationQueues[user.toKey()];
|
|
13383
14844
|
if (!queue) {
|
|
13384
|
-
queue = new MemoryMutationQueue(
|
|
14845
|
+
queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
|
|
13385
14846
|
this.mutationQueues[user.toKey()] = queue;
|
|
13386
14847
|
}
|
|
13387
14848
|
return queue;
|
|
@@ -14526,7 +15987,7 @@ const LOG_TAG$9 = 'Connection';
|
|
|
14526
15987
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
14527
15988
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
14528
15989
|
hardAssert(authToken === null || authToken.type === 'OAuth');
|
|
14529
|
-
const metadata = new
|
|
15990
|
+
const metadata = new grpc__namespace.Metadata();
|
|
14530
15991
|
if (authToken) {
|
|
14531
15992
|
authToken.headers.forEach((value, key) => metadata.set(key, value));
|
|
14532
15993
|
}
|
|
@@ -14557,10 +16018,10 @@ class GrpcConnection {
|
|
|
14557
16018
|
ensureActiveStub() {
|
|
14558
16019
|
if (!this.cachedStub) {
|
|
14559
16020
|
logDebug(LOG_TAG$9, 'Creating Firestore stub.');
|
|
14560
|
-
const credentials
|
|
14561
|
-
?
|
|
14562
|
-
:
|
|
14563
|
-
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials
|
|
16021
|
+
const credentials = this.databaseInfo.ssl
|
|
16022
|
+
? grpc__namespace.credentials.createSsl()
|
|
16023
|
+
: grpc__namespace.credentials.createInsecure();
|
|
16024
|
+
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials);
|
|
14564
16025
|
}
|
|
14565
16026
|
return this.cachedStub;
|
|
14566
16027
|
}
|
|
@@ -17392,8 +18853,8 @@ const protoLoaderOptions = {
|
|
|
17392
18853
|
* @returns The GrpcObject representing our protos.
|
|
17393
18854
|
*/
|
|
17394
18855
|
function loadProtos() {
|
|
17395
|
-
const packageDefinition =
|
|
17396
|
-
return
|
|
18856
|
+
const packageDefinition = protoLoader__namespace.fromJSON(protos$1, protoLoaderOptions);
|
|
18857
|
+
return grpc__namespace.loadPackageDefinition(packageDefinition);
|
|
17397
18858
|
}
|
|
17398
18859
|
|
|
17399
18860
|
/**
|
|
@@ -20344,12 +21805,12 @@ async function syncEngineListen(syncEngine, query) {
|
|
|
20344
21805
|
}
|
|
20345
21806
|
else {
|
|
20346
21807
|
const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
|
|
20347
|
-
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
20348
|
-
targetId = targetData.targetId;
|
|
20349
|
-
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20350
21808
|
if (syncEngineImpl.isPrimaryClient) {
|
|
20351
21809
|
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
|
|
20352
21810
|
}
|
|
21811
|
+
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
21812
|
+
targetId = targetData.targetId;
|
|
21813
|
+
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20353
21814
|
}
|
|
20354
21815
|
return viewSnapshot;
|
|
20355
21816
|
}
|
|
@@ -24857,8 +26318,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
|
|
|
24857
26318
|
this.metadata = metadata;
|
|
24858
26319
|
}
|
|
24859
26320
|
/**
|
|
24860
|
-
*
|
|
24861
|
-
* exists. True if the document exists.
|
|
26321
|
+
* Returns whether or not the data exists. True if the document exists.
|
|
24862
26322
|
*/
|
|
24863
26323
|
exists() {
|
|
24864
26324
|
return super.exists();
|
|
@@ -25232,51 +26692,53 @@ function limitToLast(limit) {
|
|
|
25232
26692
|
return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
|
|
25233
26693
|
}
|
|
25234
26694
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
25235
|
-
constructor(type, _docOrFields,
|
|
26695
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25236
26696
|
super();
|
|
25237
26697
|
this.type = type;
|
|
25238
26698
|
this._docOrFields = _docOrFields;
|
|
25239
|
-
this.
|
|
26699
|
+
this._inclusive = _inclusive;
|
|
25240
26700
|
}
|
|
25241
26701
|
_apply(query) {
|
|
25242
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26702
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25243
26703
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
25244
26704
|
}
|
|
25245
26705
|
}
|
|
25246
26706
|
function startAt(...docOrFields) {
|
|
25247
|
-
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26707
|
+
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26708
|
+
/*inclusive=*/ true);
|
|
25248
26709
|
}
|
|
25249
26710
|
function startAfter(...docOrFields) {
|
|
25250
26711
|
return new QueryStartAtConstraint('startAfter', docOrFields,
|
|
25251
|
-
/*
|
|
26712
|
+
/*inclusive=*/ false);
|
|
25252
26713
|
}
|
|
25253
26714
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
25254
|
-
constructor(type, _docOrFields,
|
|
26715
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25255
26716
|
super();
|
|
25256
26717
|
this.type = type;
|
|
25257
26718
|
this._docOrFields = _docOrFields;
|
|
25258
|
-
this.
|
|
26719
|
+
this._inclusive = _inclusive;
|
|
25259
26720
|
}
|
|
25260
26721
|
_apply(query) {
|
|
25261
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26722
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25262
26723
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
25263
26724
|
}
|
|
25264
26725
|
}
|
|
25265
26726
|
function endBefore(...docOrFields) {
|
|
25266
|
-
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26727
|
+
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26728
|
+
/*inclusive=*/ false);
|
|
25267
26729
|
}
|
|
25268
26730
|
function endAt(...docOrFields) {
|
|
25269
|
-
return new QueryEndAtConstraint('endAt', docOrFields, /*
|
|
26731
|
+
return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
|
|
25270
26732
|
}
|
|
25271
26733
|
/** Helper function to create a bound from a document or fields */
|
|
25272
|
-
function newQueryBoundFromDocOrFields(query, methodName, docOrFields,
|
|
26734
|
+
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
25273
26735
|
docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
|
|
25274
26736
|
if (docOrFields[0] instanceof DocumentSnapshot$1) {
|
|
25275
|
-
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document,
|
|
26737
|
+
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
|
|
25276
26738
|
}
|
|
25277
26739
|
else {
|
|
25278
26740
|
const reader = newUserDataReader(query.firestore);
|
|
25279
|
-
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields,
|
|
26741
|
+
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
|
|
25280
26742
|
}
|
|
25281
26743
|
}
|
|
25282
26744
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
@@ -25334,7 +26796,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
|
|
|
25334
26796
|
* of the query or if any of the fields in the order by are an uncommitted
|
|
25335
26797
|
* server timestamp.
|
|
25336
26798
|
*/
|
|
25337
|
-
function newQueryBoundFromDocument(query, databaseId, methodName, doc,
|
|
26799
|
+
function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
|
|
25338
26800
|
if (!doc) {
|
|
25339
26801
|
throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
|
|
25340
26802
|
`${methodName}().`);
|
|
@@ -25371,12 +26833,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
|
|
|
25371
26833
|
}
|
|
25372
26834
|
}
|
|
25373
26835
|
}
|
|
25374
|
-
return new Bound(components,
|
|
26836
|
+
return new Bound(components, inclusive);
|
|
25375
26837
|
}
|
|
25376
26838
|
/**
|
|
25377
26839
|
* Converts a list of field values to a `Bound` for the given query.
|
|
25378
26840
|
*/
|
|
25379
|
-
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values,
|
|
26841
|
+
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
|
|
25380
26842
|
// Use explicit order by's because it has to match the query the user made
|
|
25381
26843
|
const orderBy = query.explicitOrderBy;
|
|
25382
26844
|
if (values.length > orderBy.length) {
|
|
@@ -25413,7 +26875,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
|
|
|
25413
26875
|
components.push(wrapped);
|
|
25414
26876
|
}
|
|
25415
26877
|
}
|
|
25416
|
-
return new Bound(components,
|
|
26878
|
+
return new Bound(components, inclusive);
|
|
25417
26879
|
}
|
|
25418
26880
|
/**
|
|
25419
26881
|
* Parses the given `documentIdValue` into a `ReferenceValue`, throwing
|