@firebase/firestore 3.4.4 → 3.4.5-2022116201138
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/CHANGELOG.md +12 -0
- package/dist/firestore/src/api/credentials.d.ts +1 -0
- package/dist/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
- package/dist/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/firestore/src/api.d.ts +1 -1
- package/dist/firestore/src/core/database_info.d.ts +1 -0
- package/dist/firestore/src/core/target.d.ts +34 -5
- 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 +19 -3
- 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/remote_document_change_buffer.d.ts +3 -12
- package/dist/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/firestore/src/model/document.d.ts +7 -0
- package/dist/firestore/src/model/document_key.d.ts +6 -0
- package/dist/firestore/src/model/field_index.d.ts +87 -7
- 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/model/values.d.ts +7 -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 +5 -3
- package/dist/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/firestore/test/unit/model/field_index.test.d.ts +17 -0
- package/dist/firestore/test/unit/model/target.test.d.ts +17 -0
- package/dist/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/firestore/test/util/helpers.d.ts +8 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.esm2017.js +4815 -3708
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4500 -3545
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +2001 -389
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +1981 -390
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +4798 -3733
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +265 -26
- package/dist/lite/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
- package/dist/lite/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/lite/firestore/src/api.d.ts +1 -1
- package/dist/lite/firestore/src/core/database_info.d.ts +1 -0
- package/dist/lite/firestore/src/core/target.d.ts +34 -5
- 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 +19 -3
- 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/remote_document_change_buffer.d.ts +3 -12
- package/dist/lite/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/lite/firestore/src/model/document.d.ts +7 -0
- package/dist/lite/firestore/src/model/document_key.d.ts +6 -0
- package/dist/lite/firestore/src/model/field_index.d.ts +87 -7
- 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/model/values.d.ts +7 -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 +5 -3
- package/dist/lite/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/lite/firestore/test/unit/model/field_index.test.d.ts +17 -0
- package/dist/lite/firestore/test/unit/model/target.test.d.ts +17 -0
- package/dist/lite/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/lite/firestore/test/util/helpers.d.ts +8 -2
- package/dist/lite/index.browser.esm2017.js +1166 -1143
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +357 -337
- package/dist/lite/index.browser.esm5.js.map +1 -1
- package/dist/lite/index.node.cjs.js +64 -34
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +64 -34
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +344 -321
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +16 -2
- package/dist/lite/packages/firestore/dist/lite/index.browser.esm2017.d.ts +122 -121
- package/dist/lite/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/lite/packages/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
- package/dist/lite/packages/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/lite/packages/firestore/src/api.d.ts +1 -1
- package/dist/lite/packages/firestore/src/core/database_info.d.ts +1 -0
- package/dist/lite/packages/firestore/src/core/target.d.ts +34 -5
- 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 +19 -3
- 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/remote_document_change_buffer.d.ts +3 -12
- package/dist/lite/packages/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/lite/packages/firestore/src/model/document.d.ts +7 -0
- package/dist/lite/packages/firestore/src/model/document_key.d.ts +6 -0
- package/dist/lite/packages/firestore/src/model/field_index.d.ts +87 -7
- 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/model/values.d.ts +7 -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 +5 -3
- 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/field_index.test.d.ts +17 -0
- package/dist/lite/packages/firestore/test/unit/model/target.test.d.ts +17 -0
- 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 +8 -2
- package/dist/lite/private.d.ts +9 -2
- package/dist/packages/firestore/dist/index.esm2017.d.ts +199 -194
- package/dist/packages/firestore/src/api/credentials.d.ts +1 -0
- package/dist/packages/firestore/src/api/{query.d.ts → filter.d.ts} +0 -0
- package/dist/packages/firestore/src/api/snapshot.d.ts +1 -2
- package/dist/packages/firestore/src/api.d.ts +1 -1
- package/dist/packages/firestore/src/core/database_info.d.ts +1 -0
- package/dist/packages/firestore/src/core/target.d.ts +34 -5
- 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 +19 -3
- 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/remote_document_change_buffer.d.ts +3 -12
- package/dist/packages/firestore/src/local/simple_db.d.ts +1 -0
- package/dist/packages/firestore/src/model/document.d.ts +7 -0
- package/dist/packages/firestore/src/model/document_key.d.ts +6 -0
- package/dist/packages/firestore/src/model/field_index.d.ts +87 -7
- 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/model/values.d.ts +7 -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 +5 -3
- package/dist/packages/firestore/test/unit/local/test_remote_document_change_buffer.d.ts +1 -1
- package/dist/packages/firestore/test/unit/model/field_index.test.d.ts +17 -0
- package/dist/packages/firestore/test/unit/model/target.test.d.ts +17 -0
- package/dist/packages/firestore/test/unit/model/target_index_matcher.test.d.ts +17 -0
- package/dist/packages/firestore/test/util/helpers.d.ts +8 -2
- package/dist/private.d.ts +258 -26
- package/package.json +4 -4
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.
|
|
36
|
+
const version$1 = "3.4.5-2022116201138";
|
|
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.7-2022116201138";
|
|
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);
|
|
@@ -3378,6 +3583,75 @@ function getLocalWriteTime(value) {
|
|
|
3378
3583
|
return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
|
|
3379
3584
|
}
|
|
3380
3585
|
|
|
3586
|
+
/**
|
|
3587
|
+
* @license
|
|
3588
|
+
* Copyright 2017 Google LLC
|
|
3589
|
+
*
|
|
3590
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3591
|
+
* you may not use this file except in compliance with the License.
|
|
3592
|
+
* You may obtain a copy of the License at
|
|
3593
|
+
*
|
|
3594
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3595
|
+
*
|
|
3596
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3597
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3598
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3599
|
+
* See the License for the specific language governing permissions and
|
|
3600
|
+
* limitations under the License.
|
|
3601
|
+
*/
|
|
3602
|
+
class DatabaseInfo {
|
|
3603
|
+
/**
|
|
3604
|
+
* Constructs a DatabaseInfo using the provided host, databaseId and
|
|
3605
|
+
* persistenceKey.
|
|
3606
|
+
*
|
|
3607
|
+
* @param databaseId - The database to use.
|
|
3608
|
+
* @param appId - The Firebase App Id.
|
|
3609
|
+
* @param persistenceKey - A unique identifier for this Firestore's local
|
|
3610
|
+
* storage (used in conjunction with the databaseId).
|
|
3611
|
+
* @param host - The Firestore backend host to connect to.
|
|
3612
|
+
* @param ssl - Whether to use SSL when connecting.
|
|
3613
|
+
* @param forceLongPolling - Whether to use the forceLongPolling option
|
|
3614
|
+
* when using WebChannel as the network transport.
|
|
3615
|
+
* @param autoDetectLongPolling - Whether to use the detectBufferingProxy
|
|
3616
|
+
* option when using WebChannel as the network transport.
|
|
3617
|
+
* @param useFetchStreams Whether to use the Fetch API instead of
|
|
3618
|
+
* XMLHTTPRequest
|
|
3619
|
+
*/
|
|
3620
|
+
constructor(databaseId, appId, persistenceKey, host, ssl, forceLongPolling, autoDetectLongPolling, useFetchStreams) {
|
|
3621
|
+
this.databaseId = databaseId;
|
|
3622
|
+
this.appId = appId;
|
|
3623
|
+
this.persistenceKey = persistenceKey;
|
|
3624
|
+
this.host = host;
|
|
3625
|
+
this.ssl = ssl;
|
|
3626
|
+
this.forceLongPolling = forceLongPolling;
|
|
3627
|
+
this.autoDetectLongPolling = autoDetectLongPolling;
|
|
3628
|
+
this.useFetchStreams = useFetchStreams;
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
/** The default database name for a project. */
|
|
3632
|
+
const DEFAULT_DATABASE_NAME = '(default)';
|
|
3633
|
+
/**
|
|
3634
|
+
* Represents the database ID a Firestore client is associated with.
|
|
3635
|
+
* @internal
|
|
3636
|
+
*/
|
|
3637
|
+
class DatabaseId {
|
|
3638
|
+
constructor(projectId, database) {
|
|
3639
|
+
this.projectId = projectId;
|
|
3640
|
+
this.database = database ? database : DEFAULT_DATABASE_NAME;
|
|
3641
|
+
}
|
|
3642
|
+
static empty() {
|
|
3643
|
+
return new DatabaseId('', '');
|
|
3644
|
+
}
|
|
3645
|
+
get isDefaultDatabase() {
|
|
3646
|
+
return this.database === DEFAULT_DATABASE_NAME;
|
|
3647
|
+
}
|
|
3648
|
+
isEqual(other) {
|
|
3649
|
+
return (other instanceof DatabaseId &&
|
|
3650
|
+
other.projectId === this.projectId &&
|
|
3651
|
+
other.database === this.database);
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
|
|
3381
3655
|
/**
|
|
3382
3656
|
* @license
|
|
3383
3657
|
* Copyright 2017 Google LLC
|
|
@@ -3449,11 +3723,25 @@ class DocumentKey {
|
|
|
3449
3723
|
static fromName(name) {
|
|
3450
3724
|
return new DocumentKey(ResourcePath.fromString(name).popFirst(5));
|
|
3451
3725
|
}
|
|
3726
|
+
static empty() {
|
|
3727
|
+
return new DocumentKey(ResourcePath.emptyPath());
|
|
3728
|
+
}
|
|
3729
|
+
get collectionGroup() {
|
|
3730
|
+
return this.path.popLast().lastSegment();
|
|
3731
|
+
}
|
|
3452
3732
|
/** Returns true if the document is in the specified collectionId. */
|
|
3453
3733
|
hasCollectionId(collectionId) {
|
|
3454
3734
|
return (this.path.length >= 2 &&
|
|
3455
3735
|
this.path.get(this.path.length - 2) === collectionId);
|
|
3456
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
|
+
}
|
|
3457
3745
|
isEqual(other) {
|
|
3458
3746
|
return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
|
|
3459
3747
|
}
|
|
@@ -3493,6 +3781,13 @@ class DocumentKey {
|
|
|
3493
3781
|
* See the License for the specific language governing permissions and
|
|
3494
3782
|
* limitations under the License.
|
|
3495
3783
|
*/
|
|
3784
|
+
const MAX_VALUE = {
|
|
3785
|
+
mapValue: {
|
|
3786
|
+
fields: {
|
|
3787
|
+
'__type__': { stringValue: '__max___' }
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
};
|
|
3496
3791
|
/** Extracts the backend's type order for the provided value. */
|
|
3497
3792
|
function typeOrder(value) {
|
|
3498
3793
|
if ('nullValue' in value) {
|
|
@@ -3902,6 +4197,10 @@ function deepClone(source) {
|
|
|
3902
4197
|
else {
|
|
3903
4198
|
return Object.assign({}, source);
|
|
3904
4199
|
}
|
|
4200
|
+
}
|
|
4201
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
4202
|
+
function isMaxValue(value) {
|
|
4203
|
+
return valueEquals(value, MAX_VALUE);
|
|
3905
4204
|
}
|
|
3906
4205
|
|
|
3907
4206
|
/**
|
|
@@ -4097,10 +4396,11 @@ function extractFieldMask(value) {
|
|
|
4097
4396
|
* from all views.
|
|
4098
4397
|
*/
|
|
4099
4398
|
class MutableDocument {
|
|
4100
|
-
constructor(key, documentType, version, data, documentState) {
|
|
4399
|
+
constructor(key, documentType, version, readTime, data, documentState) {
|
|
4101
4400
|
this.key = key;
|
|
4102
4401
|
this.documentType = documentType;
|
|
4103
4402
|
this.version = version;
|
|
4403
|
+
this.readTime = readTime;
|
|
4104
4404
|
this.data = data;
|
|
4105
4405
|
this.documentState = documentState;
|
|
4106
4406
|
}
|
|
@@ -4109,18 +4409,18 @@ class MutableDocument {
|
|
|
4109
4409
|
* base document for mutations.
|
|
4110
4410
|
*/
|
|
4111
4411
|
static newInvalidDocument(documentKey) {
|
|
4112
|
-
return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
|
|
4412
|
+
return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
|
|
4113
4413
|
}
|
|
4114
4414
|
/**
|
|
4115
4415
|
* Creates a new document that is known to exist with the given data at the
|
|
4116
4416
|
* given version.
|
|
4117
4417
|
*/
|
|
4118
4418
|
static newFoundDocument(documentKey, version, value) {
|
|
4119
|
-
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, value, 0 /* SYNCED */);
|
|
4419
|
+
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
|
|
4120
4420
|
}
|
|
4121
4421
|
/** Creates a new document that is known to not exist at the given version. */
|
|
4122
4422
|
static newNoDocument(documentKey, version) {
|
|
4123
|
-
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, ObjectValue.empty(), 0 /* SYNCED */);
|
|
4423
|
+
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
|
|
4124
4424
|
}
|
|
4125
4425
|
/**
|
|
4126
4426
|
* Creates a new document that is known to exist at the given version but
|
|
@@ -4128,7 +4428,7 @@ class MutableDocument {
|
|
|
4128
4428
|
* base document).
|
|
4129
4429
|
*/
|
|
4130
4430
|
static newUnknownDocument(documentKey, version) {
|
|
4131
|
-
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
|
|
4431
|
+
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
|
|
4132
4432
|
}
|
|
4133
4433
|
/**
|
|
4134
4434
|
* Changes the document type to indicate that it exists and that its version
|
|
@@ -4172,6 +4472,10 @@ class MutableDocument {
|
|
|
4172
4472
|
this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
|
|
4173
4473
|
return this;
|
|
4174
4474
|
}
|
|
4475
|
+
setReadTime(readTime) {
|
|
4476
|
+
this.readTime = readTime;
|
|
4477
|
+
return this;
|
|
4478
|
+
}
|
|
4175
4479
|
get hasLocalMutations() {
|
|
4176
4480
|
return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
|
|
4177
4481
|
}
|
|
@@ -4202,7 +4506,7 @@ class MutableDocument {
|
|
|
4202
4506
|
this.data.isEqual(other.data));
|
|
4203
4507
|
}
|
|
4204
4508
|
mutableCopy() {
|
|
4205
|
-
return new MutableDocument(this.key, this.documentType, this.version, this.data.clone(), this.documentState);
|
|
4509
|
+
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
|
|
4206
4510
|
}
|
|
4207
4511
|
toString() {
|
|
4208
4512
|
return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
|
|
@@ -4225,6 +4529,133 @@ function compareDocumentsByField(field, d1, d2) {
|
|
|
4225
4529
|
}
|
|
4226
4530
|
}
|
|
4227
4531
|
|
|
4532
|
+
/**
|
|
4533
|
+
* @license
|
|
4534
|
+
* Copyright 2021 Google LLC
|
|
4535
|
+
*
|
|
4536
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4537
|
+
* you may not use this file except in compliance with the License.
|
|
4538
|
+
* You may obtain a copy of the License at
|
|
4539
|
+
*
|
|
4540
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
4541
|
+
*
|
|
4542
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
4543
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
4544
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
4545
|
+
* See the License for the specific language governing permissions and
|
|
4546
|
+
* limitations under the License.
|
|
4547
|
+
*/
|
|
4548
|
+
/**
|
|
4549
|
+
* The initial mutation batch id for each index. Gets updated during index
|
|
4550
|
+
* backfill.
|
|
4551
|
+
*/
|
|
4552
|
+
const INITIAL_LARGEST_BATCH_ID = -1;
|
|
4553
|
+
/**
|
|
4554
|
+
* The initial sequence number for each index. Gets updated during index
|
|
4555
|
+
* backfill.
|
|
4556
|
+
*/
|
|
4557
|
+
const INITIAL_SEQUENCE_NUMBER = 0;
|
|
4558
|
+
/**
|
|
4559
|
+
* An index definition for field indexes in Firestore.
|
|
4560
|
+
*
|
|
4561
|
+
* Every index is associated with a collection. The definition contains a list
|
|
4562
|
+
* of fields and their index kind (which can be `ASCENDING`, `DESCENDING` or
|
|
4563
|
+
* `CONTAINS` for ArrayContains/ArrayContainsAny queries).
|
|
4564
|
+
*
|
|
4565
|
+
* Unlike the backend, the SDK does not differentiate between collection or
|
|
4566
|
+
* collection group-scoped indices. Every index can be used for both single
|
|
4567
|
+
* collection and collection group queries.
|
|
4568
|
+
*/
|
|
4569
|
+
class FieldIndex {
|
|
4570
|
+
constructor(
|
|
4571
|
+
/**
|
|
4572
|
+
* The index ID. Returns -1 if the index ID is not available (e.g. the index
|
|
4573
|
+
* has not yet been persisted).
|
|
4574
|
+
*/
|
|
4575
|
+
indexId,
|
|
4576
|
+
/** The collection ID this index applies to. */
|
|
4577
|
+
collectionGroup,
|
|
4578
|
+
/** The field segments for this index. */
|
|
4579
|
+
fields,
|
|
4580
|
+
/** Shows how up-to-date the index is for the current user. */
|
|
4581
|
+
indexState) {
|
|
4582
|
+
this.indexId = indexId;
|
|
4583
|
+
this.collectionGroup = collectionGroup;
|
|
4584
|
+
this.fields = fields;
|
|
4585
|
+
this.indexState = indexState;
|
|
4586
|
+
}
|
|
4587
|
+
}
|
|
4588
|
+
/** An ID for an index that has not yet been added to persistence. */
|
|
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
|
+
}
|
|
4598
|
+
/** An index component consisting of field path and index type. */
|
|
4599
|
+
class IndexSegment {
|
|
4600
|
+
constructor(
|
|
4601
|
+
/** The field path of the component. */
|
|
4602
|
+
fieldPath,
|
|
4603
|
+
/** The fields sorting order. */
|
|
4604
|
+
kind) {
|
|
4605
|
+
this.fieldPath = fieldPath;
|
|
4606
|
+
this.kind = kind;
|
|
4607
|
+
}
|
|
4608
|
+
}
|
|
4609
|
+
/**
|
|
4610
|
+
* Stores the "high water mark" that indicates how updated the Index is for the
|
|
4611
|
+
* current user.
|
|
4612
|
+
*/
|
|
4613
|
+
class IndexState {
|
|
4614
|
+
constructor(
|
|
4615
|
+
/**
|
|
4616
|
+
* Indicates when the index was last updated (relative to other indexes).
|
|
4617
|
+
*/
|
|
4618
|
+
sequenceNumber,
|
|
4619
|
+
/** The the latest indexed read time, document and batch id. */
|
|
4620
|
+
offset) {
|
|
4621
|
+
this.sequenceNumber = sequenceNumber;
|
|
4622
|
+
this.offset = offset;
|
|
4623
|
+
}
|
|
4624
|
+
/** The state of an index that has not yet been backfilled. */
|
|
4625
|
+
static empty() {
|
|
4626
|
+
return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
4629
|
+
/**
|
|
4630
|
+
* Stores the latest read time, document and batch ID that were processed for an
|
|
4631
|
+
* index.
|
|
4632
|
+
*/
|
|
4633
|
+
class IndexOffset {
|
|
4634
|
+
constructor(
|
|
4635
|
+
/**
|
|
4636
|
+
* The latest read time version that has been indexed by Firestore for this
|
|
4637
|
+
* field index.
|
|
4638
|
+
*/
|
|
4639
|
+
readTime,
|
|
4640
|
+
/**
|
|
4641
|
+
* The key of the last document that was indexed for this query. Use
|
|
4642
|
+
* `DocumentKey.empty()` if no document has been indexed.
|
|
4643
|
+
*/
|
|
4644
|
+
documentKey,
|
|
4645
|
+
/*
|
|
4646
|
+
* The largest mutation batch id that's been processed by Firestore.
|
|
4647
|
+
*/
|
|
4648
|
+
largestBatchId) {
|
|
4649
|
+
this.readTime = readTime;
|
|
4650
|
+
this.documentKey = documentKey;
|
|
4651
|
+
this.largestBatchId = largestBatchId;
|
|
4652
|
+
}
|
|
4653
|
+
/** The state of an index that has not yet been backfilled. */
|
|
4654
|
+
static min() {
|
|
4655
|
+
return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
|
|
4228
4659
|
/**
|
|
4229
4660
|
* @license
|
|
4230
4661
|
* Copyright 2019 Google LLC
|
|
@@ -4268,27 +4699,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
|
|
|
4268
4699
|
function canonifyTarget(target) {
|
|
4269
4700
|
const targetImpl = debugCast(target);
|
|
4270
4701
|
if (targetImpl.memoizedCanonicalId === null) {
|
|
4271
|
-
let
|
|
4702
|
+
let str = targetImpl.path.canonicalString();
|
|
4272
4703
|
if (targetImpl.collectionGroup !== null) {
|
|
4273
|
-
|
|
4704
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
4274
4705
|
}
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4706
|
+
str += '|f:';
|
|
4707
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
4708
|
+
str += '|ob:';
|
|
4709
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
4279
4710
|
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
4280
|
-
|
|
4281
|
-
|
|
4711
|
+
str += '|l:';
|
|
4712
|
+
str += targetImpl.limit;
|
|
4282
4713
|
}
|
|
4283
4714
|
if (targetImpl.startAt) {
|
|
4284
|
-
|
|
4285
|
-
|
|
4715
|
+
str += '|lb:';
|
|
4716
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
4717
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4286
4718
|
}
|
|
4287
4719
|
if (targetImpl.endAt) {
|
|
4288
|
-
|
|
4289
|
-
|
|
4720
|
+
str += '|ub:';
|
|
4721
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
4722
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4290
4723
|
}
|
|
4291
|
-
targetImpl.memoizedCanonicalId =
|
|
4724
|
+
targetImpl.memoizedCanonicalId = str;
|
|
4292
4725
|
}
|
|
4293
4726
|
return targetImpl.memoizedCanonicalId;
|
|
4294
4727
|
}
|
|
@@ -4311,10 +4744,14 @@ function stringifyTarget(target) {
|
|
|
4311
4744
|
.join(', ')}]`;
|
|
4312
4745
|
}
|
|
4313
4746
|
if (target.startAt) {
|
|
4314
|
-
str += ', startAt: '
|
|
4747
|
+
str += ', startAt: ';
|
|
4748
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
4749
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4315
4750
|
}
|
|
4316
4751
|
if (target.endAt) {
|
|
4317
|
-
str += ', endAt: '
|
|
4752
|
+
str += ', endAt: ';
|
|
4753
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
4754
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4318
4755
|
}
|
|
4319
4756
|
return `Target(${str})`;
|
|
4320
4757
|
}
|
|
@@ -4349,7 +4786,7 @@ function targetEquals(left, right) {
|
|
|
4349
4786
|
}
|
|
4350
4787
|
return boundEquals(left.endAt, right.endAt);
|
|
4351
4788
|
}
|
|
4352
|
-
function
|
|
4789
|
+
function targetIsDocumentTarget(target) {
|
|
4353
4790
|
return (DocumentKey.isDocumentKey(target.path) &&
|
|
4354
4791
|
target.collectionGroup === null &&
|
|
4355
4792
|
target.filters.length === 0);
|
|
@@ -4552,17 +4989,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4552
4989
|
* just after the provided values.
|
|
4553
4990
|
*/
|
|
4554
4991
|
class Bound {
|
|
4555
|
-
constructor(position,
|
|
4992
|
+
constructor(position, inclusive) {
|
|
4556
4993
|
this.position = position;
|
|
4557
|
-
this.
|
|
4994
|
+
this.inclusive = inclusive;
|
|
4558
4995
|
}
|
|
4559
4996
|
}
|
|
4560
|
-
function canonifyBound(bound) {
|
|
4561
|
-
// TODO(b/29183165): Make this collision robust.
|
|
4562
|
-
return `${bound.before ? 'b' : 'a'}:${bound.position
|
|
4563
|
-
.map(p => canonicalId(p))
|
|
4564
|
-
.join(',')}`;
|
|
4565
|
-
}
|
|
4566
4997
|
/**
|
|
4567
4998
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
4568
4999
|
*/
|
|
@@ -4582,11 +5013,7 @@ function stringifyOrderBy(orderBy) {
|
|
|
4582
5013
|
function orderByEquals(left, right) {
|
|
4583
5014
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
4584
5015
|
}
|
|
4585
|
-
|
|
4586
|
-
* Returns true if a document sorts before a bound using the provided sort
|
|
4587
|
-
* order.
|
|
4588
|
-
*/
|
|
4589
|
-
function sortsBeforeDocument(bound, orderBy, doc) {
|
|
5016
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
4590
5017
|
let comparison = 0;
|
|
4591
5018
|
for (let i = 0; i < bound.position.length; i++) {
|
|
4592
5019
|
const orderByComponent = orderBy[i];
|
|
@@ -4605,7 +5032,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
|
|
|
4605
5032
|
break;
|
|
4606
5033
|
}
|
|
4607
5034
|
}
|
|
4608
|
-
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;
|
|
4609
5052
|
}
|
|
4610
5053
|
function boundEquals(left, right) {
|
|
4611
5054
|
if (left === null) {
|
|
@@ -4614,7 +5057,7 @@ function boundEquals(left, right) {
|
|
|
4614
5057
|
else if (right === null) {
|
|
4615
5058
|
return false;
|
|
4616
5059
|
}
|
|
4617
|
-
if (left.
|
|
5060
|
+
if (left.inclusive !== right.inclusive ||
|
|
4618
5061
|
left.position.length !== right.position.length) {
|
|
4619
5062
|
return false;
|
|
4620
5063
|
}
|
|
@@ -4818,10 +5261,10 @@ function queryToTarget(query) {
|
|
|
4818
5261
|
}
|
|
4819
5262
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
4820
5263
|
const startAt = queryImpl.endAt
|
|
4821
|
-
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.
|
|
5264
|
+
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
|
|
4822
5265
|
: null;
|
|
4823
5266
|
const endAt = queryImpl.startAt
|
|
4824
|
-
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.
|
|
5267
|
+
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
|
|
4825
5268
|
: null;
|
|
4826
5269
|
// Now return as a LimitType.First query.
|
|
4827
5270
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
|
|
@@ -4909,11 +5352,11 @@ function queryMatchesFilters(query, doc) {
|
|
|
4909
5352
|
/** Makes sure a document is within the bounds, if provided. */
|
|
4910
5353
|
function queryMatchesBounds(query, doc) {
|
|
4911
5354
|
if (query.startAt &&
|
|
4912
|
-
!
|
|
5355
|
+
!boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
|
|
4913
5356
|
return false;
|
|
4914
5357
|
}
|
|
4915
5358
|
if (query.endAt &&
|
|
4916
|
-
|
|
5359
|
+
!boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
|
|
4917
5360
|
return false;
|
|
4918
5361
|
}
|
|
4919
5362
|
return true;
|
|
@@ -5568,6 +6011,72 @@ class SortedSetIterator {
|
|
|
5568
6011
|
hasNext() {
|
|
5569
6012
|
return this.iter.hasNext();
|
|
5570
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;
|
|
5571
6080
|
}
|
|
5572
6081
|
|
|
5573
6082
|
/**
|
|
@@ -6406,6 +6915,47 @@ class MutationBatchResult {
|
|
|
6406
6915
|
}
|
|
6407
6916
|
}
|
|
6408
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
|
+
|
|
6409
6959
|
/**
|
|
6410
6960
|
* @license
|
|
6411
6961
|
* Copyright 2017 Google LLC
|
|
@@ -6878,7 +7428,7 @@ class TargetState {
|
|
|
6878
7428
|
this._current = true;
|
|
6879
7429
|
}
|
|
6880
7430
|
}
|
|
6881
|
-
const LOG_TAG$
|
|
7431
|
+
const LOG_TAG$g = 'WatchChangeAggregator';
|
|
6882
7432
|
/**
|
|
6883
7433
|
* A helper class to accumulate watch changes into a RemoteEvent.
|
|
6884
7434
|
*/
|
|
@@ -6994,7 +7544,7 @@ class WatchChangeAggregator {
|
|
|
6994
7544
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
6995
7545
|
if (targetData) {
|
|
6996
7546
|
const target = targetData.target;
|
|
6997
|
-
if (
|
|
7547
|
+
if (targetIsDocumentTarget(target)) {
|
|
6998
7548
|
if (expectedCount === 0) {
|
|
6999
7549
|
// The existence filter told us the document does not exist. We deduce
|
|
7000
7550
|
// that this document does not exist and apply a deleted document to
|
|
@@ -7029,7 +7579,7 @@ class WatchChangeAggregator {
|
|
|
7029
7579
|
this.targetStates.forEach((targetState, targetId) => {
|
|
7030
7580
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
7031
7581
|
if (targetData) {
|
|
7032
|
-
if (targetState.current &&
|
|
7582
|
+
if (targetState.current && targetIsDocumentTarget(targetData.target)) {
|
|
7033
7583
|
// Document queries for document that don't exist can produce an empty
|
|
7034
7584
|
// result set. To update our local cache, we synthesize a document
|
|
7035
7585
|
// delete if we have not previously received the document. This
|
|
@@ -7072,6 +7622,7 @@ class WatchChangeAggregator {
|
|
|
7072
7622
|
resolvedLimboDocuments = resolvedLimboDocuments.add(key);
|
|
7073
7623
|
}
|
|
7074
7624
|
});
|
|
7625
|
+
this.pendingDocumentUpdates.forEach((_, doc) => doc.setReadTime(snapshotVersion));
|
|
7075
7626
|
const remoteEvent = new RemoteEvent(snapshotVersion, targetChanges, this.pendingTargetResets, this.pendingDocumentUpdates, resolvedLimboDocuments);
|
|
7076
7627
|
this.pendingDocumentUpdates = mutableDocumentMap();
|
|
7077
7628
|
this.pendingDocumentTargetMapping = documentTargetMap();
|
|
@@ -7172,7 +7723,7 @@ class WatchChangeAggregator {
|
|
|
7172
7723
|
isActiveTarget(targetId) {
|
|
7173
7724
|
const targetActive = this.targetDataForActiveTarget(targetId) !== null;
|
|
7174
7725
|
if (!targetActive) {
|
|
7175
|
-
logDebug(LOG_TAG$
|
|
7726
|
+
logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
|
|
7176
7727
|
}
|
|
7177
7728
|
return targetActive;
|
|
7178
7729
|
}
|
|
@@ -7791,10 +8342,10 @@ function toQueryTarget(serializer, target) {
|
|
|
7791
8342
|
result.structuredQuery.limit = limit;
|
|
7792
8343
|
}
|
|
7793
8344
|
if (target.startAt) {
|
|
7794
|
-
result.structuredQuery.startAt =
|
|
8345
|
+
result.structuredQuery.startAt = toStartAtCursor(target.startAt);
|
|
7795
8346
|
}
|
|
7796
8347
|
if (target.endAt) {
|
|
7797
|
-
result.structuredQuery.endAt =
|
|
8348
|
+
result.structuredQuery.endAt = toEndAtCursor(target.endAt);
|
|
7798
8349
|
}
|
|
7799
8350
|
return result;
|
|
7800
8351
|
}
|
|
@@ -7827,11 +8378,11 @@ function convertQueryTargetToQuery(target) {
|
|
|
7827
8378
|
}
|
|
7828
8379
|
let startAt = null;
|
|
7829
8380
|
if (query.startAt) {
|
|
7830
|
-
startAt =
|
|
8381
|
+
startAt = fromStartAtCursor(query.startAt);
|
|
7831
8382
|
}
|
|
7832
8383
|
let endAt = null;
|
|
7833
8384
|
if (query.endAt) {
|
|
7834
|
-
endAt =
|
|
8385
|
+
endAt = fromEndAtCursor(query.endAt);
|
|
7835
8386
|
}
|
|
7836
8387
|
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
7837
8388
|
}
|
|
@@ -7864,7 +8415,7 @@ function toLabel(serializer, purpose) {
|
|
|
7864
8415
|
function toTarget(serializer, targetData) {
|
|
7865
8416
|
let result;
|
|
7866
8417
|
const target = targetData.target;
|
|
7867
|
-
if (
|
|
8418
|
+
if (targetIsDocumentTarget(target)) {
|
|
7868
8419
|
result = { documents: toDocumentsTarget(serializer, target) };
|
|
7869
8420
|
}
|
|
7870
8421
|
else {
|
|
@@ -7922,16 +8473,27 @@ function toOrder(orderBys) {
|
|
|
7922
8473
|
function fromOrder(orderBys) {
|
|
7923
8474
|
return orderBys.map(order => fromPropertyOrder(order));
|
|
7924
8475
|
}
|
|
7925
|
-
function
|
|
8476
|
+
function toStartAtCursor(cursor) {
|
|
7926
8477
|
return {
|
|
7927
|
-
before: cursor.
|
|
8478
|
+
before: cursor.inclusive,
|
|
7928
8479
|
values: cursor.position
|
|
7929
8480
|
};
|
|
7930
8481
|
}
|
|
7931
|
-
function
|
|
7932
|
-
|
|
8482
|
+
function toEndAtCursor(cursor) {
|
|
8483
|
+
return {
|
|
8484
|
+
before: !cursor.inclusive,
|
|
8485
|
+
values: cursor.position
|
|
8486
|
+
};
|
|
8487
|
+
}
|
|
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;
|
|
7933
8495
|
const position = cursor.values || [];
|
|
7934
|
-
return new Bound(position,
|
|
8496
|
+
return new Bound(position, inclusive);
|
|
7935
8497
|
}
|
|
7936
8498
|
// visible for testing
|
|
7937
8499
|
function toDirection(dir) {
|
|
@@ -8192,29 +8754,34 @@ class LocalSerializer {
|
|
|
8192
8754
|
}
|
|
8193
8755
|
/** Decodes a remote document from storage locally to a Document. */
|
|
8194
8756
|
function fromDbRemoteDocument(localSerializer, remoteDoc) {
|
|
8757
|
+
let doc;
|
|
8195
8758
|
if (remoteDoc.document) {
|
|
8196
|
-
|
|
8759
|
+
doc = fromDocument(localSerializer.remoteSerializer, remoteDoc.document, !!remoteDoc.hasCommittedMutations);
|
|
8197
8760
|
}
|
|
8198
8761
|
else if (remoteDoc.noDocument) {
|
|
8199
8762
|
const key = DocumentKey.fromSegments(remoteDoc.noDocument.path);
|
|
8200
8763
|
const version = fromDbTimestamp(remoteDoc.noDocument.readTime);
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8764
|
+
doc = MutableDocument.newNoDocument(key, version);
|
|
8765
|
+
if (remoteDoc.hasCommittedMutations) {
|
|
8766
|
+
doc.setHasCommittedMutations();
|
|
8767
|
+
}
|
|
8205
8768
|
}
|
|
8206
8769
|
else if (remoteDoc.unknownDocument) {
|
|
8207
8770
|
const key = DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
|
|
8208
8771
|
const version = fromDbTimestamp(remoteDoc.unknownDocument.version);
|
|
8209
|
-
|
|
8772
|
+
doc = MutableDocument.newUnknownDocument(key, version);
|
|
8210
8773
|
}
|
|
8211
8774
|
else {
|
|
8212
8775
|
return fail();
|
|
8213
8776
|
}
|
|
8777
|
+
if (remoteDoc.readTime) {
|
|
8778
|
+
doc.setReadTime(fromDbTimestampKey(remoteDoc.readTime));
|
|
8779
|
+
}
|
|
8780
|
+
return doc;
|
|
8214
8781
|
}
|
|
8215
8782
|
/** Encodes a document for storage locally. */
|
|
8216
|
-
function toDbRemoteDocument(localSerializer, document
|
|
8217
|
-
const dbReadTime = toDbTimestampKey(readTime);
|
|
8783
|
+
function toDbRemoteDocument(localSerializer, document) {
|
|
8784
|
+
const dbReadTime = toDbTimestampKey(document.readTime);
|
|
8218
8785
|
const parentPath = document.key.path.popLast().toArray();
|
|
8219
8786
|
if (document.isFoundDocument()) {
|
|
8220
8787
|
const doc = toDocument(localSerializer.remoteSerializer, document);
|
|
@@ -8225,10 +8792,10 @@ function toDbRemoteDocument(localSerializer, document, readTime) {
|
|
|
8225
8792
|
}
|
|
8226
8793
|
else if (document.isNoDocument()) {
|
|
8227
8794
|
const path = document.key.path.toArray();
|
|
8228
|
-
const
|
|
8795
|
+
const version = toDbTimestamp(document.version);
|
|
8229
8796
|
const hasCommittedMutations = document.hasCommittedMutations;
|
|
8230
8797
|
return new DbRemoteDocument(
|
|
8231
|
-
/* unknownDocument= */ null, new DbNoDocument(path,
|
|
8798
|
+
/* unknownDocument= */ null, new DbNoDocument(path, version),
|
|
8232
8799
|
/* document= */ null, hasCommittedMutations, dbReadTime, parentPath);
|
|
8233
8800
|
}
|
|
8234
8801
|
else if (document.isUnknownDocument()) {
|
|
@@ -8309,7 +8876,7 @@ function toDbTarget(localSerializer, targetData) {
|
|
|
8309
8876
|
const dbTimestamp = toDbTimestamp(targetData.snapshotVersion);
|
|
8310
8877
|
const dbLastLimboFreeTimestamp = toDbTimestamp(targetData.lastLimboFreeSnapshotVersion);
|
|
8311
8878
|
let queryProto;
|
|
8312
|
-
if (
|
|
8879
|
+
if (targetIsDocumentTarget(targetData.target)) {
|
|
8313
8880
|
queryProto = toDocumentsTarget(localSerializer.remoteSerializer, targetData.target);
|
|
8314
8881
|
}
|
|
8315
8882
|
else {
|
|
@@ -8390,6 +8957,37 @@ function fromBundleMetadata(metadata) {
|
|
|
8390
8957
|
version: metadata.version,
|
|
8391
8958
|
createTime: fromVersion(metadata.createTime)
|
|
8392
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);
|
|
8393
8991
|
}
|
|
8394
8992
|
|
|
8395
8993
|
/**
|
|
@@ -8449,6 +9047,704 @@ function namedQueriesStore(txn) {
|
|
|
8449
9047
|
return getStore(txn, DbNamedQuery.store);
|
|
8450
9048
|
}
|
|
8451
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
|
+
}
|
|
9656
|
+
}
|
|
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
|
+
}
|
|
9673
|
+
}
|
|
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
|
+
}
|
|
9696
|
+
}
|
|
9697
|
+
|
|
9698
|
+
/**
|
|
9699
|
+
* @license
|
|
9700
|
+
* Copyright 2022 Google LLC
|
|
9701
|
+
*
|
|
9702
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9703
|
+
* you may not use this file except in compliance with the License.
|
|
9704
|
+
* You may obtain a copy of the License at
|
|
9705
|
+
*
|
|
9706
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9707
|
+
*
|
|
9708
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9709
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9710
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9711
|
+
* See the License for the specific language governing permissions and
|
|
9712
|
+
* limitations under the License.
|
|
9713
|
+
*/
|
|
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;
|
|
9721
|
+
}
|
|
9722
|
+
}
|
|
9723
|
+
function indexEntryComparator(left, right) {
|
|
9724
|
+
let cmp = left.indexId - right.indexId;
|
|
9725
|
+
if (cmp !== 0) {
|
|
9726
|
+
return cmp;
|
|
9727
|
+
}
|
|
9728
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
9729
|
+
if (cmp !== 0) {
|
|
9730
|
+
return cmp;
|
|
9731
|
+
}
|
|
9732
|
+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
|
|
9733
|
+
if (cmp !== 0) {
|
|
9734
|
+
return cmp;
|
|
9735
|
+
}
|
|
9736
|
+
return compareByteArrays(left.directionalValue, right.directionalValue);
|
|
9737
|
+
}
|
|
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;
|
|
9746
|
+
}
|
|
9747
|
+
|
|
8452
9748
|
/**
|
|
8453
9749
|
* @license
|
|
8454
9750
|
* Copyright 2019 Google LLC
|
|
@@ -8479,6 +9775,38 @@ class MemoryIndexManager {
|
|
|
8479
9775
|
getCollectionParents(transaction, collectionId) {
|
|
8480
9776
|
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
8481
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
|
+
}
|
|
8482
9810
|
}
|
|
8483
9811
|
/**
|
|
8484
9812
|
* Internal implementation of the collection-parent index exposed by MemoryIndexManager.
|
|
@@ -8528,19 +9856,25 @@ class MemoryCollectionParentIndex {
|
|
|
8528
9856
|
* See the License for the specific language governing permissions and
|
|
8529
9857
|
* limitations under the License.
|
|
8530
9858
|
*/
|
|
9859
|
+
const LOG_TAG$f = 'IndexedDbIndexManager';
|
|
8531
9860
|
/**
|
|
8532
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.
|
|
8533
9865
|
*/
|
|
8534
9866
|
class IndexedDbIndexManager {
|
|
8535
|
-
constructor() {
|
|
9867
|
+
constructor(user) {
|
|
9868
|
+
this.user = user;
|
|
8536
9869
|
/**
|
|
8537
9870
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
8538
9871
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
8539
9872
|
*
|
|
8540
|
-
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
8541
|
-
* satisfy reads.
|
|
9873
|
+
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
9874
|
+
* used to satisfy reads.
|
|
8542
9875
|
*/
|
|
8543
9876
|
this.collectionParentsCache = new MemoryCollectionParentIndex();
|
|
9877
|
+
this.uid = user.uid || '';
|
|
8544
9878
|
}
|
|
8545
9879
|
/**
|
|
8546
9880
|
* Adds a new entry to the collection parent index.
|
|
@@ -8587,6 +9921,198 @@ class IndexedDbIndexManager {
|
|
|
8587
9921
|
return parentPaths;
|
|
8588
9922
|
});
|
|
8589
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
|
+
}
|
|
8590
10116
|
}
|
|
8591
10117
|
/**
|
|
8592
10118
|
* Helper to get a typed SimpleDbStore for the collectionParents
|
|
@@ -8594,6 +10120,24 @@ class IndexedDbIndexManager {
|
|
|
8594
10120
|
*/
|
|
8595
10121
|
function collectionParentsStore(txn) {
|
|
8596
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);
|
|
8597
10141
|
}
|
|
8598
10142
|
|
|
8599
10143
|
/**
|
|
@@ -9757,7 +11301,7 @@ class IndexedDbLruDelegateImpl {
|
|
|
9757
11301
|
// Our size accounting requires us to read all documents before
|
|
9758
11302
|
// removing them.
|
|
9759
11303
|
return changeBuffer.getEntry(txn, docKey).next(() => {
|
|
9760
|
-
changeBuffer.removeEntry(docKey);
|
|
11304
|
+
changeBuffer.removeEntry(docKey, SnapshotVersion.min());
|
|
9761
11305
|
return documentTargetStore(txn).delete(sentinelKey$1(docKey));
|
|
9762
11306
|
});
|
|
9763
11307
|
}
|
|
@@ -9970,27 +11514,19 @@ class ObjectMap {
|
|
|
9970
11514
|
*/
|
|
9971
11515
|
class RemoteDocumentChangeBuffer {
|
|
9972
11516
|
constructor() {
|
|
9973
|
-
// A mapping of document key to the new cache entry that should be written
|
|
9974
|
-
// existing cache entry should be removed).
|
|
11517
|
+
// A mapping of document key to the new cache entry that should be written.
|
|
9975
11518
|
this.changes = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
|
|
9976
11519
|
this.changesApplied = false;
|
|
9977
11520
|
}
|
|
9978
|
-
getReadTime(key) {
|
|
9979
|
-
const change = this.changes.get(key);
|
|
9980
|
-
if (change) {
|
|
9981
|
-
return change.readTime;
|
|
9982
|
-
}
|
|
9983
|
-
return SnapshotVersion.min();
|
|
9984
|
-
}
|
|
9985
11521
|
/**
|
|
9986
11522
|
* Buffers a `RemoteDocumentCache.addEntry()` call.
|
|
9987
11523
|
*
|
|
9988
11524
|
* You can only modify documents that have already been retrieved via
|
|
9989
11525
|
* `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).
|
|
9990
11526
|
*/
|
|
9991
|
-
addEntry(document
|
|
11527
|
+
addEntry(document) {
|
|
9992
11528
|
this.assertNotApplied();
|
|
9993
|
-
this.changes.set(document.key,
|
|
11529
|
+
this.changes.set(document.key, document);
|
|
9994
11530
|
}
|
|
9995
11531
|
/**
|
|
9996
11532
|
* Buffers a `RemoteDocumentCache.removeEntry()` call.
|
|
@@ -9998,12 +11534,9 @@ class RemoteDocumentChangeBuffer {
|
|
|
9998
11534
|
* You can only remove documents that have already been retrieved via
|
|
9999
11535
|
* `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).
|
|
10000
11536
|
*/
|
|
10001
|
-
removeEntry(key, readTime
|
|
11537
|
+
removeEntry(key, readTime) {
|
|
10002
11538
|
this.assertNotApplied();
|
|
10003
|
-
this.changes.set(key,
|
|
10004
|
-
document: MutableDocument.newInvalidDocument(key),
|
|
10005
|
-
readTime
|
|
10006
|
-
});
|
|
11539
|
+
this.changes.set(key, MutableDocument.newInvalidDocument(key).setReadTime(readTime));
|
|
10007
11540
|
}
|
|
10008
11541
|
/**
|
|
10009
11542
|
* Looks up an entry in the cache. The buffered changes will first be checked,
|
|
@@ -10020,7 +11553,7 @@ class RemoteDocumentChangeBuffer {
|
|
|
10020
11553
|
this.assertNotApplied();
|
|
10021
11554
|
const bufferedEntry = this.changes.get(documentKey);
|
|
10022
11555
|
if (bufferedEntry !== undefined) {
|
|
10023
|
-
return PersistencePromise.resolve(bufferedEntry
|
|
11556
|
+
return PersistencePromise.resolve(bufferedEntry);
|
|
10024
11557
|
}
|
|
10025
11558
|
else {
|
|
10026
11559
|
return this.getFromCache(transaction, documentKey);
|
|
@@ -10074,12 +11607,10 @@ class RemoteDocumentChangeBuffer {
|
|
|
10074
11607
|
* `newIndexedDbRemoteDocumentCache()`.
|
|
10075
11608
|
*/
|
|
10076
11609
|
class IndexedDbRemoteDocumentCacheImpl {
|
|
10077
|
-
|
|
10078
|
-
* @param serializer - The document serializer.
|
|
10079
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10080
|
-
*/
|
|
10081
|
-
constructor(serializer, indexManager) {
|
|
11610
|
+
constructor(serializer) {
|
|
10082
11611
|
this.serializer = serializer;
|
|
11612
|
+
}
|
|
11613
|
+
setIndexManager(indexManager) {
|
|
10083
11614
|
this.indexManager = indexManager;
|
|
10084
11615
|
}
|
|
10085
11616
|
/**
|
|
@@ -10201,21 +11732,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10201
11732
|
}
|
|
10202
11733
|
});
|
|
10203
11734
|
}
|
|
10204
|
-
|
|
11735
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10205
11736
|
let results = mutableDocumentMap();
|
|
10206
|
-
const immediateChildrenPathLength =
|
|
11737
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
10207
11738
|
const iterationOptions = {};
|
|
10208
11739
|
if (sinceReadTime.isEqual(SnapshotVersion.min())) {
|
|
10209
11740
|
// Documents are ordered by key, so we can use a prefix scan to narrow
|
|
10210
11741
|
// down the documents we need to match the query against.
|
|
10211
|
-
const startKey =
|
|
11742
|
+
const startKey = collection.toArray();
|
|
10212
11743
|
iterationOptions.range = IDBKeyRange.lowerBound(startKey);
|
|
10213
11744
|
}
|
|
10214
11745
|
else {
|
|
10215
11746
|
// Execute an index-free query and filter by read time. This is safe
|
|
10216
11747
|
// since all document changes to queries that have a
|
|
10217
11748
|
// lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
|
|
10218
|
-
const collectionKey =
|
|
11749
|
+
const collectionKey = collection.toArray();
|
|
10219
11750
|
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
10220
11751
|
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
10221
11752
|
/* open= */ true);
|
|
@@ -10231,13 +11762,13 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10231
11762
|
if (key.length !== immediateChildrenPathLength) {
|
|
10232
11763
|
return;
|
|
10233
11764
|
}
|
|
10234
|
-
const document =
|
|
10235
|
-
if (
|
|
10236
|
-
control.done();
|
|
10237
|
-
}
|
|
10238
|
-
else if (queryMatches(query, document)) {
|
|
11765
|
+
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
11766
|
+
if (collection.isPrefixOf(document.key.path)) {
|
|
10239
11767
|
results = results.insert(document.key, document);
|
|
10240
11768
|
}
|
|
11769
|
+
else {
|
|
11770
|
+
control.done();
|
|
11771
|
+
}
|
|
10241
11772
|
})
|
|
10242
11773
|
.next(() => results);
|
|
10243
11774
|
}
|
|
@@ -10259,8 +11790,8 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10259
11790
|
return documentGlobalStore(txn).put(DbRemoteDocumentGlobal.key, metadata);
|
|
10260
11791
|
}
|
|
10261
11792
|
/**
|
|
10262
|
-
* Decodes `
|
|
10263
|
-
* corresponds to the format used for sentinel deletes).
|
|
11793
|
+
* Decodes `dbRemoteDoc` and returns the document (or an invalid document if
|
|
11794
|
+
* the document corresponds to the format used for sentinel deletes).
|
|
10264
11795
|
*/
|
|
10265
11796
|
maybeDecodeDocument(documentKey, dbRemoteDoc) {
|
|
10266
11797
|
if (dbRemoteDoc) {
|
|
@@ -10275,14 +11806,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10275
11806
|
return MutableDocument.newInvalidDocument(documentKey);
|
|
10276
11807
|
}
|
|
10277
11808
|
}
|
|
10278
|
-
/**
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
* @param serializer - The document serializer.
|
|
10282
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10283
|
-
*/
|
|
10284
|
-
function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
|
|
10285
|
-
return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
|
|
11809
|
+
/** Creates a new IndexedDbRemoteDocumentCache. */
|
|
11810
|
+
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
11811
|
+
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
10286
11812
|
}
|
|
10287
11813
|
/**
|
|
10288
11814
|
* Returns the set of documents that have changed since the specified read
|
|
@@ -10354,8 +11880,8 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
10354
11880
|
let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
|
|
10355
11881
|
this.changes.forEach((key, documentChange) => {
|
|
10356
11882
|
const previousSize = this.documentSizes.get(key);
|
|
10357
|
-
if (documentChange.
|
|
10358
|
-
const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange
|
|
11883
|
+
if (documentChange.isValidDocument()) {
|
|
11884
|
+
const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
|
|
10359
11885
|
collectionParents = collectionParents.add(key.path.popLast());
|
|
10360
11886
|
const size = dbDocumentSize(doc);
|
|
10361
11887
|
sizeDelta += size - previousSize;
|
|
@@ -10368,7 +11894,7 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
10368
11894
|
// RemoteDocumentCache. This entry is represented by a NoDocument
|
|
10369
11895
|
// with a version of 0 and ignored by `maybeDecodeDocument()` but
|
|
10370
11896
|
// preserved in `getNewDocumentChanges()`.
|
|
10371
|
-
const deletedDoc = toDbRemoteDocument(this.documentCache.serializer,
|
|
11897
|
+
const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
|
|
10372
11898
|
promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
|
|
10373
11899
|
}
|
|
10374
11900
|
else {
|
|
@@ -10449,9 +11975,6 @@ class SchemaConverter {
|
|
|
10449
11975
|
* and local feature development.
|
|
10450
11976
|
*/
|
|
10451
11977
|
createOrUpgrade(db, txn, fromVersion, toVersion) {
|
|
10452
|
-
hardAssert(fromVersion < toVersion &&
|
|
10453
|
-
fromVersion >= 0 &&
|
|
10454
|
-
toVersion <= SCHEMA_VERSION);
|
|
10455
11978
|
const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
|
|
10456
11979
|
if (fromVersion < 1 && toVersion >= 1) {
|
|
10457
11980
|
createPrimaryClientStore(db);
|
|
@@ -10518,6 +12041,16 @@ class SchemaConverter {
|
|
|
10518
12041
|
createNamedQueriesStore(db);
|
|
10519
12042
|
});
|
|
10520
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
|
+
}
|
|
10521
12054
|
return p;
|
|
10522
12055
|
}
|
|
10523
12056
|
addDocumentGlobal(txn) {
|
|
@@ -10724,6 +12257,28 @@ function createNamedQueriesStore(db) {
|
|
|
10724
12257
|
db.createObjectStore(DbNamedQuery.store, {
|
|
10725
12258
|
keyPath: DbNamedQuery.keyPath
|
|
10726
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 });
|
|
10727
12282
|
}
|
|
10728
12283
|
|
|
10729
12284
|
/**
|
|
@@ -10835,7 +12390,7 @@ class IndexedDbPersistence {
|
|
|
10835
12390
|
* If set to true, forcefully obtains database access. Existing tabs will
|
|
10836
12391
|
* no longer be able to access IndexedDB.
|
|
10837
12392
|
*/
|
|
10838
|
-
forceOwningTab) {
|
|
12393
|
+
forceOwningTab, schemaVersion = SCHEMA_VERSION) {
|
|
10839
12394
|
this.allowTabSynchronization = allowTabSynchronization;
|
|
10840
12395
|
this.persistenceKey = persistenceKey;
|
|
10841
12396
|
this.clientId = clientId;
|
|
@@ -10844,6 +12399,7 @@ class IndexedDbPersistence {
|
|
|
10844
12399
|
this.document = document;
|
|
10845
12400
|
this.sequenceNumberSyncer = sequenceNumberSyncer;
|
|
10846
12401
|
this.forceOwningTab = forceOwningTab;
|
|
12402
|
+
this.schemaVersion = schemaVersion;
|
|
10847
12403
|
this.listenSequence = null;
|
|
10848
12404
|
this._started = false;
|
|
10849
12405
|
this.isPrimary = false;
|
|
@@ -10865,10 +12421,9 @@ class IndexedDbPersistence {
|
|
|
10865
12421
|
this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
|
|
10866
12422
|
this.dbName = persistenceKey + MAIN_DATABASE;
|
|
10867
12423
|
this.serializer = new LocalSerializer(serializer);
|
|
10868
|
-
this.simpleDb = new SimpleDb(this.dbName,
|
|
12424
|
+
this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
|
|
10869
12425
|
this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
|
|
10870
|
-
this.
|
|
10871
|
-
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
|
|
12426
|
+
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
|
|
10872
12427
|
this.bundleCache = new IndexedDbBundleCache();
|
|
10873
12428
|
if (this.window && this.window.localStorage) {
|
|
10874
12429
|
this.webStorage = this.window.localStorage;
|
|
@@ -11204,8 +12759,8 @@ class IndexedDbPersistence {
|
|
|
11204
12759
|
get started() {
|
|
11205
12760
|
return this._started;
|
|
11206
12761
|
}
|
|
11207
|
-
getMutationQueue(user) {
|
|
11208
|
-
return IndexedDbMutationQueue.forUser(user, this.serializer,
|
|
12762
|
+
getMutationQueue(user, indexManager) {
|
|
12763
|
+
return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
|
|
11209
12764
|
}
|
|
11210
12765
|
getTargetCache() {
|
|
11211
12766
|
return this.targetCache;
|
|
@@ -11213,8 +12768,11 @@ class IndexedDbPersistence {
|
|
|
11213
12768
|
getRemoteDocumentCache() {
|
|
11214
12769
|
return this.remoteDocumentCache;
|
|
11215
12770
|
}
|
|
11216
|
-
getIndexManager() {
|
|
11217
|
-
return
|
|
12771
|
+
getIndexManager(user) {
|
|
12772
|
+
return new IndexedDbIndexManager(user);
|
|
12773
|
+
}
|
|
12774
|
+
getDocumentOverlayCache(user) {
|
|
12775
|
+
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
11218
12776
|
}
|
|
11219
12777
|
getBundleCache() {
|
|
11220
12778
|
return this.bundleCache;
|
|
@@ -11222,11 +12780,12 @@ class IndexedDbPersistence {
|
|
|
11222
12780
|
runTransaction(action, mode, transactionOperation) {
|
|
11223
12781
|
logDebug(LOG_TAG$d, 'Starting transaction:', action);
|
|
11224
12782
|
const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
|
|
12783
|
+
const objectStores = getObjectStores(this.schemaVersion);
|
|
11225
12784
|
let persistenceTransaction;
|
|
11226
12785
|
// Do all transactions as readwrite against all object stores, since we
|
|
11227
12786
|
// are the only reader/writer.
|
|
11228
12787
|
return this.simpleDb
|
|
11229
|
-
.runTransaction(action, simpleDbMode,
|
|
12788
|
+
.runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
|
|
11230
12789
|
persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
|
|
11231
12790
|
? this.listenSequence.next()
|
|
11232
12791
|
: ListenSequence.INVALID);
|
|
@@ -11598,39 +13157,28 @@ class LocalDocumentsView {
|
|
|
11598
13157
|
getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
|
|
11599
13158
|
// Query the remote documents and overlay mutations.
|
|
11600
13159
|
let results;
|
|
11601
|
-
let mutationBatches;
|
|
11602
13160
|
return this.remoteDocumentCache
|
|
11603
|
-
.
|
|
13161
|
+
.getAll(transaction, query.path, sinceReadTime)
|
|
11604
13162
|
.next(queryResults => {
|
|
11605
13163
|
results = queryResults;
|
|
11606
13164
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
11607
13165
|
})
|
|
11608
|
-
.next(
|
|
11609
|
-
mutationBatches
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
11618
|
-
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
let document = results.get(key);
|
|
11622
|
-
if (document == null) {
|
|
11623
|
-
// Create invalid document to apply mutations on top of
|
|
11624
|
-
document = MutableDocument.newInvalidDocument(key);
|
|
11625
|
-
results = results.insert(key, document);
|
|
11626
|
-
}
|
|
11627
|
-
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
11628
|
-
if (!document.isFoundDocument()) {
|
|
11629
|
-
results = results.remove(key);
|
|
11630
|
-
}
|
|
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);
|
|
11631
13179
|
}
|
|
11632
13180
|
}
|
|
11633
|
-
}
|
|
13181
|
+
}
|
|
11634
13182
|
})
|
|
11635
13183
|
.next(() => {
|
|
11636
13184
|
// Finally, filter out any documents that don't actually match
|
|
@@ -11643,29 +13191,6 @@ class LocalDocumentsView {
|
|
|
11643
13191
|
return results;
|
|
11644
13192
|
});
|
|
11645
13193
|
}
|
|
11646
|
-
addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
|
|
11647
|
-
let missingBaseDocEntriesForPatching = documentKeySet();
|
|
11648
|
-
for (const batch of matchingMutationBatches) {
|
|
11649
|
-
for (const mutation of batch.mutations) {
|
|
11650
|
-
if (mutation instanceof PatchMutation &&
|
|
11651
|
-
existingDocuments.get(mutation.key) === null) {
|
|
11652
|
-
missingBaseDocEntriesForPatching =
|
|
11653
|
-
missingBaseDocEntriesForPatching.add(mutation.key);
|
|
11654
|
-
}
|
|
11655
|
-
}
|
|
11656
|
-
}
|
|
11657
|
-
let mergedDocuments = existingDocuments;
|
|
11658
|
-
return this.remoteDocumentCache
|
|
11659
|
-
.getEntries(transaction, missingBaseDocEntriesForPatching)
|
|
11660
|
-
.next(missingBaseDocs => {
|
|
11661
|
-
missingBaseDocs.forEach((key, doc) => {
|
|
11662
|
-
if (doc.isFoundDocument()) {
|
|
11663
|
-
mergedDocuments = mergedDocuments.insert(key, doc);
|
|
11664
|
-
}
|
|
11665
|
-
});
|
|
11666
|
-
return mergedDocuments;
|
|
11667
|
-
});
|
|
11668
|
-
}
|
|
11669
13194
|
}
|
|
11670
13195
|
|
|
11671
13196
|
/**
|
|
@@ -11724,11 +13249,18 @@ class LocalStoreImpl {
|
|
|
11724
13249
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
11725
13250
|
*/
|
|
11726
13251
|
this.lastDocumentChangeReadTime = SnapshotVersion.min();
|
|
11727
|
-
this.mutationQueue = persistence.getMutationQueue(initialUser);
|
|
11728
13252
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
11729
13253
|
this.targetCache = persistence.getTargetCache();
|
|
11730
|
-
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
|
|
11731
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);
|
|
11732
13264
|
this.queryEngine.setLocalDocumentsView(this.localDocuments);
|
|
11733
13265
|
}
|
|
11734
13266
|
collectGarbage(garbageCollector) {
|
|
@@ -11750,8 +13282,6 @@ persistence, queryEngine, initialUser, serializer) {
|
|
|
11750
13282
|
// change.
|
|
11751
13283
|
async function localStoreHandleUserChange(localStore, user) {
|
|
11752
13284
|
const localStoreImpl = debugCast(localStore);
|
|
11753
|
-
let newMutationQueue = localStoreImpl.mutationQueue;
|
|
11754
|
-
let newLocalDocuments = localStoreImpl.localDocuments;
|
|
11755
13285
|
const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
|
|
11756
13286
|
// Swap out the mutation queue, grabbing the pending mutation batches
|
|
11757
13287
|
// before and after.
|
|
@@ -11760,11 +13290,8 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11760
13290
|
.getAllMutationBatches(txn)
|
|
11761
13291
|
.next(promisedOldBatches => {
|
|
11762
13292
|
oldBatches = promisedOldBatches;
|
|
11763
|
-
|
|
11764
|
-
|
|
11765
|
-
// MutationQueue.
|
|
11766
|
-
newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
|
|
11767
|
-
return newMutationQueue.getAllMutationBatches(txn);
|
|
13293
|
+
localStoreImpl.initializeUserComponents(user);
|
|
13294
|
+
return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
|
|
11768
13295
|
})
|
|
11769
13296
|
.next(newBatches => {
|
|
11770
13297
|
const removedBatchIds = [];
|
|
@@ -11785,7 +13312,7 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11785
13312
|
}
|
|
11786
13313
|
// Return the set of all (potentially) changed documents and the list
|
|
11787
13314
|
// of mutation batch IDs that were affected by change.
|
|
11788
|
-
return
|
|
13315
|
+
return localStoreImpl.localDocuments
|
|
11789
13316
|
.getDocuments(txn, changedKeys)
|
|
11790
13317
|
.next(affectedDocuments => {
|
|
11791
13318
|
return {
|
|
@@ -11796,9 +13323,6 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11796
13323
|
});
|
|
11797
13324
|
});
|
|
11798
13325
|
});
|
|
11799
|
-
localStoreImpl.mutationQueue = newMutationQueue;
|
|
11800
|
-
localStoreImpl.localDocuments = newLocalDocuments;
|
|
11801
|
-
localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
|
|
11802
13326
|
return result;
|
|
11803
13327
|
}
|
|
11804
13328
|
/* Accepts locally generated Mutations and commit them to storage. */
|
|
@@ -11955,14 +13479,14 @@ function localStoreApplyRemoteEventToLocalCache(localStore, remoteEvent) {
|
|
|
11955
13479
|
}
|
|
11956
13480
|
});
|
|
11957
13481
|
let changedDocs = mutableDocumentMap();
|
|
11958
|
-
remoteEvent.documentUpdates.forEach(
|
|
13482
|
+
remoteEvent.documentUpdates.forEach(key => {
|
|
11959
13483
|
if (remoteEvent.resolvedLimboDocuments.has(key)) {
|
|
11960
13484
|
promises.push(localStoreImpl.persistence.referenceDelegate.updateLimboDocument(txn, key));
|
|
11961
13485
|
}
|
|
11962
13486
|
});
|
|
11963
13487
|
// Each loop iteration only affects its "own" doc, so it's safe to get all the remote
|
|
11964
13488
|
// documents in advance in a single call.
|
|
11965
|
-
promises.push(populateDocumentChangeBuffer(txn, documentBuffer, remoteEvent.documentUpdates
|
|
13489
|
+
promises.push(populateDocumentChangeBuffer(txn, documentBuffer, remoteEvent.documentUpdates).next(result => {
|
|
11966
13490
|
changedDocs = result;
|
|
11967
13491
|
}));
|
|
11968
13492
|
// HACK: The only reason we allow a null snapshot version is so that we
|
|
@@ -12003,17 +13527,13 @@ function localStoreApplyRemoteEventToLocalCache(localStore, remoteEvent) {
|
|
|
12003
13527
|
* Note: this function will use `documentVersions` if it is defined;
|
|
12004
13528
|
* when it is not defined, resorts to `globalVersion`.
|
|
12005
13529
|
*/
|
|
12006
|
-
function populateDocumentChangeBuffer(txn, documentBuffer, documents
|
|
12007
|
-
// TODO(wuandy): We could add `readTime` to MaybeDocument instead to remove
|
|
12008
|
-
// this parameter.
|
|
12009
|
-
documentVersions) {
|
|
13530
|
+
function populateDocumentChangeBuffer(txn, documentBuffer, documents) {
|
|
12010
13531
|
let updatedKeys = documentKeySet();
|
|
12011
13532
|
documents.forEach(k => (updatedKeys = updatedKeys.add(k)));
|
|
12012
13533
|
return documentBuffer.getEntries(txn, updatedKeys).next(existingDocs => {
|
|
12013
13534
|
let changedDocs = mutableDocumentMap();
|
|
12014
13535
|
documents.forEach((key, doc) => {
|
|
12015
13536
|
const existingDoc = existingDocs.get(key);
|
|
12016
|
-
const docReadTime = (documentVersions === null || documentVersions === void 0 ? void 0 : documentVersions.get(key)) || globalVersion;
|
|
12017
13537
|
// Note: The order of the steps below is important, since we want
|
|
12018
13538
|
// to ensure that rejected limbo resolutions (which fabricate
|
|
12019
13539
|
// NoDocuments with SnapshotVersion.min()) never add documents to
|
|
@@ -12022,14 +13542,14 @@ documentVersions) {
|
|
|
12022
13542
|
// NoDocuments with SnapshotVersion.min() are used in manufactured
|
|
12023
13543
|
// events. We remove these documents from cache since we lost
|
|
12024
13544
|
// access.
|
|
12025
|
-
documentBuffer.removeEntry(key,
|
|
13545
|
+
documentBuffer.removeEntry(key, doc.readTime);
|
|
12026
13546
|
changedDocs = changedDocs.insert(key, doc);
|
|
12027
13547
|
}
|
|
12028
13548
|
else if (!existingDoc.isValidDocument() ||
|
|
12029
13549
|
doc.version.compareTo(existingDoc.version) > 0 ||
|
|
12030
13550
|
(doc.version.compareTo(existingDoc.version) === 0 &&
|
|
12031
13551
|
existingDoc.hasPendingWrites)) {
|
|
12032
|
-
documentBuffer.addEntry(doc
|
|
13552
|
+
documentBuffer.addEntry(doc);
|
|
12033
13553
|
changedDocs = changedDocs.insert(key, doc);
|
|
12034
13554
|
}
|
|
12035
13555
|
else {
|
|
@@ -12283,7 +13803,8 @@ function applyWriteToRemoteDocuments(localStoreImpl, txn, batchResult, documentB
|
|
|
12283
13803
|
// We use the commitVersion as the readTime rather than the
|
|
12284
13804
|
// document's updateTime since the updateTime is not advanced
|
|
12285
13805
|
// for updates that do not modify the underlying document.
|
|
12286
|
-
|
|
13806
|
+
doc.setReadTime(batchResult.commitVersion);
|
|
13807
|
+
documentBuffer.addEntry(doc);
|
|
12287
13808
|
}
|
|
12288
13809
|
}
|
|
12289
13810
|
});
|
|
@@ -12384,14 +13905,14 @@ async function localStoreApplyBundledDocuments(localStore, bundleConverter, docu
|
|
|
12384
13905
|
const localStoreImpl = debugCast(localStore);
|
|
12385
13906
|
let documentKeys = documentKeySet();
|
|
12386
13907
|
let documentMap = mutableDocumentMap();
|
|
12387
|
-
let versionMap = documentVersionMap();
|
|
12388
13908
|
for (const bundleDoc of documents) {
|
|
12389
13909
|
const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name);
|
|
12390
13910
|
if (bundleDoc.document) {
|
|
12391
13911
|
documentKeys = documentKeys.add(documentKey);
|
|
12392
13912
|
}
|
|
12393
|
-
|
|
12394
|
-
|
|
13913
|
+
const doc = bundleConverter.toMutableDocument(bundleDoc);
|
|
13914
|
+
doc.setReadTime(bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime));
|
|
13915
|
+
documentMap = documentMap.insert(documentKey, doc);
|
|
12395
13916
|
}
|
|
12396
13917
|
const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({
|
|
12397
13918
|
trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()`
|
|
@@ -12400,7 +13921,7 @@ async function localStoreApplyBundledDocuments(localStore, bundleConverter, docu
|
|
|
12400
13921
|
// they will not get garbage collected right away.
|
|
12401
13922
|
const umbrellaTargetData = await localStoreAllocateTarget(localStoreImpl, umbrellaTarget(bundleName));
|
|
12402
13923
|
return localStoreImpl.persistence.runTransaction('Apply bundle documents', 'readwrite', txn => {
|
|
12403
|
-
return populateDocumentChangeBuffer(txn, documentBuffer, documentMap
|
|
13924
|
+
return populateDocumentChangeBuffer(txn, documentBuffer, documentMap)
|
|
12404
13925
|
.next(changedDocs => {
|
|
12405
13926
|
documentBuffer.apply(txn);
|
|
12406
13927
|
return changedDocs;
|
|
@@ -12514,6 +14035,122 @@ class MemoryBundleCache {
|
|
|
12514
14035
|
}
|
|
12515
14036
|
}
|
|
12516
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
|
+
|
|
12517
14154
|
/**
|
|
12518
14155
|
* @license
|
|
12519
14156
|
* Copyright 2017 Google LLC
|
|
@@ -12867,29 +14504,30 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
12867
14504
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
12868
14505
|
* calculating the size.
|
|
12869
14506
|
*/
|
|
12870
|
-
constructor(
|
|
12871
|
-
this.indexManager = indexManager;
|
|
14507
|
+
constructor(sizer) {
|
|
12872
14508
|
this.sizer = sizer;
|
|
12873
14509
|
/** Underlying cache of documents and their read times. */
|
|
12874
14510
|
this.docs = documentEntryMap();
|
|
12875
14511
|
/** Size of all cached documents. */
|
|
12876
14512
|
this.size = 0;
|
|
12877
14513
|
}
|
|
14514
|
+
setIndexManager(indexManager) {
|
|
14515
|
+
this.indexManager = indexManager;
|
|
14516
|
+
}
|
|
12878
14517
|
/**
|
|
12879
14518
|
* Adds the supplied entry to the cache and updates the cache size as appropriate.
|
|
12880
14519
|
*
|
|
12881
14520
|
* All calls of `addEntry` are required to go through the RemoteDocumentChangeBuffer
|
|
12882
14521
|
* returned by `newChangeBuffer()`.
|
|
12883
14522
|
*/
|
|
12884
|
-
addEntry(transaction, doc
|
|
14523
|
+
addEntry(transaction, doc) {
|
|
12885
14524
|
const key = doc.key;
|
|
12886
14525
|
const entry = this.docs.get(key);
|
|
12887
14526
|
const previousSize = entry ? entry.size : 0;
|
|
12888
14527
|
const currentSize = this.sizer(doc);
|
|
12889
14528
|
this.docs = this.docs.insert(key, {
|
|
12890
14529
|
document: doc.mutableCopy(),
|
|
12891
|
-
size: currentSize
|
|
12892
|
-
readTime
|
|
14530
|
+
size: currentSize
|
|
12893
14531
|
});
|
|
12894
14532
|
this.size += currentSize - previousSize;
|
|
12895
14533
|
return this.indexManager.addToCollectionParentIndex(transaction, key.path.popLast());
|
|
@@ -12923,21 +14561,22 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
12923
14561
|
});
|
|
12924
14562
|
return PersistencePromise.resolve(results);
|
|
12925
14563
|
}
|
|
12926
|
-
|
|
14564
|
+
getAll(transaction, collectionPath, sinceReadTime) {
|
|
12927
14565
|
let results = mutableDocumentMap();
|
|
12928
14566
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
12929
14567
|
// the documents we need to match the query against.
|
|
12930
|
-
const prefix = new DocumentKey(
|
|
14568
|
+
const prefix = new DocumentKey(collectionPath.child(''));
|
|
12931
14569
|
const iterator = this.docs.getIteratorFrom(prefix);
|
|
12932
14570
|
while (iterator.hasNext()) {
|
|
12933
|
-
const { key, value: { document
|
|
12934
|
-
if (!
|
|
14571
|
+
const { key, value: { document } } = iterator.getNext();
|
|
14572
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
12935
14573
|
break;
|
|
12936
14574
|
}
|
|
12937
|
-
if (
|
|
14575
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14576
|
+
// Exclude entries from subcollections.
|
|
12938
14577
|
continue;
|
|
12939
14578
|
}
|
|
12940
|
-
if (
|
|
14579
|
+
if (document.readTime.compareTo(sinceReadTime) <= 0) {
|
|
12941
14580
|
continue;
|
|
12942
14581
|
}
|
|
12943
14582
|
results = results.insert(document.key, document.mutableCopy());
|
|
@@ -12959,13 +14598,12 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
12959
14598
|
/**
|
|
12960
14599
|
* Creates a new memory-only RemoteDocumentCache.
|
|
12961
14600
|
*
|
|
12962
|
-
* @param indexManager - A class that manages collection group indices.
|
|
12963
14601
|
* @param sizer - Used to assess the size of a document. For eager GC, this is
|
|
12964
14602
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
12965
14603
|
* calculating the size.
|
|
12966
14604
|
*/
|
|
12967
|
-
function newMemoryRemoteDocumentCache(
|
|
12968
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
14605
|
+
function newMemoryRemoteDocumentCache(sizer) {
|
|
14606
|
+
return new MemoryRemoteDocumentCacheImpl(sizer);
|
|
12969
14607
|
}
|
|
12970
14608
|
/**
|
|
12971
14609
|
* Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
|
|
@@ -12978,8 +14616,8 @@ class MemoryRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12978
14616
|
applyChanges(transaction) {
|
|
12979
14617
|
const promises = [];
|
|
12980
14618
|
this.changes.forEach((key, doc) => {
|
|
12981
|
-
if (doc.
|
|
12982
|
-
promises.push(this.documentCache.addEntry(transaction, doc
|
|
14619
|
+
if (doc.isValidDocument()) {
|
|
14620
|
+
promises.push(this.documentCache.addEntry(transaction, doc));
|
|
12983
14621
|
}
|
|
12984
14622
|
else {
|
|
12985
14623
|
this.documentCache.removeEntry(key);
|
|
@@ -13159,6 +14797,7 @@ class MemoryPersistence {
|
|
|
13159
14797
|
*/
|
|
13160
14798
|
constructor(referenceDelegateFactory, serializer) {
|
|
13161
14799
|
this.mutationQueues = {};
|
|
14800
|
+
this.overlays = {};
|
|
13162
14801
|
this.listenSequence = new ListenSequence(0);
|
|
13163
14802
|
this._started = false;
|
|
13164
14803
|
this._started = true;
|
|
@@ -13166,7 +14805,7 @@ class MemoryPersistence {
|
|
|
13166
14805
|
this.targetCache = new MemoryTargetCache(this);
|
|
13167
14806
|
const sizer = (doc) => this.referenceDelegate.documentSize(doc);
|
|
13168
14807
|
this.indexManager = new MemoryIndexManager();
|
|
13169
|
-
this.remoteDocumentCache = newMemoryRemoteDocumentCache(
|
|
14808
|
+
this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
|
|
13170
14809
|
this.serializer = new LocalSerializer(serializer);
|
|
13171
14810
|
this.bundleCache = new MemoryBundleCache(this.serializer);
|
|
13172
14811
|
}
|
|
@@ -13187,13 +14826,23 @@ class MemoryPersistence {
|
|
|
13187
14826
|
setNetworkEnabled() {
|
|
13188
14827
|
// No op.
|
|
13189
14828
|
}
|
|
13190
|
-
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.
|
|
13191
14832
|
return this.indexManager;
|
|
13192
14833
|
}
|
|
13193
|
-
|
|
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) {
|
|
13194
14843
|
let queue = this.mutationQueues[user.toKey()];
|
|
13195
14844
|
if (!queue) {
|
|
13196
|
-
queue = new MemoryMutationQueue(
|
|
14845
|
+
queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
|
|
13197
14846
|
this.mutationQueues[user.toKey()] = queue;
|
|
13198
14847
|
}
|
|
13199
14848
|
return queue;
|
|
@@ -13292,7 +14941,7 @@ class MemoryEagerDelegate {
|
|
|
13292
14941
|
const key = DocumentKey.fromPath(path);
|
|
13293
14942
|
return this.isReferenced(txn, key).next(isReferenced => {
|
|
13294
14943
|
if (!isReferenced) {
|
|
13295
|
-
changeBuffer.removeEntry(key);
|
|
14944
|
+
changeBuffer.removeEntry(key, SnapshotVersion.min());
|
|
13296
14945
|
}
|
|
13297
14946
|
});
|
|
13298
14947
|
}).next(() => {
|
|
@@ -14338,7 +15987,7 @@ const LOG_TAG$9 = 'Connection';
|
|
|
14338
15987
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
14339
15988
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
14340
15989
|
hardAssert(authToken === null || authToken.type === 'OAuth');
|
|
14341
|
-
const metadata = new
|
|
15990
|
+
const metadata = new grpc__namespace.Metadata();
|
|
14342
15991
|
if (authToken) {
|
|
14343
15992
|
authToken.headers.forEach((value, key) => metadata.set(key, value));
|
|
14344
15993
|
}
|
|
@@ -14369,10 +16018,10 @@ class GrpcConnection {
|
|
|
14369
16018
|
ensureActiveStub() {
|
|
14370
16019
|
if (!this.cachedStub) {
|
|
14371
16020
|
logDebug(LOG_TAG$9, 'Creating Firestore stub.');
|
|
14372
|
-
const credentials
|
|
14373
|
-
?
|
|
14374
|
-
:
|
|
14375
|
-
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);
|
|
14376
16025
|
}
|
|
14377
16026
|
return this.cachedStub;
|
|
14378
16027
|
}
|
|
@@ -17072,6 +18721,40 @@ const nested = {
|
|
|
17072
18721
|
id: 2
|
|
17073
18722
|
}
|
|
17074
18723
|
}
|
|
18724
|
+
},
|
|
18725
|
+
methodSignature: {
|
|
18726
|
+
rule: "repeated",
|
|
18727
|
+
type: "string",
|
|
18728
|
+
id: 1051,
|
|
18729
|
+
extend: "google.protobuf.MethodOptions"
|
|
18730
|
+
},
|
|
18731
|
+
defaultHost: {
|
|
18732
|
+
type: "string",
|
|
18733
|
+
id: 1049,
|
|
18734
|
+
extend: "google.protobuf.ServiceOptions"
|
|
18735
|
+
},
|
|
18736
|
+
oauthScopes: {
|
|
18737
|
+
type: "string",
|
|
18738
|
+
id: 1050,
|
|
18739
|
+
extend: "google.protobuf.ServiceOptions"
|
|
18740
|
+
},
|
|
18741
|
+
fieldBehavior: {
|
|
18742
|
+
rule: "repeated",
|
|
18743
|
+
type: "google.api.FieldBehavior",
|
|
18744
|
+
id: 1052,
|
|
18745
|
+
extend: "google.protobuf.FieldOptions"
|
|
18746
|
+
},
|
|
18747
|
+
FieldBehavior: {
|
|
18748
|
+
values: {
|
|
18749
|
+
FIELD_BEHAVIOR_UNSPECIFIED: 0,
|
|
18750
|
+
OPTIONAL: 1,
|
|
18751
|
+
REQUIRED: 2,
|
|
18752
|
+
OUTPUT_ONLY: 3,
|
|
18753
|
+
INPUT_ONLY: 4,
|
|
18754
|
+
IMMUTABLE: 5,
|
|
18755
|
+
UNORDERED_LIST: 6,
|
|
18756
|
+
NON_EMPTY_DEFAULT: 7
|
|
18757
|
+
}
|
|
17075
18758
|
}
|
|
17076
18759
|
}
|
|
17077
18760
|
},
|
|
@@ -17170,8 +18853,8 @@ const protoLoaderOptions = {
|
|
|
17170
18853
|
* @returns The GrpcObject representing our protos.
|
|
17171
18854
|
*/
|
|
17172
18855
|
function loadProtos() {
|
|
17173
|
-
const packageDefinition =
|
|
17174
|
-
return
|
|
18856
|
+
const packageDefinition = protoLoader__namespace.fromJSON(protos$1, protoLoaderOptions);
|
|
18857
|
+
return grpc__namespace.loadPackageDefinition(packageDefinition);
|
|
17175
18858
|
}
|
|
17176
18859
|
|
|
17177
18860
|
/**
|
|
@@ -20122,12 +21805,12 @@ async function syncEngineListen(syncEngine, query) {
|
|
|
20122
21805
|
}
|
|
20123
21806
|
else {
|
|
20124
21807
|
const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
|
|
20125
|
-
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
20126
|
-
targetId = targetData.targetId;
|
|
20127
|
-
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20128
21808
|
if (syncEngineImpl.isPrimaryClient) {
|
|
20129
21809
|
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
|
|
20130
21810
|
}
|
|
21811
|
+
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
21812
|
+
targetId = targetData.targetId;
|
|
21813
|
+
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20131
21814
|
}
|
|
20132
21815
|
return viewSnapshot;
|
|
20133
21816
|
}
|
|
@@ -20315,6 +21998,9 @@ async function syncEngineRejectListen(syncEngine, targetId, err) {
|
|
|
20315
21998
|
// store to purge a document. However, it would be tricky to keep all of
|
|
20316
21999
|
// the local store's invariants with another method.
|
|
20317
22000
|
let documentUpdates = new SortedMap(DocumentKey.comparator);
|
|
22001
|
+
// TODO(b/217189216): This limbo document should ideally have a read time,
|
|
22002
|
+
// so that it is picked up by any read-time based scans. The backend,
|
|
22003
|
+
// however, does not send a read time for target removals.
|
|
20318
22004
|
documentUpdates = documentUpdates.insert(limboKey, MutableDocument.newNoDocument(limboKey, SnapshotVersion.min()));
|
|
20319
22005
|
const resolvedLimboDocuments = documentKeySet().add(limboKey);
|
|
20320
22006
|
const event = new RemoteEvent(SnapshotVersion.min(),
|
|
@@ -22259,72 +23945,6 @@ function createBundleReader(data, serializer) {
|
|
|
22259
23945
|
return newBundleReader(toByteStreamReader(content), serializer);
|
|
22260
23946
|
}
|
|
22261
23947
|
|
|
22262
|
-
/**
|
|
22263
|
-
* @license
|
|
22264
|
-
* Copyright 2017 Google LLC
|
|
22265
|
-
*
|
|
22266
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
22267
|
-
* you may not use this file except in compliance with the License.
|
|
22268
|
-
* You may obtain a copy of the License at
|
|
22269
|
-
*
|
|
22270
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
22271
|
-
*
|
|
22272
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
22273
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
22274
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22275
|
-
* See the License for the specific language governing permissions and
|
|
22276
|
-
* limitations under the License.
|
|
22277
|
-
*/
|
|
22278
|
-
class DatabaseInfo {
|
|
22279
|
-
/**
|
|
22280
|
-
* Constructs a DatabaseInfo using the provided host, databaseId and
|
|
22281
|
-
* persistenceKey.
|
|
22282
|
-
*
|
|
22283
|
-
* @param databaseId - The database to use.
|
|
22284
|
-
* @param appId - The Firebase App Id.
|
|
22285
|
-
* @param persistenceKey - A unique identifier for this Firestore's local
|
|
22286
|
-
* storage (used in conjunction with the databaseId).
|
|
22287
|
-
* @param host - The Firestore backend host to connect to.
|
|
22288
|
-
* @param ssl - Whether to use SSL when connecting.
|
|
22289
|
-
* @param forceLongPolling - Whether to use the forceLongPolling option
|
|
22290
|
-
* when using WebChannel as the network transport.
|
|
22291
|
-
* @param autoDetectLongPolling - Whether to use the detectBufferingProxy
|
|
22292
|
-
* option when using WebChannel as the network transport.
|
|
22293
|
-
* @param useFetchStreams Whether to use the Fetch API instead of
|
|
22294
|
-
* XMLHTTPRequest
|
|
22295
|
-
*/
|
|
22296
|
-
constructor(databaseId, appId, persistenceKey, host, ssl, forceLongPolling, autoDetectLongPolling, useFetchStreams) {
|
|
22297
|
-
this.databaseId = databaseId;
|
|
22298
|
-
this.appId = appId;
|
|
22299
|
-
this.persistenceKey = persistenceKey;
|
|
22300
|
-
this.host = host;
|
|
22301
|
-
this.ssl = ssl;
|
|
22302
|
-
this.forceLongPolling = forceLongPolling;
|
|
22303
|
-
this.autoDetectLongPolling = autoDetectLongPolling;
|
|
22304
|
-
this.useFetchStreams = useFetchStreams;
|
|
22305
|
-
}
|
|
22306
|
-
}
|
|
22307
|
-
/** The default database name for a project. */
|
|
22308
|
-
const DEFAULT_DATABASE_NAME = '(default)';
|
|
22309
|
-
/**
|
|
22310
|
-
* Represents the database ID a Firestore client is associated with.
|
|
22311
|
-
* @internal
|
|
22312
|
-
*/
|
|
22313
|
-
class DatabaseId {
|
|
22314
|
-
constructor(projectId, database) {
|
|
22315
|
-
this.projectId = projectId;
|
|
22316
|
-
this.database = database ? database : DEFAULT_DATABASE_NAME;
|
|
22317
|
-
}
|
|
22318
|
-
get isDefaultDatabase() {
|
|
22319
|
-
return this.database === DEFAULT_DATABASE_NAME;
|
|
22320
|
-
}
|
|
22321
|
-
isEqual(other) {
|
|
22322
|
-
return (other instanceof DatabaseId &&
|
|
22323
|
-
other.projectId === this.projectId &&
|
|
22324
|
-
other.database === this.database);
|
|
22325
|
-
}
|
|
22326
|
-
}
|
|
22327
|
-
|
|
22328
23948
|
/**
|
|
22329
23949
|
* @license
|
|
22330
23950
|
* Copyright 2020 Google LLC
|
|
@@ -24698,8 +26318,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
|
|
|
24698
26318
|
this.metadata = metadata;
|
|
24699
26319
|
}
|
|
24700
26320
|
/**
|
|
24701
|
-
*
|
|
24702
|
-
* exists. True if the document exists.
|
|
26321
|
+
* Returns whether or not the data exists. True if the document exists.
|
|
24703
26322
|
*/
|
|
24704
26323
|
exists() {
|
|
24705
26324
|
return super.exists();
|
|
@@ -25073,51 +26692,53 @@ function limitToLast(limit) {
|
|
|
25073
26692
|
return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
|
|
25074
26693
|
}
|
|
25075
26694
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
25076
|
-
constructor(type, _docOrFields,
|
|
26695
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25077
26696
|
super();
|
|
25078
26697
|
this.type = type;
|
|
25079
26698
|
this._docOrFields = _docOrFields;
|
|
25080
|
-
this.
|
|
26699
|
+
this._inclusive = _inclusive;
|
|
25081
26700
|
}
|
|
25082
26701
|
_apply(query) {
|
|
25083
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26702
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25084
26703
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
25085
26704
|
}
|
|
25086
26705
|
}
|
|
25087
26706
|
function startAt(...docOrFields) {
|
|
25088
|
-
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26707
|
+
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26708
|
+
/*inclusive=*/ true);
|
|
25089
26709
|
}
|
|
25090
26710
|
function startAfter(...docOrFields) {
|
|
25091
26711
|
return new QueryStartAtConstraint('startAfter', docOrFields,
|
|
25092
|
-
/*
|
|
26712
|
+
/*inclusive=*/ false);
|
|
25093
26713
|
}
|
|
25094
26714
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
25095
|
-
constructor(type, _docOrFields,
|
|
26715
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25096
26716
|
super();
|
|
25097
26717
|
this.type = type;
|
|
25098
26718
|
this._docOrFields = _docOrFields;
|
|
25099
|
-
this.
|
|
26719
|
+
this._inclusive = _inclusive;
|
|
25100
26720
|
}
|
|
25101
26721
|
_apply(query) {
|
|
25102
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26722
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25103
26723
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
25104
26724
|
}
|
|
25105
26725
|
}
|
|
25106
26726
|
function endBefore(...docOrFields) {
|
|
25107
|
-
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26727
|
+
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26728
|
+
/*inclusive=*/ false);
|
|
25108
26729
|
}
|
|
25109
26730
|
function endAt(...docOrFields) {
|
|
25110
|
-
return new QueryEndAtConstraint('endAt', docOrFields, /*
|
|
26731
|
+
return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
|
|
25111
26732
|
}
|
|
25112
26733
|
/** Helper function to create a bound from a document or fields */
|
|
25113
|
-
function newQueryBoundFromDocOrFields(query, methodName, docOrFields,
|
|
26734
|
+
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
25114
26735
|
docOrFields[0] = util$1.getModularInstance(docOrFields[0]);
|
|
25115
26736
|
if (docOrFields[0] instanceof DocumentSnapshot$1) {
|
|
25116
|
-
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document,
|
|
26737
|
+
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
|
|
25117
26738
|
}
|
|
25118
26739
|
else {
|
|
25119
26740
|
const reader = newUserDataReader(query.firestore);
|
|
25120
|
-
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields,
|
|
26741
|
+
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
|
|
25121
26742
|
}
|
|
25122
26743
|
}
|
|
25123
26744
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
@@ -25175,7 +26796,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
|
|
|
25175
26796
|
* of the query or if any of the fields in the order by are an uncommitted
|
|
25176
26797
|
* server timestamp.
|
|
25177
26798
|
*/
|
|
25178
|
-
function newQueryBoundFromDocument(query, databaseId, methodName, doc,
|
|
26799
|
+
function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
|
|
25179
26800
|
if (!doc) {
|
|
25180
26801
|
throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
|
|
25181
26802
|
`${methodName}().`);
|
|
@@ -25212,12 +26833,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
|
|
|
25212
26833
|
}
|
|
25213
26834
|
}
|
|
25214
26835
|
}
|
|
25215
|
-
return new Bound(components,
|
|
26836
|
+
return new Bound(components, inclusive);
|
|
25216
26837
|
}
|
|
25217
26838
|
/**
|
|
25218
26839
|
* Converts a list of field values to a `Bound` for the given query.
|
|
25219
26840
|
*/
|
|
25220
|
-
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values,
|
|
26841
|
+
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
|
|
25221
26842
|
// Use explicit order by's because it has to match the query the user made
|
|
25222
26843
|
const orderBy = query.explicitOrderBy;
|
|
25223
26844
|
if (values.length > orderBy.length) {
|
|
@@ -25254,7 +26875,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
|
|
|
25254
26875
|
components.push(wrapped);
|
|
25255
26876
|
}
|
|
25256
26877
|
}
|
|
25257
|
-
return new Bound(components,
|
|
26878
|
+
return new Bound(components, inclusive);
|
|
25258
26879
|
}
|
|
25259
26880
|
/**
|
|
25260
26881
|
* Parses the given `documentIdValue` into a `ReferenceValue`, throwing
|
|
@@ -26238,17 +27859,6 @@ function writeBatch(firestore) {
|
|
|
26238
27859
|
ensureFirestoreConfigured(firestore);
|
|
26239
27860
|
return new WriteBatch(firestore, mutations => executeWrite(firestore, mutations));
|
|
26240
27861
|
}
|
|
26241
|
-
/** An index component consisting of field path and index type. */
|
|
26242
|
-
class Segment {
|
|
26243
|
-
constructor(
|
|
26244
|
-
/** The field path of the component. */
|
|
26245
|
-
fieldPath,
|
|
26246
|
-
/** The fields sorting order. */
|
|
26247
|
-
kind) {
|
|
26248
|
-
this.fieldPath = fieldPath;
|
|
26249
|
-
this.kind = kind;
|
|
26250
|
-
}
|
|
26251
|
-
}
|
|
26252
27862
|
|
|
26253
27863
|
/**
|
|
26254
27864
|
* @license
|
|
@@ -26272,27 +27882,29 @@ function setIndexConfiguration(firestore, jsonOrConfiguration) {
|
|
|
26272
27882
|
const indexConfiguration = typeof jsonOrConfiguration === 'string'
|
|
26273
27883
|
? tryParseJson(jsonOrConfiguration)
|
|
26274
27884
|
: jsonOrConfiguration;
|
|
27885
|
+
const parsedIndexes = [];
|
|
26275
27886
|
// PORTING NOTE: We don't return an error if the user has not enabled
|
|
26276
27887
|
// persistence since `enableIndexeddbPersistence()` can fail on the Web.
|
|
26277
27888
|
if (Array.isArray(indexConfiguration.indexes)) {
|
|
26278
27889
|
for (const index of indexConfiguration.indexes) {
|
|
26279
|
-
tryGetString(index, 'collectionGroup');
|
|
27890
|
+
const collectionGroup = tryGetString(index, 'collectionGroup');
|
|
26280
27891
|
const segments = [];
|
|
26281
27892
|
if (Array.isArray(index.fields)) {
|
|
26282
27893
|
for (const field of index.fields) {
|
|
26283
27894
|
const fieldPathString = tryGetString(field, 'fieldPath');
|
|
26284
27895
|
const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
|
|
26285
27896
|
if (field.arrayConfig === 'CONTAINS') {
|
|
26286
|
-
segments.push(new
|
|
27897
|
+
segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
|
|
26287
27898
|
}
|
|
26288
27899
|
else if (field.order === 'ASCENDING') {
|
|
26289
|
-
segments.push(new
|
|
27900
|
+
segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
|
|
26290
27901
|
}
|
|
26291
27902
|
else if (field.order === 'DESCENDING') {
|
|
26292
|
-
segments.push(new
|
|
27903
|
+
segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
|
|
26293
27904
|
}
|
|
26294
27905
|
}
|
|
26295
27906
|
}
|
|
27907
|
+
parsedIndexes.push(new FieldIndex(FieldIndex.UNKNOWN_ID, collectionGroup, segments, IndexState.empty()));
|
|
26296
27908
|
}
|
|
26297
27909
|
}
|
|
26298
27910
|
// TODO(indexing): Configure indexes
|