@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.mjs
CHANGED
|
@@ -4,11 +4,11 @@ import { Logger, LogLevel } from '@firebase/logger';
|
|
|
4
4
|
import { inspect, TextEncoder, TextDecoder } from 'util';
|
|
5
5
|
import { FirebaseError, createMockUserToken, getModularInstance, deepEqual, getUA, isIndexedDBAvailable, isSafari } from '@firebase/util';
|
|
6
6
|
import { randomBytes as randomBytes$1 } from 'crypto';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import * as grpc from '@grpc/grpc-js';
|
|
8
|
+
import * as protoLoader from '@grpc/proto-loader';
|
|
9
9
|
|
|
10
10
|
const name = "@firebase/firestore";
|
|
11
|
-
const version$1 = "3.4.
|
|
11
|
+
const version$1 = "3.4.5-2022116201138";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* @license
|
|
@@ -61,7 +61,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
61
61
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
62
62
|
User.MOCK_USER = new User('mock-user');
|
|
63
63
|
|
|
64
|
-
const version = "9.6.
|
|
64
|
+
const version = "9.6.7-2022116201138";
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @license
|
|
@@ -666,13 +666,19 @@ class FirebaseAppCheckTokenProvider {
|
|
|
666
666
|
this.appCheckProvider = appCheckProvider;
|
|
667
667
|
this.forceRefresh = false;
|
|
668
668
|
this.appCheck = null;
|
|
669
|
+
this.latestAppCheckToken = null;
|
|
669
670
|
}
|
|
670
671
|
start(asyncQueue, changeListener) {
|
|
671
672
|
const onTokenChanged = tokenResult => {
|
|
672
673
|
if (tokenResult.error != null) {
|
|
673
674
|
logDebug('FirebaseAppCheckTokenProvider', `Error getting App Check token; using placeholder token instead. Error: ${tokenResult.error.message}`);
|
|
674
675
|
}
|
|
675
|
-
|
|
676
|
+
const tokenUpdated = tokenResult.token !== this.latestAppCheckToken;
|
|
677
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
678
|
+
logDebug('FirebaseAppCheckTokenProvider', `Received ${tokenUpdated ? 'new' : 'existing'} token.`);
|
|
679
|
+
return tokenUpdated
|
|
680
|
+
? changeListener(tokenResult.token)
|
|
681
|
+
: Promise.resolve();
|
|
676
682
|
};
|
|
677
683
|
this.tokenListener = (tokenResult) => {
|
|
678
684
|
asyncQueue.enqueueRetryable(() => onTokenChanged(tokenResult));
|
|
@@ -707,6 +713,7 @@ class FirebaseAppCheckTokenProvider {
|
|
|
707
713
|
return this.appCheck.getToken(forceRefresh).then(tokenResult => {
|
|
708
714
|
if (tokenResult) {
|
|
709
715
|
hardAssert(typeof tokenResult.token === 'string');
|
|
716
|
+
this.latestAppCheckToken = tokenResult.token;
|
|
710
717
|
return new AppCheckToken(tokenResult.token);
|
|
711
718
|
}
|
|
712
719
|
else {
|
|
@@ -1192,23 +1199,6 @@ function decodeResourcePath(path) {
|
|
|
1192
1199
|
}
|
|
1193
1200
|
return new ResourcePath(segments);
|
|
1194
1201
|
}
|
|
1195
|
-
|
|
1196
|
-
/**
|
|
1197
|
-
* @license
|
|
1198
|
-
* Copyright 2017 Google LLC
|
|
1199
|
-
*
|
|
1200
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1201
|
-
* you may not use this file except in compliance with the License.
|
|
1202
|
-
* You may obtain a copy of the License at
|
|
1203
|
-
*
|
|
1204
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1205
|
-
*
|
|
1206
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
1207
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1208
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1209
|
-
* See the License for the specific language governing permissions and
|
|
1210
|
-
* limitations under the License.
|
|
1211
|
-
*/
|
|
1212
1202
|
/**
|
|
1213
1203
|
* Schema Version for the Web client:
|
|
1214
1204
|
* 1. Initial version including Mutation Queue, Query Cache, and Remote
|
|
@@ -1227,8 +1217,10 @@ function decodeResourcePath(path) {
|
|
|
1227
1217
|
* an auto-incrementing ID. This is required for Index-Free queries.
|
|
1228
1218
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
1229
1219
|
* 11. Add bundles and named_queries for bundle support.
|
|
1220
|
+
* 12. Add document overlays.
|
|
1221
|
+
* 13. Add indexing support.
|
|
1230
1222
|
*/
|
|
1231
|
-
const SCHEMA_VERSION =
|
|
1223
|
+
const SCHEMA_VERSION = 12;
|
|
1232
1224
|
/**
|
|
1233
1225
|
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.
|
|
1234
1226
|
*/
|
|
@@ -1753,9 +1745,7 @@ class DbClientMetadata {
|
|
|
1753
1745
|
DbClientMetadata.store = 'clientMetadata';
|
|
1754
1746
|
/** Keys are automatically assigned via the clientId properties. */
|
|
1755
1747
|
DbClientMetadata.keyPath = 'clientId';
|
|
1756
|
-
/**
|
|
1757
|
-
* A object representing a bundle loaded by the SDK.
|
|
1758
|
-
*/
|
|
1748
|
+
/** An object representing a bundle loaded by the SDK. */
|
|
1759
1749
|
class DbBundle {
|
|
1760
1750
|
constructor(
|
|
1761
1751
|
/** The ID of the loaded bundle. */
|
|
@@ -1772,9 +1762,7 @@ class DbBundle {
|
|
|
1772
1762
|
/** Name of the IndexedDb object store. */
|
|
1773
1763
|
DbBundle.store = 'bundles';
|
|
1774
1764
|
DbBundle.keyPath = 'bundleId';
|
|
1775
|
-
/**
|
|
1776
|
-
* A object representing a named query loaded by the SDK via a bundle.
|
|
1777
|
-
*/
|
|
1765
|
+
/** An object representing a named query loaded by the SDK via a bundle. */
|
|
1778
1766
|
class DbNamedQuery {
|
|
1779
1767
|
constructor(
|
|
1780
1768
|
/** The name of the query. */
|
|
@@ -1791,6 +1779,158 @@ class DbNamedQuery {
|
|
|
1791
1779
|
/** Name of the IndexedDb object store. */
|
|
1792
1780
|
DbNamedQuery.store = 'namedQueries';
|
|
1793
1781
|
DbNamedQuery.keyPath = 'name';
|
|
1782
|
+
/** An object representing the global configuration for a field index. */
|
|
1783
|
+
class DbIndexConfiguration {
|
|
1784
|
+
constructor(
|
|
1785
|
+
/**
|
|
1786
|
+
* The index id for this entry. Undefined for indexes that are not yet
|
|
1787
|
+
* persisted.
|
|
1788
|
+
*/
|
|
1789
|
+
indexId,
|
|
1790
|
+
/** The collection group this index belongs to. */
|
|
1791
|
+
collectionGroup,
|
|
1792
|
+
/** The fields to index for this index. */
|
|
1793
|
+
fields) {
|
|
1794
|
+
this.indexId = indexId;
|
|
1795
|
+
this.collectionGroup = collectionGroup;
|
|
1796
|
+
this.fields = fields;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
/** Name of the IndexedDb object store. */
|
|
1800
|
+
DbIndexConfiguration.store = 'indexConfiguration';
|
|
1801
|
+
DbIndexConfiguration.keyPath = 'indexId';
|
|
1802
|
+
/**
|
|
1803
|
+
* An index that provides access to the index configurations by collection
|
|
1804
|
+
* group.
|
|
1805
|
+
*
|
|
1806
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1807
|
+
* not possible here as the Web client supports concurrent access to
|
|
1808
|
+
* persistence via multi-tab.
|
|
1809
|
+
*/
|
|
1810
|
+
DbIndexConfiguration.collectionGroupIndex = 'collectionGroupIndex';
|
|
1811
|
+
DbIndexConfiguration.collectionGroupIndexPath = 'collectionGroup';
|
|
1812
|
+
/**
|
|
1813
|
+
* An object describing how up-to-date the index backfill is for each user and
|
|
1814
|
+
* index.
|
|
1815
|
+
*/
|
|
1816
|
+
class DbIndexState {
|
|
1817
|
+
constructor(
|
|
1818
|
+
/** The index id for this entry. */
|
|
1819
|
+
indexId,
|
|
1820
|
+
/** The user id for this entry. */
|
|
1821
|
+
uid,
|
|
1822
|
+
/**
|
|
1823
|
+
* A number that indicates when the index was last updated (relative to
|
|
1824
|
+
* other indexes).
|
|
1825
|
+
*/
|
|
1826
|
+
sequenceNumber,
|
|
1827
|
+
/**
|
|
1828
|
+
* The latest read time that has been indexed by Firestore for this field
|
|
1829
|
+
* index. Set to `{seconds: 0, nanos: 0}` if no documents have been indexed.
|
|
1830
|
+
*/
|
|
1831
|
+
readTime,
|
|
1832
|
+
/**
|
|
1833
|
+
* The last document that has been indexed for this field index. Empty if
|
|
1834
|
+
* no documents have been indexed.
|
|
1835
|
+
*/
|
|
1836
|
+
documentKey,
|
|
1837
|
+
/**
|
|
1838
|
+
* The largest mutation batch id that has been processed for this index. -1
|
|
1839
|
+
* if no mutations have been indexed.
|
|
1840
|
+
*/
|
|
1841
|
+
largestBatchId) {
|
|
1842
|
+
this.indexId = indexId;
|
|
1843
|
+
this.uid = uid;
|
|
1844
|
+
this.sequenceNumber = sequenceNumber;
|
|
1845
|
+
this.readTime = readTime;
|
|
1846
|
+
this.documentKey = documentKey;
|
|
1847
|
+
this.largestBatchId = largestBatchId;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
/** Name of the IndexedDb object store. */
|
|
1851
|
+
DbIndexState.store = 'indexState';
|
|
1852
|
+
DbIndexState.keyPath = ['indexId', 'uid'];
|
|
1853
|
+
/**
|
|
1854
|
+
* An index that provides access to documents in a collection sorted by last
|
|
1855
|
+
* update time. Used by the backfiller.
|
|
1856
|
+
*
|
|
1857
|
+
* PORTING NOTE: iOS and Android maintain this index in-memory, but this is
|
|
1858
|
+
* not possible here as the Web client supports concurrent access to
|
|
1859
|
+
* persistence via multi-tab.
|
|
1860
|
+
*/
|
|
1861
|
+
DbIndexState.sequenceNumberIndex = 'sequenceNumberIndex';
|
|
1862
|
+
DbIndexState.sequenceNumberIndexPath = ['uid', 'sequenceNumber'];
|
|
1863
|
+
/** An object that stores the encoded entries for all documents and fields. */
|
|
1864
|
+
class DbIndexEntry {
|
|
1865
|
+
constructor(
|
|
1866
|
+
/** The index id for this entry. */
|
|
1867
|
+
indexId,
|
|
1868
|
+
/** The user id for this entry. */
|
|
1869
|
+
uid,
|
|
1870
|
+
/** The encoded array index value for this entry. */
|
|
1871
|
+
arrayValue,
|
|
1872
|
+
/** The encoded directional value for equality and inequality filters. */
|
|
1873
|
+
directionalValue,
|
|
1874
|
+
/** The document key this entry points to. */
|
|
1875
|
+
documentKey) {
|
|
1876
|
+
this.indexId = indexId;
|
|
1877
|
+
this.uid = uid;
|
|
1878
|
+
this.arrayValue = arrayValue;
|
|
1879
|
+
this.directionalValue = directionalValue;
|
|
1880
|
+
this.documentKey = documentKey;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
/** Name of the IndexedDb object store. */
|
|
1884
|
+
DbIndexEntry.store = 'indexEntries';
|
|
1885
|
+
DbIndexEntry.keyPath = [
|
|
1886
|
+
'indexId',
|
|
1887
|
+
'uid',
|
|
1888
|
+
'arrayValue',
|
|
1889
|
+
'directionalValue',
|
|
1890
|
+
'documentKey'
|
|
1891
|
+
];
|
|
1892
|
+
DbIndexEntry.documentKeyIndex = 'documentKeyIndex';
|
|
1893
|
+
DbIndexEntry.documentKeyIndexPath = ['indexId', 'uid', 'documentKey'];
|
|
1894
|
+
/**
|
|
1895
|
+
* An object representing a document overlay.
|
|
1896
|
+
*/
|
|
1897
|
+
class DbDocumentOverlay {
|
|
1898
|
+
constructor(
|
|
1899
|
+
/** The user ID to whom this overlay belongs. */
|
|
1900
|
+
userId,
|
|
1901
|
+
/** The path to the collection that contains the document. */
|
|
1902
|
+
collectionPath,
|
|
1903
|
+
/** The ID (key) of the document within the collection. */
|
|
1904
|
+
documentId,
|
|
1905
|
+
/** The collection group to which the document belongs. */
|
|
1906
|
+
collectionGroup,
|
|
1907
|
+
/** The largest batch ID that's been applied for this overlay. */
|
|
1908
|
+
largestBatchId,
|
|
1909
|
+
/** The overlay mutation. */
|
|
1910
|
+
overlayMutation) {
|
|
1911
|
+
this.userId = userId;
|
|
1912
|
+
this.collectionPath = collectionPath;
|
|
1913
|
+
this.documentId = documentId;
|
|
1914
|
+
this.collectionGroup = collectionGroup;
|
|
1915
|
+
this.largestBatchId = largestBatchId;
|
|
1916
|
+
this.overlayMutation = overlayMutation;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
/** Name of the IndexedDb object store. */
|
|
1920
|
+
DbDocumentOverlay.store = 'documentOverlays';
|
|
1921
|
+
DbDocumentOverlay.keyPath = ['userId', 'collectionPath', 'documentId'];
|
|
1922
|
+
DbDocumentOverlay.collectionPathOverlayIndex = 'collectionPathOverlayIndex';
|
|
1923
|
+
DbDocumentOverlay.collectionPathOverlayIndexPath = [
|
|
1924
|
+
'userId',
|
|
1925
|
+
'collectionPath',
|
|
1926
|
+
'largestBatchId'
|
|
1927
|
+
];
|
|
1928
|
+
DbDocumentOverlay.collectionGroupOverlayIndex = 'collectionGroupOverlayIndex';
|
|
1929
|
+
DbDocumentOverlay.collectionGroupOverlayIndexPath = [
|
|
1930
|
+
'userId',
|
|
1931
|
+
'collectionGroup',
|
|
1932
|
+
'largestBatchId'
|
|
1933
|
+
];
|
|
1794
1934
|
// Visible for testing
|
|
1795
1935
|
const V1_STORES = [
|
|
1796
1936
|
DbMutationQueue.store,
|
|
@@ -1805,7 +1945,6 @@ const V1_STORES = [
|
|
|
1805
1945
|
// V2 is no longer usable (see comment at top of file)
|
|
1806
1946
|
// Visible for testing
|
|
1807
1947
|
const V3_STORES = V1_STORES;
|
|
1808
|
-
// Visible for testing
|
|
1809
1948
|
// Note: DbRemoteDocumentChanges is no longer used and dropped with v9.
|
|
1810
1949
|
const V4_STORES = [...V3_STORES, DbClientMetadata.store];
|
|
1811
1950
|
// V5 does not change the set of stores.
|
|
@@ -1815,12 +1954,28 @@ const V8_STORES = [...V6_STORES, DbCollectionParent.store];
|
|
|
1815
1954
|
// V9 does not change the set of stores.
|
|
1816
1955
|
// V10 does not change the set of stores.
|
|
1817
1956
|
const V11_STORES = [...V8_STORES, DbBundle.store, DbNamedQuery.store];
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1957
|
+
const V12_STORES = [...V11_STORES, DbDocumentOverlay.store];
|
|
1958
|
+
const V13_STORES = [
|
|
1959
|
+
...V12_STORES,
|
|
1960
|
+
DbIndexConfiguration.store,
|
|
1961
|
+
DbIndexState.store,
|
|
1962
|
+
DbIndexEntry.store
|
|
1963
|
+
];
|
|
1964
|
+
/** Returns the object stores for the provided schema. */
|
|
1965
|
+
function getObjectStores(schemaVersion) {
|
|
1966
|
+
if (schemaVersion === 13) {
|
|
1967
|
+
return V13_STORES;
|
|
1968
|
+
}
|
|
1969
|
+
else if (schemaVersion === 12) {
|
|
1970
|
+
return V12_STORES;
|
|
1971
|
+
}
|
|
1972
|
+
else if (schemaVersion === 11) {
|
|
1973
|
+
return V11_STORES;
|
|
1974
|
+
}
|
|
1975
|
+
else {
|
|
1976
|
+
fail();
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1824
1979
|
|
|
1825
1980
|
/**
|
|
1826
1981
|
* @license
|
|
@@ -2061,7 +2216,7 @@ class PersistencePromise {
|
|
|
2061
2216
|
*/
|
|
2062
2217
|
// References to `window` are guarded by SimpleDb.isAvailable()
|
|
2063
2218
|
/* eslint-disable no-restricted-globals */
|
|
2064
|
-
const LOG_TAG$
|
|
2219
|
+
const LOG_TAG$h = 'SimpleDb';
|
|
2065
2220
|
/**
|
|
2066
2221
|
* The maximum number of retry attempts for an IndexedDb transaction that fails
|
|
2067
2222
|
* with a DOMException.
|
|
@@ -2112,11 +2267,20 @@ class SimpleDbTransaction {
|
|
|
2112
2267
|
this.completionDeferred.reject(error);
|
|
2113
2268
|
}
|
|
2114
2269
|
if (!this.aborted) {
|
|
2115
|
-
logDebug(LOG_TAG$
|
|
2270
|
+
logDebug(LOG_TAG$h, 'Aborting transaction:', error ? error.message : 'Client-initiated abort');
|
|
2116
2271
|
this.aborted = true;
|
|
2117
2272
|
this.transaction.abort();
|
|
2118
2273
|
}
|
|
2119
2274
|
}
|
|
2275
|
+
maybeCommit() {
|
|
2276
|
+
// If the browser supports V3 IndexedDB, we invoke commit() explicitly to
|
|
2277
|
+
// speed up index DB processing if the event loop remains blocks.
|
|
2278
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2279
|
+
const maybeV3IndexedDb = this.transaction;
|
|
2280
|
+
if (!this.aborted && typeof maybeV3IndexedDb.commit === 'function') {
|
|
2281
|
+
maybeV3IndexedDb.commit();
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2120
2284
|
/**
|
|
2121
2285
|
* Returns a SimpleDbStore<KeyType, ValueType> for the specified store. All
|
|
2122
2286
|
* operations performed on the SimpleDbStore happen within the context of this
|
|
@@ -2166,7 +2330,7 @@ class SimpleDb {
|
|
|
2166
2330
|
}
|
|
2167
2331
|
/** Deletes the specified database. */
|
|
2168
2332
|
static delete(name) {
|
|
2169
|
-
logDebug(LOG_TAG$
|
|
2333
|
+
logDebug(LOG_TAG$h, 'Removing database:', name);
|
|
2170
2334
|
return wrapRequest(window.indexedDB.deleteDatabase(name)).toPromise();
|
|
2171
2335
|
}
|
|
2172
2336
|
/** Returns true if IndexedDB is available in the current environment. */
|
|
@@ -2245,7 +2409,7 @@ class SimpleDb {
|
|
|
2245
2409
|
*/
|
|
2246
2410
|
async ensureDb(action) {
|
|
2247
2411
|
if (!this.db) {
|
|
2248
|
-
logDebug(LOG_TAG$
|
|
2412
|
+
logDebug(LOG_TAG$h, 'Opening database:', this.name);
|
|
2249
2413
|
this.db = await new Promise((resolve, reject) => {
|
|
2250
2414
|
// TODO(mikelehen): Investigate browser compatibility.
|
|
2251
2415
|
// https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB
|
|
@@ -2281,12 +2445,12 @@ class SimpleDb {
|
|
|
2281
2445
|
}
|
|
2282
2446
|
};
|
|
2283
2447
|
request.onupgradeneeded = (event) => {
|
|
2284
|
-
logDebug(LOG_TAG$
|
|
2448
|
+
logDebug(LOG_TAG$h, 'Database "' + this.name + '" requires upgrade from version:', event.oldVersion);
|
|
2285
2449
|
const db = event.target.result;
|
|
2286
2450
|
this.schemaConverter
|
|
2287
2451
|
.createOrUpgrade(db, request.transaction, event.oldVersion, this.version)
|
|
2288
2452
|
.next(() => {
|
|
2289
|
-
logDebug(LOG_TAG$
|
|
2453
|
+
logDebug(LOG_TAG$h, 'Database upgrade to version ' + this.version + ' complete');
|
|
2290
2454
|
});
|
|
2291
2455
|
};
|
|
2292
2456
|
});
|
|
@@ -2313,6 +2477,10 @@ class SimpleDb {
|
|
|
2313
2477
|
this.db = await this.ensureDb(action);
|
|
2314
2478
|
const transaction = SimpleDbTransaction.open(this.db, action, readonly ? 'readonly' : 'readwrite', objectStores);
|
|
2315
2479
|
const transactionFnResult = transactionFn(transaction)
|
|
2480
|
+
.next(result => {
|
|
2481
|
+
transaction.maybeCommit();
|
|
2482
|
+
return result;
|
|
2483
|
+
})
|
|
2316
2484
|
.catch(error => {
|
|
2317
2485
|
// Abort the transaction if there was an error.
|
|
2318
2486
|
transaction.abort(error);
|
|
@@ -2340,7 +2508,7 @@ class SimpleDb {
|
|
|
2340
2508
|
// exception is wrapped in a generic error by our async/await handling.
|
|
2341
2509
|
const retryable = error.name !== 'FirebaseError' &&
|
|
2342
2510
|
attemptNumber < TRANSACTION_RETRY_COUNT;
|
|
2343
|
-
logDebug(LOG_TAG$
|
|
2511
|
+
logDebug(LOG_TAG$h, 'Transaction failed with error:', error.message, 'Retrying:', retryable);
|
|
2344
2512
|
this.close();
|
|
2345
2513
|
if (!retryable) {
|
|
2346
2514
|
return Promise.reject(error);
|
|
@@ -2427,11 +2595,11 @@ class SimpleDbStore {
|
|
|
2427
2595
|
put(keyOrValue, value) {
|
|
2428
2596
|
let request;
|
|
2429
2597
|
if (value !== undefined) {
|
|
2430
|
-
logDebug(LOG_TAG$
|
|
2598
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, keyOrValue, value);
|
|
2431
2599
|
request = this.store.put(value, keyOrValue);
|
|
2432
2600
|
}
|
|
2433
2601
|
else {
|
|
2434
|
-
logDebug(LOG_TAG$
|
|
2602
|
+
logDebug(LOG_TAG$h, 'PUT', this.store.name, '<auto-key>', keyOrValue);
|
|
2435
2603
|
request = this.store.put(keyOrValue);
|
|
2436
2604
|
}
|
|
2437
2605
|
return wrapRequest(request);
|
|
@@ -2444,7 +2612,7 @@ class SimpleDbStore {
|
|
|
2444
2612
|
* @returns The key of the value to add.
|
|
2445
2613
|
*/
|
|
2446
2614
|
add(value) {
|
|
2447
|
-
logDebug(LOG_TAG$
|
|
2615
|
+
logDebug(LOG_TAG$h, 'ADD', this.store.name, value, value);
|
|
2448
2616
|
const request = this.store.add(value);
|
|
2449
2617
|
return wrapRequest(request);
|
|
2450
2618
|
}
|
|
@@ -2464,12 +2632,12 @@ class SimpleDbStore {
|
|
|
2464
2632
|
if (result === undefined) {
|
|
2465
2633
|
result = null;
|
|
2466
2634
|
}
|
|
2467
|
-
logDebug(LOG_TAG$
|
|
2635
|
+
logDebug(LOG_TAG$h, 'GET', this.store.name, key, result);
|
|
2468
2636
|
return result;
|
|
2469
2637
|
});
|
|
2470
2638
|
}
|
|
2471
2639
|
delete(key) {
|
|
2472
|
-
logDebug(LOG_TAG$
|
|
2640
|
+
logDebug(LOG_TAG$h, 'DELETE', this.store.name, key);
|
|
2473
2641
|
const request = this.store.delete(key);
|
|
2474
2642
|
return wrapRequest(request);
|
|
2475
2643
|
}
|
|
@@ -2480,21 +2648,37 @@ class SimpleDbStore {
|
|
|
2480
2648
|
* Returns the number of rows in the store.
|
|
2481
2649
|
*/
|
|
2482
2650
|
count() {
|
|
2483
|
-
logDebug(LOG_TAG$
|
|
2651
|
+
logDebug(LOG_TAG$h, 'COUNT', this.store.name);
|
|
2484
2652
|
const request = this.store.count();
|
|
2485
2653
|
return wrapRequest(request);
|
|
2486
2654
|
}
|
|
2487
2655
|
loadAll(indexOrRange, range) {
|
|
2488
|
-
const
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
return
|
|
2494
|
-
|
|
2656
|
+
const iterateOptions = this.options(indexOrRange, range);
|
|
2657
|
+
// Use `getAll()` if the browser supports IndexedDB v3, as it is roughly
|
|
2658
|
+
// 20% faster. Unfortunately, getAll() does not support custom indices.
|
|
2659
|
+
if (!iterateOptions.index && typeof this.store.getAll === 'function') {
|
|
2660
|
+
const request = this.store.getAll(iterateOptions.range);
|
|
2661
|
+
return new PersistencePromise((resolve, reject) => {
|
|
2662
|
+
request.onerror = (event) => {
|
|
2663
|
+
reject(event.target.error);
|
|
2664
|
+
};
|
|
2665
|
+
request.onsuccess = (event) => {
|
|
2666
|
+
resolve(event.target.result);
|
|
2667
|
+
};
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
else {
|
|
2671
|
+
const cursor = this.cursor(iterateOptions);
|
|
2672
|
+
const results = [];
|
|
2673
|
+
return this.iterateCursor(cursor, (key, value) => {
|
|
2674
|
+
results.push(value);
|
|
2675
|
+
}).next(() => {
|
|
2676
|
+
return results;
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2495
2679
|
}
|
|
2496
2680
|
deleteAll(indexOrRange, range) {
|
|
2497
|
-
logDebug(LOG_TAG$
|
|
2681
|
+
logDebug(LOG_TAG$h, 'DELETE ALL', this.store.name);
|
|
2498
2682
|
const options = this.options(indexOrRange, range);
|
|
2499
2683
|
options.keysOnly = false;
|
|
2500
2684
|
const cursor = this.cursor(options);
|
|
@@ -3374,6 +3558,75 @@ function getLocalWriteTime(value) {
|
|
|
3374
3558
|
return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
|
|
3375
3559
|
}
|
|
3376
3560
|
|
|
3561
|
+
/**
|
|
3562
|
+
* @license
|
|
3563
|
+
* Copyright 2017 Google LLC
|
|
3564
|
+
*
|
|
3565
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3566
|
+
* you may not use this file except in compliance with the License.
|
|
3567
|
+
* You may obtain a copy of the License at
|
|
3568
|
+
*
|
|
3569
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3570
|
+
*
|
|
3571
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3572
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3573
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3574
|
+
* See the License for the specific language governing permissions and
|
|
3575
|
+
* limitations under the License.
|
|
3576
|
+
*/
|
|
3577
|
+
class DatabaseInfo {
|
|
3578
|
+
/**
|
|
3579
|
+
* Constructs a DatabaseInfo using the provided host, databaseId and
|
|
3580
|
+
* persistenceKey.
|
|
3581
|
+
*
|
|
3582
|
+
* @param databaseId - The database to use.
|
|
3583
|
+
* @param appId - The Firebase App Id.
|
|
3584
|
+
* @param persistenceKey - A unique identifier for this Firestore's local
|
|
3585
|
+
* storage (used in conjunction with the databaseId).
|
|
3586
|
+
* @param host - The Firestore backend host to connect to.
|
|
3587
|
+
* @param ssl - Whether to use SSL when connecting.
|
|
3588
|
+
* @param forceLongPolling - Whether to use the forceLongPolling option
|
|
3589
|
+
* when using WebChannel as the network transport.
|
|
3590
|
+
* @param autoDetectLongPolling - Whether to use the detectBufferingProxy
|
|
3591
|
+
* option when using WebChannel as the network transport.
|
|
3592
|
+
* @param useFetchStreams Whether to use the Fetch API instead of
|
|
3593
|
+
* XMLHTTPRequest
|
|
3594
|
+
*/
|
|
3595
|
+
constructor(databaseId, appId, persistenceKey, host, ssl, forceLongPolling, autoDetectLongPolling, useFetchStreams) {
|
|
3596
|
+
this.databaseId = databaseId;
|
|
3597
|
+
this.appId = appId;
|
|
3598
|
+
this.persistenceKey = persistenceKey;
|
|
3599
|
+
this.host = host;
|
|
3600
|
+
this.ssl = ssl;
|
|
3601
|
+
this.forceLongPolling = forceLongPolling;
|
|
3602
|
+
this.autoDetectLongPolling = autoDetectLongPolling;
|
|
3603
|
+
this.useFetchStreams = useFetchStreams;
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
/** The default database name for a project. */
|
|
3607
|
+
const DEFAULT_DATABASE_NAME = '(default)';
|
|
3608
|
+
/**
|
|
3609
|
+
* Represents the database ID a Firestore client is associated with.
|
|
3610
|
+
* @internal
|
|
3611
|
+
*/
|
|
3612
|
+
class DatabaseId {
|
|
3613
|
+
constructor(projectId, database) {
|
|
3614
|
+
this.projectId = projectId;
|
|
3615
|
+
this.database = database ? database : DEFAULT_DATABASE_NAME;
|
|
3616
|
+
}
|
|
3617
|
+
static empty() {
|
|
3618
|
+
return new DatabaseId('', '');
|
|
3619
|
+
}
|
|
3620
|
+
get isDefaultDatabase() {
|
|
3621
|
+
return this.database === DEFAULT_DATABASE_NAME;
|
|
3622
|
+
}
|
|
3623
|
+
isEqual(other) {
|
|
3624
|
+
return (other instanceof DatabaseId &&
|
|
3625
|
+
other.projectId === this.projectId &&
|
|
3626
|
+
other.database === this.database);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3377
3630
|
/**
|
|
3378
3631
|
* @license
|
|
3379
3632
|
* Copyright 2017 Google LLC
|
|
@@ -3445,11 +3698,25 @@ class DocumentKey {
|
|
|
3445
3698
|
static fromName(name) {
|
|
3446
3699
|
return new DocumentKey(ResourcePath.fromString(name).popFirst(5));
|
|
3447
3700
|
}
|
|
3701
|
+
static empty() {
|
|
3702
|
+
return new DocumentKey(ResourcePath.emptyPath());
|
|
3703
|
+
}
|
|
3704
|
+
get collectionGroup() {
|
|
3705
|
+
return this.path.popLast().lastSegment();
|
|
3706
|
+
}
|
|
3448
3707
|
/** Returns true if the document is in the specified collectionId. */
|
|
3449
3708
|
hasCollectionId(collectionId) {
|
|
3450
3709
|
return (this.path.length >= 2 &&
|
|
3451
3710
|
this.path.get(this.path.length - 2) === collectionId);
|
|
3452
3711
|
}
|
|
3712
|
+
/** Returns the collection group (i.e. the name of the parent collection) for this key. */
|
|
3713
|
+
getCollectionGroup() {
|
|
3714
|
+
return this.path.get(this.path.length - 2);
|
|
3715
|
+
}
|
|
3716
|
+
/** Returns the fully qualified path to the parent collection. */
|
|
3717
|
+
getCollectionPath() {
|
|
3718
|
+
return this.path.popLast();
|
|
3719
|
+
}
|
|
3453
3720
|
isEqual(other) {
|
|
3454
3721
|
return (other !== null && ResourcePath.comparator(this.path, other.path) === 0);
|
|
3455
3722
|
}
|
|
@@ -3489,6 +3756,13 @@ class DocumentKey {
|
|
|
3489
3756
|
* See the License for the specific language governing permissions and
|
|
3490
3757
|
* limitations under the License.
|
|
3491
3758
|
*/
|
|
3759
|
+
const MAX_VALUE = {
|
|
3760
|
+
mapValue: {
|
|
3761
|
+
fields: {
|
|
3762
|
+
'__type__': { stringValue: '__max___' }
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3492
3766
|
/** Extracts the backend's type order for the provided value. */
|
|
3493
3767
|
function typeOrder(value) {
|
|
3494
3768
|
if ('nullValue' in value) {
|
|
@@ -3898,6 +4172,10 @@ function deepClone(source) {
|
|
|
3898
4172
|
else {
|
|
3899
4173
|
return Object.assign({}, source);
|
|
3900
4174
|
}
|
|
4175
|
+
}
|
|
4176
|
+
/** Returns true if the Value represents the canonical {@link #MAX_VALUE} . */
|
|
4177
|
+
function isMaxValue(value) {
|
|
4178
|
+
return valueEquals(value, MAX_VALUE);
|
|
3901
4179
|
}
|
|
3902
4180
|
|
|
3903
4181
|
/**
|
|
@@ -4093,10 +4371,11 @@ function extractFieldMask(value) {
|
|
|
4093
4371
|
* from all views.
|
|
4094
4372
|
*/
|
|
4095
4373
|
class MutableDocument {
|
|
4096
|
-
constructor(key, documentType, version, data, documentState) {
|
|
4374
|
+
constructor(key, documentType, version, readTime, data, documentState) {
|
|
4097
4375
|
this.key = key;
|
|
4098
4376
|
this.documentType = documentType;
|
|
4099
4377
|
this.version = version;
|
|
4378
|
+
this.readTime = readTime;
|
|
4100
4379
|
this.data = data;
|
|
4101
4380
|
this.documentState = documentState;
|
|
4102
4381
|
}
|
|
@@ -4105,18 +4384,18 @@ class MutableDocument {
|
|
|
4105
4384
|
* base document for mutations.
|
|
4106
4385
|
*/
|
|
4107
4386
|
static newInvalidDocument(documentKey) {
|
|
4108
|
-
return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
|
|
4387
|
+
return new MutableDocument(documentKey, 0 /* INVALID */, SnapshotVersion.min(), SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
|
|
4109
4388
|
}
|
|
4110
4389
|
/**
|
|
4111
4390
|
* Creates a new document that is known to exist with the given data at the
|
|
4112
4391
|
* given version.
|
|
4113
4392
|
*/
|
|
4114
4393
|
static newFoundDocument(documentKey, version, value) {
|
|
4115
|
-
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, value, 0 /* SYNCED */);
|
|
4394
|
+
return new MutableDocument(documentKey, 1 /* FOUND_DOCUMENT */, version, SnapshotVersion.min(), value, 0 /* SYNCED */);
|
|
4116
4395
|
}
|
|
4117
4396
|
/** Creates a new document that is known to not exist at the given version. */
|
|
4118
4397
|
static newNoDocument(documentKey, version) {
|
|
4119
|
-
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, ObjectValue.empty(), 0 /* SYNCED */);
|
|
4398
|
+
return new MutableDocument(documentKey, 2 /* NO_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 0 /* SYNCED */);
|
|
4120
4399
|
}
|
|
4121
4400
|
/**
|
|
4122
4401
|
* Creates a new document that is known to exist at the given version but
|
|
@@ -4124,7 +4403,7 @@ class MutableDocument {
|
|
|
4124
4403
|
* base document).
|
|
4125
4404
|
*/
|
|
4126
4405
|
static newUnknownDocument(documentKey, version) {
|
|
4127
|
-
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
|
|
4406
|
+
return new MutableDocument(documentKey, 3 /* UNKNOWN_DOCUMENT */, version, SnapshotVersion.min(), ObjectValue.empty(), 2 /* HAS_COMMITTED_MUTATIONS */);
|
|
4128
4407
|
}
|
|
4129
4408
|
/**
|
|
4130
4409
|
* Changes the document type to indicate that it exists and that its version
|
|
@@ -4168,6 +4447,10 @@ class MutableDocument {
|
|
|
4168
4447
|
this.documentState = 1 /* HAS_LOCAL_MUTATIONS */;
|
|
4169
4448
|
return this;
|
|
4170
4449
|
}
|
|
4450
|
+
setReadTime(readTime) {
|
|
4451
|
+
this.readTime = readTime;
|
|
4452
|
+
return this;
|
|
4453
|
+
}
|
|
4171
4454
|
get hasLocalMutations() {
|
|
4172
4455
|
return this.documentState === 1 /* HAS_LOCAL_MUTATIONS */;
|
|
4173
4456
|
}
|
|
@@ -4198,7 +4481,7 @@ class MutableDocument {
|
|
|
4198
4481
|
this.data.isEqual(other.data));
|
|
4199
4482
|
}
|
|
4200
4483
|
mutableCopy() {
|
|
4201
|
-
return new MutableDocument(this.key, this.documentType, this.version, this.data.clone(), this.documentState);
|
|
4484
|
+
return new MutableDocument(this.key, this.documentType, this.version, this.readTime, this.data.clone(), this.documentState);
|
|
4202
4485
|
}
|
|
4203
4486
|
toString() {
|
|
4204
4487
|
return (`Document(${this.key}, ${this.version}, ${JSON.stringify(this.data.value)}, ` +
|
|
@@ -4221,6 +4504,133 @@ function compareDocumentsByField(field, d1, d2) {
|
|
|
4221
4504
|
}
|
|
4222
4505
|
}
|
|
4223
4506
|
|
|
4507
|
+
/**
|
|
4508
|
+
* @license
|
|
4509
|
+
* Copyright 2021 Google LLC
|
|
4510
|
+
*
|
|
4511
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4512
|
+
* you may not use this file except in compliance with the License.
|
|
4513
|
+
* You may obtain a copy of the License at
|
|
4514
|
+
*
|
|
4515
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
4516
|
+
*
|
|
4517
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
4518
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
4519
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
4520
|
+
* See the License for the specific language governing permissions and
|
|
4521
|
+
* limitations under the License.
|
|
4522
|
+
*/
|
|
4523
|
+
/**
|
|
4524
|
+
* The initial mutation batch id for each index. Gets updated during index
|
|
4525
|
+
* backfill.
|
|
4526
|
+
*/
|
|
4527
|
+
const INITIAL_LARGEST_BATCH_ID = -1;
|
|
4528
|
+
/**
|
|
4529
|
+
* The initial sequence number for each index. Gets updated during index
|
|
4530
|
+
* backfill.
|
|
4531
|
+
*/
|
|
4532
|
+
const INITIAL_SEQUENCE_NUMBER = 0;
|
|
4533
|
+
/**
|
|
4534
|
+
* An index definition for field indexes in Firestore.
|
|
4535
|
+
*
|
|
4536
|
+
* Every index is associated with a collection. The definition contains a list
|
|
4537
|
+
* of fields and their index kind (which can be `ASCENDING`, `DESCENDING` or
|
|
4538
|
+
* `CONTAINS` for ArrayContains/ArrayContainsAny queries).
|
|
4539
|
+
*
|
|
4540
|
+
* Unlike the backend, the SDK does not differentiate between collection or
|
|
4541
|
+
* collection group-scoped indices. Every index can be used for both single
|
|
4542
|
+
* collection and collection group queries.
|
|
4543
|
+
*/
|
|
4544
|
+
class FieldIndex {
|
|
4545
|
+
constructor(
|
|
4546
|
+
/**
|
|
4547
|
+
* The index ID. Returns -1 if the index ID is not available (e.g. the index
|
|
4548
|
+
* has not yet been persisted).
|
|
4549
|
+
*/
|
|
4550
|
+
indexId,
|
|
4551
|
+
/** The collection ID this index applies to. */
|
|
4552
|
+
collectionGroup,
|
|
4553
|
+
/** The field segments for this index. */
|
|
4554
|
+
fields,
|
|
4555
|
+
/** Shows how up-to-date the index is for the current user. */
|
|
4556
|
+
indexState) {
|
|
4557
|
+
this.indexId = indexId;
|
|
4558
|
+
this.collectionGroup = collectionGroup;
|
|
4559
|
+
this.fields = fields;
|
|
4560
|
+
this.indexState = indexState;
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
/** An ID for an index that has not yet been added to persistence. */
|
|
4564
|
+
FieldIndex.UNKNOWN_ID = -1;
|
|
4565
|
+
/** Returns the ArrayContains/ArrayContainsAny segment for this index. */
|
|
4566
|
+
function fieldIndexGetArraySegment(fieldIndex) {
|
|
4567
|
+
return fieldIndex.fields.find(s => s.kind === 2 /* CONTAINS */);
|
|
4568
|
+
}
|
|
4569
|
+
/** Returns all directional (ascending/descending) segments for this index. */
|
|
4570
|
+
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4571
|
+
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4572
|
+
}
|
|
4573
|
+
/** An index component consisting of field path and index type. */
|
|
4574
|
+
class IndexSegment {
|
|
4575
|
+
constructor(
|
|
4576
|
+
/** The field path of the component. */
|
|
4577
|
+
fieldPath,
|
|
4578
|
+
/** The fields sorting order. */
|
|
4579
|
+
kind) {
|
|
4580
|
+
this.fieldPath = fieldPath;
|
|
4581
|
+
this.kind = kind;
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4584
|
+
/**
|
|
4585
|
+
* Stores the "high water mark" that indicates how updated the Index is for the
|
|
4586
|
+
* current user.
|
|
4587
|
+
*/
|
|
4588
|
+
class IndexState {
|
|
4589
|
+
constructor(
|
|
4590
|
+
/**
|
|
4591
|
+
* Indicates when the index was last updated (relative to other indexes).
|
|
4592
|
+
*/
|
|
4593
|
+
sequenceNumber,
|
|
4594
|
+
/** The the latest indexed read time, document and batch id. */
|
|
4595
|
+
offset) {
|
|
4596
|
+
this.sequenceNumber = sequenceNumber;
|
|
4597
|
+
this.offset = offset;
|
|
4598
|
+
}
|
|
4599
|
+
/** The state of an index that has not yet been backfilled. */
|
|
4600
|
+
static empty() {
|
|
4601
|
+
return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
/**
|
|
4605
|
+
* Stores the latest read time, document and batch ID that were processed for an
|
|
4606
|
+
* index.
|
|
4607
|
+
*/
|
|
4608
|
+
class IndexOffset {
|
|
4609
|
+
constructor(
|
|
4610
|
+
/**
|
|
4611
|
+
* The latest read time version that has been indexed by Firestore for this
|
|
4612
|
+
* field index.
|
|
4613
|
+
*/
|
|
4614
|
+
readTime,
|
|
4615
|
+
/**
|
|
4616
|
+
* The key of the last document that was indexed for this query. Use
|
|
4617
|
+
* `DocumentKey.empty()` if no document has been indexed.
|
|
4618
|
+
*/
|
|
4619
|
+
documentKey,
|
|
4620
|
+
/*
|
|
4621
|
+
* The largest mutation batch id that's been processed by Firestore.
|
|
4622
|
+
*/
|
|
4623
|
+
largestBatchId) {
|
|
4624
|
+
this.readTime = readTime;
|
|
4625
|
+
this.documentKey = documentKey;
|
|
4626
|
+
this.largestBatchId = largestBatchId;
|
|
4627
|
+
}
|
|
4628
|
+
/** The state of an index that has not yet been backfilled. */
|
|
4629
|
+
static min() {
|
|
4630
|
+
return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
|
|
4224
4634
|
/**
|
|
4225
4635
|
* @license
|
|
4226
4636
|
* Copyright 2019 Google LLC
|
|
@@ -4264,27 +4674,29 @@ function newTarget(path, collectionGroup = null, orderBy = [], filters = [], lim
|
|
|
4264
4674
|
function canonifyTarget(target) {
|
|
4265
4675
|
const targetImpl = debugCast(target);
|
|
4266
4676
|
if (targetImpl.memoizedCanonicalId === null) {
|
|
4267
|
-
let
|
|
4677
|
+
let str = targetImpl.path.canonicalString();
|
|
4268
4678
|
if (targetImpl.collectionGroup !== null) {
|
|
4269
|
-
|
|
4679
|
+
str += '|cg:' + targetImpl.collectionGroup;
|
|
4270
4680
|
}
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4681
|
+
str += '|f:';
|
|
4682
|
+
str += targetImpl.filters.map(f => canonifyFilter(f)).join(',');
|
|
4683
|
+
str += '|ob:';
|
|
4684
|
+
str += targetImpl.orderBy.map(o => canonifyOrderBy(o)).join(',');
|
|
4275
4685
|
if (!isNullOrUndefined(targetImpl.limit)) {
|
|
4276
|
-
|
|
4277
|
-
|
|
4686
|
+
str += '|l:';
|
|
4687
|
+
str += targetImpl.limit;
|
|
4278
4688
|
}
|
|
4279
4689
|
if (targetImpl.startAt) {
|
|
4280
|
-
|
|
4281
|
-
|
|
4690
|
+
str += '|lb:';
|
|
4691
|
+
str += targetImpl.startAt.inclusive ? 'b:' : 'a:';
|
|
4692
|
+
str += targetImpl.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4282
4693
|
}
|
|
4283
4694
|
if (targetImpl.endAt) {
|
|
4284
|
-
|
|
4285
|
-
|
|
4695
|
+
str += '|ub:';
|
|
4696
|
+
str += targetImpl.endAt.inclusive ? 'a:' : 'b:';
|
|
4697
|
+
str += targetImpl.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4286
4698
|
}
|
|
4287
|
-
targetImpl.memoizedCanonicalId =
|
|
4699
|
+
targetImpl.memoizedCanonicalId = str;
|
|
4288
4700
|
}
|
|
4289
4701
|
return targetImpl.memoizedCanonicalId;
|
|
4290
4702
|
}
|
|
@@ -4307,10 +4719,14 @@ function stringifyTarget(target) {
|
|
|
4307
4719
|
.join(', ')}]`;
|
|
4308
4720
|
}
|
|
4309
4721
|
if (target.startAt) {
|
|
4310
|
-
str += ', startAt: '
|
|
4722
|
+
str += ', startAt: ';
|
|
4723
|
+
str += target.startAt.inclusive ? 'b:' : 'a:';
|
|
4724
|
+
str += target.startAt.position.map(p => canonicalId(p)).join(',');
|
|
4311
4725
|
}
|
|
4312
4726
|
if (target.endAt) {
|
|
4313
|
-
str += ', endAt: '
|
|
4727
|
+
str += ', endAt: ';
|
|
4728
|
+
str += target.endAt.inclusive ? 'a:' : 'b:';
|
|
4729
|
+
str += target.endAt.position.map(p => canonicalId(p)).join(',');
|
|
4314
4730
|
}
|
|
4315
4731
|
return `Target(${str})`;
|
|
4316
4732
|
}
|
|
@@ -4345,7 +4761,7 @@ function targetEquals(left, right) {
|
|
|
4345
4761
|
}
|
|
4346
4762
|
return boundEquals(left.endAt, right.endAt);
|
|
4347
4763
|
}
|
|
4348
|
-
function
|
|
4764
|
+
function targetIsDocumentTarget(target) {
|
|
4349
4765
|
return (DocumentKey.isDocumentKey(target.path) &&
|
|
4350
4766
|
target.collectionGroup === null &&
|
|
4351
4767
|
target.filters.length === 0);
|
|
@@ -4548,17 +4964,11 @@ class ArrayContainsAnyFilter extends FieldFilter {
|
|
|
4548
4964
|
* just after the provided values.
|
|
4549
4965
|
*/
|
|
4550
4966
|
class Bound {
|
|
4551
|
-
constructor(position,
|
|
4967
|
+
constructor(position, inclusive) {
|
|
4552
4968
|
this.position = position;
|
|
4553
|
-
this.
|
|
4969
|
+
this.inclusive = inclusive;
|
|
4554
4970
|
}
|
|
4555
4971
|
}
|
|
4556
|
-
function canonifyBound(bound) {
|
|
4557
|
-
// TODO(b/29183165): Make this collision robust.
|
|
4558
|
-
return `${bound.before ? 'b' : 'a'}:${bound.position
|
|
4559
|
-
.map(p => canonicalId(p))
|
|
4560
|
-
.join(',')}`;
|
|
4561
|
-
}
|
|
4562
4972
|
/**
|
|
4563
4973
|
* An ordering on a field, in some Direction. Direction defaults to ASCENDING.
|
|
4564
4974
|
*/
|
|
@@ -4578,11 +4988,7 @@ function stringifyOrderBy(orderBy) {
|
|
|
4578
4988
|
function orderByEquals(left, right) {
|
|
4579
4989
|
return left.dir === right.dir && left.field.isEqual(right.field);
|
|
4580
4990
|
}
|
|
4581
|
-
|
|
4582
|
-
* Returns true if a document sorts before a bound using the provided sort
|
|
4583
|
-
* order.
|
|
4584
|
-
*/
|
|
4585
|
-
function sortsBeforeDocument(bound, orderBy, doc) {
|
|
4991
|
+
function boundCompareToDocument(bound, orderBy, doc) {
|
|
4586
4992
|
let comparison = 0;
|
|
4587
4993
|
for (let i = 0; i < bound.position.length; i++) {
|
|
4588
4994
|
const orderByComponent = orderBy[i];
|
|
@@ -4601,7 +5007,23 @@ function sortsBeforeDocument(bound, orderBy, doc) {
|
|
|
4601
5007
|
break;
|
|
4602
5008
|
}
|
|
4603
5009
|
}
|
|
4604
|
-
return
|
|
5010
|
+
return comparison;
|
|
5011
|
+
}
|
|
5012
|
+
/**
|
|
5013
|
+
* Returns true if a document sorts after a bound using the provided sort
|
|
5014
|
+
* order.
|
|
5015
|
+
*/
|
|
5016
|
+
function boundSortsAfterDocument(bound, orderBy, doc) {
|
|
5017
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5018
|
+
return bound.inclusive ? comparison >= 0 : comparison > 0;
|
|
5019
|
+
}
|
|
5020
|
+
/**
|
|
5021
|
+
* Returns true if a document sorts before a bound using the provided sort
|
|
5022
|
+
* order.
|
|
5023
|
+
*/
|
|
5024
|
+
function boundSortsBeforeDocument(bound, orderBy, doc) {
|
|
5025
|
+
const comparison = boundCompareToDocument(bound, orderBy, doc);
|
|
5026
|
+
return bound.inclusive ? comparison <= 0 : comparison < 0;
|
|
4605
5027
|
}
|
|
4606
5028
|
function boundEquals(left, right) {
|
|
4607
5029
|
if (left === null) {
|
|
@@ -4610,7 +5032,7 @@ function boundEquals(left, right) {
|
|
|
4610
5032
|
else if (right === null) {
|
|
4611
5033
|
return false;
|
|
4612
5034
|
}
|
|
4613
|
-
if (left.
|
|
5035
|
+
if (left.inclusive !== right.inclusive ||
|
|
4614
5036
|
left.position.length !== right.position.length) {
|
|
4615
5037
|
return false;
|
|
4616
5038
|
}
|
|
@@ -4814,10 +5236,10 @@ function queryToTarget(query) {
|
|
|
4814
5236
|
}
|
|
4815
5237
|
// We need to swap the cursors to match the now-flipped query ordering.
|
|
4816
5238
|
const startAt = queryImpl.endAt
|
|
4817
|
-
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.
|
|
5239
|
+
? new Bound(queryImpl.endAt.position, !queryImpl.endAt.inclusive)
|
|
4818
5240
|
: null;
|
|
4819
5241
|
const endAt = queryImpl.startAt
|
|
4820
|
-
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.
|
|
5242
|
+
? new Bound(queryImpl.startAt.position, !queryImpl.startAt.inclusive)
|
|
4821
5243
|
: null;
|
|
4822
5244
|
// Now return as a LimitType.First query.
|
|
4823
5245
|
queryImpl.memoizedTarget = newTarget(queryImpl.path, queryImpl.collectionGroup, orderBys, queryImpl.filters, queryImpl.limit, startAt, endAt);
|
|
@@ -4905,11 +5327,11 @@ function queryMatchesFilters(query, doc) {
|
|
|
4905
5327
|
/** Makes sure a document is within the bounds, if provided. */
|
|
4906
5328
|
function queryMatchesBounds(query, doc) {
|
|
4907
5329
|
if (query.startAt &&
|
|
4908
|
-
!
|
|
5330
|
+
!boundSortsBeforeDocument(query.startAt, queryOrderBy(query), doc)) {
|
|
4909
5331
|
return false;
|
|
4910
5332
|
}
|
|
4911
5333
|
if (query.endAt &&
|
|
4912
|
-
|
|
5334
|
+
!boundSortsAfterDocument(query.endAt, queryOrderBy(query), doc)) {
|
|
4913
5335
|
return false;
|
|
4914
5336
|
}
|
|
4915
5337
|
return true;
|
|
@@ -5564,6 +5986,72 @@ class SortedSetIterator {
|
|
|
5564
5986
|
hasNext() {
|
|
5565
5987
|
return this.iter.hasNext();
|
|
5566
5988
|
}
|
|
5989
|
+
}
|
|
5990
|
+
/**
|
|
5991
|
+
* Compares two sorted sets for equality using their natural ordering. The
|
|
5992
|
+
* method computes the intersection and invokes `onAdd` for every element that
|
|
5993
|
+
* is in `after` but not `before`. `onRemove` is invoked for every element in
|
|
5994
|
+
* `before` but missing from `after`.
|
|
5995
|
+
*
|
|
5996
|
+
* The method creates a copy of both `before` and `after` and runs in O(n log
|
|
5997
|
+
* n), where n is the size of the two lists.
|
|
5998
|
+
*
|
|
5999
|
+
* @param before - The elements that exist in the original set.
|
|
6000
|
+
* @param after - The elements to diff against the original set.
|
|
6001
|
+
* @param comparator - The comparator for the elements in before and after.
|
|
6002
|
+
* @param onAdd - A function to invoke for every element that is part of `
|
|
6003
|
+
* after` but not `before`.
|
|
6004
|
+
* @param onRemove - A function to invoke for every element that is part of
|
|
6005
|
+
* `before` but not `after`.
|
|
6006
|
+
*/
|
|
6007
|
+
function diffSortedSets(before, after, comparator, onAdd, onRemove) {
|
|
6008
|
+
const beforeIt = before.getIterator();
|
|
6009
|
+
const afterIt = after.getIterator();
|
|
6010
|
+
let beforeValue = advanceIterator(beforeIt);
|
|
6011
|
+
let afterValue = advanceIterator(afterIt);
|
|
6012
|
+
// Walk through the two sets at the same time, using the ordering defined by
|
|
6013
|
+
// `comparator`.
|
|
6014
|
+
while (beforeValue || afterValue) {
|
|
6015
|
+
let added = false;
|
|
6016
|
+
let removed = false;
|
|
6017
|
+
if (beforeValue && afterValue) {
|
|
6018
|
+
const cmp = comparator(beforeValue, afterValue);
|
|
6019
|
+
if (cmp < 0) {
|
|
6020
|
+
// The element was removed if the next element in our ordered
|
|
6021
|
+
// walkthrough is only in `before`.
|
|
6022
|
+
removed = true;
|
|
6023
|
+
}
|
|
6024
|
+
else if (cmp > 0) {
|
|
6025
|
+
// The element was added if the next element in our ordered walkthrough
|
|
6026
|
+
// is only in `after`.
|
|
6027
|
+
added = true;
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
else if (beforeValue != null) {
|
|
6031
|
+
removed = true;
|
|
6032
|
+
}
|
|
6033
|
+
else {
|
|
6034
|
+
added = true;
|
|
6035
|
+
}
|
|
6036
|
+
if (added) {
|
|
6037
|
+
onAdd(afterValue);
|
|
6038
|
+
afterValue = advanceIterator(afterIt);
|
|
6039
|
+
}
|
|
6040
|
+
else if (removed) {
|
|
6041
|
+
onRemove(beforeValue);
|
|
6042
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6043
|
+
}
|
|
6044
|
+
else {
|
|
6045
|
+
beforeValue = advanceIterator(beforeIt);
|
|
6046
|
+
afterValue = advanceIterator(afterIt);
|
|
6047
|
+
}
|
|
6048
|
+
}
|
|
6049
|
+
}
|
|
6050
|
+
/**
|
|
6051
|
+
* Returns the next element from the iterator or `undefined` if none available.
|
|
6052
|
+
*/
|
|
6053
|
+
function advanceIterator(it) {
|
|
6054
|
+
return it.hasNext() ? it.getNext() : undefined;
|
|
5567
6055
|
}
|
|
5568
6056
|
|
|
5569
6057
|
/**
|
|
@@ -6402,6 +6890,47 @@ class MutationBatchResult {
|
|
|
6402
6890
|
}
|
|
6403
6891
|
}
|
|
6404
6892
|
|
|
6893
|
+
/**
|
|
6894
|
+
* @license
|
|
6895
|
+
* Copyright 2022 Google LLC
|
|
6896
|
+
*
|
|
6897
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6898
|
+
* you may not use this file except in compliance with the License.
|
|
6899
|
+
* You may obtain a copy of the License at
|
|
6900
|
+
*
|
|
6901
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
6902
|
+
*
|
|
6903
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
6904
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
6905
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
6906
|
+
* See the License for the specific language governing permissions and
|
|
6907
|
+
* limitations under the License.
|
|
6908
|
+
*/
|
|
6909
|
+
/**
|
|
6910
|
+
* Representation of an overlay computed by Firestore.
|
|
6911
|
+
*
|
|
6912
|
+
* Holds information about a mutation and the largest batch id in Firestore when
|
|
6913
|
+
* the mutation was created.
|
|
6914
|
+
*/
|
|
6915
|
+
class Overlay {
|
|
6916
|
+
constructor(largestBatchId, mutation) {
|
|
6917
|
+
this.largestBatchId = largestBatchId;
|
|
6918
|
+
this.mutation = mutation;
|
|
6919
|
+
}
|
|
6920
|
+
getKey() {
|
|
6921
|
+
return this.mutation.key;
|
|
6922
|
+
}
|
|
6923
|
+
isEqual(other) {
|
|
6924
|
+
return other !== null && this.mutation === other.mutation;
|
|
6925
|
+
}
|
|
6926
|
+
toString() {
|
|
6927
|
+
return `Overlay{
|
|
6928
|
+
largestBatchId: ${this.largestBatchId},
|
|
6929
|
+
mutation: ${this.mutation.toString()}
|
|
6930
|
+
}`;
|
|
6931
|
+
}
|
|
6932
|
+
}
|
|
6933
|
+
|
|
6405
6934
|
/**
|
|
6406
6935
|
* @license
|
|
6407
6936
|
* Copyright 2017 Google LLC
|
|
@@ -6874,7 +7403,7 @@ class TargetState {
|
|
|
6874
7403
|
this._current = true;
|
|
6875
7404
|
}
|
|
6876
7405
|
}
|
|
6877
|
-
const LOG_TAG$
|
|
7406
|
+
const LOG_TAG$g = 'WatchChangeAggregator';
|
|
6878
7407
|
/**
|
|
6879
7408
|
* A helper class to accumulate watch changes into a RemoteEvent.
|
|
6880
7409
|
*/
|
|
@@ -6990,7 +7519,7 @@ class WatchChangeAggregator {
|
|
|
6990
7519
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
6991
7520
|
if (targetData) {
|
|
6992
7521
|
const target = targetData.target;
|
|
6993
|
-
if (
|
|
7522
|
+
if (targetIsDocumentTarget(target)) {
|
|
6994
7523
|
if (expectedCount === 0) {
|
|
6995
7524
|
// The existence filter told us the document does not exist. We deduce
|
|
6996
7525
|
// that this document does not exist and apply a deleted document to
|
|
@@ -7025,7 +7554,7 @@ class WatchChangeAggregator {
|
|
|
7025
7554
|
this.targetStates.forEach((targetState, targetId) => {
|
|
7026
7555
|
const targetData = this.targetDataForActiveTarget(targetId);
|
|
7027
7556
|
if (targetData) {
|
|
7028
|
-
if (targetState.current &&
|
|
7557
|
+
if (targetState.current && targetIsDocumentTarget(targetData.target)) {
|
|
7029
7558
|
// Document queries for document that don't exist can produce an empty
|
|
7030
7559
|
// result set. To update our local cache, we synthesize a document
|
|
7031
7560
|
// delete if we have not previously received the document. This
|
|
@@ -7068,6 +7597,7 @@ class WatchChangeAggregator {
|
|
|
7068
7597
|
resolvedLimboDocuments = resolvedLimboDocuments.add(key);
|
|
7069
7598
|
}
|
|
7070
7599
|
});
|
|
7600
|
+
this.pendingDocumentUpdates.forEach((_, doc) => doc.setReadTime(snapshotVersion));
|
|
7071
7601
|
const remoteEvent = new RemoteEvent(snapshotVersion, targetChanges, this.pendingTargetResets, this.pendingDocumentUpdates, resolvedLimboDocuments);
|
|
7072
7602
|
this.pendingDocumentUpdates = mutableDocumentMap();
|
|
7073
7603
|
this.pendingDocumentTargetMapping = documentTargetMap();
|
|
@@ -7168,7 +7698,7 @@ class WatchChangeAggregator {
|
|
|
7168
7698
|
isActiveTarget(targetId) {
|
|
7169
7699
|
const targetActive = this.targetDataForActiveTarget(targetId) !== null;
|
|
7170
7700
|
if (!targetActive) {
|
|
7171
|
-
logDebug(LOG_TAG$
|
|
7701
|
+
logDebug(LOG_TAG$g, 'Detected inactive target', targetId);
|
|
7172
7702
|
}
|
|
7173
7703
|
return targetActive;
|
|
7174
7704
|
}
|
|
@@ -7787,10 +8317,10 @@ function toQueryTarget(serializer, target) {
|
|
|
7787
8317
|
result.structuredQuery.limit = limit;
|
|
7788
8318
|
}
|
|
7789
8319
|
if (target.startAt) {
|
|
7790
|
-
result.structuredQuery.startAt =
|
|
8320
|
+
result.structuredQuery.startAt = toStartAtCursor(target.startAt);
|
|
7791
8321
|
}
|
|
7792
8322
|
if (target.endAt) {
|
|
7793
|
-
result.structuredQuery.endAt =
|
|
8323
|
+
result.structuredQuery.endAt = toEndAtCursor(target.endAt);
|
|
7794
8324
|
}
|
|
7795
8325
|
return result;
|
|
7796
8326
|
}
|
|
@@ -7823,11 +8353,11 @@ function convertQueryTargetToQuery(target) {
|
|
|
7823
8353
|
}
|
|
7824
8354
|
let startAt = null;
|
|
7825
8355
|
if (query.startAt) {
|
|
7826
|
-
startAt =
|
|
8356
|
+
startAt = fromStartAtCursor(query.startAt);
|
|
7827
8357
|
}
|
|
7828
8358
|
let endAt = null;
|
|
7829
8359
|
if (query.endAt) {
|
|
7830
|
-
endAt =
|
|
8360
|
+
endAt = fromEndAtCursor(query.endAt);
|
|
7831
8361
|
}
|
|
7832
8362
|
return newQuery(path, collectionGroup, orderBy, filterBy, limit, "F" /* First */, startAt, endAt);
|
|
7833
8363
|
}
|
|
@@ -7860,7 +8390,7 @@ function toLabel(serializer, purpose) {
|
|
|
7860
8390
|
function toTarget(serializer, targetData) {
|
|
7861
8391
|
let result;
|
|
7862
8392
|
const target = targetData.target;
|
|
7863
|
-
if (
|
|
8393
|
+
if (targetIsDocumentTarget(target)) {
|
|
7864
8394
|
result = { documents: toDocumentsTarget(serializer, target) };
|
|
7865
8395
|
}
|
|
7866
8396
|
else {
|
|
@@ -7918,16 +8448,27 @@ function toOrder(orderBys) {
|
|
|
7918
8448
|
function fromOrder(orderBys) {
|
|
7919
8449
|
return orderBys.map(order => fromPropertyOrder(order));
|
|
7920
8450
|
}
|
|
7921
|
-
function
|
|
8451
|
+
function toStartAtCursor(cursor) {
|
|
7922
8452
|
return {
|
|
7923
|
-
before: cursor.
|
|
8453
|
+
before: cursor.inclusive,
|
|
7924
8454
|
values: cursor.position
|
|
7925
8455
|
};
|
|
7926
8456
|
}
|
|
7927
|
-
function
|
|
7928
|
-
|
|
8457
|
+
function toEndAtCursor(cursor) {
|
|
8458
|
+
return {
|
|
8459
|
+
before: !cursor.inclusive,
|
|
8460
|
+
values: cursor.position
|
|
8461
|
+
};
|
|
8462
|
+
}
|
|
8463
|
+
function fromStartAtCursor(cursor) {
|
|
8464
|
+
const inclusive = !!cursor.before;
|
|
8465
|
+
const position = cursor.values || [];
|
|
8466
|
+
return new Bound(position, inclusive);
|
|
8467
|
+
}
|
|
8468
|
+
function fromEndAtCursor(cursor) {
|
|
8469
|
+
const inclusive = !cursor.before;
|
|
7929
8470
|
const position = cursor.values || [];
|
|
7930
|
-
return new Bound(position,
|
|
8471
|
+
return new Bound(position, inclusive);
|
|
7931
8472
|
}
|
|
7932
8473
|
// visible for testing
|
|
7933
8474
|
function toDirection(dir) {
|
|
@@ -8188,29 +8729,34 @@ class LocalSerializer {
|
|
|
8188
8729
|
}
|
|
8189
8730
|
/** Decodes a remote document from storage locally to a Document. */
|
|
8190
8731
|
function fromDbRemoteDocument(localSerializer, remoteDoc) {
|
|
8732
|
+
let doc;
|
|
8191
8733
|
if (remoteDoc.document) {
|
|
8192
|
-
|
|
8734
|
+
doc = fromDocument(localSerializer.remoteSerializer, remoteDoc.document, !!remoteDoc.hasCommittedMutations);
|
|
8193
8735
|
}
|
|
8194
8736
|
else if (remoteDoc.noDocument) {
|
|
8195
8737
|
const key = DocumentKey.fromSegments(remoteDoc.noDocument.path);
|
|
8196
8738
|
const version = fromDbTimestamp(remoteDoc.noDocument.readTime);
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8739
|
+
doc = MutableDocument.newNoDocument(key, version);
|
|
8740
|
+
if (remoteDoc.hasCommittedMutations) {
|
|
8741
|
+
doc.setHasCommittedMutations();
|
|
8742
|
+
}
|
|
8201
8743
|
}
|
|
8202
8744
|
else if (remoteDoc.unknownDocument) {
|
|
8203
8745
|
const key = DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
|
|
8204
8746
|
const version = fromDbTimestamp(remoteDoc.unknownDocument.version);
|
|
8205
|
-
|
|
8747
|
+
doc = MutableDocument.newUnknownDocument(key, version);
|
|
8206
8748
|
}
|
|
8207
8749
|
else {
|
|
8208
8750
|
return fail();
|
|
8209
8751
|
}
|
|
8752
|
+
if (remoteDoc.readTime) {
|
|
8753
|
+
doc.setReadTime(fromDbTimestampKey(remoteDoc.readTime));
|
|
8754
|
+
}
|
|
8755
|
+
return doc;
|
|
8210
8756
|
}
|
|
8211
8757
|
/** Encodes a document for storage locally. */
|
|
8212
|
-
function toDbRemoteDocument(localSerializer, document
|
|
8213
|
-
const dbReadTime = toDbTimestampKey(readTime);
|
|
8758
|
+
function toDbRemoteDocument(localSerializer, document) {
|
|
8759
|
+
const dbReadTime = toDbTimestampKey(document.readTime);
|
|
8214
8760
|
const parentPath = document.key.path.popLast().toArray();
|
|
8215
8761
|
if (document.isFoundDocument()) {
|
|
8216
8762
|
const doc = toDocument(localSerializer.remoteSerializer, document);
|
|
@@ -8221,10 +8767,10 @@ function toDbRemoteDocument(localSerializer, document, readTime) {
|
|
|
8221
8767
|
}
|
|
8222
8768
|
else if (document.isNoDocument()) {
|
|
8223
8769
|
const path = document.key.path.toArray();
|
|
8224
|
-
const
|
|
8770
|
+
const version = toDbTimestamp(document.version);
|
|
8225
8771
|
const hasCommittedMutations = document.hasCommittedMutations;
|
|
8226
8772
|
return new DbRemoteDocument(
|
|
8227
|
-
/* unknownDocument= */ null, new DbNoDocument(path,
|
|
8773
|
+
/* unknownDocument= */ null, new DbNoDocument(path, version),
|
|
8228
8774
|
/* document= */ null, hasCommittedMutations, dbReadTime, parentPath);
|
|
8229
8775
|
}
|
|
8230
8776
|
else if (document.isUnknownDocument()) {
|
|
@@ -8305,7 +8851,7 @@ function toDbTarget(localSerializer, targetData) {
|
|
|
8305
8851
|
const dbTimestamp = toDbTimestamp(targetData.snapshotVersion);
|
|
8306
8852
|
const dbLastLimboFreeTimestamp = toDbTimestamp(targetData.lastLimboFreeSnapshotVersion);
|
|
8307
8853
|
let queryProto;
|
|
8308
|
-
if (
|
|
8854
|
+
if (targetIsDocumentTarget(targetData.target)) {
|
|
8309
8855
|
queryProto = toDocumentsTarget(localSerializer.remoteSerializer, targetData.target);
|
|
8310
8856
|
}
|
|
8311
8857
|
else {
|
|
@@ -8386,6 +8932,37 @@ function fromBundleMetadata(metadata) {
|
|
|
8386
8932
|
version: metadata.version,
|
|
8387
8933
|
createTime: fromVersion(metadata.createTime)
|
|
8388
8934
|
};
|
|
8935
|
+
}
|
|
8936
|
+
/** Encodes a DbDocumentOverlay object to an Overlay model object. */
|
|
8937
|
+
function fromDbDocumentOverlay(localSerializer, dbDocumentOverlay) {
|
|
8938
|
+
return new Overlay(dbDocumentOverlay.largestBatchId, fromMutation(localSerializer.remoteSerializer, dbDocumentOverlay.overlayMutation));
|
|
8939
|
+
}
|
|
8940
|
+
/** Decodes an Overlay model object into a DbDocumentOverlay object. */
|
|
8941
|
+
function toDbDocumentOverlay(localSerializer, userId, overlay) {
|
|
8942
|
+
const [_, collectionPath, documentId] = toDbDocumentOverlayKey(userId, overlay.mutation.key);
|
|
8943
|
+
return new DbDocumentOverlay(userId, collectionPath, documentId, overlay.mutation.key.getCollectionGroup(), overlay.largestBatchId, toMutation(localSerializer.remoteSerializer, overlay.mutation));
|
|
8944
|
+
}
|
|
8945
|
+
/**
|
|
8946
|
+
* Returns the DbDocumentOverlayKey corresponding to the given user and
|
|
8947
|
+
* document key.
|
|
8948
|
+
*/
|
|
8949
|
+
function toDbDocumentOverlayKey(userId, docKey) {
|
|
8950
|
+
const docId = docKey.path.lastSegment();
|
|
8951
|
+
const collectionPath = encodeResourcePath(docKey.path.popLast());
|
|
8952
|
+
return [userId, collectionPath, docId];
|
|
8953
|
+
}
|
|
8954
|
+
function toDbIndexConfiguration(index) {
|
|
8955
|
+
return new DbIndexConfiguration(index.indexId, index.collectionGroup, index.fields.map(s => [s.fieldPath.canonicalString(), s.kind]));
|
|
8956
|
+
}
|
|
8957
|
+
function fromDbIndexConfiguration(index, state) {
|
|
8958
|
+
const decodedState = state
|
|
8959
|
+
? new IndexState(state.sequenceNumber, new IndexOffset(fromDbTimestamp(state.readTime), new DocumentKey(decodeResourcePath(state.documentKey)), state.largestBatchId))
|
|
8960
|
+
: IndexState.empty();
|
|
8961
|
+
const decodedSegments = index.fields.map(([fieldPath, kind]) => new IndexSegment(FieldPath$1.fromServerFormat(fieldPath), kind));
|
|
8962
|
+
return new FieldIndex(index.indexId, index.collectionGroup, decodedSegments, decodedState);
|
|
8963
|
+
}
|
|
8964
|
+
function toDbIndexState(indexId, user, sequenceNumber, offset) {
|
|
8965
|
+
return new DbIndexState(indexId, user.uid || '', sequenceNumber, toDbTimestamp(offset.readTime), encodeResourcePath(offset.documentKey.path), offset.largestBatchId);
|
|
8389
8966
|
}
|
|
8390
8967
|
|
|
8391
8968
|
/**
|
|
@@ -8445,6 +9022,704 @@ function namedQueriesStore(txn) {
|
|
|
8445
9022
|
return getStore(txn, DbNamedQuery.store);
|
|
8446
9023
|
}
|
|
8447
9024
|
|
|
9025
|
+
/**
|
|
9026
|
+
* @license
|
|
9027
|
+
* Copyright 2022 Google LLC
|
|
9028
|
+
*
|
|
9029
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9030
|
+
* you may not use this file except in compliance with the License.
|
|
9031
|
+
* You may obtain a copy of the License at
|
|
9032
|
+
*
|
|
9033
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9034
|
+
*
|
|
9035
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9036
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9037
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9038
|
+
* See the License for the specific language governing permissions and
|
|
9039
|
+
* limitations under the License.
|
|
9040
|
+
*/
|
|
9041
|
+
/**
|
|
9042
|
+
* Implementation of DocumentOverlayCache using IndexedDb.
|
|
9043
|
+
*/
|
|
9044
|
+
class IndexedDbDocumentOverlayCache {
|
|
9045
|
+
/**
|
|
9046
|
+
* @param serializer - The document serializer.
|
|
9047
|
+
* @param userId - The userId for which we are accessing overlays.
|
|
9048
|
+
*/
|
|
9049
|
+
constructor(serializer, userId) {
|
|
9050
|
+
this.serializer = serializer;
|
|
9051
|
+
this.userId = userId;
|
|
9052
|
+
}
|
|
9053
|
+
static forUser(serializer, user) {
|
|
9054
|
+
const userId = user.uid || '';
|
|
9055
|
+
return new IndexedDbDocumentOverlayCache(serializer, userId);
|
|
9056
|
+
}
|
|
9057
|
+
getOverlay(transaction, key) {
|
|
9058
|
+
return documentOverlayStore(transaction)
|
|
9059
|
+
.get(toDbDocumentOverlayKey(this.userId, key))
|
|
9060
|
+
.next(dbOverlay => {
|
|
9061
|
+
if (dbOverlay) {
|
|
9062
|
+
return fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9063
|
+
}
|
|
9064
|
+
return null;
|
|
9065
|
+
});
|
|
9066
|
+
}
|
|
9067
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
9068
|
+
const promises = [];
|
|
9069
|
+
overlays.forEach(mutation => {
|
|
9070
|
+
const overlay = new Overlay(largestBatchId, mutation);
|
|
9071
|
+
promises.push(this.saveOverlay(transaction, overlay));
|
|
9072
|
+
});
|
|
9073
|
+
return PersistencePromise.waitFor(promises);
|
|
9074
|
+
}
|
|
9075
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
9076
|
+
const collectionPaths = new Set();
|
|
9077
|
+
// Get the set of unique collection paths.
|
|
9078
|
+
documentKeys.forEach(key => collectionPaths.add(encodeResourcePath(key.getCollectionPath())));
|
|
9079
|
+
const promises = [];
|
|
9080
|
+
collectionPaths.forEach(collectionPath => {
|
|
9081
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, batchId], [this.userId, collectionPath, batchId + 1],
|
|
9082
|
+
/*lowerOpen=*/ false,
|
|
9083
|
+
/*upperOpen=*/ true);
|
|
9084
|
+
promises.push(documentOverlayStore(transaction).deleteAll(DbDocumentOverlay.collectionPathOverlayIndex, range));
|
|
9085
|
+
});
|
|
9086
|
+
return PersistencePromise.waitFor(promises);
|
|
9087
|
+
}
|
|
9088
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
9089
|
+
const result = new Map();
|
|
9090
|
+
const collectionPath = encodeResourcePath(collection);
|
|
9091
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9092
|
+
// is not inclusive.
|
|
9093
|
+
const range = IDBKeyRange.bound([this.userId, collectionPath, sinceBatchId], [this.userId, collectionPath, Number.POSITIVE_INFINITY],
|
|
9094
|
+
/*lowerOpen=*/ true);
|
|
9095
|
+
return documentOverlayStore(transaction)
|
|
9096
|
+
.loadAll(DbDocumentOverlay.collectionPathOverlayIndex, range)
|
|
9097
|
+
.next(dbOverlays => {
|
|
9098
|
+
for (const dbOverlay of dbOverlays) {
|
|
9099
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9100
|
+
result.set(overlay.getKey(), overlay);
|
|
9101
|
+
}
|
|
9102
|
+
return result;
|
|
9103
|
+
});
|
|
9104
|
+
}
|
|
9105
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
9106
|
+
const result = new Map();
|
|
9107
|
+
let currentBatchId = undefined;
|
|
9108
|
+
// We want batch IDs larger than `sinceBatchId`, and so the lower bound
|
|
9109
|
+
// is not inclusive.
|
|
9110
|
+
const range = IDBKeyRange.bound([this.userId, collectionGroup, sinceBatchId], [this.userId, collectionGroup, Number.POSITIVE_INFINITY],
|
|
9111
|
+
/*lowerOpen=*/ true);
|
|
9112
|
+
return documentOverlayStore(transaction)
|
|
9113
|
+
.iterate({
|
|
9114
|
+
index: DbDocumentOverlay.collectionGroupOverlayIndex,
|
|
9115
|
+
range
|
|
9116
|
+
}, (_, dbOverlay, control) => {
|
|
9117
|
+
// We do not want to return partial batch overlays, even if the size
|
|
9118
|
+
// of the result set exceeds the given `count` argument. Therefore, we
|
|
9119
|
+
// continue to aggregate results even after the result size exceeds
|
|
9120
|
+
// `count` if there are more overlays from the `currentBatchId`.
|
|
9121
|
+
const overlay = fromDbDocumentOverlay(this.serializer, dbOverlay);
|
|
9122
|
+
if (result.size < count ||
|
|
9123
|
+
overlay.largestBatchId === currentBatchId) {
|
|
9124
|
+
result.set(overlay.getKey(), overlay);
|
|
9125
|
+
currentBatchId = overlay.largestBatchId;
|
|
9126
|
+
}
|
|
9127
|
+
else {
|
|
9128
|
+
control.done();
|
|
9129
|
+
}
|
|
9130
|
+
})
|
|
9131
|
+
.next(() => result);
|
|
9132
|
+
}
|
|
9133
|
+
saveOverlay(transaction, overlay) {
|
|
9134
|
+
return documentOverlayStore(transaction).put(toDbDocumentOverlay(this.serializer, this.userId, overlay));
|
|
9135
|
+
}
|
|
9136
|
+
}
|
|
9137
|
+
/**
|
|
9138
|
+
* Helper to get a typed SimpleDbStore for the document overlay object store.
|
|
9139
|
+
*/
|
|
9140
|
+
function documentOverlayStore(txn) {
|
|
9141
|
+
return getStore(txn, DbDocumentOverlay.store);
|
|
9142
|
+
}
|
|
9143
|
+
|
|
9144
|
+
/**
|
|
9145
|
+
* @license
|
|
9146
|
+
* Copyright 2021 Google LLC
|
|
9147
|
+
*
|
|
9148
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9149
|
+
* you may not use this file except in compliance with the License.
|
|
9150
|
+
* You may obtain a copy of the License at
|
|
9151
|
+
*
|
|
9152
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9153
|
+
*
|
|
9154
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9155
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9156
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9157
|
+
* See the License for the specific language governing permissions and
|
|
9158
|
+
* limitations under the License.
|
|
9159
|
+
*/
|
|
9160
|
+
// Note: This code is copied from the backend. Code that is not used by
|
|
9161
|
+
// Firestore was removed.
|
|
9162
|
+
const INDEX_TYPE_NULL = 5;
|
|
9163
|
+
const INDEX_TYPE_BOOLEAN = 10;
|
|
9164
|
+
const INDEX_TYPE_NAN = 13;
|
|
9165
|
+
const INDEX_TYPE_NUMBER = 15;
|
|
9166
|
+
const INDEX_TYPE_TIMESTAMP = 20;
|
|
9167
|
+
const INDEX_TYPE_STRING = 25;
|
|
9168
|
+
const INDEX_TYPE_BLOB = 30;
|
|
9169
|
+
const INDEX_TYPE_REFERENCE = 37;
|
|
9170
|
+
const INDEX_TYPE_GEOPOINT = 45;
|
|
9171
|
+
const INDEX_TYPE_ARRAY = 50;
|
|
9172
|
+
const INDEX_TYPE_MAP = 55;
|
|
9173
|
+
const INDEX_TYPE_REFERENCE_SEGMENT = 60;
|
|
9174
|
+
// A terminator that indicates that a truncatable value was not truncated.
|
|
9175
|
+
// This must be smaller than all other type labels.
|
|
9176
|
+
const NOT_TRUNCATED = 2;
|
|
9177
|
+
/** Firestore index value writer. */
|
|
9178
|
+
class FirestoreIndexValueWriter {
|
|
9179
|
+
constructor() { }
|
|
9180
|
+
// The write methods below short-circuit writing terminators for values
|
|
9181
|
+
// containing a (terminating) truncated value.
|
|
9182
|
+
//
|
|
9183
|
+
// As an example, consider the resulting encoding for:
|
|
9184
|
+
//
|
|
9185
|
+
// ["bar", [2, "foo"]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TERM, TERM, TERM)
|
|
9186
|
+
// ["bar", [2, truncated("foo")]] -> (STRING, "bar", TERM, ARRAY, NUMBER, 2, STRING, "foo", TRUNC)
|
|
9187
|
+
// ["bar", truncated(["foo"])] -> (STRING, "bar", TERM, ARRAY. STRING, "foo", TERM, TRUNC)
|
|
9188
|
+
/** Writes an index value. */
|
|
9189
|
+
writeIndexValue(value, encoder) {
|
|
9190
|
+
this.writeIndexValueAux(value, encoder);
|
|
9191
|
+
// Write separator to split index values
|
|
9192
|
+
// (see go/firestore-storage-format#encodings).
|
|
9193
|
+
encoder.writeInfinity();
|
|
9194
|
+
}
|
|
9195
|
+
writeIndexValueAux(indexValue, encoder) {
|
|
9196
|
+
if ('nullValue' in indexValue) {
|
|
9197
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NULL);
|
|
9198
|
+
}
|
|
9199
|
+
else if ('booleanValue' in indexValue) {
|
|
9200
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BOOLEAN);
|
|
9201
|
+
encoder.writeNumber(indexValue.booleanValue ? 1 : 0);
|
|
9202
|
+
}
|
|
9203
|
+
else if ('integerValue' in indexValue) {
|
|
9204
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9205
|
+
encoder.writeNumber(normalizeNumber(indexValue.integerValue));
|
|
9206
|
+
}
|
|
9207
|
+
else if ('doubleValue' in indexValue) {
|
|
9208
|
+
const n = normalizeNumber(indexValue.doubleValue);
|
|
9209
|
+
if (isNaN(n)) {
|
|
9210
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NAN);
|
|
9211
|
+
}
|
|
9212
|
+
else {
|
|
9213
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_NUMBER);
|
|
9214
|
+
if (isNegativeZero(n)) {
|
|
9215
|
+
// -0.0, 0 and 0.0 are all considered the same
|
|
9216
|
+
encoder.writeNumber(0.0);
|
|
9217
|
+
}
|
|
9218
|
+
else {
|
|
9219
|
+
encoder.writeNumber(n);
|
|
9220
|
+
}
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
else if ('timestampValue' in indexValue) {
|
|
9224
|
+
const timestamp = indexValue.timestampValue;
|
|
9225
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_TIMESTAMP);
|
|
9226
|
+
if (typeof timestamp === 'string') {
|
|
9227
|
+
encoder.writeString(timestamp);
|
|
9228
|
+
}
|
|
9229
|
+
else {
|
|
9230
|
+
encoder.writeString(`${timestamp.seconds || ''}`);
|
|
9231
|
+
encoder.writeNumber(timestamp.nanos || 0);
|
|
9232
|
+
}
|
|
9233
|
+
}
|
|
9234
|
+
else if ('stringValue' in indexValue) {
|
|
9235
|
+
this.writeIndexString(indexValue.stringValue, encoder);
|
|
9236
|
+
this.writeTruncationMarker(encoder);
|
|
9237
|
+
}
|
|
9238
|
+
else if ('bytesValue' in indexValue) {
|
|
9239
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_BLOB);
|
|
9240
|
+
encoder.writeBytes(normalizeByteString(indexValue.bytesValue));
|
|
9241
|
+
this.writeTruncationMarker(encoder);
|
|
9242
|
+
}
|
|
9243
|
+
else if ('referenceValue' in indexValue) {
|
|
9244
|
+
this.writeIndexEntityRef(indexValue.referenceValue, encoder);
|
|
9245
|
+
}
|
|
9246
|
+
else if ('geoPointValue' in indexValue) {
|
|
9247
|
+
const geoPoint = indexValue.geoPointValue;
|
|
9248
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_GEOPOINT);
|
|
9249
|
+
encoder.writeNumber(geoPoint.latitude || 0);
|
|
9250
|
+
encoder.writeNumber(geoPoint.longitude || 0);
|
|
9251
|
+
}
|
|
9252
|
+
else if ('mapValue' in indexValue) {
|
|
9253
|
+
if (isMaxValue(indexValue)) {
|
|
9254
|
+
this.writeValueTypeLabel(encoder, Number.MAX_SAFE_INTEGER);
|
|
9255
|
+
}
|
|
9256
|
+
else {
|
|
9257
|
+
this.writeIndexMap(indexValue.mapValue, encoder);
|
|
9258
|
+
this.writeTruncationMarker(encoder);
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
else if ('arrayValue' in indexValue) {
|
|
9262
|
+
this.writeIndexArray(indexValue.arrayValue, encoder);
|
|
9263
|
+
this.writeTruncationMarker(encoder);
|
|
9264
|
+
}
|
|
9265
|
+
else {
|
|
9266
|
+
fail();
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
writeIndexString(stringIndexValue, encoder) {
|
|
9270
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_STRING);
|
|
9271
|
+
this.writeUnlabeledIndexString(stringIndexValue, encoder);
|
|
9272
|
+
}
|
|
9273
|
+
writeUnlabeledIndexString(stringIndexValue, encoder) {
|
|
9274
|
+
encoder.writeString(stringIndexValue);
|
|
9275
|
+
}
|
|
9276
|
+
writeIndexMap(mapIndexValue, encoder) {
|
|
9277
|
+
const map = mapIndexValue.fields || {};
|
|
9278
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_MAP);
|
|
9279
|
+
for (const key of Object.keys(map)) {
|
|
9280
|
+
this.writeIndexString(key, encoder);
|
|
9281
|
+
this.writeIndexValueAux(map[key], encoder);
|
|
9282
|
+
}
|
|
9283
|
+
}
|
|
9284
|
+
writeIndexArray(arrayIndexValue, encoder) {
|
|
9285
|
+
const values = arrayIndexValue.values || [];
|
|
9286
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_ARRAY);
|
|
9287
|
+
for (const element of values) {
|
|
9288
|
+
this.writeIndexValueAux(element, encoder);
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
writeIndexEntityRef(referenceValue, encoder) {
|
|
9292
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE);
|
|
9293
|
+
const path = DocumentKey.fromName(referenceValue).path;
|
|
9294
|
+
path.forEach(segment => {
|
|
9295
|
+
this.writeValueTypeLabel(encoder, INDEX_TYPE_REFERENCE_SEGMENT);
|
|
9296
|
+
this.writeUnlabeledIndexString(segment, encoder);
|
|
9297
|
+
});
|
|
9298
|
+
}
|
|
9299
|
+
writeValueTypeLabel(encoder, typeOrder) {
|
|
9300
|
+
encoder.writeNumber(typeOrder);
|
|
9301
|
+
}
|
|
9302
|
+
writeTruncationMarker(encoder) {
|
|
9303
|
+
// While the SDK does not implement truncation, the truncation marker is
|
|
9304
|
+
// used to terminate all variable length values (which are strings, bytes,
|
|
9305
|
+
// references, arrays and maps).
|
|
9306
|
+
encoder.writeNumber(NOT_TRUNCATED);
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9309
|
+
FirestoreIndexValueWriter.INSTANCE = new FirestoreIndexValueWriter();
|
|
9310
|
+
|
|
9311
|
+
/**
|
|
9312
|
+
* @license
|
|
9313
|
+
* Copyright 2021 Google LLC
|
|
9314
|
+
*
|
|
9315
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9316
|
+
* you may not use this file except in compliance with the License.
|
|
9317
|
+
* You may obtain a copy of the License at
|
|
9318
|
+
*
|
|
9319
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9320
|
+
*
|
|
9321
|
+
* Unless required by applicable law | agreed to in writing, software
|
|
9322
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9323
|
+
* WITHOUT WARRANTIES | CONDITIONS OF ANY KIND, either express | implied.
|
|
9324
|
+
* See the License for the specific language governing permissions and
|
|
9325
|
+
* limitations under the License.
|
|
9326
|
+
*/
|
|
9327
|
+
/** These constants are taken from the backend. */
|
|
9328
|
+
const MIN_SURROGATE = '\uD800';
|
|
9329
|
+
const MAX_SURROGATE = '\uDBFF';
|
|
9330
|
+
const ESCAPE1 = 0x00;
|
|
9331
|
+
const NULL_BYTE = 0xff; // Combined with ESCAPE1
|
|
9332
|
+
const SEPARATOR = 0x01; // Combined with ESCAPE1
|
|
9333
|
+
const ESCAPE2 = 0xff;
|
|
9334
|
+
const INFINITY = 0xff; // Combined with ESCAPE2
|
|
9335
|
+
const FF_BYTE = 0x00; // Combined with ESCAPE2
|
|
9336
|
+
const LONG_SIZE = 64;
|
|
9337
|
+
const BYTE_SIZE = 8;
|
|
9338
|
+
/**
|
|
9339
|
+
* The default size of the buffer. This is arbitrary, but likely larger than
|
|
9340
|
+
* most index values so that less copies of the underlying buffer will be made.
|
|
9341
|
+
* For large values, a single copy will made to double the buffer length.
|
|
9342
|
+
*/
|
|
9343
|
+
const DEFAULT_BUFFER_SIZE = 1024;
|
|
9344
|
+
/** Converts a JavaScript number to a byte array (using big endian encoding). */
|
|
9345
|
+
function doubleToLongBits(value) {
|
|
9346
|
+
const dv = new DataView(new ArrayBuffer(8));
|
|
9347
|
+
dv.setFloat64(0, value, /* littleEndian= */ false);
|
|
9348
|
+
return new Uint8Array(dv.buffer);
|
|
9349
|
+
}
|
|
9350
|
+
/**
|
|
9351
|
+
* Counts the number of zeros in a byte.
|
|
9352
|
+
*
|
|
9353
|
+
* Visible for testing.
|
|
9354
|
+
*/
|
|
9355
|
+
function numberOfLeadingZerosInByte(x) {
|
|
9356
|
+
if (x === 0) {
|
|
9357
|
+
return 8;
|
|
9358
|
+
}
|
|
9359
|
+
let zeros = 0;
|
|
9360
|
+
if (x >> 4 === 0) {
|
|
9361
|
+
// Test if the first four bits are zero.
|
|
9362
|
+
zeros += 4;
|
|
9363
|
+
x = x << 4;
|
|
9364
|
+
}
|
|
9365
|
+
if (x >> 6 === 0) {
|
|
9366
|
+
// Test if the first two (or next two) bits are zero.
|
|
9367
|
+
zeros += 2;
|
|
9368
|
+
x = x << 2;
|
|
9369
|
+
}
|
|
9370
|
+
if (x >> 7 === 0) {
|
|
9371
|
+
// Test if the remaining bit is zero.
|
|
9372
|
+
zeros += 1;
|
|
9373
|
+
}
|
|
9374
|
+
return zeros;
|
|
9375
|
+
}
|
|
9376
|
+
/** Counts the number of leading zeros in the given byte array. */
|
|
9377
|
+
function numberOfLeadingZeros(bytes) {
|
|
9378
|
+
let leadingZeros = 0;
|
|
9379
|
+
for (let i = 0; i < 8; ++i) {
|
|
9380
|
+
const zeros = numberOfLeadingZerosInByte(bytes[i] & 0xff);
|
|
9381
|
+
leadingZeros += zeros;
|
|
9382
|
+
if (zeros !== 8) {
|
|
9383
|
+
break;
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
return leadingZeros;
|
|
9387
|
+
}
|
|
9388
|
+
/**
|
|
9389
|
+
* Returns the number of bytes required to store "value". Leading zero bytes
|
|
9390
|
+
* are skipped.
|
|
9391
|
+
*/
|
|
9392
|
+
function unsignedNumLength(value) {
|
|
9393
|
+
// This is just the number of bytes for the unsigned representation of the number.
|
|
9394
|
+
const numBits = LONG_SIZE - numberOfLeadingZeros(value);
|
|
9395
|
+
return Math.ceil(numBits / BYTE_SIZE);
|
|
9396
|
+
}
|
|
9397
|
+
/**
|
|
9398
|
+
* OrderedCodeWriter is a minimal-allocation implementation of the writing
|
|
9399
|
+
* behavior defined by the backend.
|
|
9400
|
+
*
|
|
9401
|
+
* The code is ported from its Java counterpart.
|
|
9402
|
+
*/
|
|
9403
|
+
class OrderedCodeWriter {
|
|
9404
|
+
constructor() {
|
|
9405
|
+
this.buffer = new Uint8Array(DEFAULT_BUFFER_SIZE);
|
|
9406
|
+
this.position = 0;
|
|
9407
|
+
}
|
|
9408
|
+
writeBytesAscending(value) {
|
|
9409
|
+
const it = value[Symbol.iterator]();
|
|
9410
|
+
let byte = it.next();
|
|
9411
|
+
while (!byte.done) {
|
|
9412
|
+
this.writeByteAscending(byte.value);
|
|
9413
|
+
byte = it.next();
|
|
9414
|
+
}
|
|
9415
|
+
this.writeSeparatorAscending();
|
|
9416
|
+
}
|
|
9417
|
+
writeBytesDescending(value) {
|
|
9418
|
+
const it = value[Symbol.iterator]();
|
|
9419
|
+
let byte = it.next();
|
|
9420
|
+
while (!byte.done) {
|
|
9421
|
+
this.writeByteDescending(byte.value);
|
|
9422
|
+
byte = it.next();
|
|
9423
|
+
}
|
|
9424
|
+
this.writeSeparatorDescending();
|
|
9425
|
+
}
|
|
9426
|
+
/** Writes utf8 bytes into this byte sequence, ascending. */
|
|
9427
|
+
writeUtf8Ascending(sequence) {
|
|
9428
|
+
for (const c of sequence) {
|
|
9429
|
+
const charCode = c.charCodeAt(0);
|
|
9430
|
+
if (charCode < 0x80) {
|
|
9431
|
+
this.writeByteAscending(charCode);
|
|
9432
|
+
}
|
|
9433
|
+
else if (charCode < 0x800) {
|
|
9434
|
+
this.writeByteAscending((0x0f << 6) | (charCode >>> 6));
|
|
9435
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9436
|
+
}
|
|
9437
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9438
|
+
this.writeByteAscending((0x0f << 5) | (charCode >>> 12));
|
|
9439
|
+
this.writeByteAscending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9440
|
+
this.writeByteAscending(0x80 | (0x3f & charCode));
|
|
9441
|
+
}
|
|
9442
|
+
else {
|
|
9443
|
+
const codePoint = c.codePointAt(0);
|
|
9444
|
+
this.writeByteAscending((0x0f << 4) | (codePoint >>> 18));
|
|
9445
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9446
|
+
this.writeByteAscending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9447
|
+
this.writeByteAscending(0x80 | (0x3f & codePoint));
|
|
9448
|
+
}
|
|
9449
|
+
}
|
|
9450
|
+
this.writeSeparatorAscending();
|
|
9451
|
+
}
|
|
9452
|
+
/** Writes utf8 bytes into this byte sequence, descending */
|
|
9453
|
+
writeUtf8Descending(sequence) {
|
|
9454
|
+
for (const c of sequence) {
|
|
9455
|
+
const charCode = c.charCodeAt(0);
|
|
9456
|
+
if (charCode < 0x80) {
|
|
9457
|
+
this.writeByteDescending(charCode);
|
|
9458
|
+
}
|
|
9459
|
+
else if (charCode < 0x800) {
|
|
9460
|
+
this.writeByteDescending((0x0f << 6) | (charCode >>> 6));
|
|
9461
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9462
|
+
}
|
|
9463
|
+
else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
|
|
9464
|
+
this.writeByteDescending((0x0f << 5) | (charCode >>> 12));
|
|
9465
|
+
this.writeByteDescending(0x80 | (0x3f & (charCode >>> 6)));
|
|
9466
|
+
this.writeByteDescending(0x80 | (0x3f & charCode));
|
|
9467
|
+
}
|
|
9468
|
+
else {
|
|
9469
|
+
const codePoint = c.codePointAt(0);
|
|
9470
|
+
this.writeByteDescending((0x0f << 4) | (codePoint >>> 18));
|
|
9471
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 12)));
|
|
9472
|
+
this.writeByteDescending(0x80 | (0x3f & (codePoint >>> 6)));
|
|
9473
|
+
this.writeByteDescending(0x80 | (0x3f & codePoint));
|
|
9474
|
+
}
|
|
9475
|
+
}
|
|
9476
|
+
this.writeSeparatorDescending();
|
|
9477
|
+
}
|
|
9478
|
+
writeNumberAscending(val) {
|
|
9479
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9480
|
+
// actual value in big-endian format with leading 0 bytes dropped.
|
|
9481
|
+
const value = this.toOrderedBits(val);
|
|
9482
|
+
const len = unsignedNumLength(value);
|
|
9483
|
+
this.ensureAvailable(1 + len);
|
|
9484
|
+
this.buffer[this.position++] = len & 0xff; // Write the length
|
|
9485
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9486
|
+
this.buffer[this.position++] = value[i] & 0xff;
|
|
9487
|
+
}
|
|
9488
|
+
}
|
|
9489
|
+
writeNumberDescending(val) {
|
|
9490
|
+
// Values are encoded with a single byte length prefix, followed by the
|
|
9491
|
+
// inverted value in big-endian format with leading 0 bytes dropped.
|
|
9492
|
+
const value = this.toOrderedBits(val);
|
|
9493
|
+
const len = unsignedNumLength(value);
|
|
9494
|
+
this.ensureAvailable(1 + len);
|
|
9495
|
+
this.buffer[this.position++] = ~(len & 0xff); // Write the length
|
|
9496
|
+
for (let i = value.length - len; i < value.length; ++i) {
|
|
9497
|
+
this.buffer[this.position++] = ~(value[i] & 0xff);
|
|
9498
|
+
}
|
|
9499
|
+
}
|
|
9500
|
+
/**
|
|
9501
|
+
* Writes the "infinity" byte sequence that sorts after all other byte
|
|
9502
|
+
* sequences written in ascending order.
|
|
9503
|
+
*/
|
|
9504
|
+
writeInfinityAscending() {
|
|
9505
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9506
|
+
this.writeEscapedByteAscending(INFINITY);
|
|
9507
|
+
}
|
|
9508
|
+
/**
|
|
9509
|
+
* Writes the "infinity" byte sequence that sorts before all other byte
|
|
9510
|
+
* sequences written in descending order.
|
|
9511
|
+
*/
|
|
9512
|
+
writeInfinityDescending() {
|
|
9513
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9514
|
+
this.writeEscapedByteDescending(INFINITY);
|
|
9515
|
+
}
|
|
9516
|
+
/**
|
|
9517
|
+
* Resets the buffer such that it is the same as when it was newly
|
|
9518
|
+
* constructed.
|
|
9519
|
+
*/
|
|
9520
|
+
reset() {
|
|
9521
|
+
this.position = 0;
|
|
9522
|
+
}
|
|
9523
|
+
seed(encodedBytes) {
|
|
9524
|
+
this.ensureAvailable(encodedBytes.length);
|
|
9525
|
+
this.buffer.set(encodedBytes, this.position);
|
|
9526
|
+
this.position += encodedBytes.length;
|
|
9527
|
+
}
|
|
9528
|
+
/** Makes a copy of the encoded bytes in this buffer. */
|
|
9529
|
+
encodedBytes() {
|
|
9530
|
+
return this.buffer.slice(0, this.position);
|
|
9531
|
+
}
|
|
9532
|
+
/**
|
|
9533
|
+
* Encodes `val` into an encoding so that the order matches the IEEE 754
|
|
9534
|
+
* floating-point comparison results with the following exceptions:
|
|
9535
|
+
* -0.0 < 0.0
|
|
9536
|
+
* all non-NaN < NaN
|
|
9537
|
+
* NaN = NaN
|
|
9538
|
+
*/
|
|
9539
|
+
toOrderedBits(val) {
|
|
9540
|
+
const value = doubleToLongBits(val);
|
|
9541
|
+
// Check if the first bit is set. We use a bit mask since value[0] is
|
|
9542
|
+
// encoded as a number from 0 to 255.
|
|
9543
|
+
const isNegative = (value[0] & 0x80) !== 0;
|
|
9544
|
+
// Revert the two complement to get natural ordering
|
|
9545
|
+
value[0] ^= isNegative ? 0xff : 0x80;
|
|
9546
|
+
for (let i = 1; i < value.length; ++i) {
|
|
9547
|
+
value[i] ^= isNegative ? 0xff : 0x00;
|
|
9548
|
+
}
|
|
9549
|
+
return value;
|
|
9550
|
+
}
|
|
9551
|
+
/** Writes a single byte ascending to the buffer. */
|
|
9552
|
+
writeByteAscending(b) {
|
|
9553
|
+
const masked = b & 0xff;
|
|
9554
|
+
if (masked === ESCAPE1) {
|
|
9555
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9556
|
+
this.writeEscapedByteAscending(NULL_BYTE);
|
|
9557
|
+
}
|
|
9558
|
+
else if (masked === ESCAPE2) {
|
|
9559
|
+
this.writeEscapedByteAscending(ESCAPE2);
|
|
9560
|
+
this.writeEscapedByteAscending(FF_BYTE);
|
|
9561
|
+
}
|
|
9562
|
+
else {
|
|
9563
|
+
this.writeEscapedByteAscending(masked);
|
|
9564
|
+
}
|
|
9565
|
+
}
|
|
9566
|
+
/** Writes a single byte descending to the buffer. */
|
|
9567
|
+
writeByteDescending(b) {
|
|
9568
|
+
const masked = b & 0xff;
|
|
9569
|
+
if (masked === ESCAPE1) {
|
|
9570
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9571
|
+
this.writeEscapedByteDescending(NULL_BYTE);
|
|
9572
|
+
}
|
|
9573
|
+
else if (masked === ESCAPE2) {
|
|
9574
|
+
this.writeEscapedByteDescending(ESCAPE2);
|
|
9575
|
+
this.writeEscapedByteDescending(FF_BYTE);
|
|
9576
|
+
}
|
|
9577
|
+
else {
|
|
9578
|
+
this.writeEscapedByteDescending(b);
|
|
9579
|
+
}
|
|
9580
|
+
}
|
|
9581
|
+
writeSeparatorAscending() {
|
|
9582
|
+
this.writeEscapedByteAscending(ESCAPE1);
|
|
9583
|
+
this.writeEscapedByteAscending(SEPARATOR);
|
|
9584
|
+
}
|
|
9585
|
+
writeSeparatorDescending() {
|
|
9586
|
+
this.writeEscapedByteDescending(ESCAPE1);
|
|
9587
|
+
this.writeEscapedByteDescending(SEPARATOR);
|
|
9588
|
+
}
|
|
9589
|
+
writeEscapedByteAscending(b) {
|
|
9590
|
+
this.ensureAvailable(1);
|
|
9591
|
+
this.buffer[this.position++] = b;
|
|
9592
|
+
}
|
|
9593
|
+
writeEscapedByteDescending(b) {
|
|
9594
|
+
this.ensureAvailable(1);
|
|
9595
|
+
this.buffer[this.position++] = ~b;
|
|
9596
|
+
}
|
|
9597
|
+
ensureAvailable(bytes) {
|
|
9598
|
+
const minCapacity = bytes + this.position;
|
|
9599
|
+
if (minCapacity <= this.buffer.length) {
|
|
9600
|
+
return;
|
|
9601
|
+
}
|
|
9602
|
+
// Try doubling.
|
|
9603
|
+
let newLength = this.buffer.length * 2;
|
|
9604
|
+
// Still not big enough? Just allocate the right size.
|
|
9605
|
+
if (newLength < minCapacity) {
|
|
9606
|
+
newLength = minCapacity;
|
|
9607
|
+
}
|
|
9608
|
+
// Create the new buffer.
|
|
9609
|
+
const newBuffer = new Uint8Array(newLength);
|
|
9610
|
+
newBuffer.set(this.buffer); // copy old data
|
|
9611
|
+
this.buffer = newBuffer;
|
|
9612
|
+
}
|
|
9613
|
+
}
|
|
9614
|
+
|
|
9615
|
+
class AscendingIndexByteEncoder {
|
|
9616
|
+
constructor(orderedCode) {
|
|
9617
|
+
this.orderedCode = orderedCode;
|
|
9618
|
+
}
|
|
9619
|
+
writeBytes(value) {
|
|
9620
|
+
this.orderedCode.writeBytesAscending(value);
|
|
9621
|
+
}
|
|
9622
|
+
writeString(value) {
|
|
9623
|
+
this.orderedCode.writeUtf8Ascending(value);
|
|
9624
|
+
}
|
|
9625
|
+
writeNumber(value) {
|
|
9626
|
+
this.orderedCode.writeNumberAscending(value);
|
|
9627
|
+
}
|
|
9628
|
+
writeInfinity() {
|
|
9629
|
+
this.orderedCode.writeInfinityAscending();
|
|
9630
|
+
}
|
|
9631
|
+
}
|
|
9632
|
+
class DescendingIndexByteEncoder {
|
|
9633
|
+
constructor(orderedCode) {
|
|
9634
|
+
this.orderedCode = orderedCode;
|
|
9635
|
+
}
|
|
9636
|
+
writeBytes(value) {
|
|
9637
|
+
this.orderedCode.writeBytesDescending(value);
|
|
9638
|
+
}
|
|
9639
|
+
writeString(value) {
|
|
9640
|
+
this.orderedCode.writeUtf8Descending(value);
|
|
9641
|
+
}
|
|
9642
|
+
writeNumber(value) {
|
|
9643
|
+
this.orderedCode.writeNumberDescending(value);
|
|
9644
|
+
}
|
|
9645
|
+
writeInfinity() {
|
|
9646
|
+
this.orderedCode.writeInfinityDescending();
|
|
9647
|
+
}
|
|
9648
|
+
}
|
|
9649
|
+
/**
|
|
9650
|
+
* Implements `DirectionalIndexByteEncoder` using `OrderedCodeWriter` for the
|
|
9651
|
+
* actual encoding.
|
|
9652
|
+
*/
|
|
9653
|
+
class IndexByteEncoder {
|
|
9654
|
+
constructor() {
|
|
9655
|
+
this.orderedCode = new OrderedCodeWriter();
|
|
9656
|
+
this.ascending = new AscendingIndexByteEncoder(this.orderedCode);
|
|
9657
|
+
this.descending = new DescendingIndexByteEncoder(this.orderedCode);
|
|
9658
|
+
}
|
|
9659
|
+
seed(encodedBytes) {
|
|
9660
|
+
this.orderedCode.seed(encodedBytes);
|
|
9661
|
+
}
|
|
9662
|
+
forKind(kind) {
|
|
9663
|
+
return kind === 0 /* ASCENDING */ ? this.ascending : this.descending;
|
|
9664
|
+
}
|
|
9665
|
+
encodedBytes() {
|
|
9666
|
+
return this.orderedCode.encodedBytes();
|
|
9667
|
+
}
|
|
9668
|
+
reset() {
|
|
9669
|
+
this.orderedCode.reset();
|
|
9670
|
+
}
|
|
9671
|
+
}
|
|
9672
|
+
|
|
9673
|
+
/**
|
|
9674
|
+
* @license
|
|
9675
|
+
* Copyright 2022 Google LLC
|
|
9676
|
+
*
|
|
9677
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
9678
|
+
* you may not use this file except in compliance with the License.
|
|
9679
|
+
* You may obtain a copy of the License at
|
|
9680
|
+
*
|
|
9681
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9682
|
+
*
|
|
9683
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
9684
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
9685
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
9686
|
+
* See the License for the specific language governing permissions and
|
|
9687
|
+
* limitations under the License.
|
|
9688
|
+
*/
|
|
9689
|
+
/** Represents an index entry saved by the SDK in persisted storage. */
|
|
9690
|
+
class IndexEntry {
|
|
9691
|
+
constructor(indexId, documentKey, arrayValue, directionalValue) {
|
|
9692
|
+
this.indexId = indexId;
|
|
9693
|
+
this.documentKey = documentKey;
|
|
9694
|
+
this.arrayValue = arrayValue;
|
|
9695
|
+
this.directionalValue = directionalValue;
|
|
9696
|
+
}
|
|
9697
|
+
}
|
|
9698
|
+
function indexEntryComparator(left, right) {
|
|
9699
|
+
let cmp = left.indexId - right.indexId;
|
|
9700
|
+
if (cmp !== 0) {
|
|
9701
|
+
return cmp;
|
|
9702
|
+
}
|
|
9703
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
9704
|
+
if (cmp !== 0) {
|
|
9705
|
+
return cmp;
|
|
9706
|
+
}
|
|
9707
|
+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
|
|
9708
|
+
if (cmp !== 0) {
|
|
9709
|
+
return cmp;
|
|
9710
|
+
}
|
|
9711
|
+
return compareByteArrays(left.directionalValue, right.directionalValue);
|
|
9712
|
+
}
|
|
9713
|
+
function compareByteArrays(left, right) {
|
|
9714
|
+
for (let i = 0; i < left.length && i < right.length; ++i) {
|
|
9715
|
+
const compare = left[i] - right[i];
|
|
9716
|
+
if (compare !== 0) {
|
|
9717
|
+
return compare;
|
|
9718
|
+
}
|
|
9719
|
+
}
|
|
9720
|
+
return left.length - right.length;
|
|
9721
|
+
}
|
|
9722
|
+
|
|
8448
9723
|
/**
|
|
8449
9724
|
* @license
|
|
8450
9725
|
* Copyright 2019 Google LLC
|
|
@@ -8475,6 +9750,38 @@ class MemoryIndexManager {
|
|
|
8475
9750
|
getCollectionParents(transaction, collectionId) {
|
|
8476
9751
|
return PersistencePromise.resolve(this.collectionParentIndex.getEntries(collectionId));
|
|
8477
9752
|
}
|
|
9753
|
+
addFieldIndex(transaction, index) {
|
|
9754
|
+
// Field indices are not supported with memory persistence.
|
|
9755
|
+
return PersistencePromise.resolve();
|
|
9756
|
+
}
|
|
9757
|
+
deleteFieldIndex(transaction, index) {
|
|
9758
|
+
// Field indices are not supported with memory persistence.
|
|
9759
|
+
return PersistencePromise.resolve();
|
|
9760
|
+
}
|
|
9761
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9762
|
+
// Field indices are not supported with memory persistence.
|
|
9763
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9764
|
+
}
|
|
9765
|
+
getFieldIndex(transaction, target) {
|
|
9766
|
+
// Field indices are not supported with memory persistence.
|
|
9767
|
+
return PersistencePromise.resolve(null);
|
|
9768
|
+
}
|
|
9769
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9770
|
+
// Field indices are not supported with memory persistence.
|
|
9771
|
+
return PersistencePromise.resolve([]);
|
|
9772
|
+
}
|
|
9773
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9774
|
+
// Field indices are not supported with memory persistence.
|
|
9775
|
+
return PersistencePromise.resolve(null);
|
|
9776
|
+
}
|
|
9777
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9778
|
+
// Field indices are not supported with memory persistence.
|
|
9779
|
+
return PersistencePromise.resolve();
|
|
9780
|
+
}
|
|
9781
|
+
updateIndexEntries(transaction, documents) {
|
|
9782
|
+
// Field indices are not supported with memory persistence.
|
|
9783
|
+
return PersistencePromise.resolve();
|
|
9784
|
+
}
|
|
8478
9785
|
}
|
|
8479
9786
|
/**
|
|
8480
9787
|
* Internal implementation of the collection-parent index exposed by MemoryIndexManager.
|
|
@@ -8524,19 +9831,25 @@ class MemoryCollectionParentIndex {
|
|
|
8524
9831
|
* See the License for the specific language governing permissions and
|
|
8525
9832
|
* limitations under the License.
|
|
8526
9833
|
*/
|
|
9834
|
+
const LOG_TAG$f = 'IndexedDbIndexManager';
|
|
8527
9835
|
/**
|
|
8528
9836
|
* A persisted implementation of IndexManager.
|
|
9837
|
+
*
|
|
9838
|
+
* PORTING NOTE: Unlike iOS and Android, the Web SDK does not memoize index
|
|
9839
|
+
* data as it supports multi-tab access.
|
|
8529
9840
|
*/
|
|
8530
9841
|
class IndexedDbIndexManager {
|
|
8531
|
-
constructor() {
|
|
9842
|
+
constructor(user) {
|
|
9843
|
+
this.user = user;
|
|
8532
9844
|
/**
|
|
8533
9845
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
8534
9846
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
8535
9847
|
*
|
|
8536
|
-
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
8537
|
-
* satisfy reads.
|
|
9848
|
+
* This is *NOT* a complete cache of what's in persistence and so can never be
|
|
9849
|
+
* used to satisfy reads.
|
|
8538
9850
|
*/
|
|
8539
9851
|
this.collectionParentsCache = new MemoryCollectionParentIndex();
|
|
9852
|
+
this.uid = user.uid || '';
|
|
8540
9853
|
}
|
|
8541
9854
|
/**
|
|
8542
9855
|
* Adds a new entry to the collection parent index.
|
|
@@ -8583,6 +9896,198 @@ class IndexedDbIndexManager {
|
|
|
8583
9896
|
return parentPaths;
|
|
8584
9897
|
});
|
|
8585
9898
|
}
|
|
9899
|
+
addFieldIndex(transaction, index) {
|
|
9900
|
+
// TODO(indexing): Verify that the auto-incrementing index ID works in
|
|
9901
|
+
// Safari & Firefox.
|
|
9902
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9903
|
+
const dbIndex = toDbIndexConfiguration(index);
|
|
9904
|
+
delete dbIndex.indexId; // `indexId` is auto-populated by IndexedDb
|
|
9905
|
+
return indexes.add(dbIndex).next();
|
|
9906
|
+
}
|
|
9907
|
+
deleteFieldIndex(transaction, index) {
|
|
9908
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9909
|
+
const states = indexStateStore(transaction);
|
|
9910
|
+
const entries = indexEntriesStore(transaction);
|
|
9911
|
+
return indexes
|
|
9912
|
+
.delete(index.indexId)
|
|
9913
|
+
.next(() => states.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9914
|
+
/*lowerOpen=*/ false,
|
|
9915
|
+
/*upperOpen=*/ true)))
|
|
9916
|
+
.next(() => entries.delete(IDBKeyRange.bound([index.indexId], [index.indexId + 1],
|
|
9917
|
+
/*lowerOpen=*/ false,
|
|
9918
|
+
/*upperOpen=*/ true)));
|
|
9919
|
+
}
|
|
9920
|
+
getDocumentsMatchingTarget(transaction, fieldIndex, target) {
|
|
9921
|
+
// TODO(indexing): Implement
|
|
9922
|
+
return PersistencePromise.resolve(documentKeySet());
|
|
9923
|
+
}
|
|
9924
|
+
getFieldIndex(transaction, target) {
|
|
9925
|
+
// TODO(indexing): Implement
|
|
9926
|
+
return PersistencePromise.resolve(null);
|
|
9927
|
+
}
|
|
9928
|
+
/**
|
|
9929
|
+
* Returns the byte encoded form of the directional values in the field index.
|
|
9930
|
+
* Returns `null` if the document does not have all fields specified in the
|
|
9931
|
+
* index.
|
|
9932
|
+
*/
|
|
9933
|
+
encodeDirectionalElements(fieldIndex, document) {
|
|
9934
|
+
const encoder = new IndexByteEncoder();
|
|
9935
|
+
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
9936
|
+
const field = document.data.field(segment.fieldPath);
|
|
9937
|
+
if (field == null) {
|
|
9938
|
+
return null;
|
|
9939
|
+
}
|
|
9940
|
+
const directionalEncoder = encoder.forKind(segment.kind);
|
|
9941
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(field, directionalEncoder);
|
|
9942
|
+
}
|
|
9943
|
+
return encoder.encodedBytes();
|
|
9944
|
+
}
|
|
9945
|
+
/** Encodes a single value to the ascending index format. */
|
|
9946
|
+
encodeSingleElement(value) {
|
|
9947
|
+
const encoder = new IndexByteEncoder();
|
|
9948
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
9949
|
+
return encoder.encodedBytes();
|
|
9950
|
+
}
|
|
9951
|
+
getFieldIndexes(transaction, collectionGroup) {
|
|
9952
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9953
|
+
const states = indexStateStore(transaction);
|
|
9954
|
+
return (collectionGroup
|
|
9955
|
+
? indexes.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9956
|
+
: indexes.loadAll()).next(indexConfigs => {
|
|
9957
|
+
const result = [];
|
|
9958
|
+
return PersistencePromise.forEach(indexConfigs, (indexConfig) => {
|
|
9959
|
+
return states
|
|
9960
|
+
.get([indexConfig.indexId, this.uid])
|
|
9961
|
+
.next(indexState => {
|
|
9962
|
+
result.push(fromDbIndexConfiguration(indexConfig, indexState));
|
|
9963
|
+
});
|
|
9964
|
+
}).next(() => result);
|
|
9965
|
+
});
|
|
9966
|
+
}
|
|
9967
|
+
getNextCollectionGroupToUpdate(transaction) {
|
|
9968
|
+
return this.getFieldIndexes(transaction).next(indexes => {
|
|
9969
|
+
if (indexes.length === 0) {
|
|
9970
|
+
return null;
|
|
9971
|
+
}
|
|
9972
|
+
indexes.sort((l, r) => l.indexState.sequenceNumber - r.indexState.sequenceNumber);
|
|
9973
|
+
return indexes[0].collectionGroup;
|
|
9974
|
+
});
|
|
9975
|
+
}
|
|
9976
|
+
updateCollectionGroup(transaction, collectionGroup, offset) {
|
|
9977
|
+
const indexes = indexConfigurationStore(transaction);
|
|
9978
|
+
const states = indexStateStore(transaction);
|
|
9979
|
+
return this.getNextSequenceNumber(transaction).next(nextSequenceNumber => indexes
|
|
9980
|
+
.loadAll(DbIndexConfiguration.collectionGroupIndex, IDBKeyRange.bound(collectionGroup, collectionGroup))
|
|
9981
|
+
.next(configs => PersistencePromise.forEach(configs, (config) => states.put(toDbIndexState(config.indexId, this.user, nextSequenceNumber, offset)))));
|
|
9982
|
+
}
|
|
9983
|
+
updateIndexEntries(transaction, documents) {
|
|
9984
|
+
// Porting Note: `getFieldIndexes()` on Web does not cache index lookups as
|
|
9985
|
+
// it could be used across different IndexedDB transactions. As any cached
|
|
9986
|
+
// data might be invalidated by other multi-tab clients, we can only trust
|
|
9987
|
+
// data within a single IndexedDB transaction. We therefore add a cache
|
|
9988
|
+
// here.
|
|
9989
|
+
const memoizedIndexes = new Map();
|
|
9990
|
+
return PersistencePromise.forEach(documents, (key, doc) => {
|
|
9991
|
+
const memoizedCollectionIndexes = memoizedIndexes.get(key.collectionGroup);
|
|
9992
|
+
const fieldIndexes = memoizedCollectionIndexes
|
|
9993
|
+
? PersistencePromise.resolve(memoizedCollectionIndexes)
|
|
9994
|
+
: this.getFieldIndexes(transaction, key.collectionGroup);
|
|
9995
|
+
return fieldIndexes.next(fieldIndexes => {
|
|
9996
|
+
memoizedIndexes.set(key.collectionGroup, fieldIndexes);
|
|
9997
|
+
return PersistencePromise.forEach(fieldIndexes, (fieldIndex) => {
|
|
9998
|
+
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
9999
|
+
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10000
|
+
if (!existingEntries.isEqual(newEntries)) {
|
|
10001
|
+
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10002
|
+
}
|
|
10003
|
+
return PersistencePromise.resolve();
|
|
10004
|
+
});
|
|
10005
|
+
});
|
|
10006
|
+
});
|
|
10007
|
+
});
|
|
10008
|
+
}
|
|
10009
|
+
addIndexEntry(transaction, document, indexEntry) {
|
|
10010
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10011
|
+
return indexEntries.put(new DbIndexEntry(indexEntry.indexId, this.uid, indexEntry.arrayValue, indexEntry.directionalValue, encodeResourcePath(document.key.path)));
|
|
10012
|
+
}
|
|
10013
|
+
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10014
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10015
|
+
return indexEntries.delete([
|
|
10016
|
+
indexEntry.indexId,
|
|
10017
|
+
this.uid,
|
|
10018
|
+
indexEntry.arrayValue,
|
|
10019
|
+
indexEntry.directionalValue,
|
|
10020
|
+
encodeResourcePath(document.key.path)
|
|
10021
|
+
]);
|
|
10022
|
+
}
|
|
10023
|
+
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
10024
|
+
const indexEntries = indexEntriesStore(transaction);
|
|
10025
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10026
|
+
return indexEntries
|
|
10027
|
+
.iterate({
|
|
10028
|
+
index: DbIndexEntry.documentKeyIndex,
|
|
10029
|
+
range: IDBKeyRange.only([
|
|
10030
|
+
fieldIndex.indexId,
|
|
10031
|
+
this.uid,
|
|
10032
|
+
encodeResourcePath(documentKey.path)
|
|
10033
|
+
])
|
|
10034
|
+
}, (_, entry) => {
|
|
10035
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
10036
|
+
})
|
|
10037
|
+
.next(() => results);
|
|
10038
|
+
}
|
|
10039
|
+
/** Creates the index entries for the given document. */
|
|
10040
|
+
computeIndexEntries(document, fieldIndex) {
|
|
10041
|
+
let results = new SortedSet(indexEntryComparator);
|
|
10042
|
+
const directionalValue = this.encodeDirectionalElements(fieldIndex, document);
|
|
10043
|
+
if (directionalValue == null) {
|
|
10044
|
+
return results;
|
|
10045
|
+
}
|
|
10046
|
+
const arraySegment = fieldIndexGetArraySegment(fieldIndex);
|
|
10047
|
+
if (arraySegment != null) {
|
|
10048
|
+
const value = document.data.field(arraySegment.fieldPath);
|
|
10049
|
+
if (isArray(value)) {
|
|
10050
|
+
for (const arrayValue of value.arrayValue.values || []) {
|
|
10051
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, this.encodeSingleElement(arrayValue), directionalValue));
|
|
10052
|
+
}
|
|
10053
|
+
}
|
|
10054
|
+
}
|
|
10055
|
+
else {
|
|
10056
|
+
results = results.add(new IndexEntry(fieldIndex.indexId, document.key, new Uint8Array(), directionalValue));
|
|
10057
|
+
}
|
|
10058
|
+
return results;
|
|
10059
|
+
}
|
|
10060
|
+
/**
|
|
10061
|
+
* Updates the index entries for the provided document by deleting entries
|
|
10062
|
+
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10063
|
+
* entries.
|
|
10064
|
+
*/
|
|
10065
|
+
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10066
|
+
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10067
|
+
const promises = [];
|
|
10068
|
+
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10069
|
+
/* onAdd= */ entry => {
|
|
10070
|
+
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10071
|
+
},
|
|
10072
|
+
/* onRemove= */ entry => {
|
|
10073
|
+
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10074
|
+
});
|
|
10075
|
+
return PersistencePromise.waitFor(promises);
|
|
10076
|
+
}
|
|
10077
|
+
getNextSequenceNumber(transaction) {
|
|
10078
|
+
let nextSequenceNumber = 1;
|
|
10079
|
+
const states = indexStateStore(transaction);
|
|
10080
|
+
return states
|
|
10081
|
+
.iterate({
|
|
10082
|
+
index: DbIndexState.sequenceNumberIndex,
|
|
10083
|
+
reverse: true,
|
|
10084
|
+
range: IDBKeyRange.upperBound([this.uid, Number.MAX_SAFE_INTEGER])
|
|
10085
|
+
}, (_, state, controller) => {
|
|
10086
|
+
controller.done();
|
|
10087
|
+
nextSequenceNumber = state.sequenceNumber + 1;
|
|
10088
|
+
})
|
|
10089
|
+
.next(() => nextSequenceNumber);
|
|
10090
|
+
}
|
|
8586
10091
|
}
|
|
8587
10092
|
/**
|
|
8588
10093
|
* Helper to get a typed SimpleDbStore for the collectionParents
|
|
@@ -8590,6 +10095,24 @@ class IndexedDbIndexManager {
|
|
|
8590
10095
|
*/
|
|
8591
10096
|
function collectionParentsStore(txn) {
|
|
8592
10097
|
return getStore(txn, DbCollectionParent.store);
|
|
10098
|
+
}
|
|
10099
|
+
/**
|
|
10100
|
+
* Helper to get a typed SimpleDbStore for the index entry object store.
|
|
10101
|
+
*/
|
|
10102
|
+
function indexEntriesStore(txn) {
|
|
10103
|
+
return getStore(txn, DbIndexEntry.store);
|
|
10104
|
+
}
|
|
10105
|
+
/**
|
|
10106
|
+
* Helper to get a typed SimpleDbStore for the index configuration object store.
|
|
10107
|
+
*/
|
|
10108
|
+
function indexConfigurationStore(txn) {
|
|
10109
|
+
return getStore(txn, DbIndexConfiguration.store);
|
|
10110
|
+
}
|
|
10111
|
+
/**
|
|
10112
|
+
* Helper to get a typed SimpleDbStore for the index state object store.
|
|
10113
|
+
*/
|
|
10114
|
+
function indexStateStore(txn) {
|
|
10115
|
+
return getStore(txn, DbIndexState.store);
|
|
8593
10116
|
}
|
|
8594
10117
|
|
|
8595
10118
|
/**
|
|
@@ -9753,7 +11276,7 @@ class IndexedDbLruDelegateImpl {
|
|
|
9753
11276
|
// Our size accounting requires us to read all documents before
|
|
9754
11277
|
// removing them.
|
|
9755
11278
|
return changeBuffer.getEntry(txn, docKey).next(() => {
|
|
9756
|
-
changeBuffer.removeEntry(docKey);
|
|
11279
|
+
changeBuffer.removeEntry(docKey, SnapshotVersion.min());
|
|
9757
11280
|
return documentTargetStore(txn).delete(sentinelKey$1(docKey));
|
|
9758
11281
|
});
|
|
9759
11282
|
}
|
|
@@ -9966,27 +11489,19 @@ class ObjectMap {
|
|
|
9966
11489
|
*/
|
|
9967
11490
|
class RemoteDocumentChangeBuffer {
|
|
9968
11491
|
constructor() {
|
|
9969
|
-
// A mapping of document key to the new cache entry that should be written
|
|
9970
|
-
// existing cache entry should be removed).
|
|
11492
|
+
// A mapping of document key to the new cache entry that should be written.
|
|
9971
11493
|
this.changes = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
|
|
9972
11494
|
this.changesApplied = false;
|
|
9973
11495
|
}
|
|
9974
|
-
getReadTime(key) {
|
|
9975
|
-
const change = this.changes.get(key);
|
|
9976
|
-
if (change) {
|
|
9977
|
-
return change.readTime;
|
|
9978
|
-
}
|
|
9979
|
-
return SnapshotVersion.min();
|
|
9980
|
-
}
|
|
9981
11496
|
/**
|
|
9982
11497
|
* Buffers a `RemoteDocumentCache.addEntry()` call.
|
|
9983
11498
|
*
|
|
9984
11499
|
* You can only modify documents that have already been retrieved via
|
|
9985
11500
|
* `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).
|
|
9986
11501
|
*/
|
|
9987
|
-
addEntry(document
|
|
11502
|
+
addEntry(document) {
|
|
9988
11503
|
this.assertNotApplied();
|
|
9989
|
-
this.changes.set(document.key,
|
|
11504
|
+
this.changes.set(document.key, document);
|
|
9990
11505
|
}
|
|
9991
11506
|
/**
|
|
9992
11507
|
* Buffers a `RemoteDocumentCache.removeEntry()` call.
|
|
@@ -9994,12 +11509,9 @@ class RemoteDocumentChangeBuffer {
|
|
|
9994
11509
|
* You can only remove documents that have already been retrieved via
|
|
9995
11510
|
* `getEntry()/getEntries()` (enforced via IndexedDbs `apply()`).
|
|
9996
11511
|
*/
|
|
9997
|
-
removeEntry(key, readTime
|
|
11512
|
+
removeEntry(key, readTime) {
|
|
9998
11513
|
this.assertNotApplied();
|
|
9999
|
-
this.changes.set(key,
|
|
10000
|
-
document: MutableDocument.newInvalidDocument(key),
|
|
10001
|
-
readTime
|
|
10002
|
-
});
|
|
11514
|
+
this.changes.set(key, MutableDocument.newInvalidDocument(key).setReadTime(readTime));
|
|
10003
11515
|
}
|
|
10004
11516
|
/**
|
|
10005
11517
|
* Looks up an entry in the cache. The buffered changes will first be checked,
|
|
@@ -10016,7 +11528,7 @@ class RemoteDocumentChangeBuffer {
|
|
|
10016
11528
|
this.assertNotApplied();
|
|
10017
11529
|
const bufferedEntry = this.changes.get(documentKey);
|
|
10018
11530
|
if (bufferedEntry !== undefined) {
|
|
10019
|
-
return PersistencePromise.resolve(bufferedEntry
|
|
11531
|
+
return PersistencePromise.resolve(bufferedEntry);
|
|
10020
11532
|
}
|
|
10021
11533
|
else {
|
|
10022
11534
|
return this.getFromCache(transaction, documentKey);
|
|
@@ -10070,12 +11582,10 @@ class RemoteDocumentChangeBuffer {
|
|
|
10070
11582
|
* `newIndexedDbRemoteDocumentCache()`.
|
|
10071
11583
|
*/
|
|
10072
11584
|
class IndexedDbRemoteDocumentCacheImpl {
|
|
10073
|
-
|
|
10074
|
-
* @param serializer - The document serializer.
|
|
10075
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10076
|
-
*/
|
|
10077
|
-
constructor(serializer, indexManager) {
|
|
11585
|
+
constructor(serializer) {
|
|
10078
11586
|
this.serializer = serializer;
|
|
11587
|
+
}
|
|
11588
|
+
setIndexManager(indexManager) {
|
|
10079
11589
|
this.indexManager = indexManager;
|
|
10080
11590
|
}
|
|
10081
11591
|
/**
|
|
@@ -10197,21 +11707,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10197
11707
|
}
|
|
10198
11708
|
});
|
|
10199
11709
|
}
|
|
10200
|
-
|
|
11710
|
+
getAll(transaction, collection, sinceReadTime) {
|
|
10201
11711
|
let results = mutableDocumentMap();
|
|
10202
|
-
const immediateChildrenPathLength =
|
|
11712
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
10203
11713
|
const iterationOptions = {};
|
|
10204
11714
|
if (sinceReadTime.isEqual(SnapshotVersion.min())) {
|
|
10205
11715
|
// Documents are ordered by key, so we can use a prefix scan to narrow
|
|
10206
11716
|
// down the documents we need to match the query against.
|
|
10207
|
-
const startKey =
|
|
11717
|
+
const startKey = collection.toArray();
|
|
10208
11718
|
iterationOptions.range = IDBKeyRange.lowerBound(startKey);
|
|
10209
11719
|
}
|
|
10210
11720
|
else {
|
|
10211
11721
|
// Execute an index-free query and filter by read time. This is safe
|
|
10212
11722
|
// since all document changes to queries that have a
|
|
10213
11723
|
// lastLimboFreeSnapshotVersion (`sinceReadTime`) have a read time set.
|
|
10214
|
-
const collectionKey =
|
|
11724
|
+
const collectionKey = collection.toArray();
|
|
10215
11725
|
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
10216
11726
|
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
10217
11727
|
/* open= */ true);
|
|
@@ -10227,13 +11737,13 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10227
11737
|
if (key.length !== immediateChildrenPathLength) {
|
|
10228
11738
|
return;
|
|
10229
11739
|
}
|
|
10230
|
-
const document =
|
|
10231
|
-
if (
|
|
10232
|
-
control.done();
|
|
10233
|
-
}
|
|
10234
|
-
else if (queryMatches(query, document)) {
|
|
11740
|
+
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
11741
|
+
if (collection.isPrefixOf(document.key.path)) {
|
|
10235
11742
|
results = results.insert(document.key, document);
|
|
10236
11743
|
}
|
|
11744
|
+
else {
|
|
11745
|
+
control.done();
|
|
11746
|
+
}
|
|
10237
11747
|
})
|
|
10238
11748
|
.next(() => results);
|
|
10239
11749
|
}
|
|
@@ -10255,8 +11765,8 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10255
11765
|
return documentGlobalStore(txn).put(DbRemoteDocumentGlobal.key, metadata);
|
|
10256
11766
|
}
|
|
10257
11767
|
/**
|
|
10258
|
-
* Decodes `
|
|
10259
|
-
* corresponds to the format used for sentinel deletes).
|
|
11768
|
+
* Decodes `dbRemoteDoc` and returns the document (or an invalid document if
|
|
11769
|
+
* the document corresponds to the format used for sentinel deletes).
|
|
10260
11770
|
*/
|
|
10261
11771
|
maybeDecodeDocument(documentKey, dbRemoteDoc) {
|
|
10262
11772
|
if (dbRemoteDoc) {
|
|
@@ -10271,14 +11781,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
10271
11781
|
return MutableDocument.newInvalidDocument(documentKey);
|
|
10272
11782
|
}
|
|
10273
11783
|
}
|
|
10274
|
-
/**
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
* @param serializer - The document serializer.
|
|
10278
|
-
* @param indexManager - The query indexes that need to be maintained.
|
|
10279
|
-
*/
|
|
10280
|
-
function newIndexedDbRemoteDocumentCache(serializer, indexManager) {
|
|
10281
|
-
return new IndexedDbRemoteDocumentCacheImpl(serializer, indexManager);
|
|
11784
|
+
/** Creates a new IndexedDbRemoteDocumentCache. */
|
|
11785
|
+
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
11786
|
+
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
10282
11787
|
}
|
|
10283
11788
|
/**
|
|
10284
11789
|
* Returns the set of documents that have changed since the specified read
|
|
@@ -10350,8 +11855,8 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
10350
11855
|
let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
|
|
10351
11856
|
this.changes.forEach((key, documentChange) => {
|
|
10352
11857
|
const previousSize = this.documentSizes.get(key);
|
|
10353
|
-
if (documentChange.
|
|
10354
|
-
const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange
|
|
11858
|
+
if (documentChange.isValidDocument()) {
|
|
11859
|
+
const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
|
|
10355
11860
|
collectionParents = collectionParents.add(key.path.popLast());
|
|
10356
11861
|
const size = dbDocumentSize(doc);
|
|
10357
11862
|
sizeDelta += size - previousSize;
|
|
@@ -10364,7 +11869,7 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
10364
11869
|
// RemoteDocumentCache. This entry is represented by a NoDocument
|
|
10365
11870
|
// with a version of 0 and ignored by `maybeDecodeDocument()` but
|
|
10366
11871
|
// preserved in `getNewDocumentChanges()`.
|
|
10367
|
-
const deletedDoc = toDbRemoteDocument(this.documentCache.serializer,
|
|
11872
|
+
const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
|
|
10368
11873
|
promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
|
|
10369
11874
|
}
|
|
10370
11875
|
else {
|
|
@@ -10445,9 +11950,6 @@ class SchemaConverter {
|
|
|
10445
11950
|
* and local feature development.
|
|
10446
11951
|
*/
|
|
10447
11952
|
createOrUpgrade(db, txn, fromVersion, toVersion) {
|
|
10448
|
-
hardAssert(fromVersion < toVersion &&
|
|
10449
|
-
fromVersion >= 0 &&
|
|
10450
|
-
toVersion <= SCHEMA_VERSION);
|
|
10451
11953
|
const simpleDbTransaction = new SimpleDbTransaction('createOrUpgrade', txn);
|
|
10452
11954
|
if (fromVersion < 1 && toVersion >= 1) {
|
|
10453
11955
|
createPrimaryClientStore(db);
|
|
@@ -10514,6 +12016,16 @@ class SchemaConverter {
|
|
|
10514
12016
|
createNamedQueriesStore(db);
|
|
10515
12017
|
});
|
|
10516
12018
|
}
|
|
12019
|
+
if (fromVersion < 12 && toVersion >= 12) {
|
|
12020
|
+
p = p.next(() => {
|
|
12021
|
+
createDocumentOverlayStore(db);
|
|
12022
|
+
});
|
|
12023
|
+
}
|
|
12024
|
+
if (fromVersion < 13 && toVersion >= 13) {
|
|
12025
|
+
p = p.next(() => {
|
|
12026
|
+
createFieldIndex(db);
|
|
12027
|
+
});
|
|
12028
|
+
}
|
|
10517
12029
|
return p;
|
|
10518
12030
|
}
|
|
10519
12031
|
addDocumentGlobal(txn) {
|
|
@@ -10720,6 +12232,28 @@ function createNamedQueriesStore(db) {
|
|
|
10720
12232
|
db.createObjectStore(DbNamedQuery.store, {
|
|
10721
12233
|
keyPath: DbNamedQuery.keyPath
|
|
10722
12234
|
});
|
|
12235
|
+
}
|
|
12236
|
+
function createFieldIndex(db) {
|
|
12237
|
+
const indexConfiguratioStore = db.createObjectStore(DbIndexConfiguration.store, {
|
|
12238
|
+
keyPath: DbIndexConfiguration.keyPath,
|
|
12239
|
+
autoIncrement: true
|
|
12240
|
+
});
|
|
12241
|
+
indexConfiguratioStore.createIndex(DbIndexConfiguration.collectionGroupIndex, DbIndexConfiguration.collectionGroupIndexPath, { unique: false });
|
|
12242
|
+
const indexStateStore = db.createObjectStore(DbIndexState.store, {
|
|
12243
|
+
keyPath: DbIndexState.keyPath
|
|
12244
|
+
});
|
|
12245
|
+
indexStateStore.createIndex(DbIndexState.sequenceNumberIndex, DbIndexState.sequenceNumberIndexPath, { unique: false });
|
|
12246
|
+
const indexEntryStore = db.createObjectStore(DbIndexEntry.store, {
|
|
12247
|
+
keyPath: DbIndexEntry.keyPath
|
|
12248
|
+
});
|
|
12249
|
+
indexEntryStore.createIndex(DbIndexEntry.documentKeyIndex, DbIndexEntry.documentKeyIndexPath, { unique: false });
|
|
12250
|
+
}
|
|
12251
|
+
function createDocumentOverlayStore(db) {
|
|
12252
|
+
const documentOverlayStore = db.createObjectStore(DbDocumentOverlay.store, {
|
|
12253
|
+
keyPath: DbDocumentOverlay.keyPath
|
|
12254
|
+
});
|
|
12255
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionPathOverlayIndex, DbDocumentOverlay.collectionPathOverlayIndexPath, { unique: false });
|
|
12256
|
+
documentOverlayStore.createIndex(DbDocumentOverlay.collectionGroupOverlayIndex, DbDocumentOverlay.collectionGroupOverlayIndexPath, { unique: false });
|
|
10723
12257
|
}
|
|
10724
12258
|
|
|
10725
12259
|
/**
|
|
@@ -10831,7 +12365,7 @@ class IndexedDbPersistence {
|
|
|
10831
12365
|
* If set to true, forcefully obtains database access. Existing tabs will
|
|
10832
12366
|
* no longer be able to access IndexedDB.
|
|
10833
12367
|
*/
|
|
10834
|
-
forceOwningTab) {
|
|
12368
|
+
forceOwningTab, schemaVersion = SCHEMA_VERSION) {
|
|
10835
12369
|
this.allowTabSynchronization = allowTabSynchronization;
|
|
10836
12370
|
this.persistenceKey = persistenceKey;
|
|
10837
12371
|
this.clientId = clientId;
|
|
@@ -10840,6 +12374,7 @@ class IndexedDbPersistence {
|
|
|
10840
12374
|
this.document = document;
|
|
10841
12375
|
this.sequenceNumberSyncer = sequenceNumberSyncer;
|
|
10842
12376
|
this.forceOwningTab = forceOwningTab;
|
|
12377
|
+
this.schemaVersion = schemaVersion;
|
|
10843
12378
|
this.listenSequence = null;
|
|
10844
12379
|
this._started = false;
|
|
10845
12380
|
this.isPrimary = false;
|
|
@@ -10861,10 +12396,9 @@ class IndexedDbPersistence {
|
|
|
10861
12396
|
this.referenceDelegate = new IndexedDbLruDelegateImpl(this, lruParams);
|
|
10862
12397
|
this.dbName = persistenceKey + MAIN_DATABASE;
|
|
10863
12398
|
this.serializer = new LocalSerializer(serializer);
|
|
10864
|
-
this.simpleDb = new SimpleDb(this.dbName,
|
|
12399
|
+
this.simpleDb = new SimpleDb(this.dbName, this.schemaVersion, new SchemaConverter(this.serializer));
|
|
10865
12400
|
this.targetCache = new IndexedDbTargetCache(this.referenceDelegate, this.serializer);
|
|
10866
|
-
this.
|
|
10867
|
-
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer, this.indexManager);
|
|
12401
|
+
this.remoteDocumentCache = newIndexedDbRemoteDocumentCache(this.serializer);
|
|
10868
12402
|
this.bundleCache = new IndexedDbBundleCache();
|
|
10869
12403
|
if (this.window && this.window.localStorage) {
|
|
10870
12404
|
this.webStorage = this.window.localStorage;
|
|
@@ -11200,8 +12734,8 @@ class IndexedDbPersistence {
|
|
|
11200
12734
|
get started() {
|
|
11201
12735
|
return this._started;
|
|
11202
12736
|
}
|
|
11203
|
-
getMutationQueue(user) {
|
|
11204
|
-
return IndexedDbMutationQueue.forUser(user, this.serializer,
|
|
12737
|
+
getMutationQueue(user, indexManager) {
|
|
12738
|
+
return IndexedDbMutationQueue.forUser(user, this.serializer, indexManager, this.referenceDelegate);
|
|
11205
12739
|
}
|
|
11206
12740
|
getTargetCache() {
|
|
11207
12741
|
return this.targetCache;
|
|
@@ -11209,8 +12743,11 @@ class IndexedDbPersistence {
|
|
|
11209
12743
|
getRemoteDocumentCache() {
|
|
11210
12744
|
return this.remoteDocumentCache;
|
|
11211
12745
|
}
|
|
11212
|
-
getIndexManager() {
|
|
11213
|
-
return
|
|
12746
|
+
getIndexManager(user) {
|
|
12747
|
+
return new IndexedDbIndexManager(user);
|
|
12748
|
+
}
|
|
12749
|
+
getDocumentOverlayCache(user) {
|
|
12750
|
+
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
11214
12751
|
}
|
|
11215
12752
|
getBundleCache() {
|
|
11216
12753
|
return this.bundleCache;
|
|
@@ -11218,11 +12755,12 @@ class IndexedDbPersistence {
|
|
|
11218
12755
|
runTransaction(action, mode, transactionOperation) {
|
|
11219
12756
|
logDebug(LOG_TAG$d, 'Starting transaction:', action);
|
|
11220
12757
|
const simpleDbMode = mode === 'readonly' ? 'readonly' : 'readwrite';
|
|
12758
|
+
const objectStores = getObjectStores(this.schemaVersion);
|
|
11221
12759
|
let persistenceTransaction;
|
|
11222
12760
|
// Do all transactions as readwrite against all object stores, since we
|
|
11223
12761
|
// are the only reader/writer.
|
|
11224
12762
|
return this.simpleDb
|
|
11225
|
-
.runTransaction(action, simpleDbMode,
|
|
12763
|
+
.runTransaction(action, simpleDbMode, objectStores, simpleDbTxn => {
|
|
11226
12764
|
persistenceTransaction = new IndexedDbTransaction(simpleDbTxn, this.listenSequence
|
|
11227
12765
|
? this.listenSequence.next()
|
|
11228
12766
|
: ListenSequence.INVALID);
|
|
@@ -11594,39 +13132,28 @@ class LocalDocumentsView {
|
|
|
11594
13132
|
getDocumentsMatchingCollectionQuery(transaction, query, sinceReadTime) {
|
|
11595
13133
|
// Query the remote documents and overlay mutations.
|
|
11596
13134
|
let results;
|
|
11597
|
-
let mutationBatches;
|
|
11598
13135
|
return this.remoteDocumentCache
|
|
11599
|
-
.
|
|
13136
|
+
.getAll(transaction, query.path, sinceReadTime)
|
|
11600
13137
|
.next(queryResults => {
|
|
11601
13138
|
results = queryResults;
|
|
11602
13139
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
11603
13140
|
})
|
|
11604
|
-
.next(
|
|
11605
|
-
mutationBatches
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
let document = results.get(key);
|
|
11618
|
-
if (document == null) {
|
|
11619
|
-
// Create invalid document to apply mutations on top of
|
|
11620
|
-
document = MutableDocument.newInvalidDocument(key);
|
|
11621
|
-
results = results.insert(key, document);
|
|
11622
|
-
}
|
|
11623
|
-
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
11624
|
-
if (!document.isFoundDocument()) {
|
|
11625
|
-
results = results.remove(key);
|
|
11626
|
-
}
|
|
13141
|
+
.next(mutationBatches => {
|
|
13142
|
+
for (const batch of mutationBatches) {
|
|
13143
|
+
for (const mutation of batch.mutations) {
|
|
13144
|
+
const key = mutation.key;
|
|
13145
|
+
let document = results.get(key);
|
|
13146
|
+
if (document == null) {
|
|
13147
|
+
// Create invalid document to apply mutations on top of
|
|
13148
|
+
document = MutableDocument.newInvalidDocument(key);
|
|
13149
|
+
results = results.insert(key, document);
|
|
13150
|
+
}
|
|
13151
|
+
mutationApplyToLocalView(mutation, document, batch.localWriteTime);
|
|
13152
|
+
if (!document.isFoundDocument()) {
|
|
13153
|
+
results = results.remove(key);
|
|
11627
13154
|
}
|
|
11628
13155
|
}
|
|
11629
|
-
}
|
|
13156
|
+
}
|
|
11630
13157
|
})
|
|
11631
13158
|
.next(() => {
|
|
11632
13159
|
// Finally, filter out any documents that don't actually match
|
|
@@ -11639,29 +13166,6 @@ class LocalDocumentsView {
|
|
|
11639
13166
|
return results;
|
|
11640
13167
|
});
|
|
11641
13168
|
}
|
|
11642
|
-
addMissingBaseDocuments(transaction, matchingMutationBatches, existingDocuments) {
|
|
11643
|
-
let missingBaseDocEntriesForPatching = documentKeySet();
|
|
11644
|
-
for (const batch of matchingMutationBatches) {
|
|
11645
|
-
for (const mutation of batch.mutations) {
|
|
11646
|
-
if (mutation instanceof PatchMutation &&
|
|
11647
|
-
existingDocuments.get(mutation.key) === null) {
|
|
11648
|
-
missingBaseDocEntriesForPatching =
|
|
11649
|
-
missingBaseDocEntriesForPatching.add(mutation.key);
|
|
11650
|
-
}
|
|
11651
|
-
}
|
|
11652
|
-
}
|
|
11653
|
-
let mergedDocuments = existingDocuments;
|
|
11654
|
-
return this.remoteDocumentCache
|
|
11655
|
-
.getEntries(transaction, missingBaseDocEntriesForPatching)
|
|
11656
|
-
.next(missingBaseDocs => {
|
|
11657
|
-
missingBaseDocs.forEach((key, doc) => {
|
|
11658
|
-
if (doc.isFoundDocument()) {
|
|
11659
|
-
mergedDocuments = mergedDocuments.insert(key, doc);
|
|
11660
|
-
}
|
|
11661
|
-
});
|
|
11662
|
-
return mergedDocuments;
|
|
11663
|
-
});
|
|
11664
|
-
}
|
|
11665
13169
|
}
|
|
11666
13170
|
|
|
11667
13171
|
/**
|
|
@@ -11720,11 +13224,18 @@ class LocalStoreImpl {
|
|
|
11720
13224
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
11721
13225
|
*/
|
|
11722
13226
|
this.lastDocumentChangeReadTime = SnapshotVersion.min();
|
|
11723
|
-
this.mutationQueue = persistence.getMutationQueue(initialUser);
|
|
11724
13227
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
11725
13228
|
this.targetCache = persistence.getTargetCache();
|
|
11726
|
-
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.persistence.getIndexManager());
|
|
11727
13229
|
this.bundleCache = persistence.getBundleCache();
|
|
13230
|
+
this.initializeUserComponents(initialUser);
|
|
13231
|
+
}
|
|
13232
|
+
initializeUserComponents(user) {
|
|
13233
|
+
// TODO(indexing): Add spec tests that test these components change after a
|
|
13234
|
+
// user change
|
|
13235
|
+
this.indexManager = this.persistence.getIndexManager(user);
|
|
13236
|
+
this.mutationQueue = this.persistence.getMutationQueue(user, this.indexManager);
|
|
13237
|
+
this.localDocuments = new LocalDocumentsView(this.remoteDocuments, this.mutationQueue, this.indexManager);
|
|
13238
|
+
this.remoteDocuments.setIndexManager(this.indexManager);
|
|
11728
13239
|
this.queryEngine.setLocalDocumentsView(this.localDocuments);
|
|
11729
13240
|
}
|
|
11730
13241
|
collectGarbage(garbageCollector) {
|
|
@@ -11746,8 +13257,6 @@ persistence, queryEngine, initialUser, serializer) {
|
|
|
11746
13257
|
// change.
|
|
11747
13258
|
async function localStoreHandleUserChange(localStore, user) {
|
|
11748
13259
|
const localStoreImpl = debugCast(localStore);
|
|
11749
|
-
let newMutationQueue = localStoreImpl.mutationQueue;
|
|
11750
|
-
let newLocalDocuments = localStoreImpl.localDocuments;
|
|
11751
13260
|
const result = await localStoreImpl.persistence.runTransaction('Handle user change', 'readonly', txn => {
|
|
11752
13261
|
// Swap out the mutation queue, grabbing the pending mutation batches
|
|
11753
13262
|
// before and after.
|
|
@@ -11756,11 +13265,8 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11756
13265
|
.getAllMutationBatches(txn)
|
|
11757
13266
|
.next(promisedOldBatches => {
|
|
11758
13267
|
oldBatches = promisedOldBatches;
|
|
11759
|
-
|
|
11760
|
-
|
|
11761
|
-
// MutationQueue.
|
|
11762
|
-
newLocalDocuments = new LocalDocumentsView(localStoreImpl.remoteDocuments, newMutationQueue, localStoreImpl.persistence.getIndexManager());
|
|
11763
|
-
return newMutationQueue.getAllMutationBatches(txn);
|
|
13268
|
+
localStoreImpl.initializeUserComponents(user);
|
|
13269
|
+
return localStoreImpl.mutationQueue.getAllMutationBatches(txn);
|
|
11764
13270
|
})
|
|
11765
13271
|
.next(newBatches => {
|
|
11766
13272
|
const removedBatchIds = [];
|
|
@@ -11781,7 +13287,7 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11781
13287
|
}
|
|
11782
13288
|
// Return the set of all (potentially) changed documents and the list
|
|
11783
13289
|
// of mutation batch IDs that were affected by change.
|
|
11784
|
-
return
|
|
13290
|
+
return localStoreImpl.localDocuments
|
|
11785
13291
|
.getDocuments(txn, changedKeys)
|
|
11786
13292
|
.next(affectedDocuments => {
|
|
11787
13293
|
return {
|
|
@@ -11792,9 +13298,6 @@ async function localStoreHandleUserChange(localStore, user) {
|
|
|
11792
13298
|
});
|
|
11793
13299
|
});
|
|
11794
13300
|
});
|
|
11795
|
-
localStoreImpl.mutationQueue = newMutationQueue;
|
|
11796
|
-
localStoreImpl.localDocuments = newLocalDocuments;
|
|
11797
|
-
localStoreImpl.queryEngine.setLocalDocumentsView(localStoreImpl.localDocuments);
|
|
11798
13301
|
return result;
|
|
11799
13302
|
}
|
|
11800
13303
|
/* Accepts locally generated Mutations and commit them to storage. */
|
|
@@ -11951,14 +13454,14 @@ function localStoreApplyRemoteEventToLocalCache(localStore, remoteEvent) {
|
|
|
11951
13454
|
}
|
|
11952
13455
|
});
|
|
11953
13456
|
let changedDocs = mutableDocumentMap();
|
|
11954
|
-
remoteEvent.documentUpdates.forEach(
|
|
13457
|
+
remoteEvent.documentUpdates.forEach(key => {
|
|
11955
13458
|
if (remoteEvent.resolvedLimboDocuments.has(key)) {
|
|
11956
13459
|
promises.push(localStoreImpl.persistence.referenceDelegate.updateLimboDocument(txn, key));
|
|
11957
13460
|
}
|
|
11958
13461
|
});
|
|
11959
13462
|
// Each loop iteration only affects its "own" doc, so it's safe to get all the remote
|
|
11960
13463
|
// documents in advance in a single call.
|
|
11961
|
-
promises.push(populateDocumentChangeBuffer(txn, documentBuffer, remoteEvent.documentUpdates
|
|
13464
|
+
promises.push(populateDocumentChangeBuffer(txn, documentBuffer, remoteEvent.documentUpdates).next(result => {
|
|
11962
13465
|
changedDocs = result;
|
|
11963
13466
|
}));
|
|
11964
13467
|
// HACK: The only reason we allow a null snapshot version is so that we
|
|
@@ -11999,17 +13502,13 @@ function localStoreApplyRemoteEventToLocalCache(localStore, remoteEvent) {
|
|
|
11999
13502
|
* Note: this function will use `documentVersions` if it is defined;
|
|
12000
13503
|
* when it is not defined, resorts to `globalVersion`.
|
|
12001
13504
|
*/
|
|
12002
|
-
function populateDocumentChangeBuffer(txn, documentBuffer, documents
|
|
12003
|
-
// TODO(wuandy): We could add `readTime` to MaybeDocument instead to remove
|
|
12004
|
-
// this parameter.
|
|
12005
|
-
documentVersions) {
|
|
13505
|
+
function populateDocumentChangeBuffer(txn, documentBuffer, documents) {
|
|
12006
13506
|
let updatedKeys = documentKeySet();
|
|
12007
13507
|
documents.forEach(k => (updatedKeys = updatedKeys.add(k)));
|
|
12008
13508
|
return documentBuffer.getEntries(txn, updatedKeys).next(existingDocs => {
|
|
12009
13509
|
let changedDocs = mutableDocumentMap();
|
|
12010
13510
|
documents.forEach((key, doc) => {
|
|
12011
13511
|
const existingDoc = existingDocs.get(key);
|
|
12012
|
-
const docReadTime = (documentVersions === null || documentVersions === void 0 ? void 0 : documentVersions.get(key)) || globalVersion;
|
|
12013
13512
|
// Note: The order of the steps below is important, since we want
|
|
12014
13513
|
// to ensure that rejected limbo resolutions (which fabricate
|
|
12015
13514
|
// NoDocuments with SnapshotVersion.min()) never add documents to
|
|
@@ -12018,14 +13517,14 @@ documentVersions) {
|
|
|
12018
13517
|
// NoDocuments with SnapshotVersion.min() are used in manufactured
|
|
12019
13518
|
// events. We remove these documents from cache since we lost
|
|
12020
13519
|
// access.
|
|
12021
|
-
documentBuffer.removeEntry(key,
|
|
13520
|
+
documentBuffer.removeEntry(key, doc.readTime);
|
|
12022
13521
|
changedDocs = changedDocs.insert(key, doc);
|
|
12023
13522
|
}
|
|
12024
13523
|
else if (!existingDoc.isValidDocument() ||
|
|
12025
13524
|
doc.version.compareTo(existingDoc.version) > 0 ||
|
|
12026
13525
|
(doc.version.compareTo(existingDoc.version) === 0 &&
|
|
12027
13526
|
existingDoc.hasPendingWrites)) {
|
|
12028
|
-
documentBuffer.addEntry(doc
|
|
13527
|
+
documentBuffer.addEntry(doc);
|
|
12029
13528
|
changedDocs = changedDocs.insert(key, doc);
|
|
12030
13529
|
}
|
|
12031
13530
|
else {
|
|
@@ -12279,7 +13778,8 @@ function applyWriteToRemoteDocuments(localStoreImpl, txn, batchResult, documentB
|
|
|
12279
13778
|
// We use the commitVersion as the readTime rather than the
|
|
12280
13779
|
// document's updateTime since the updateTime is not advanced
|
|
12281
13780
|
// for updates that do not modify the underlying document.
|
|
12282
|
-
|
|
13781
|
+
doc.setReadTime(batchResult.commitVersion);
|
|
13782
|
+
documentBuffer.addEntry(doc);
|
|
12283
13783
|
}
|
|
12284
13784
|
}
|
|
12285
13785
|
});
|
|
@@ -12380,14 +13880,14 @@ async function localStoreApplyBundledDocuments(localStore, bundleConverter, docu
|
|
|
12380
13880
|
const localStoreImpl = debugCast(localStore);
|
|
12381
13881
|
let documentKeys = documentKeySet();
|
|
12382
13882
|
let documentMap = mutableDocumentMap();
|
|
12383
|
-
let versionMap = documentVersionMap();
|
|
12384
13883
|
for (const bundleDoc of documents) {
|
|
12385
13884
|
const documentKey = bundleConverter.toDocumentKey(bundleDoc.metadata.name);
|
|
12386
13885
|
if (bundleDoc.document) {
|
|
12387
13886
|
documentKeys = documentKeys.add(documentKey);
|
|
12388
13887
|
}
|
|
12389
|
-
|
|
12390
|
-
|
|
13888
|
+
const doc = bundleConverter.toMutableDocument(bundleDoc);
|
|
13889
|
+
doc.setReadTime(bundleConverter.toSnapshotVersion(bundleDoc.metadata.readTime));
|
|
13890
|
+
documentMap = documentMap.insert(documentKey, doc);
|
|
12391
13891
|
}
|
|
12392
13892
|
const documentBuffer = localStoreImpl.remoteDocuments.newChangeBuffer({
|
|
12393
13893
|
trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()`
|
|
@@ -12396,7 +13896,7 @@ async function localStoreApplyBundledDocuments(localStore, bundleConverter, docu
|
|
|
12396
13896
|
// they will not get garbage collected right away.
|
|
12397
13897
|
const umbrellaTargetData = await localStoreAllocateTarget(localStoreImpl, umbrellaTarget(bundleName));
|
|
12398
13898
|
return localStoreImpl.persistence.runTransaction('Apply bundle documents', 'readwrite', txn => {
|
|
12399
|
-
return populateDocumentChangeBuffer(txn, documentBuffer, documentMap
|
|
13899
|
+
return populateDocumentChangeBuffer(txn, documentBuffer, documentMap)
|
|
12400
13900
|
.next(changedDocs => {
|
|
12401
13901
|
documentBuffer.apply(txn);
|
|
12402
13902
|
return changedDocs;
|
|
@@ -12510,6 +14010,122 @@ class MemoryBundleCache {
|
|
|
12510
14010
|
}
|
|
12511
14011
|
}
|
|
12512
14012
|
|
|
14013
|
+
/**
|
|
14014
|
+
* @license
|
|
14015
|
+
* Copyright 2022 Google LLC
|
|
14016
|
+
*
|
|
14017
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
14018
|
+
* you may not use this file except in compliance with the License.
|
|
14019
|
+
* You may obtain a copy of the License at
|
|
14020
|
+
*
|
|
14021
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
14022
|
+
*
|
|
14023
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14024
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14025
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14026
|
+
* See the License for the specific language governing permissions and
|
|
14027
|
+
* limitations under the License.
|
|
14028
|
+
*/
|
|
14029
|
+
/**
|
|
14030
|
+
* An in-memory implementation of DocumentOverlayCache.
|
|
14031
|
+
*/
|
|
14032
|
+
class MemoryDocumentOverlayCache {
|
|
14033
|
+
constructor() {
|
|
14034
|
+
// A map sorted by DocumentKey, whose value is a pair of the largest batch id
|
|
14035
|
+
// for the overlay and the overlay itself.
|
|
14036
|
+
this.overlays = new SortedMap(DocumentKey.comparator);
|
|
14037
|
+
this.overlayByBatchId = new Map();
|
|
14038
|
+
}
|
|
14039
|
+
getOverlay(transaction, key) {
|
|
14040
|
+
return PersistencePromise.resolve(this.overlays.get(key));
|
|
14041
|
+
}
|
|
14042
|
+
saveOverlays(transaction, largestBatchId, overlays) {
|
|
14043
|
+
overlays.forEach(mutation => {
|
|
14044
|
+
this.saveOverlay(transaction, largestBatchId, mutation);
|
|
14045
|
+
});
|
|
14046
|
+
return PersistencePromise.resolve();
|
|
14047
|
+
}
|
|
14048
|
+
removeOverlaysForBatchId(transaction, documentKeys, batchId) {
|
|
14049
|
+
const keys = this.overlayByBatchId.get(batchId);
|
|
14050
|
+
if (keys !== undefined) {
|
|
14051
|
+
keys.forEach(key => (this.overlays = this.overlays.remove(key)));
|
|
14052
|
+
this.overlayByBatchId.delete(batchId);
|
|
14053
|
+
}
|
|
14054
|
+
return PersistencePromise.resolve();
|
|
14055
|
+
}
|
|
14056
|
+
getOverlaysForCollection(transaction, collection, sinceBatchId) {
|
|
14057
|
+
const result = new Map();
|
|
14058
|
+
const immediateChildrenPathLength = collection.length + 1;
|
|
14059
|
+
const prefix = new DocumentKey(collection.child(''));
|
|
14060
|
+
const iter = this.overlays.getIteratorFrom(prefix);
|
|
14061
|
+
while (iter.hasNext()) {
|
|
14062
|
+
const entry = iter.getNext();
|
|
14063
|
+
const overlay = entry.value;
|
|
14064
|
+
const key = overlay.getKey();
|
|
14065
|
+
if (!collection.isPrefixOf(key.path)) {
|
|
14066
|
+
break;
|
|
14067
|
+
}
|
|
14068
|
+
// Documents from sub-collections
|
|
14069
|
+
if (key.path.length !== immediateChildrenPathLength) {
|
|
14070
|
+
continue;
|
|
14071
|
+
}
|
|
14072
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14073
|
+
result.set(overlay.getKey(), overlay);
|
|
14074
|
+
}
|
|
14075
|
+
}
|
|
14076
|
+
return PersistencePromise.resolve(result);
|
|
14077
|
+
}
|
|
14078
|
+
getOverlaysForCollectionGroup(transaction, collectionGroup, sinceBatchId, count) {
|
|
14079
|
+
let batchIdToOverlays = new SortedMap((key1, key2) => key1 - key2);
|
|
14080
|
+
const iter = this.overlays.getIterator();
|
|
14081
|
+
while (iter.hasNext()) {
|
|
14082
|
+
const entry = iter.getNext();
|
|
14083
|
+
const overlay = entry.value;
|
|
14084
|
+
const key = overlay.getKey();
|
|
14085
|
+
if (key.getCollectionGroup() !== collectionGroup) {
|
|
14086
|
+
continue;
|
|
14087
|
+
}
|
|
14088
|
+
if (overlay.largestBatchId > sinceBatchId) {
|
|
14089
|
+
let overlaysForBatchId = batchIdToOverlays.get(overlay.largestBatchId);
|
|
14090
|
+
if (overlaysForBatchId === null) {
|
|
14091
|
+
overlaysForBatchId = new Map();
|
|
14092
|
+
batchIdToOverlays = batchIdToOverlays.insert(overlay.largestBatchId, overlaysForBatchId);
|
|
14093
|
+
}
|
|
14094
|
+
overlaysForBatchId.set(overlay.getKey(), overlay);
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14097
|
+
const result = new Map();
|
|
14098
|
+
const batchIter = batchIdToOverlays.getIterator();
|
|
14099
|
+
while (batchIter.hasNext()) {
|
|
14100
|
+
const entry = batchIter.getNext();
|
|
14101
|
+
const overlays = entry.value;
|
|
14102
|
+
overlays.forEach((overlay, key) => result.set(key, overlay));
|
|
14103
|
+
if (result.size >= count) {
|
|
14104
|
+
break;
|
|
14105
|
+
}
|
|
14106
|
+
}
|
|
14107
|
+
return PersistencePromise.resolve(result);
|
|
14108
|
+
}
|
|
14109
|
+
saveOverlay(transaction, largestBatchId, mutation) {
|
|
14110
|
+
if (mutation === null) {
|
|
14111
|
+
return;
|
|
14112
|
+
}
|
|
14113
|
+
// Remove the association of the overlay to its batch id.
|
|
14114
|
+
const existing = this.overlays.get(mutation.key);
|
|
14115
|
+
if (existing !== null) {
|
|
14116
|
+
this.overlayByBatchId.get(existing.largestBatchId).delete(mutation.key);
|
|
14117
|
+
}
|
|
14118
|
+
this.overlays = this.overlays.insert(mutation.key, new Overlay(largestBatchId, mutation));
|
|
14119
|
+
// Create the association of this overlay to the given largestBatchId.
|
|
14120
|
+
let batch = this.overlayByBatchId.get(largestBatchId);
|
|
14121
|
+
if (batch === undefined) {
|
|
14122
|
+
batch = new Set();
|
|
14123
|
+
this.overlayByBatchId.set(largestBatchId, batch);
|
|
14124
|
+
}
|
|
14125
|
+
batch.add(mutation.key);
|
|
14126
|
+
}
|
|
14127
|
+
}
|
|
14128
|
+
|
|
12513
14129
|
/**
|
|
12514
14130
|
* @license
|
|
12515
14131
|
* Copyright 2017 Google LLC
|
|
@@ -12863,29 +14479,30 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
12863
14479
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
12864
14480
|
* calculating the size.
|
|
12865
14481
|
*/
|
|
12866
|
-
constructor(
|
|
12867
|
-
this.indexManager = indexManager;
|
|
14482
|
+
constructor(sizer) {
|
|
12868
14483
|
this.sizer = sizer;
|
|
12869
14484
|
/** Underlying cache of documents and their read times. */
|
|
12870
14485
|
this.docs = documentEntryMap();
|
|
12871
14486
|
/** Size of all cached documents. */
|
|
12872
14487
|
this.size = 0;
|
|
12873
14488
|
}
|
|
14489
|
+
setIndexManager(indexManager) {
|
|
14490
|
+
this.indexManager = indexManager;
|
|
14491
|
+
}
|
|
12874
14492
|
/**
|
|
12875
14493
|
* Adds the supplied entry to the cache and updates the cache size as appropriate.
|
|
12876
14494
|
*
|
|
12877
14495
|
* All calls of `addEntry` are required to go through the RemoteDocumentChangeBuffer
|
|
12878
14496
|
* returned by `newChangeBuffer()`.
|
|
12879
14497
|
*/
|
|
12880
|
-
addEntry(transaction, doc
|
|
14498
|
+
addEntry(transaction, doc) {
|
|
12881
14499
|
const key = doc.key;
|
|
12882
14500
|
const entry = this.docs.get(key);
|
|
12883
14501
|
const previousSize = entry ? entry.size : 0;
|
|
12884
14502
|
const currentSize = this.sizer(doc);
|
|
12885
14503
|
this.docs = this.docs.insert(key, {
|
|
12886
14504
|
document: doc.mutableCopy(),
|
|
12887
|
-
size: currentSize
|
|
12888
|
-
readTime
|
|
14505
|
+
size: currentSize
|
|
12889
14506
|
});
|
|
12890
14507
|
this.size += currentSize - previousSize;
|
|
12891
14508
|
return this.indexManager.addToCollectionParentIndex(transaction, key.path.popLast());
|
|
@@ -12919,21 +14536,22 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
12919
14536
|
});
|
|
12920
14537
|
return PersistencePromise.resolve(results);
|
|
12921
14538
|
}
|
|
12922
|
-
|
|
14539
|
+
getAll(transaction, collectionPath, sinceReadTime) {
|
|
12923
14540
|
let results = mutableDocumentMap();
|
|
12924
14541
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
12925
14542
|
// the documents we need to match the query against.
|
|
12926
|
-
const prefix = new DocumentKey(
|
|
14543
|
+
const prefix = new DocumentKey(collectionPath.child(''));
|
|
12927
14544
|
const iterator = this.docs.getIteratorFrom(prefix);
|
|
12928
14545
|
while (iterator.hasNext()) {
|
|
12929
|
-
const { key, value: { document
|
|
12930
|
-
if (!
|
|
14546
|
+
const { key, value: { document } } = iterator.getNext();
|
|
14547
|
+
if (!collectionPath.isPrefixOf(key.path)) {
|
|
12931
14548
|
break;
|
|
12932
14549
|
}
|
|
12933
|
-
if (
|
|
14550
|
+
if (key.path.length > collectionPath.length + 1) {
|
|
14551
|
+
// Exclude entries from subcollections.
|
|
12934
14552
|
continue;
|
|
12935
14553
|
}
|
|
12936
|
-
if (
|
|
14554
|
+
if (document.readTime.compareTo(sinceReadTime) <= 0) {
|
|
12937
14555
|
continue;
|
|
12938
14556
|
}
|
|
12939
14557
|
results = results.insert(document.key, document.mutableCopy());
|
|
@@ -12955,13 +14573,12 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
12955
14573
|
/**
|
|
12956
14574
|
* Creates a new memory-only RemoteDocumentCache.
|
|
12957
14575
|
*
|
|
12958
|
-
* @param indexManager - A class that manages collection group indices.
|
|
12959
14576
|
* @param sizer - Used to assess the size of a document. For eager GC, this is
|
|
12960
14577
|
* expected to just return 0 to avoid unnecessarily doing the work of
|
|
12961
14578
|
* calculating the size.
|
|
12962
14579
|
*/
|
|
12963
|
-
function newMemoryRemoteDocumentCache(
|
|
12964
|
-
return new MemoryRemoteDocumentCacheImpl(
|
|
14580
|
+
function newMemoryRemoteDocumentCache(sizer) {
|
|
14581
|
+
return new MemoryRemoteDocumentCacheImpl(sizer);
|
|
12965
14582
|
}
|
|
12966
14583
|
/**
|
|
12967
14584
|
* Handles the details of adding and updating documents in the MemoryRemoteDocumentCache.
|
|
@@ -12974,8 +14591,8 @@ class MemoryRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12974
14591
|
applyChanges(transaction) {
|
|
12975
14592
|
const promises = [];
|
|
12976
14593
|
this.changes.forEach((key, doc) => {
|
|
12977
|
-
if (doc.
|
|
12978
|
-
promises.push(this.documentCache.addEntry(transaction, doc
|
|
14594
|
+
if (doc.isValidDocument()) {
|
|
14595
|
+
promises.push(this.documentCache.addEntry(transaction, doc));
|
|
12979
14596
|
}
|
|
12980
14597
|
else {
|
|
12981
14598
|
this.documentCache.removeEntry(key);
|
|
@@ -13155,6 +14772,7 @@ class MemoryPersistence {
|
|
|
13155
14772
|
*/
|
|
13156
14773
|
constructor(referenceDelegateFactory, serializer) {
|
|
13157
14774
|
this.mutationQueues = {};
|
|
14775
|
+
this.overlays = {};
|
|
13158
14776
|
this.listenSequence = new ListenSequence(0);
|
|
13159
14777
|
this._started = false;
|
|
13160
14778
|
this._started = true;
|
|
@@ -13162,7 +14780,7 @@ class MemoryPersistence {
|
|
|
13162
14780
|
this.targetCache = new MemoryTargetCache(this);
|
|
13163
14781
|
const sizer = (doc) => this.referenceDelegate.documentSize(doc);
|
|
13164
14782
|
this.indexManager = new MemoryIndexManager();
|
|
13165
|
-
this.remoteDocumentCache = newMemoryRemoteDocumentCache(
|
|
14783
|
+
this.remoteDocumentCache = newMemoryRemoteDocumentCache(sizer);
|
|
13166
14784
|
this.serializer = new LocalSerializer(serializer);
|
|
13167
14785
|
this.bundleCache = new MemoryBundleCache(this.serializer);
|
|
13168
14786
|
}
|
|
@@ -13183,13 +14801,23 @@ class MemoryPersistence {
|
|
|
13183
14801
|
setNetworkEnabled() {
|
|
13184
14802
|
// No op.
|
|
13185
14803
|
}
|
|
13186
|
-
getIndexManager() {
|
|
14804
|
+
getIndexManager(user) {
|
|
14805
|
+
// We do not currently support indices for memory persistence, so we can
|
|
14806
|
+
// return the same shared instance of the memory index manager.
|
|
13187
14807
|
return this.indexManager;
|
|
13188
14808
|
}
|
|
13189
|
-
|
|
14809
|
+
getDocumentOverlayCache(user) {
|
|
14810
|
+
let overlay = this.overlays[user.toKey()];
|
|
14811
|
+
if (!overlay) {
|
|
14812
|
+
overlay = new MemoryDocumentOverlayCache();
|
|
14813
|
+
this.overlays[user.toKey()] = overlay;
|
|
14814
|
+
}
|
|
14815
|
+
return overlay;
|
|
14816
|
+
}
|
|
14817
|
+
getMutationQueue(user, indexManager) {
|
|
13190
14818
|
let queue = this.mutationQueues[user.toKey()];
|
|
13191
14819
|
if (!queue) {
|
|
13192
|
-
queue = new MemoryMutationQueue(
|
|
14820
|
+
queue = new MemoryMutationQueue(indexManager, this.referenceDelegate);
|
|
13193
14821
|
this.mutationQueues[user.toKey()] = queue;
|
|
13194
14822
|
}
|
|
13195
14823
|
return queue;
|
|
@@ -13288,7 +14916,7 @@ class MemoryEagerDelegate {
|
|
|
13288
14916
|
const key = DocumentKey.fromPath(path);
|
|
13289
14917
|
return this.isReferenced(txn, key).next(isReferenced => {
|
|
13290
14918
|
if (!isReferenced) {
|
|
13291
|
-
changeBuffer.removeEntry(key);
|
|
14919
|
+
changeBuffer.removeEntry(key, SnapshotVersion.min());
|
|
13292
14920
|
}
|
|
13293
14921
|
});
|
|
13294
14922
|
}).next(() => {
|
|
@@ -14334,7 +15962,7 @@ const LOG_TAG$9 = 'Connection';
|
|
|
14334
15962
|
const X_GOOG_API_CLIENT_VALUE = `gl-node/${process.versions.node} fire/${SDK_VERSION} grpc/${grpcVersion}`;
|
|
14335
15963
|
function createMetadata(databasePath, authToken, appCheckToken, appId) {
|
|
14336
15964
|
hardAssert(authToken === null || authToken.type === 'OAuth');
|
|
14337
|
-
const metadata = new Metadata();
|
|
15965
|
+
const metadata = new grpc.Metadata();
|
|
14338
15966
|
if (authToken) {
|
|
14339
15967
|
authToken.headers.forEach((value, key) => metadata.set(key, value));
|
|
14340
15968
|
}
|
|
@@ -14365,10 +15993,10 @@ class GrpcConnection {
|
|
|
14365
15993
|
ensureActiveStub() {
|
|
14366
15994
|
if (!this.cachedStub) {
|
|
14367
15995
|
logDebug(LOG_TAG$9, 'Creating Firestore stub.');
|
|
14368
|
-
const credentials
|
|
14369
|
-
? credentials.createSsl()
|
|
14370
|
-
: credentials.createInsecure();
|
|
14371
|
-
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials
|
|
15996
|
+
const credentials = this.databaseInfo.ssl
|
|
15997
|
+
? grpc.credentials.createSsl()
|
|
15998
|
+
: grpc.credentials.createInsecure();
|
|
15999
|
+
this.cachedStub = new this.firestore.Firestore(this.databaseInfo.host, credentials);
|
|
14372
16000
|
}
|
|
14373
16001
|
return this.cachedStub;
|
|
14374
16002
|
}
|
|
@@ -17068,6 +18696,40 @@ const nested = {
|
|
|
17068
18696
|
id: 2
|
|
17069
18697
|
}
|
|
17070
18698
|
}
|
|
18699
|
+
},
|
|
18700
|
+
methodSignature: {
|
|
18701
|
+
rule: "repeated",
|
|
18702
|
+
type: "string",
|
|
18703
|
+
id: 1051,
|
|
18704
|
+
extend: "google.protobuf.MethodOptions"
|
|
18705
|
+
},
|
|
18706
|
+
defaultHost: {
|
|
18707
|
+
type: "string",
|
|
18708
|
+
id: 1049,
|
|
18709
|
+
extend: "google.protobuf.ServiceOptions"
|
|
18710
|
+
},
|
|
18711
|
+
oauthScopes: {
|
|
18712
|
+
type: "string",
|
|
18713
|
+
id: 1050,
|
|
18714
|
+
extend: "google.protobuf.ServiceOptions"
|
|
18715
|
+
},
|
|
18716
|
+
fieldBehavior: {
|
|
18717
|
+
rule: "repeated",
|
|
18718
|
+
type: "google.api.FieldBehavior",
|
|
18719
|
+
id: 1052,
|
|
18720
|
+
extend: "google.protobuf.FieldOptions"
|
|
18721
|
+
},
|
|
18722
|
+
FieldBehavior: {
|
|
18723
|
+
values: {
|
|
18724
|
+
FIELD_BEHAVIOR_UNSPECIFIED: 0,
|
|
18725
|
+
OPTIONAL: 1,
|
|
18726
|
+
REQUIRED: 2,
|
|
18727
|
+
OUTPUT_ONLY: 3,
|
|
18728
|
+
INPUT_ONLY: 4,
|
|
18729
|
+
IMMUTABLE: 5,
|
|
18730
|
+
UNORDERED_LIST: 6,
|
|
18731
|
+
NON_EMPTY_DEFAULT: 7
|
|
18732
|
+
}
|
|
17071
18733
|
}
|
|
17072
18734
|
}
|
|
17073
18735
|
},
|
|
@@ -17166,8 +18828,8 @@ const protoLoaderOptions = {
|
|
|
17166
18828
|
* @returns The GrpcObject representing our protos.
|
|
17167
18829
|
*/
|
|
17168
18830
|
function loadProtos() {
|
|
17169
|
-
const packageDefinition = fromJSON(protos$1, protoLoaderOptions);
|
|
17170
|
-
return loadPackageDefinition(packageDefinition);
|
|
18831
|
+
const packageDefinition = protoLoader.fromJSON(protos$1, protoLoaderOptions);
|
|
18832
|
+
return grpc.loadPackageDefinition(packageDefinition);
|
|
17171
18833
|
}
|
|
17172
18834
|
|
|
17173
18835
|
/**
|
|
@@ -20118,12 +21780,12 @@ async function syncEngineListen(syncEngine, query) {
|
|
|
20118
21780
|
}
|
|
20119
21781
|
else {
|
|
20120
21782
|
const targetData = await localStoreAllocateTarget(syncEngineImpl.localStore, queryToTarget(query));
|
|
20121
|
-
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
20122
|
-
targetId = targetData.targetId;
|
|
20123
|
-
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20124
21783
|
if (syncEngineImpl.isPrimaryClient) {
|
|
20125
21784
|
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
|
|
20126
21785
|
}
|
|
21786
|
+
const status = syncEngineImpl.sharedClientState.addLocalQueryTarget(targetData.targetId);
|
|
21787
|
+
targetId = targetData.targetId;
|
|
21788
|
+
viewSnapshot = await initializeViewAndComputeSnapshot(syncEngineImpl, query, targetId, status === 'current');
|
|
20127
21789
|
}
|
|
20128
21790
|
return viewSnapshot;
|
|
20129
21791
|
}
|
|
@@ -20311,6 +21973,9 @@ async function syncEngineRejectListen(syncEngine, targetId, err) {
|
|
|
20311
21973
|
// store to purge a document. However, it would be tricky to keep all of
|
|
20312
21974
|
// the local store's invariants with another method.
|
|
20313
21975
|
let documentUpdates = new SortedMap(DocumentKey.comparator);
|
|
21976
|
+
// TODO(b/217189216): This limbo document should ideally have a read time,
|
|
21977
|
+
// so that it is picked up by any read-time based scans. The backend,
|
|
21978
|
+
// however, does not send a read time for target removals.
|
|
20314
21979
|
documentUpdates = documentUpdates.insert(limboKey, MutableDocument.newNoDocument(limboKey, SnapshotVersion.min()));
|
|
20315
21980
|
const resolvedLimboDocuments = documentKeySet().add(limboKey);
|
|
20316
21981
|
const event = new RemoteEvent(SnapshotVersion.min(),
|
|
@@ -22255,72 +23920,6 @@ function createBundleReader(data, serializer) {
|
|
|
22255
23920
|
return newBundleReader(toByteStreamReader(content), serializer);
|
|
22256
23921
|
}
|
|
22257
23922
|
|
|
22258
|
-
/**
|
|
22259
|
-
* @license
|
|
22260
|
-
* Copyright 2017 Google LLC
|
|
22261
|
-
*
|
|
22262
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
22263
|
-
* you may not use this file except in compliance with the License.
|
|
22264
|
-
* You may obtain a copy of the License at
|
|
22265
|
-
*
|
|
22266
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
22267
|
-
*
|
|
22268
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
22269
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
22270
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22271
|
-
* See the License for the specific language governing permissions and
|
|
22272
|
-
* limitations under the License.
|
|
22273
|
-
*/
|
|
22274
|
-
class DatabaseInfo {
|
|
22275
|
-
/**
|
|
22276
|
-
* Constructs a DatabaseInfo using the provided host, databaseId and
|
|
22277
|
-
* persistenceKey.
|
|
22278
|
-
*
|
|
22279
|
-
* @param databaseId - The database to use.
|
|
22280
|
-
* @param appId - The Firebase App Id.
|
|
22281
|
-
* @param persistenceKey - A unique identifier for this Firestore's local
|
|
22282
|
-
* storage (used in conjunction with the databaseId).
|
|
22283
|
-
* @param host - The Firestore backend host to connect to.
|
|
22284
|
-
* @param ssl - Whether to use SSL when connecting.
|
|
22285
|
-
* @param forceLongPolling - Whether to use the forceLongPolling option
|
|
22286
|
-
* when using WebChannel as the network transport.
|
|
22287
|
-
* @param autoDetectLongPolling - Whether to use the detectBufferingProxy
|
|
22288
|
-
* option when using WebChannel as the network transport.
|
|
22289
|
-
* @param useFetchStreams Whether to use the Fetch API instead of
|
|
22290
|
-
* XMLHTTPRequest
|
|
22291
|
-
*/
|
|
22292
|
-
constructor(databaseId, appId, persistenceKey, host, ssl, forceLongPolling, autoDetectLongPolling, useFetchStreams) {
|
|
22293
|
-
this.databaseId = databaseId;
|
|
22294
|
-
this.appId = appId;
|
|
22295
|
-
this.persistenceKey = persistenceKey;
|
|
22296
|
-
this.host = host;
|
|
22297
|
-
this.ssl = ssl;
|
|
22298
|
-
this.forceLongPolling = forceLongPolling;
|
|
22299
|
-
this.autoDetectLongPolling = autoDetectLongPolling;
|
|
22300
|
-
this.useFetchStreams = useFetchStreams;
|
|
22301
|
-
}
|
|
22302
|
-
}
|
|
22303
|
-
/** The default database name for a project. */
|
|
22304
|
-
const DEFAULT_DATABASE_NAME = '(default)';
|
|
22305
|
-
/**
|
|
22306
|
-
* Represents the database ID a Firestore client is associated with.
|
|
22307
|
-
* @internal
|
|
22308
|
-
*/
|
|
22309
|
-
class DatabaseId {
|
|
22310
|
-
constructor(projectId, database) {
|
|
22311
|
-
this.projectId = projectId;
|
|
22312
|
-
this.database = database ? database : DEFAULT_DATABASE_NAME;
|
|
22313
|
-
}
|
|
22314
|
-
get isDefaultDatabase() {
|
|
22315
|
-
return this.database === DEFAULT_DATABASE_NAME;
|
|
22316
|
-
}
|
|
22317
|
-
isEqual(other) {
|
|
22318
|
-
return (other instanceof DatabaseId &&
|
|
22319
|
-
other.projectId === this.projectId &&
|
|
22320
|
-
other.database === this.database);
|
|
22321
|
-
}
|
|
22322
|
-
}
|
|
22323
|
-
|
|
22324
23923
|
/**
|
|
22325
23924
|
* @license
|
|
22326
23925
|
* Copyright 2020 Google LLC
|
|
@@ -24694,8 +26293,7 @@ class DocumentSnapshot extends DocumentSnapshot$1 {
|
|
|
24694
26293
|
this.metadata = metadata;
|
|
24695
26294
|
}
|
|
24696
26295
|
/**
|
|
24697
|
-
*
|
|
24698
|
-
* exists. True if the document exists.
|
|
26296
|
+
* Returns whether or not the data exists. True if the document exists.
|
|
24699
26297
|
*/
|
|
24700
26298
|
exists() {
|
|
24701
26299
|
return super.exists();
|
|
@@ -25069,51 +26667,53 @@ function limitToLast(limit) {
|
|
|
25069
26667
|
return new QueryLimitConstraint('limitToLast', limit, "L" /* Last */);
|
|
25070
26668
|
}
|
|
25071
26669
|
class QueryStartAtConstraint extends QueryConstraint {
|
|
25072
|
-
constructor(type, _docOrFields,
|
|
26670
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25073
26671
|
super();
|
|
25074
26672
|
this.type = type;
|
|
25075
26673
|
this._docOrFields = _docOrFields;
|
|
25076
|
-
this.
|
|
26674
|
+
this._inclusive = _inclusive;
|
|
25077
26675
|
}
|
|
25078
26676
|
_apply(query) {
|
|
25079
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26677
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25080
26678
|
return new Query(query.firestore, query.converter, queryWithStartAt(query._query, bound));
|
|
25081
26679
|
}
|
|
25082
26680
|
}
|
|
25083
26681
|
function startAt(...docOrFields) {
|
|
25084
|
-
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26682
|
+
return new QueryStartAtConstraint('startAt', docOrFields,
|
|
26683
|
+
/*inclusive=*/ true);
|
|
25085
26684
|
}
|
|
25086
26685
|
function startAfter(...docOrFields) {
|
|
25087
26686
|
return new QueryStartAtConstraint('startAfter', docOrFields,
|
|
25088
|
-
/*
|
|
26687
|
+
/*inclusive=*/ false);
|
|
25089
26688
|
}
|
|
25090
26689
|
class QueryEndAtConstraint extends QueryConstraint {
|
|
25091
|
-
constructor(type, _docOrFields,
|
|
26690
|
+
constructor(type, _docOrFields, _inclusive) {
|
|
25092
26691
|
super();
|
|
25093
26692
|
this.type = type;
|
|
25094
26693
|
this._docOrFields = _docOrFields;
|
|
25095
|
-
this.
|
|
26694
|
+
this._inclusive = _inclusive;
|
|
25096
26695
|
}
|
|
25097
26696
|
_apply(query) {
|
|
25098
|
-
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this.
|
|
26697
|
+
const bound = newQueryBoundFromDocOrFields(query, this.type, this._docOrFields, this._inclusive);
|
|
25099
26698
|
return new Query(query.firestore, query.converter, queryWithEndAt(query._query, bound));
|
|
25100
26699
|
}
|
|
25101
26700
|
}
|
|
25102
26701
|
function endBefore(...docOrFields) {
|
|
25103
|
-
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26702
|
+
return new QueryEndAtConstraint('endBefore', docOrFields,
|
|
26703
|
+
/*inclusive=*/ false);
|
|
25104
26704
|
}
|
|
25105
26705
|
function endAt(...docOrFields) {
|
|
25106
|
-
return new QueryEndAtConstraint('endAt', docOrFields, /*
|
|
26706
|
+
return new QueryEndAtConstraint('endAt', docOrFields, /*inclusive=*/ true);
|
|
25107
26707
|
}
|
|
25108
26708
|
/** Helper function to create a bound from a document or fields */
|
|
25109
|
-
function newQueryBoundFromDocOrFields(query, methodName, docOrFields,
|
|
26709
|
+
function newQueryBoundFromDocOrFields(query, methodName, docOrFields, inclusive) {
|
|
25110
26710
|
docOrFields[0] = getModularInstance(docOrFields[0]);
|
|
25111
26711
|
if (docOrFields[0] instanceof DocumentSnapshot$1) {
|
|
25112
|
-
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document,
|
|
26712
|
+
return newQueryBoundFromDocument(query._query, query.firestore._databaseId, methodName, docOrFields[0]._document, inclusive);
|
|
25113
26713
|
}
|
|
25114
26714
|
else {
|
|
25115
26715
|
const reader = newUserDataReader(query.firestore);
|
|
25116
|
-
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields,
|
|
26716
|
+
return newQueryBoundFromFields(query._query, query.firestore._databaseId, reader, methodName, docOrFields, inclusive);
|
|
25117
26717
|
}
|
|
25118
26718
|
}
|
|
25119
26719
|
function newQueryFilter(query, methodName, dataReader, databaseId, fieldPath, op, value) {
|
|
@@ -25171,7 +26771,7 @@ function newQueryOrderBy(query, fieldPath, direction) {
|
|
|
25171
26771
|
* of the query or if any of the fields in the order by are an uncommitted
|
|
25172
26772
|
* server timestamp.
|
|
25173
26773
|
*/
|
|
25174
|
-
function newQueryBoundFromDocument(query, databaseId, methodName, doc,
|
|
26774
|
+
function newQueryBoundFromDocument(query, databaseId, methodName, doc, inclusive) {
|
|
25175
26775
|
if (!doc) {
|
|
25176
26776
|
throw new FirestoreError(Code.NOT_FOUND, `Can't use a DocumentSnapshot that doesn't exist for ` +
|
|
25177
26777
|
`${methodName}().`);
|
|
@@ -25208,12 +26808,12 @@ function newQueryBoundFromDocument(query, databaseId, methodName, doc, before) {
|
|
|
25208
26808
|
}
|
|
25209
26809
|
}
|
|
25210
26810
|
}
|
|
25211
|
-
return new Bound(components,
|
|
26811
|
+
return new Bound(components, inclusive);
|
|
25212
26812
|
}
|
|
25213
26813
|
/**
|
|
25214
26814
|
* Converts a list of field values to a `Bound` for the given query.
|
|
25215
26815
|
*/
|
|
25216
|
-
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values,
|
|
26816
|
+
function newQueryBoundFromFields(query, databaseId, dataReader, methodName, values, inclusive) {
|
|
25217
26817
|
// Use explicit order by's because it has to match the query the user made
|
|
25218
26818
|
const orderBy = query.explicitOrderBy;
|
|
25219
26819
|
if (values.length > orderBy.length) {
|
|
@@ -25250,7 +26850,7 @@ function newQueryBoundFromFields(query, databaseId, dataReader, methodName, valu
|
|
|
25250
26850
|
components.push(wrapped);
|
|
25251
26851
|
}
|
|
25252
26852
|
}
|
|
25253
|
-
return new Bound(components,
|
|
26853
|
+
return new Bound(components, inclusive);
|
|
25254
26854
|
}
|
|
25255
26855
|
/**
|
|
25256
26856
|
* Parses the given `documentIdValue` into a `ReferenceValue`, throwing
|
|
@@ -26234,17 +27834,6 @@ function writeBatch(firestore) {
|
|
|
26234
27834
|
ensureFirestoreConfigured(firestore);
|
|
26235
27835
|
return new WriteBatch(firestore, mutations => executeWrite(firestore, mutations));
|
|
26236
27836
|
}
|
|
26237
|
-
/** An index component consisting of field path and index type. */
|
|
26238
|
-
class Segment {
|
|
26239
|
-
constructor(
|
|
26240
|
-
/** The field path of the component. */
|
|
26241
|
-
fieldPath,
|
|
26242
|
-
/** The fields sorting order. */
|
|
26243
|
-
kind) {
|
|
26244
|
-
this.fieldPath = fieldPath;
|
|
26245
|
-
this.kind = kind;
|
|
26246
|
-
}
|
|
26247
|
-
}
|
|
26248
27837
|
|
|
26249
27838
|
/**
|
|
26250
27839
|
* @license
|
|
@@ -26268,27 +27857,29 @@ function setIndexConfiguration(firestore, jsonOrConfiguration) {
|
|
|
26268
27857
|
const indexConfiguration = typeof jsonOrConfiguration === 'string'
|
|
26269
27858
|
? tryParseJson(jsonOrConfiguration)
|
|
26270
27859
|
: jsonOrConfiguration;
|
|
27860
|
+
const parsedIndexes = [];
|
|
26271
27861
|
// PORTING NOTE: We don't return an error if the user has not enabled
|
|
26272
27862
|
// persistence since `enableIndexeddbPersistence()` can fail on the Web.
|
|
26273
27863
|
if (Array.isArray(indexConfiguration.indexes)) {
|
|
26274
27864
|
for (const index of indexConfiguration.indexes) {
|
|
26275
|
-
tryGetString(index, 'collectionGroup');
|
|
27865
|
+
const collectionGroup = tryGetString(index, 'collectionGroup');
|
|
26276
27866
|
const segments = [];
|
|
26277
27867
|
if (Array.isArray(index.fields)) {
|
|
26278
27868
|
for (const field of index.fields) {
|
|
26279
27869
|
const fieldPathString = tryGetString(field, 'fieldPath');
|
|
26280
27870
|
const fieldPath = fieldPathFromDotSeparatedString('setIndexConfiguration', fieldPathString);
|
|
26281
27871
|
if (field.arrayConfig === 'CONTAINS') {
|
|
26282
|
-
segments.push(new
|
|
27872
|
+
segments.push(new IndexSegment(fieldPath, 2 /* CONTAINS */));
|
|
26283
27873
|
}
|
|
26284
27874
|
else if (field.order === 'ASCENDING') {
|
|
26285
|
-
segments.push(new
|
|
27875
|
+
segments.push(new IndexSegment(fieldPath, 0 /* ASCENDING */));
|
|
26286
27876
|
}
|
|
26287
27877
|
else if (field.order === 'DESCENDING') {
|
|
26288
|
-
segments.push(new
|
|
27878
|
+
segments.push(new IndexSegment(fieldPath, 1 /* DESCENDING */));
|
|
26289
27879
|
}
|
|
26290
27880
|
}
|
|
26291
27881
|
}
|
|
27882
|
+
parsedIndexes.push(new FieldIndex(FieldIndex.UNKNOWN_ID, collectionGroup, segments, IndexState.empty()));
|
|
26292
27883
|
}
|
|
26293
27884
|
}
|
|
26294
27885
|
// TODO(indexing): Configure indexes
|