@firebase/firestore 3.4.6 → 3.4.7-canary.05dc9d6a0
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 +10 -0
- package/dist/firestore/src/core/bundle.d.ts +2 -2
- package/dist/firestore/src/core/bundle_impl.d.ts +2 -0
- package/dist/firestore/src/core/query.d.ts +7 -0
- package/dist/firestore/src/core/snapshot_version.d.ts +1 -0
- package/dist/firestore/src/core/sync_engine_impl.d.ts +1 -1
- package/dist/firestore/src/local/index_manager.d.ts +3 -2
- package/dist/firestore/src/local/indexeddb_index_manager.d.ts +11 -3
- package/dist/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
- package/dist/firestore/src/local/indexeddb_remote_document_cache.d.ts +7 -15
- package/dist/firestore/src/local/indexeddb_schema.d.ts +23 -15
- package/dist/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
- package/dist/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
- package/dist/firestore/src/local/indexeddb_sentinels.d.ts +31 -19
- package/dist/firestore/src/local/local_documents_view.d.ts +3 -4
- package/dist/firestore/src/local/local_serializer.d.ts +2 -1
- package/dist/firestore/src/local/local_store_impl.d.ts +1 -7
- package/dist/firestore/src/local/memory_index_manager.d.ts +3 -2
- package/dist/firestore/src/local/remote_document_cache.d.ts +13 -4
- package/dist/firestore/src/local/shared_client_state.d.ts +6 -3
- package/dist/firestore/src/local/shared_client_state_schema.d.ts +1 -1
- package/dist/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
- package/dist/firestore/src/local/simple_db.d.ts +2 -0
- package/dist/firestore/src/model/field_index.d.ts +14 -1
- package/dist/firestore/test/unit/local/test_index_manager.d.ts +3 -2
- package/dist/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -5
- package/dist/index.esm2017.js +3531 -3400
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm5.js +4157 -3978
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +589 -362
- package/dist/index.node.cjs.js.map +1 -1
- package/dist/index.node.mjs +589 -362
- package/dist/index.node.mjs.map +1 -1
- package/dist/index.rn.js +3643 -3512
- package/dist/index.rn.js.map +1 -1
- package/dist/internal.d.ts +20 -6
- package/dist/lite/firestore/src/core/bundle.d.ts +2 -2
- package/dist/lite/firestore/src/core/bundle_impl.d.ts +2 -0
- package/dist/lite/firestore/src/core/query.d.ts +7 -0
- package/dist/lite/firestore/src/core/snapshot_version.d.ts +1 -0
- package/dist/lite/firestore/src/core/sync_engine_impl.d.ts +1 -1
- package/dist/lite/firestore/src/local/index_manager.d.ts +3 -2
- package/dist/lite/firestore/src/local/indexeddb_index_manager.d.ts +11 -3
- package/dist/lite/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
- package/dist/lite/firestore/src/local/indexeddb_remote_document_cache.d.ts +7 -15
- package/dist/lite/firestore/src/local/indexeddb_schema.d.ts +23 -15
- package/dist/lite/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
- package/dist/lite/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
- package/dist/lite/firestore/src/local/indexeddb_sentinels.d.ts +31 -19
- package/dist/lite/firestore/src/local/local_documents_view.d.ts +3 -4
- package/dist/lite/firestore/src/local/local_serializer.d.ts +2 -1
- package/dist/lite/firestore/src/local/local_store_impl.d.ts +1 -7
- package/dist/lite/firestore/src/local/memory_index_manager.d.ts +3 -2
- package/dist/lite/firestore/src/local/remote_document_cache.d.ts +13 -4
- package/dist/lite/firestore/src/local/shared_client_state.d.ts +6 -3
- package/dist/lite/firestore/src/local/shared_client_state_schema.d.ts +1 -1
- package/dist/lite/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
- package/dist/lite/firestore/src/local/simple_db.d.ts +2 -0
- package/dist/lite/firestore/src/model/field_index.d.ts +14 -1
- package/dist/lite/firestore/test/unit/local/test_index_manager.d.ts +3 -2
- package/dist/lite/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -5
- package/dist/lite/index.browser.esm2017.js +5 -2
- package/dist/lite/index.browser.esm2017.js.map +1 -1
- package/dist/lite/index.browser.esm5.js +94 -92
- package/dist/lite/index.browser.esm5.js.map +1 -1
- package/dist/lite/index.node.cjs.js +5 -2
- package/dist/lite/index.node.cjs.js.map +1 -1
- package/dist/lite/index.node.mjs +5 -2
- package/dist/lite/index.node.mjs.map +1 -1
- package/dist/lite/index.rn.esm2017.js +5 -2
- package/dist/lite/index.rn.esm2017.js.map +1 -1
- package/dist/lite/internal.d.ts +1 -0
- package/dist/lite/packages/firestore/src/core/bundle.d.ts +2 -2
- package/dist/lite/packages/firestore/src/core/bundle_impl.d.ts +2 -0
- package/dist/lite/packages/firestore/src/core/query.d.ts +7 -0
- package/dist/lite/packages/firestore/src/core/snapshot_version.d.ts +1 -0
- package/dist/lite/packages/firestore/src/core/sync_engine_impl.d.ts +1 -1
- package/dist/lite/packages/firestore/src/local/index_manager.d.ts +3 -2
- package/dist/lite/packages/firestore/src/local/indexeddb_index_manager.d.ts +11 -3
- package/dist/lite/packages/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
- package/dist/lite/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +7 -15
- package/dist/lite/packages/firestore/src/local/indexeddb_schema.d.ts +23 -15
- package/dist/lite/packages/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
- package/dist/lite/packages/firestore/src/local/indexeddb_sentinels.d.ts +31 -19
- package/dist/lite/packages/firestore/src/local/local_documents_view.d.ts +3 -4
- package/dist/lite/packages/firestore/src/local/local_serializer.d.ts +2 -1
- package/dist/lite/packages/firestore/src/local/local_store_impl.d.ts +1 -7
- package/dist/lite/packages/firestore/src/local/memory_index_manager.d.ts +3 -2
- package/dist/lite/packages/firestore/src/local/remote_document_cache.d.ts +13 -4
- package/dist/lite/packages/firestore/src/local/shared_client_state.d.ts +6 -3
- package/dist/lite/packages/firestore/src/local/shared_client_state_schema.d.ts +1 -1
- package/dist/lite/packages/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
- package/dist/lite/packages/firestore/src/local/simple_db.d.ts +2 -0
- package/dist/lite/packages/firestore/src/model/field_index.d.ts +14 -1
- package/dist/lite/packages/firestore/test/unit/local/test_index_manager.d.ts +3 -2
- package/dist/lite/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -5
- package/dist/lite/private.d.ts +1 -0
- package/dist/packages/firestore/dist/index.esm2017.d.ts +180 -180
- package/dist/packages/firestore/src/core/bundle.d.ts +2 -2
- package/dist/packages/firestore/src/core/bundle_impl.d.ts +2 -0
- package/dist/packages/firestore/src/core/query.d.ts +7 -0
- package/dist/packages/firestore/src/core/snapshot_version.d.ts +1 -0
- package/dist/packages/firestore/src/core/sync_engine_impl.d.ts +1 -1
- package/dist/packages/firestore/src/local/index_manager.d.ts +3 -2
- package/dist/packages/firestore/src/local/indexeddb_index_manager.d.ts +11 -3
- package/dist/packages/firestore/src/local/indexeddb_mutation_batch_impl.d.ts +2 -1
- package/dist/packages/firestore/src/local/indexeddb_remote_document_cache.d.ts +7 -15
- package/dist/packages/firestore/src/local/indexeddb_schema.d.ts +23 -15
- package/dist/packages/firestore/src/local/indexeddb_schema_converter.d.ts +1 -0
- package/dist/packages/firestore/src/local/indexeddb_schema_legacy.d.ts +29 -0
- package/dist/packages/firestore/src/local/indexeddb_sentinels.d.ts +31 -19
- package/dist/packages/firestore/src/local/local_documents_view.d.ts +3 -4
- package/dist/packages/firestore/src/local/local_serializer.d.ts +2 -1
- package/dist/packages/firestore/src/local/local_store_impl.d.ts +1 -7
- package/dist/packages/firestore/src/local/memory_index_manager.d.ts +3 -2
- package/dist/packages/firestore/src/local/remote_document_cache.d.ts +13 -4
- package/dist/packages/firestore/src/local/shared_client_state.d.ts +6 -3
- package/dist/packages/firestore/src/local/shared_client_state_schema.d.ts +1 -1
- package/dist/packages/firestore/src/local/shared_client_state_syncer.d.ts +1 -1
- package/dist/packages/firestore/src/local/simple_db.d.ts +2 -0
- package/dist/packages/firestore/src/model/field_index.d.ts +14 -1
- package/dist/packages/firestore/test/unit/local/test_index_manager.d.ts +3 -2
- package/dist/packages/firestore/test/unit/local/test_remote_document_cache.d.ts +3 -5
- package/dist/private.d.ts +20 -6
- package/package.json +9 -9
package/dist/index.node.mjs
CHANGED
|
@@ -8,7 +8,7 @@ import * as grpc from '@grpc/grpc-js';
|
|
|
8
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.7-canary.05dc9d6a0";
|
|
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.10-canary.05dc9d6a0";
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @license
|
|
@@ -1200,6 +1200,24 @@ function decodeResourcePath(path) {
|
|
|
1200
1200
|
return new ResourcePath(segments);
|
|
1201
1201
|
}
|
|
1202
1202
|
|
|
1203
|
+
/**
|
|
1204
|
+
* @license
|
|
1205
|
+
* Copyright 2022 Google LLC
|
|
1206
|
+
*
|
|
1207
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1208
|
+
* you may not use this file except in compliance with the License.
|
|
1209
|
+
* You may obtain a copy of the License at
|
|
1210
|
+
*
|
|
1211
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1212
|
+
*
|
|
1213
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1214
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1215
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1216
|
+
* See the License for the specific language governing permissions and
|
|
1217
|
+
* limitations under the License.
|
|
1218
|
+
*/
|
|
1219
|
+
const DbRemoteDocumentStore$1 = 'remoteDocuments';
|
|
1220
|
+
|
|
1203
1221
|
/**
|
|
1204
1222
|
* @license
|
|
1205
1223
|
* Copyright 2022 Google LLC
|
|
@@ -1270,26 +1288,36 @@ function newDbDocumentMutationKey(userId, path, batchId) {
|
|
|
1270
1288
|
*/
|
|
1271
1289
|
const DbDocumentMutationPlaceholder = {};
|
|
1272
1290
|
const DbDocumentMutationStore = 'documentMutations';
|
|
1273
|
-
const DbRemoteDocumentStore = '
|
|
1291
|
+
const DbRemoteDocumentStore = 'remoteDocumentsV14';
|
|
1274
1292
|
/**
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1278
|
-
* This index is used to provide a changelog for Multi-Tab.
|
|
1293
|
+
* The primary key of the remote documents store, which allows for efficient
|
|
1294
|
+
* access by collection path and read time.
|
|
1279
1295
|
*/
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1296
|
+
const DbRemoteDocumentKeyPath = [
|
|
1297
|
+
'prefixPath',
|
|
1298
|
+
'collectionGroup',
|
|
1299
|
+
'readTime',
|
|
1300
|
+
'documentId'
|
|
1301
|
+
];
|
|
1302
|
+
/** An index that provides access to documents by key. */
|
|
1303
|
+
const DbRemoteDocumentDocumentKeyIndex = 'documentKeyIndex';
|
|
1304
|
+
const DbRemoteDocumentDocumentKeyIndexPath = [
|
|
1305
|
+
'prefixPath',
|
|
1306
|
+
'collectionGroup',
|
|
1307
|
+
'documentId'
|
|
1308
|
+
];
|
|
1282
1309
|
/**
|
|
1283
|
-
* An index that provides access to documents
|
|
1310
|
+
* An index that provides access to documents by collection group and read
|
|
1284
1311
|
* time.
|
|
1285
1312
|
*
|
|
1286
|
-
* This index is used
|
|
1287
|
-
* documents in a collection.
|
|
1313
|
+
* This index is used by the index backfiller.
|
|
1288
1314
|
*/
|
|
1289
|
-
const
|
|
1290
|
-
const
|
|
1291
|
-
'
|
|
1292
|
-
'readTime'
|
|
1315
|
+
const DbRemoteDocumentCollectionGroupIndex = 'collectionGroupIndex';
|
|
1316
|
+
const DbRemoteDocumentCollectionGroupIndexPath = [
|
|
1317
|
+
'collectionGroup',
|
|
1318
|
+
'readTime',
|
|
1319
|
+
'prefixPath',
|
|
1320
|
+
'documentId'
|
|
1293
1321
|
];
|
|
1294
1322
|
const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
|
|
1295
1323
|
const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
|
|
@@ -1365,13 +1393,14 @@ const DbIndexEntryKeyPath = [
|
|
|
1365
1393
|
'uid',
|
|
1366
1394
|
'arrayValue',
|
|
1367
1395
|
'directionalValue',
|
|
1396
|
+
'orderedDocumentKey',
|
|
1368
1397
|
'documentKey'
|
|
1369
1398
|
];
|
|
1370
1399
|
const DbIndexEntryDocumentKeyIndex = 'documentKeyIndex';
|
|
1371
1400
|
const DbIndexEntryDocumentKeyIndexPath = [
|
|
1372
1401
|
'indexId',
|
|
1373
1402
|
'uid',
|
|
1374
|
-
'
|
|
1403
|
+
'orderedDocumentKey'
|
|
1375
1404
|
];
|
|
1376
1405
|
/** Name of the IndexedDb object store. */
|
|
1377
1406
|
const DbDocumentOverlayStore = 'documentOverlays';
|
|
@@ -1397,7 +1426,7 @@ const V1_STORES = [
|
|
|
1397
1426
|
DbMutationQueueStore,
|
|
1398
1427
|
DbMutationBatchStore,
|
|
1399
1428
|
DbDocumentMutationStore,
|
|
1400
|
-
DbRemoteDocumentStore,
|
|
1429
|
+
DbRemoteDocumentStore$1,
|
|
1401
1430
|
DbTargetStore,
|
|
1402
1431
|
DbPrimaryClientStore,
|
|
1403
1432
|
DbTargetGlobalStore,
|
|
@@ -1412,14 +1441,33 @@ const V8_STORES = [...V6_STORES, DbCollectionParentStore];
|
|
|
1412
1441
|
const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
|
|
1413
1442
|
const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
|
|
1414
1443
|
const V13_STORES = [
|
|
1415
|
-
|
|
1444
|
+
DbMutationQueueStore,
|
|
1445
|
+
DbMutationBatchStore,
|
|
1446
|
+
DbDocumentMutationStore,
|
|
1447
|
+
DbRemoteDocumentStore,
|
|
1448
|
+
DbTargetStore,
|
|
1449
|
+
DbPrimaryClientStore,
|
|
1450
|
+
DbTargetGlobalStore,
|
|
1451
|
+
DbTargetDocumentStore,
|
|
1452
|
+
DbClientMetadataStore,
|
|
1453
|
+
DbRemoteDocumentGlobalStore,
|
|
1454
|
+
DbCollectionParentStore,
|
|
1455
|
+
DbBundleStore,
|
|
1456
|
+
DbNamedQueryStore,
|
|
1457
|
+
DbDocumentOverlayStore
|
|
1458
|
+
];
|
|
1459
|
+
const V14_STORES = [
|
|
1460
|
+
...V13_STORES,
|
|
1416
1461
|
DbIndexConfigurationStore,
|
|
1417
1462
|
DbIndexStateStore,
|
|
1418
1463
|
DbIndexEntryStore
|
|
1419
1464
|
];
|
|
1420
1465
|
/** Returns the object stores for the provided schema. */
|
|
1421
1466
|
function getObjectStores(schemaVersion) {
|
|
1422
|
-
if (schemaVersion ===
|
|
1467
|
+
if (schemaVersion === 14) {
|
|
1468
|
+
return V14_STORES;
|
|
1469
|
+
}
|
|
1470
|
+
else if (schemaVersion === 13) {
|
|
1423
1471
|
return V13_STORES;
|
|
1424
1472
|
}
|
|
1425
1473
|
else if (schemaVersion === 12) {
|
|
@@ -2238,9 +2286,7 @@ class SimpleDbStore {
|
|
|
2238
2286
|
cursor.continue(controller.skipToKey);
|
|
2239
2287
|
}
|
|
2240
2288
|
};
|
|
2241
|
-
}).next(() =>
|
|
2242
|
-
return PersistencePromise.waitFor(results);
|
|
2243
|
-
});
|
|
2289
|
+
}).next(() => PersistencePromise.waitFor(results));
|
|
2244
2290
|
}
|
|
2245
2291
|
options(indexOrRange, range) {
|
|
2246
2292
|
let indexName = undefined;
|
|
@@ -2630,6 +2676,9 @@ class SnapshotVersion {
|
|
|
2630
2676
|
static min() {
|
|
2631
2677
|
return new SnapshotVersion(new Timestamp(0, 0));
|
|
2632
2678
|
}
|
|
2679
|
+
static max() {
|
|
2680
|
+
return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
|
|
2681
|
+
}
|
|
2633
2682
|
compareTo(other) {
|
|
2634
2683
|
return this.timestamp._compareTo(other.timestamp);
|
|
2635
2684
|
}
|
|
@@ -4146,6 +4195,17 @@ function fieldIndexGetArraySegment(fieldIndex) {
|
|
|
4146
4195
|
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4147
4196
|
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4148
4197
|
}
|
|
4198
|
+
/**
|
|
4199
|
+
* Returns the order of the document key component for the given index.
|
|
4200
|
+
*
|
|
4201
|
+
* PORTING NOTE: This is only used in the Web IndexedDb implementation.
|
|
4202
|
+
*/
|
|
4203
|
+
function fieldIndexGetKeyOrder(fieldIndex) {
|
|
4204
|
+
const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
|
|
4205
|
+
return directionalSegments.length === 0
|
|
4206
|
+
? 0 /* ASCENDING */
|
|
4207
|
+
: directionalSegments[directionalSegments.length - 1].kind;
|
|
4208
|
+
}
|
|
4149
4209
|
/** Returns a debug representation of the field index */
|
|
4150
4210
|
function fieldIndexToString(fieldIndex) {
|
|
4151
4211
|
return `id=${fieldIndex.indexId}|cg=${fieldIndex.collectionGroup}|f=${fieldIndex.fields.map(f => `${f.fieldPath}:${f.kind}`).join(',')}`;
|
|
@@ -4181,6 +4241,28 @@ class IndexState {
|
|
|
4181
4241
|
return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
|
|
4182
4242
|
}
|
|
4183
4243
|
}
|
|
4244
|
+
/**
|
|
4245
|
+
* Creates an offset that matches all documents with a read time higher than
|
|
4246
|
+
* `readTime`.
|
|
4247
|
+
*/
|
|
4248
|
+
function newIndexOffsetSuccessorFromReadTime(readTime, largestBatchId) {
|
|
4249
|
+
// We want to create an offset that matches all documents with a read time
|
|
4250
|
+
// greater than the provided read time. To do so, we technically need to
|
|
4251
|
+
// create an offset for `(readTime, MAX_DOCUMENT_KEY)`. While we could use
|
|
4252
|
+
// Unicode codepoints to generate MAX_DOCUMENT_KEY, it is much easier to use
|
|
4253
|
+
// `(readTime + 1, DocumentKey.empty())` since `> DocumentKey.empty()` matches
|
|
4254
|
+
// all valid document IDs.
|
|
4255
|
+
const successorSeconds = readTime.toTimestamp().seconds;
|
|
4256
|
+
const successorNanos = readTime.toTimestamp().nanoseconds + 1;
|
|
4257
|
+
const successor = SnapshotVersion.fromTimestamp(successorNanos === 1e9
|
|
4258
|
+
? new Timestamp(successorSeconds + 1, 0)
|
|
4259
|
+
: new Timestamp(successorSeconds, successorNanos));
|
|
4260
|
+
return new IndexOffset(successor, DocumentKey.empty(), largestBatchId);
|
|
4261
|
+
}
|
|
4262
|
+
/** Creates a new offset based on the provided document. */
|
|
4263
|
+
function newIndexOffsetFromDocument(document) {
|
|
4264
|
+
return new IndexOffset(document.readTime, document.key, INITIAL_LARGEST_BATCH_ID);
|
|
4265
|
+
}
|
|
4184
4266
|
/**
|
|
4185
4267
|
* Stores the latest read time, document and batch ID that were processed for an
|
|
4186
4268
|
* index.
|
|
@@ -4205,10 +4287,25 @@ class IndexOffset {
|
|
|
4205
4287
|
this.documentKey = documentKey;
|
|
4206
4288
|
this.largestBatchId = largestBatchId;
|
|
4207
4289
|
}
|
|
4208
|
-
/**
|
|
4290
|
+
/** Returns an offset that sorts before all regular offsets. */
|
|
4209
4291
|
static min() {
|
|
4210
4292
|
return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
|
|
4211
4293
|
}
|
|
4294
|
+
/** Returns an offset that sorts after all regular offsets. */
|
|
4295
|
+
static max() {
|
|
4296
|
+
return new IndexOffset(SnapshotVersion.max(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
function indexOffsetComparator(left, right) {
|
|
4300
|
+
let cmp = left.readTime.compareTo(right.readTime);
|
|
4301
|
+
if (cmp !== 0) {
|
|
4302
|
+
return cmp;
|
|
4303
|
+
}
|
|
4304
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
4305
|
+
if (cmp !== 0) {
|
|
4306
|
+
return cmp;
|
|
4307
|
+
}
|
|
4308
|
+
return primitiveComparator(left.largestBatchId, right.largestBatchId);
|
|
4212
4309
|
}
|
|
4213
4310
|
|
|
4214
4311
|
/**
|
|
@@ -4409,58 +4506,15 @@ function targetGetLowerBound(target, fieldIndex) {
|
|
|
4409
4506
|
// For each segment, retrieve a lower bound if there is a suitable filter or
|
|
4410
4507
|
// startAt.
|
|
4411
4508
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
let filterValue = undefined;
|
|
4417
|
-
let filterInclusive = true;
|
|
4418
|
-
switch (fieldFilter.op) {
|
|
4419
|
-
case "<" /* LESS_THAN */:
|
|
4420
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4421
|
-
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
4422
|
-
break;
|
|
4423
|
-
case "==" /* EQUAL */:
|
|
4424
|
-
case "in" /* IN */:
|
|
4425
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4426
|
-
filterValue = fieldFilter.value;
|
|
4427
|
-
break;
|
|
4428
|
-
case ">" /* GREATER_THAN */:
|
|
4429
|
-
filterValue = fieldFilter.value;
|
|
4430
|
-
filterInclusive = false;
|
|
4431
|
-
break;
|
|
4432
|
-
case "!=" /* NOT_EQUAL */:
|
|
4433
|
-
case "not-in" /* NOT_IN */:
|
|
4434
|
-
filterValue = MIN_VALUE;
|
|
4435
|
-
break;
|
|
4436
|
-
// Remaining filters cannot be used as lower bounds.
|
|
4437
|
-
}
|
|
4438
|
-
if (valuesMax(segmentValue, filterValue) === filterValue) {
|
|
4439
|
-
segmentValue = filterValue;
|
|
4440
|
-
segmentInclusive = filterInclusive;
|
|
4441
|
-
}
|
|
4442
|
-
}
|
|
4443
|
-
// If there is a startAt bound, compare the values against the existing
|
|
4444
|
-
// boundary to see if we can narrow the scope.
|
|
4445
|
-
if (target.startAt !== null) {
|
|
4446
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4447
|
-
const orderBy = target.orderBy[i];
|
|
4448
|
-
if (orderBy.field.isEqual(segment.fieldPath)) {
|
|
4449
|
-
const cursorValue = target.startAt.position[i];
|
|
4450
|
-
if (valuesMax(segmentValue, cursorValue) === cursorValue) {
|
|
4451
|
-
segmentValue = cursorValue;
|
|
4452
|
-
segmentInclusive = target.startAt.inclusive;
|
|
4453
|
-
}
|
|
4454
|
-
break;
|
|
4455
|
-
}
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
if (segmentValue === undefined) {
|
|
4509
|
+
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
4510
|
+
? targetGetLowerBoundForField(target, segment.fieldPath, target.startAt)
|
|
4511
|
+
: targetGetUpperBoundForField(target, segment.fieldPath, target.startAt);
|
|
4512
|
+
if (!segmentBound.value) {
|
|
4459
4513
|
// No lower bound exists
|
|
4460
4514
|
return null;
|
|
4461
4515
|
}
|
|
4462
|
-
values.push(
|
|
4463
|
-
inclusive && (inclusive =
|
|
4516
|
+
values.push(segmentBound.value);
|
|
4517
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
4464
4518
|
}
|
|
4465
4519
|
return new Bound(values, inclusive);
|
|
4466
4520
|
}
|
|
@@ -4475,62 +4529,125 @@ function targetGetUpperBound(target, fieldIndex) {
|
|
|
4475
4529
|
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
4476
4530
|
// endAt.
|
|
4477
4531
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
let filterValue = undefined;
|
|
4483
|
-
let filterInclusive = true;
|
|
4484
|
-
switch (fieldFilter.op) {
|
|
4485
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4486
|
-
case ">" /* GREATER_THAN */:
|
|
4487
|
-
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
4488
|
-
filterInclusive = false;
|
|
4489
|
-
break;
|
|
4490
|
-
case "==" /* EQUAL */:
|
|
4491
|
-
case "in" /* IN */:
|
|
4492
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4493
|
-
filterValue = fieldFilter.value;
|
|
4494
|
-
break;
|
|
4495
|
-
case "<" /* LESS_THAN */:
|
|
4496
|
-
filterValue = fieldFilter.value;
|
|
4497
|
-
filterInclusive = false;
|
|
4498
|
-
break;
|
|
4499
|
-
case "!=" /* NOT_EQUAL */:
|
|
4500
|
-
case "not-in" /* NOT_IN */:
|
|
4501
|
-
filterValue = MAX_VALUE;
|
|
4502
|
-
break;
|
|
4503
|
-
// Remaining filters cannot be used as upper bounds.
|
|
4504
|
-
}
|
|
4505
|
-
if (valuesMin(segmentValue, filterValue) === filterValue) {
|
|
4506
|
-
segmentValue = filterValue;
|
|
4507
|
-
segmentInclusive = filterInclusive;
|
|
4508
|
-
}
|
|
4509
|
-
}
|
|
4510
|
-
// If there is a endAt bound, compare the values against the existing
|
|
4511
|
-
// boundary to see if we can narrow the scope.
|
|
4512
|
-
if (target.endAt !== null) {
|
|
4513
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4514
|
-
const orderBy = target.orderBy[i];
|
|
4515
|
-
if (orderBy.field.isEqual(segment.fieldPath)) {
|
|
4516
|
-
const cursorValue = target.endAt.position[i];
|
|
4517
|
-
if (valuesMin(segmentValue, cursorValue) === cursorValue) {
|
|
4518
|
-
segmentValue = cursorValue;
|
|
4519
|
-
segmentInclusive = target.endAt.inclusive;
|
|
4520
|
-
}
|
|
4521
|
-
break;
|
|
4522
|
-
}
|
|
4523
|
-
}
|
|
4524
|
-
}
|
|
4525
|
-
if (segmentValue === undefined) {
|
|
4532
|
+
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
4533
|
+
? targetGetUpperBoundForField(target, segment.fieldPath, target.endAt)
|
|
4534
|
+
: targetGetLowerBoundForField(target, segment.fieldPath, target.endAt);
|
|
4535
|
+
if (!segmentBound.value) {
|
|
4526
4536
|
// No upper bound exists
|
|
4527
4537
|
return null;
|
|
4528
4538
|
}
|
|
4529
|
-
values.push(
|
|
4530
|
-
inclusive && (inclusive =
|
|
4539
|
+
values.push(segmentBound.value);
|
|
4540
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
4531
4541
|
}
|
|
4532
4542
|
return new Bound(values, inclusive);
|
|
4533
4543
|
}
|
|
4544
|
+
/**
|
|
4545
|
+
* Returns the value to use as the lower bound for ascending index segment at
|
|
4546
|
+
* the provided `fieldPath` (or the upper bound for an descending segment).
|
|
4547
|
+
*/
|
|
4548
|
+
function targetGetLowerBoundForField(target, fieldPath, bound) {
|
|
4549
|
+
let value = undefined;
|
|
4550
|
+
let inclusive = true;
|
|
4551
|
+
// Process all filters to find a value for the current field segment
|
|
4552
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
4553
|
+
let filterValue = undefined;
|
|
4554
|
+
let filterInclusive = true;
|
|
4555
|
+
switch (fieldFilter.op) {
|
|
4556
|
+
case "<" /* LESS_THAN */:
|
|
4557
|
+
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4558
|
+
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
4559
|
+
break;
|
|
4560
|
+
case "==" /* EQUAL */:
|
|
4561
|
+
case "in" /* IN */:
|
|
4562
|
+
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4563
|
+
filterValue = fieldFilter.value;
|
|
4564
|
+
break;
|
|
4565
|
+
case ">" /* GREATER_THAN */:
|
|
4566
|
+
filterValue = fieldFilter.value;
|
|
4567
|
+
filterInclusive = false;
|
|
4568
|
+
break;
|
|
4569
|
+
case "!=" /* NOT_EQUAL */:
|
|
4570
|
+
case "not-in" /* NOT_IN */:
|
|
4571
|
+
filterValue = MIN_VALUE;
|
|
4572
|
+
break;
|
|
4573
|
+
// Remaining filters cannot be used as lower bounds.
|
|
4574
|
+
}
|
|
4575
|
+
if (valuesMax(value, filterValue) === filterValue) {
|
|
4576
|
+
value = filterValue;
|
|
4577
|
+
inclusive = filterInclusive;
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
// If there is an additional bound, compare the values against the existing
|
|
4581
|
+
// range to see if we can narrow the scope.
|
|
4582
|
+
if (bound !== null) {
|
|
4583
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4584
|
+
const orderBy = target.orderBy[i];
|
|
4585
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
4586
|
+
const cursorValue = bound.position[i];
|
|
4587
|
+
if (valuesMax(value, cursorValue) === cursorValue) {
|
|
4588
|
+
value = cursorValue;
|
|
4589
|
+
inclusive = bound.inclusive;
|
|
4590
|
+
}
|
|
4591
|
+
break;
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
return { value, inclusive };
|
|
4596
|
+
}
|
|
4597
|
+
/**
|
|
4598
|
+
* Returns the value to use as the upper bound for ascending index segment at
|
|
4599
|
+
* the provided `fieldPath` (or the lower bound for an descending segment).
|
|
4600
|
+
*/
|
|
4601
|
+
function targetGetUpperBoundForField(target, fieldPath, bound) {
|
|
4602
|
+
let value = undefined;
|
|
4603
|
+
let inclusive = true;
|
|
4604
|
+
// Process all filters to find a value for the current field segment
|
|
4605
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
4606
|
+
let filterValue = undefined;
|
|
4607
|
+
let filterInclusive = true;
|
|
4608
|
+
switch (fieldFilter.op) {
|
|
4609
|
+
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4610
|
+
case ">" /* GREATER_THAN */:
|
|
4611
|
+
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
4612
|
+
filterInclusive = false;
|
|
4613
|
+
break;
|
|
4614
|
+
case "==" /* EQUAL */:
|
|
4615
|
+
case "in" /* IN */:
|
|
4616
|
+
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4617
|
+
filterValue = fieldFilter.value;
|
|
4618
|
+
break;
|
|
4619
|
+
case "<" /* LESS_THAN */:
|
|
4620
|
+
filterValue = fieldFilter.value;
|
|
4621
|
+
filterInclusive = false;
|
|
4622
|
+
break;
|
|
4623
|
+
case "!=" /* NOT_EQUAL */:
|
|
4624
|
+
case "not-in" /* NOT_IN */:
|
|
4625
|
+
filterValue = MAX_VALUE;
|
|
4626
|
+
break;
|
|
4627
|
+
// Remaining filters cannot be used as upper bounds.
|
|
4628
|
+
}
|
|
4629
|
+
if (valuesMin(value, filterValue) === filterValue) {
|
|
4630
|
+
value = filterValue;
|
|
4631
|
+
inclusive = filterInclusive;
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
// If there is an additional bound, compare the values against the existing
|
|
4635
|
+
// range to see if we can narrow the scope.
|
|
4636
|
+
if (bound !== null) {
|
|
4637
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4638
|
+
const orderBy = target.orderBy[i];
|
|
4639
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
4640
|
+
const cursorValue = bound.position[i];
|
|
4641
|
+
if (valuesMin(value, cursorValue) === cursorValue) {
|
|
4642
|
+
value = cursorValue;
|
|
4643
|
+
inclusive = bound.inclusive;
|
|
4644
|
+
}
|
|
4645
|
+
break;
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
return { value, inclusive };
|
|
4650
|
+
}
|
|
4534
4651
|
class Filter {
|
|
4535
4652
|
}
|
|
4536
4653
|
class FieldFilter extends Filter {
|
|
@@ -5101,6 +5218,18 @@ function queryMatchesBounds(query, doc) {
|
|
|
5101
5218
|
}
|
|
5102
5219
|
return true;
|
|
5103
5220
|
}
|
|
5221
|
+
/**
|
|
5222
|
+
* Returns the collection group that this query targets.
|
|
5223
|
+
*
|
|
5224
|
+
* PORTING NOTE: This is only used in the Web SDK to facilitate multi-tab
|
|
5225
|
+
* synchronization for query results.
|
|
5226
|
+
*/
|
|
5227
|
+
function queryCollectionGroup(query) {
|
|
5228
|
+
return (query.collectionGroup ||
|
|
5229
|
+
(query.path.length % 2 === 1
|
|
5230
|
+
? query.path.lastSegment()
|
|
5231
|
+
: query.path.get(query.path.length - 2)));
|
|
5232
|
+
}
|
|
5104
5233
|
/**
|
|
5105
5234
|
* Returns a new comparator function that can be used to compare two documents
|
|
5106
5235
|
* based on the Query's ordering constraint.
|
|
@@ -8634,40 +8763,33 @@ function fromDbRemoteDocument(localSerializer, remoteDoc) {
|
|
|
8634
8763
|
}
|
|
8635
8764
|
/** Encodes a document for storage locally. */
|
|
8636
8765
|
function toDbRemoteDocument(localSerializer, document) {
|
|
8637
|
-
const
|
|
8638
|
-
const
|
|
8766
|
+
const key = document.key;
|
|
8767
|
+
const remoteDoc = {
|
|
8768
|
+
prefixPath: key.getCollectionPath().popLast().toArray(),
|
|
8769
|
+
collectionGroup: key.collectionGroup,
|
|
8770
|
+
documentId: key.path.lastSegment(),
|
|
8771
|
+
readTime: toDbTimestampKey(document.readTime),
|
|
8772
|
+
hasCommittedMutations: document.hasCommittedMutations
|
|
8773
|
+
};
|
|
8639
8774
|
if (document.isFoundDocument()) {
|
|
8640
|
-
|
|
8641
|
-
const hasCommittedMutations = document.hasCommittedMutations;
|
|
8642
|
-
return {
|
|
8643
|
-
document: doc,
|
|
8644
|
-
hasCommittedMutations,
|
|
8645
|
-
readTime,
|
|
8646
|
-
parentPath
|
|
8647
|
-
};
|
|
8775
|
+
remoteDoc.document = toDocument(localSerializer.remoteSerializer, document);
|
|
8648
8776
|
}
|
|
8649
8777
|
else if (document.isNoDocument()) {
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
noDocument: { path, readTime: toDbTimestamp(document.version) },
|
|
8654
|
-
hasCommittedMutations,
|
|
8655
|
-
readTime,
|
|
8656
|
-
parentPath
|
|
8778
|
+
remoteDoc.noDocument = {
|
|
8779
|
+
path: key.path.toArray(),
|
|
8780
|
+
readTime: toDbTimestamp(document.version)
|
|
8657
8781
|
};
|
|
8658
8782
|
}
|
|
8659
8783
|
else if (document.isUnknownDocument()) {
|
|
8660
|
-
|
|
8661
|
-
|
|
8662
|
-
|
|
8663
|
-
hasCommittedMutations: true,
|
|
8664
|
-
readTime,
|
|
8665
|
-
parentPath
|
|
8784
|
+
remoteDoc.unknownDocument = {
|
|
8785
|
+
path: key.path.toArray(),
|
|
8786
|
+
version: toDbTimestamp(document.version)
|
|
8666
8787
|
};
|
|
8667
8788
|
}
|
|
8668
8789
|
else {
|
|
8669
8790
|
return fail();
|
|
8670
8791
|
}
|
|
8792
|
+
return remoteDoc;
|
|
8671
8793
|
}
|
|
8672
8794
|
function toDbTimestampKey(snapshotVersion) {
|
|
8673
8795
|
const timestamp = snapshotVersion.toTimestamp();
|
|
@@ -9929,8 +10051,9 @@ const EMPTY_VALUE = new Uint8Array(0);
|
|
|
9929
10051
|
* data as it supports multi-tab access.
|
|
9930
10052
|
*/
|
|
9931
10053
|
class IndexedDbIndexManager {
|
|
9932
|
-
constructor(user) {
|
|
10054
|
+
constructor(user, databaseId) {
|
|
9933
10055
|
this.user = user;
|
|
10056
|
+
this.databaseId = databaseId;
|
|
9934
10057
|
/**
|
|
9935
10058
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
9936
10059
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
@@ -10026,7 +10149,8 @@ class IndexedDbIndexManager {
|
|
|
10026
10149
|
return PersistencePromise.resolve(null);
|
|
10027
10150
|
}
|
|
10028
10151
|
else {
|
|
10029
|
-
let
|
|
10152
|
+
let existingKeys = documentKeySet();
|
|
10153
|
+
const result = [];
|
|
10030
10154
|
return PersistencePromise.forEach(indexes, (index, subTarget) => {
|
|
10031
10155
|
logDebug(LOG_TAG$f, `Using index ${fieldIndexToString(index)} to execute ${canonifyTarget(target)}`);
|
|
10032
10156
|
const arrayValues = targetGetArrayValues(subTarget, index);
|
|
@@ -10042,7 +10166,11 @@ class IndexedDbIndexManager {
|
|
|
10042
10166
|
.loadFirst(indexRange, target.limit)
|
|
10043
10167
|
.next(entries => {
|
|
10044
10168
|
entries.forEach(entry => {
|
|
10045
|
-
|
|
10169
|
+
const documentKey = DocumentKey.fromSegments(entry.documentKey);
|
|
10170
|
+
if (!existingKeys.has(documentKey)) {
|
|
10171
|
+
existingKeys = existingKeys.add(documentKey);
|
|
10172
|
+
result.push(documentKey);
|
|
10173
|
+
}
|
|
10046
10174
|
});
|
|
10047
10175
|
});
|
|
10048
10176
|
});
|
|
@@ -10141,6 +10269,15 @@ class IndexedDbIndexManager {
|
|
|
10141
10269
|
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
10142
10270
|
return encoder.encodedBytes();
|
|
10143
10271
|
}
|
|
10272
|
+
/**
|
|
10273
|
+
* Returns an encoded form of the document key that sorts based on the key
|
|
10274
|
+
* ordering of the field index.
|
|
10275
|
+
*/
|
|
10276
|
+
encodeDirectionalKey(fieldIndex, documentKey) {
|
|
10277
|
+
const encoder = new IndexByteEncoder();
|
|
10278
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(refValue(this.databaseId, documentKey), encoder.forKind(fieldIndexGetKeyOrder(fieldIndex)));
|
|
10279
|
+
return encoder.encodedBytes();
|
|
10280
|
+
}
|
|
10144
10281
|
/**
|
|
10145
10282
|
* Encodes the given field values according to the specification in `target`.
|
|
10146
10283
|
* For IN queries, a list of possible values is returned.
|
|
@@ -10264,7 +10401,7 @@ class IndexedDbIndexManager {
|
|
|
10264
10401
|
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
10265
10402
|
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10266
10403
|
if (!existingEntries.isEqual(newEntries)) {
|
|
10267
|
-
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10404
|
+
return this.updateEntries(transaction, doc, fieldIndex, existingEntries, newEntries);
|
|
10268
10405
|
}
|
|
10269
10406
|
return PersistencePromise.resolve();
|
|
10270
10407
|
});
|
|
@@ -10272,24 +10409,26 @@ class IndexedDbIndexManager {
|
|
|
10272
10409
|
});
|
|
10273
10410
|
});
|
|
10274
10411
|
}
|
|
10275
|
-
addIndexEntry(transaction, document, indexEntry) {
|
|
10412
|
+
addIndexEntry(transaction, document, fieldIndex, indexEntry) {
|
|
10276
10413
|
const indexEntries = indexEntriesStore(transaction);
|
|
10277
10414
|
return indexEntries.put({
|
|
10278
10415
|
indexId: indexEntry.indexId,
|
|
10279
10416
|
uid: this.uid,
|
|
10280
10417
|
arrayValue: indexEntry.arrayValue,
|
|
10281
10418
|
directionalValue: indexEntry.directionalValue,
|
|
10282
|
-
|
|
10419
|
+
orderedDocumentKey: this.encodeDirectionalKey(fieldIndex, document.key),
|
|
10420
|
+
documentKey: document.key.path.toArray()
|
|
10283
10421
|
});
|
|
10284
10422
|
}
|
|
10285
|
-
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10423
|
+
deleteIndexEntry(transaction, document, fieldIndex, indexEntry) {
|
|
10286
10424
|
const indexEntries = indexEntriesStore(transaction);
|
|
10287
10425
|
return indexEntries.delete([
|
|
10288
10426
|
indexEntry.indexId,
|
|
10289
10427
|
this.uid,
|
|
10290
10428
|
indexEntry.arrayValue,
|
|
10291
10429
|
indexEntry.directionalValue,
|
|
10292
|
-
|
|
10430
|
+
this.encodeDirectionalKey(fieldIndex, document.key),
|
|
10431
|
+
document.key.path.toArray()
|
|
10293
10432
|
]);
|
|
10294
10433
|
}
|
|
10295
10434
|
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
@@ -10301,7 +10440,7 @@ class IndexedDbIndexManager {
|
|
|
10301
10440
|
range: IDBKeyRange.only([
|
|
10302
10441
|
fieldIndex.indexId,
|
|
10303
10442
|
this.uid,
|
|
10304
|
-
|
|
10443
|
+
this.encodeDirectionalKey(fieldIndex, documentKey)
|
|
10305
10444
|
])
|
|
10306
10445
|
}, (_, entry) => {
|
|
10307
10446
|
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
@@ -10334,15 +10473,15 @@ class IndexedDbIndexManager {
|
|
|
10334
10473
|
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10335
10474
|
* entries.
|
|
10336
10475
|
*/
|
|
10337
|
-
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10476
|
+
updateEntries(transaction, document, fieldIndex, existingEntries, newEntries) {
|
|
10338
10477
|
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10339
10478
|
const promises = [];
|
|
10340
10479
|
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10341
10480
|
/* onAdd= */ entry => {
|
|
10342
|
-
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10481
|
+
promises.push(this.addIndexEntry(transaction, document, fieldIndex, entry));
|
|
10343
10482
|
},
|
|
10344
10483
|
/* onRemove= */ entry => {
|
|
10345
|
-
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10484
|
+
promises.push(this.deleteIndexEntry(transaction, document, fieldIndex, entry));
|
|
10346
10485
|
});
|
|
10347
10486
|
return PersistencePromise.waitFor(promises);
|
|
10348
10487
|
}
|
|
@@ -10399,13 +10538,15 @@ class IndexedDbIndexManager {
|
|
|
10399
10538
|
this.uid,
|
|
10400
10539
|
bounds[i].arrayValue,
|
|
10401
10540
|
bounds[i].directionalValue,
|
|
10402
|
-
|
|
10541
|
+
EMPTY_VALUE,
|
|
10542
|
+
[]
|
|
10403
10543
|
], [
|
|
10404
10544
|
bounds[i + 1].indexId,
|
|
10405
10545
|
this.uid,
|
|
10406
10546
|
bounds[i + 1].arrayValue,
|
|
10407
10547
|
bounds[i + 1].directionalValue,
|
|
10408
|
-
|
|
10548
|
+
EMPTY_VALUE,
|
|
10549
|
+
[]
|
|
10409
10550
|
]));
|
|
10410
10551
|
}
|
|
10411
10552
|
return ranges;
|
|
@@ -11819,7 +11960,7 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11819
11960
|
*/
|
|
11820
11961
|
addEntry(transaction, key, doc) {
|
|
11821
11962
|
const documentStore = remoteDocumentsStore(transaction);
|
|
11822
|
-
return documentStore.put(
|
|
11963
|
+
return documentStore.put(doc);
|
|
11823
11964
|
}
|
|
11824
11965
|
/**
|
|
11825
11966
|
* Removes a document from the cache.
|
|
@@ -11827,10 +11968,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11827
11968
|
* All calls of `removeEntry` are required to go through the RemoteDocumentChangeBuffer
|
|
11828
11969
|
* returned by `newChangeBuffer()` to ensure proper accounting of metadata.
|
|
11829
11970
|
*/
|
|
11830
|
-
removeEntry(transaction, documentKey) {
|
|
11971
|
+
removeEntry(transaction, documentKey, readTime) {
|
|
11831
11972
|
const store = remoteDocumentsStore(transaction);
|
|
11832
|
-
|
|
11833
|
-
return store.delete(key);
|
|
11973
|
+
return store.delete(dbReadTimeKey(documentKey, readTime));
|
|
11834
11974
|
}
|
|
11835
11975
|
/**
|
|
11836
11976
|
* Updates the current cache size.
|
|
@@ -11845,11 +11985,15 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11845
11985
|
});
|
|
11846
11986
|
}
|
|
11847
11987
|
getEntry(transaction, documentKey) {
|
|
11988
|
+
let doc = MutableDocument.newInvalidDocument(documentKey);
|
|
11848
11989
|
return remoteDocumentsStore(transaction)
|
|
11849
|
-
.
|
|
11850
|
-
|
|
11851
|
-
|
|
11852
|
-
})
|
|
11990
|
+
.iterate({
|
|
11991
|
+
index: DbRemoteDocumentDocumentKeyIndex,
|
|
11992
|
+
range: IDBKeyRange.only(dbKey(documentKey))
|
|
11993
|
+
}, (_, dbRemoteDoc) => {
|
|
11994
|
+
doc = this.maybeDecodeDocument(documentKey, dbRemoteDoc);
|
|
11995
|
+
})
|
|
11996
|
+
.next(() => doc);
|
|
11853
11997
|
}
|
|
11854
11998
|
/**
|
|
11855
11999
|
* Looks up an entry in the cache.
|
|
@@ -11858,15 +12002,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11858
12002
|
* @returns The cached document entry and its size.
|
|
11859
12003
|
*/
|
|
11860
12004
|
getSizedEntry(transaction, documentKey) {
|
|
12005
|
+
let result = {
|
|
12006
|
+
size: 0,
|
|
12007
|
+
document: MutableDocument.newInvalidDocument(documentKey)
|
|
12008
|
+
};
|
|
11861
12009
|
return remoteDocumentsStore(transaction)
|
|
11862
|
-
.
|
|
11863
|
-
|
|
11864
|
-
|
|
11865
|
-
|
|
11866
|
-
|
|
12010
|
+
.iterate({
|
|
12011
|
+
index: DbRemoteDocumentDocumentKeyIndex,
|
|
12012
|
+
range: IDBKeyRange.only(dbKey(documentKey))
|
|
12013
|
+
}, (_, dbRemoteDoc) => {
|
|
12014
|
+
result = {
|
|
12015
|
+
document: this.maybeDecodeDocument(documentKey, dbRemoteDoc),
|
|
11867
12016
|
size: dbDocumentSize(dbRemoteDoc)
|
|
11868
12017
|
};
|
|
11869
|
-
})
|
|
12018
|
+
})
|
|
12019
|
+
.next(() => result);
|
|
11870
12020
|
}
|
|
11871
12021
|
getEntries(transaction, documentKeys) {
|
|
11872
12022
|
let results = mutableDocumentMap();
|
|
@@ -11897,14 +12047,20 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11897
12047
|
if (documentKeys.isEmpty()) {
|
|
11898
12048
|
return PersistencePromise.resolve();
|
|
11899
12049
|
}
|
|
11900
|
-
|
|
11901
|
-
|
|
12050
|
+
let sortedKeys = new SortedSet(dbKeyComparator);
|
|
12051
|
+
documentKeys.forEach(e => (sortedKeys = sortedKeys.add(e)));
|
|
12052
|
+
const range = IDBKeyRange.bound(dbKey(sortedKeys.first()), dbKey(sortedKeys.last()));
|
|
12053
|
+
const keyIter = sortedKeys.getIterator();
|
|
11902
12054
|
let nextKey = keyIter.getNext();
|
|
11903
12055
|
return remoteDocumentsStore(transaction)
|
|
11904
|
-
.iterate({ range }, (
|
|
11905
|
-
const potentialKey = DocumentKey.fromSegments(
|
|
12056
|
+
.iterate({ index: DbRemoteDocumentDocumentKeyIndex, range }, (_, dbRemoteDoc, control) => {
|
|
12057
|
+
const potentialKey = DocumentKey.fromSegments([
|
|
12058
|
+
...dbRemoteDoc.prefixPath,
|
|
12059
|
+
dbRemoteDoc.collectionGroup,
|
|
12060
|
+
dbRemoteDoc.documentId
|
|
12061
|
+
]);
|
|
11906
12062
|
// Go through keys not found in cache.
|
|
11907
|
-
while (nextKey &&
|
|
12063
|
+
while (nextKey && dbKeyComparator(nextKey, potentialKey) < 0) {
|
|
11908
12064
|
callback(nextKey, null);
|
|
11909
12065
|
nextKey = keyIter.getNext();
|
|
11910
12066
|
}
|
|
@@ -11915,7 +12071,7 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11915
12071
|
}
|
|
11916
12072
|
// Skip to the next key (if there is one).
|
|
11917
12073
|
if (nextKey) {
|
|
11918
|
-
control.skip(nextKey
|
|
12074
|
+
control.skip(dbKey(nextKey));
|
|
11919
12075
|
}
|
|
11920
12076
|
else {
|
|
11921
12077
|
control.done();
|
|
@@ -11930,41 +12086,44 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11930
12086
|
}
|
|
11931
12087
|
});
|
|
11932
12088
|
}
|
|
11933
|
-
|
|
11934
|
-
|
|
11935
|
-
|
|
11936
|
-
|
|
11937
|
-
|
|
11938
|
-
|
|
11939
|
-
|
|
11940
|
-
|
|
11941
|
-
|
|
11942
|
-
|
|
11943
|
-
|
|
11944
|
-
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
11949
|
-
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
11950
|
-
/* open= */ true);
|
|
11951
|
-
iterationOptions.index = DbRemoteDocumentCollectionReadTimeIndex;
|
|
11952
|
-
}
|
|
12089
|
+
getAllFromCollection(transaction, collection, offset) {
|
|
12090
|
+
const startKey = [
|
|
12091
|
+
collection.popLast().toArray(),
|
|
12092
|
+
collection.lastSegment(),
|
|
12093
|
+
toDbTimestampKey(offset.readTime),
|
|
12094
|
+
offset.documentKey.path.isEmpty()
|
|
12095
|
+
? ''
|
|
12096
|
+
: offset.documentKey.path.lastSegment()
|
|
12097
|
+
];
|
|
12098
|
+
const endKey = [
|
|
12099
|
+
collection.popLast().toArray(),
|
|
12100
|
+
collection.lastSegment(),
|
|
12101
|
+
[Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
|
|
12102
|
+
''
|
|
12103
|
+
];
|
|
11953
12104
|
return remoteDocumentsStore(transaction)
|
|
11954
|
-
.
|
|
11955
|
-
|
|
11956
|
-
|
|
11957
|
-
|
|
11958
|
-
|
|
11959
|
-
// more than one segment longer than the query path.
|
|
11960
|
-
if (key.length !== immediateChildrenPathLength) {
|
|
11961
|
-
return;
|
|
11962
|
-
}
|
|
11963
|
-
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
11964
|
-
if (collection.isPrefixOf(document.key.path)) {
|
|
12105
|
+
.loadAll(IDBKeyRange.bound(startKey, endKey, true))
|
|
12106
|
+
.next(dbRemoteDocs => {
|
|
12107
|
+
let results = mutableDocumentMap();
|
|
12108
|
+
for (const dbRemoteDoc of dbRemoteDocs) {
|
|
12109
|
+
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
|
|
11965
12110
|
results = results.insert(document.key, document);
|
|
11966
12111
|
}
|
|
11967
|
-
|
|
12112
|
+
return results;
|
|
12113
|
+
});
|
|
12114
|
+
}
|
|
12115
|
+
getAllFromCollectionGroup(transaction, collectionGroup, offset, limit) {
|
|
12116
|
+
let results = mutableDocumentMap();
|
|
12117
|
+
const startKey = dbCollectionGroupKey(collectionGroup, offset);
|
|
12118
|
+
const endKey = dbCollectionGroupKey(collectionGroup, IndexOffset.max());
|
|
12119
|
+
return remoteDocumentsStore(transaction)
|
|
12120
|
+
.iterate({
|
|
12121
|
+
index: DbRemoteDocumentCollectionGroupIndex,
|
|
12122
|
+
range: IDBKeyRange.bound(startKey, endKey, true)
|
|
12123
|
+
}, (_, dbRemoteDoc, control) => {
|
|
12124
|
+
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
|
|
12125
|
+
results = results.insert(document.key, document);
|
|
12126
|
+
if (results.size === limit) {
|
|
11968
12127
|
control.done();
|
|
11969
12128
|
}
|
|
11970
12129
|
})
|
|
@@ -12008,50 +12167,6 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
12008
12167
|
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
12009
12168
|
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
12010
12169
|
}
|
|
12011
|
-
/**
|
|
12012
|
-
* Returns the set of documents that have changed since the specified read
|
|
12013
|
-
* time.
|
|
12014
|
-
*/
|
|
12015
|
-
// PORTING NOTE: This is only used for multi-tab synchronization.
|
|
12016
|
-
function remoteDocumentCacheGetNewDocumentChanges(remoteDocumentCache, transaction, sinceReadTime) {
|
|
12017
|
-
const remoteDocumentCacheImpl = debugCast(remoteDocumentCache);
|
|
12018
|
-
let changedDocs = mutableDocumentMap();
|
|
12019
|
-
let lastReadTime = toDbTimestampKey(sinceReadTime);
|
|
12020
|
-
const documentsStore = remoteDocumentsStore(transaction);
|
|
12021
|
-
const range = IDBKeyRange.lowerBound(lastReadTime, true);
|
|
12022
|
-
return documentsStore
|
|
12023
|
-
.iterate({ index: DbRemoteDocumentReadTimeIndex, range }, (_, dbRemoteDoc) => {
|
|
12024
|
-
// Unlike `getEntry()` and others, `getNewDocumentChanges()` parses
|
|
12025
|
-
// the documents directly since we want to keep sentinel deletes.
|
|
12026
|
-
const doc = fromDbRemoteDocument(remoteDocumentCacheImpl.serializer, dbRemoteDoc);
|
|
12027
|
-
changedDocs = changedDocs.insert(doc.key, doc);
|
|
12028
|
-
lastReadTime = dbRemoteDoc.readTime;
|
|
12029
|
-
})
|
|
12030
|
-
.next(() => {
|
|
12031
|
-
return {
|
|
12032
|
-
changedDocs,
|
|
12033
|
-
readTime: fromDbTimestampKey(lastReadTime)
|
|
12034
|
-
};
|
|
12035
|
-
});
|
|
12036
|
-
}
|
|
12037
|
-
/**
|
|
12038
|
-
* Returns the read time of the most recently read document in the cache, or
|
|
12039
|
-
* SnapshotVersion.min() if not available.
|
|
12040
|
-
*/
|
|
12041
|
-
// PORTING NOTE: This is only used for multi-tab synchronization.
|
|
12042
|
-
function remoteDocumentCacheGetLastReadTime(transaction) {
|
|
12043
|
-
const documentsStore = remoteDocumentsStore(transaction);
|
|
12044
|
-
// If there are no existing entries, we return SnapshotVersion.min().
|
|
12045
|
-
let readTime = SnapshotVersion.min();
|
|
12046
|
-
return documentsStore
|
|
12047
|
-
.iterate({ index: DbRemoteDocumentReadTimeIndex, reverse: true }, (key, dbRemoteDoc, control) => {
|
|
12048
|
-
if (dbRemoteDoc.readTime) {
|
|
12049
|
-
readTime = fromDbTimestampKey(dbRemoteDoc.readTime);
|
|
12050
|
-
}
|
|
12051
|
-
control.done();
|
|
12052
|
-
})
|
|
12053
|
-
.next(() => readTime);
|
|
12054
|
-
}
|
|
12055
12170
|
/**
|
|
12056
12171
|
* Handles the details of adding and updating documents in the IndexedDbRemoteDocumentCache.
|
|
12057
12172
|
*
|
|
@@ -12069,24 +12184,26 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12069
12184
|
super();
|
|
12070
12185
|
this.documentCache = documentCache;
|
|
12071
12186
|
this.trackRemovals = trackRemovals;
|
|
12072
|
-
// A map of document sizes prior to applying the changes in
|
|
12073
|
-
this
|
|
12187
|
+
// A map of document sizes and read times prior to applying the changes in
|
|
12188
|
+
// this buffer.
|
|
12189
|
+
this.documentStates = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
|
|
12074
12190
|
}
|
|
12075
12191
|
applyChanges(transaction) {
|
|
12076
12192
|
const promises = [];
|
|
12077
12193
|
let sizeDelta = 0;
|
|
12078
12194
|
let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
|
|
12079
12195
|
this.changes.forEach((key, documentChange) => {
|
|
12080
|
-
const
|
|
12196
|
+
const previousDoc = this.documentStates.get(key);
|
|
12197
|
+
promises.push(this.documentCache.removeEntry(transaction, key, previousDoc.readTime));
|
|
12081
12198
|
if (documentChange.isValidDocument()) {
|
|
12082
12199
|
const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
|
|
12083
12200
|
collectionParents = collectionParents.add(key.path.popLast());
|
|
12084
12201
|
const size = dbDocumentSize(doc);
|
|
12085
|
-
sizeDelta += size -
|
|
12202
|
+
sizeDelta += size - previousDoc.size;
|
|
12086
12203
|
promises.push(this.documentCache.addEntry(transaction, key, doc));
|
|
12087
12204
|
}
|
|
12088
12205
|
else {
|
|
12089
|
-
sizeDelta -=
|
|
12206
|
+
sizeDelta -= previousDoc.size;
|
|
12090
12207
|
if (this.trackRemovals) {
|
|
12091
12208
|
// In order to track removals, we store a "sentinel delete" in the
|
|
12092
12209
|
// RemoteDocumentCache. This entry is represented by a NoDocument
|
|
@@ -12095,9 +12212,6 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12095
12212
|
const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
|
|
12096
12213
|
promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
|
|
12097
12214
|
}
|
|
12098
|
-
else {
|
|
12099
|
-
promises.push(this.documentCache.removeEntry(transaction, key));
|
|
12100
|
-
}
|
|
12101
12215
|
}
|
|
12102
12216
|
});
|
|
12103
12217
|
collectionParents.forEach(parent => {
|
|
@@ -12111,7 +12225,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12111
12225
|
return this.documentCache
|
|
12112
12226
|
.getSizedEntry(transaction, documentKey)
|
|
12113
12227
|
.next(getResult => {
|
|
12114
|
-
this.
|
|
12228
|
+
this.documentStates.set(documentKey, {
|
|
12229
|
+
size: getResult.size,
|
|
12230
|
+
readTime: getResult.document.readTime
|
|
12231
|
+
});
|
|
12115
12232
|
return getResult.document;
|
|
12116
12233
|
});
|
|
12117
12234
|
}
|
|
@@ -12125,7 +12242,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12125
12242
|
// keys to `DocumentSizeEntry`s. This is to allow returning the
|
|
12126
12243
|
// `MutableDocumentMap` directly, without a conversion.
|
|
12127
12244
|
sizeMap.forEach((documentKey, size) => {
|
|
12128
|
-
this.
|
|
12245
|
+
this.documentStates.set(documentKey, {
|
|
12246
|
+
size,
|
|
12247
|
+
readTime: documents.get(documentKey).readTime
|
|
12248
|
+
});
|
|
12129
12249
|
});
|
|
12130
12250
|
return documents;
|
|
12131
12251
|
});
|
|
@@ -12140,8 +12260,71 @@ function documentGlobalStore(txn) {
|
|
|
12140
12260
|
function remoteDocumentsStore(txn) {
|
|
12141
12261
|
return getStore(txn, DbRemoteDocumentStore);
|
|
12142
12262
|
}
|
|
12143
|
-
|
|
12144
|
-
|
|
12263
|
+
/**
|
|
12264
|
+
* Returns a key that can be used for document lookups on the
|
|
12265
|
+
* `DbRemoteDocumentDocumentKeyIndex` index.
|
|
12266
|
+
*/
|
|
12267
|
+
function dbKey(documentKey) {
|
|
12268
|
+
const path = documentKey.path.toArray();
|
|
12269
|
+
return [
|
|
12270
|
+
/* prefix path */ path.slice(0, path.length - 2),
|
|
12271
|
+
/* collection id */ path[path.length - 2],
|
|
12272
|
+
/* document id */ path[path.length - 1]
|
|
12273
|
+
];
|
|
12274
|
+
}
|
|
12275
|
+
/**
|
|
12276
|
+
* Returns a key that can be used for document lookups via the primary key of
|
|
12277
|
+
* the DbRemoteDocument object store.
|
|
12278
|
+
*/
|
|
12279
|
+
function dbReadTimeKey(documentKey, readTime) {
|
|
12280
|
+
const path = documentKey.path.toArray();
|
|
12281
|
+
return [
|
|
12282
|
+
/* prefix path */ path.slice(0, path.length - 2),
|
|
12283
|
+
/* collection id */ path[path.length - 2],
|
|
12284
|
+
toDbTimestampKey(readTime),
|
|
12285
|
+
/* document id */ path[path.length - 1]
|
|
12286
|
+
];
|
|
12287
|
+
}
|
|
12288
|
+
/**
|
|
12289
|
+
* Returns a key that can be used for document lookups on the
|
|
12290
|
+
* `DbRemoteDocumentDocumentCollectionGroupIndex` index.
|
|
12291
|
+
*/
|
|
12292
|
+
function dbCollectionGroupKey(collectionGroup, offset) {
|
|
12293
|
+
const path = offset.documentKey.path.toArray();
|
|
12294
|
+
return [
|
|
12295
|
+
/* collection id */ collectionGroup,
|
|
12296
|
+
toDbTimestampKey(offset.readTime),
|
|
12297
|
+
/* prefix path */ path.slice(0, path.length - 2),
|
|
12298
|
+
/* document id */ path.length > 0 ? path[path.length - 1] : ''
|
|
12299
|
+
];
|
|
12300
|
+
}
|
|
12301
|
+
/**
|
|
12302
|
+
* Comparator that compares document keys according to the primary key sorting
|
|
12303
|
+
* used by the `DbRemoteDocumentDocument` store (by prefix path, collection id
|
|
12304
|
+
* and then document ID).
|
|
12305
|
+
*
|
|
12306
|
+
* Visible for testing.
|
|
12307
|
+
*/
|
|
12308
|
+
function dbKeyComparator(l, r) {
|
|
12309
|
+
const left = l.path.toArray();
|
|
12310
|
+
const right = r.path.toArray();
|
|
12311
|
+
// The ordering is based on https://chromium.googlesource.com/chromium/blink/+/fe5c21fef94dae71c1c3344775b8d8a7f7e6d9ec/Source/modules/indexeddb/IDBKey.cpp#74
|
|
12312
|
+
let cmp = 0;
|
|
12313
|
+
for (let i = 0; i < left.length - 2 && i < right.length - 2; ++i) {
|
|
12314
|
+
cmp = primitiveComparator(left[i], right[i]);
|
|
12315
|
+
if (cmp) {
|
|
12316
|
+
return cmp;
|
|
12317
|
+
}
|
|
12318
|
+
}
|
|
12319
|
+
cmp = primitiveComparator(left.length, right.length);
|
|
12320
|
+
if (cmp) {
|
|
12321
|
+
return cmp;
|
|
12322
|
+
}
|
|
12323
|
+
cmp = primitiveComparator(left[left.length - 2], right[right.length - 2]);
|
|
12324
|
+
if (cmp) {
|
|
12325
|
+
return cmp;
|
|
12326
|
+
}
|
|
12327
|
+
return primitiveComparator(left[left.length - 1], right[right.length - 1]);
|
|
12145
12328
|
}
|
|
12146
12329
|
/**
|
|
12147
12330
|
* Schema Version for the Web client:
|
|
@@ -12162,9 +12345,11 @@ function dbKey(docKey) {
|
|
|
12162
12345
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
12163
12346
|
* 11. Add bundles and named_queries for bundle support.
|
|
12164
12347
|
* 12. Add document overlays.
|
|
12165
|
-
* 13.
|
|
12348
|
+
* 13. Rewrite the keys of the remote document cache to allow for efficient
|
|
12349
|
+
* document lookup via `getAll()`.
|
|
12350
|
+
* 14. Add indexing support.
|
|
12166
12351
|
*/
|
|
12167
|
-
const SCHEMA_VERSION =
|
|
12352
|
+
const SCHEMA_VERSION = 13;
|
|
12168
12353
|
|
|
12169
12354
|
/**
|
|
12170
12355
|
* @license
|
|
@@ -12200,7 +12385,7 @@ class SchemaConverter {
|
|
|
12200
12385
|
createPrimaryClientStore(db);
|
|
12201
12386
|
createMutationQueue(db);
|
|
12202
12387
|
createQueryCache(db);
|
|
12203
|
-
|
|
12388
|
+
createLegacyRemoteDocumentCache(db);
|
|
12204
12389
|
}
|
|
12205
12390
|
// Migration 2 to populate the targetGlobal object no longer needed since
|
|
12206
12391
|
// migration 3 unconditionally clears it.
|
|
@@ -12249,7 +12434,8 @@ class SchemaConverter {
|
|
|
12249
12434
|
// to the DbRemoteDocument object store itself. Since the previous change
|
|
12250
12435
|
// log only contained transient data, we can drop its object store.
|
|
12251
12436
|
dropRemoteDocumentChangesStore(db);
|
|
12252
|
-
|
|
12437
|
+
// Note: Schema version 9 used to create a read time index for the
|
|
12438
|
+
// RemoteDocumentCache. This is now done with schema version 13.
|
|
12253
12439
|
});
|
|
12254
12440
|
}
|
|
12255
12441
|
if (fromVersion < 10 && toVersion >= 10) {
|
|
@@ -12267,6 +12453,12 @@ class SchemaConverter {
|
|
|
12267
12453
|
});
|
|
12268
12454
|
}
|
|
12269
12455
|
if (fromVersion < 13 && toVersion >= 13) {
|
|
12456
|
+
p = p
|
|
12457
|
+
.next(() => createRemoteDocumentCache(db))
|
|
12458
|
+
.next(() => this.rewriteRemoteDocumentCache(db, simpleDbTransaction))
|
|
12459
|
+
.next(() => db.deleteObjectStore(DbRemoteDocumentStore$1));
|
|
12460
|
+
}
|
|
12461
|
+
if (fromVersion < 14 && toVersion >= 14) {
|
|
12270
12462
|
p = p.next(() => {
|
|
12271
12463
|
createFieldIndex(db);
|
|
12272
12464
|
});
|
|
@@ -12276,7 +12468,7 @@ class SchemaConverter {
|
|
|
12276
12468
|
addDocumentGlobal(txn) {
|
|
12277
12469
|
let byteSize = 0;
|
|
12278
12470
|
return txn
|
|
12279
|
-
.store(DbRemoteDocumentStore)
|
|
12471
|
+
.store(DbRemoteDocumentStore$1)
|
|
12280
12472
|
.iterate((_, doc) => {
|
|
12281
12473
|
byteSize += dbDocumentSize(doc);
|
|
12282
12474
|
})
|
|
@@ -12311,7 +12503,7 @@ class SchemaConverter {
|
|
|
12311
12503
|
*/
|
|
12312
12504
|
ensureSequenceNumbers(txn) {
|
|
12313
12505
|
const documentTargetStore = txn.store(DbTargetDocumentStore);
|
|
12314
|
-
const documentsStore = txn.store(DbRemoteDocumentStore);
|
|
12506
|
+
const documentsStore = txn.store(DbRemoteDocumentStore$1);
|
|
12315
12507
|
const globalTargetStore = txn.store(DbTargetGlobalStore);
|
|
12316
12508
|
return globalTargetStore.get(DbTargetGlobalKey).next(metadata => {
|
|
12317
12509
|
const writeSentinelKey = (path) => {
|
|
@@ -12358,7 +12550,7 @@ class SchemaConverter {
|
|
|
12358
12550
|
};
|
|
12359
12551
|
// Index existing remote documents.
|
|
12360
12552
|
return txn
|
|
12361
|
-
.store(DbRemoteDocumentStore)
|
|
12553
|
+
.store(DbRemoteDocumentStore$1)
|
|
12362
12554
|
.iterate({ keysOnly: true }, (pathSegments, _) => {
|
|
12363
12555
|
const path = new ResourcePath(pathSegments);
|
|
12364
12556
|
return addEntry(path.popLast());
|
|
@@ -12381,6 +12573,27 @@ class SchemaConverter {
|
|
|
12381
12573
|
return targetStore.put(updatedDbTarget);
|
|
12382
12574
|
});
|
|
12383
12575
|
}
|
|
12576
|
+
rewriteRemoteDocumentCache(db, transaction) {
|
|
12577
|
+
const legacyRemoteDocumentStore = transaction.store(DbRemoteDocumentStore$1);
|
|
12578
|
+
const writes = [];
|
|
12579
|
+
return legacyRemoteDocumentStore
|
|
12580
|
+
.iterate((_, legacyDocument) => {
|
|
12581
|
+
const remoteDocumentStore = transaction.store(DbRemoteDocumentStore);
|
|
12582
|
+
const path = extractKey(legacyDocument).path.toArray();
|
|
12583
|
+
const dbRemoteDocument = {
|
|
12584
|
+
prefixPath: path.slice(0, path.length - 2),
|
|
12585
|
+
collectionGroup: path[path.length - 2],
|
|
12586
|
+
documentId: path[path.length - 1],
|
|
12587
|
+
readTime: legacyDocument.readTime || [0, 0],
|
|
12588
|
+
unknownDocument: legacyDocument.unknownDocument,
|
|
12589
|
+
noDocument: legacyDocument.noDocument,
|
|
12590
|
+
document: legacyDocument.document,
|
|
12591
|
+
hasCommittedMutations: !!legacyDocument.hasCommittedMutations
|
|
12592
|
+
};
|
|
12593
|
+
writes.push(remoteDocumentStore.put(dbRemoteDocument));
|
|
12594
|
+
})
|
|
12595
|
+
.next(() => PersistencePromise.waitFor(writes));
|
|
12596
|
+
}
|
|
12384
12597
|
}
|
|
12385
12598
|
function sentinelKey(path) {
|
|
12386
12599
|
return [0, encodeResourcePath(path)];
|
|
@@ -12417,8 +12630,15 @@ function upgradeMutationBatchSchemaAndMigrateData(db, txn) {
|
|
|
12417
12630
|
return PersistencePromise.waitFor(writeAll);
|
|
12418
12631
|
});
|
|
12419
12632
|
}
|
|
12633
|
+
function createLegacyRemoteDocumentCache(db) {
|
|
12634
|
+
db.createObjectStore(DbRemoteDocumentStore$1);
|
|
12635
|
+
}
|
|
12420
12636
|
function createRemoteDocumentCache(db) {
|
|
12421
|
-
db.createObjectStore(DbRemoteDocumentStore
|
|
12637
|
+
const remoteDocumentStore = db.createObjectStore(DbRemoteDocumentStore, {
|
|
12638
|
+
keyPath: DbRemoteDocumentKeyPath
|
|
12639
|
+
});
|
|
12640
|
+
remoteDocumentStore.createIndex(DbRemoteDocumentDocumentKeyIndex, DbRemoteDocumentDocumentKeyIndexPath);
|
|
12641
|
+
remoteDocumentStore.createIndex(DbRemoteDocumentCollectionGroupIndex, DbRemoteDocumentCollectionGroupIndexPath);
|
|
12422
12642
|
}
|
|
12423
12643
|
function createDocumentGlobalStore(db) {
|
|
12424
12644
|
db.createObjectStore(DbRemoteDocumentGlobalStore);
|
|
@@ -12460,15 +12680,6 @@ function writeEmptyTargetGlobalEntry(txn) {
|
|
|
12460
12680
|
};
|
|
12461
12681
|
return globalStore.put(DbTargetGlobalKey, metadata);
|
|
12462
12682
|
}
|
|
12463
|
-
/**
|
|
12464
|
-
* Creates indices on the RemoteDocuments store used for both multi-tab
|
|
12465
|
-
* and Index-Free queries.
|
|
12466
|
-
*/
|
|
12467
|
-
function createRemoteDocumentReadTimeIndex(txn) {
|
|
12468
|
-
const remoteDocumentStore = txn.objectStore(DbRemoteDocumentStore);
|
|
12469
|
-
remoteDocumentStore.createIndex(DbRemoteDocumentReadTimeIndex, DbRemoteDocumentReadTimeIndexPath, { unique: false });
|
|
12470
|
-
remoteDocumentStore.createIndex(DbRemoteDocumentCollectionReadTimeIndex, DbRemoteDocumentCollectionReadTimeIndexPath, { unique: false });
|
|
12471
|
-
}
|
|
12472
12683
|
function createClientMetadataStore(db) {
|
|
12473
12684
|
db.createObjectStore(DbClientMetadataStore, {
|
|
12474
12685
|
keyPath: DbClientMetadataKeyPath
|
|
@@ -12485,11 +12696,11 @@ function createNamedQueriesStore(db) {
|
|
|
12485
12696
|
});
|
|
12486
12697
|
}
|
|
12487
12698
|
function createFieldIndex(db) {
|
|
12488
|
-
const
|
|
12699
|
+
const indexConfigurationStore = db.createObjectStore(DbIndexConfigurationStore, {
|
|
12489
12700
|
keyPath: DbIndexConfigurationKeyPath,
|
|
12490
12701
|
autoIncrement: true
|
|
12491
12702
|
});
|
|
12492
|
-
|
|
12703
|
+
indexConfigurationStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
|
|
12493
12704
|
const indexStateStore = db.createObjectStore(DbIndexStateStore, {
|
|
12494
12705
|
keyPath: DbIndexStateKeyPath
|
|
12495
12706
|
});
|
|
@@ -12505,6 +12716,20 @@ function createDocumentOverlayStore(db) {
|
|
|
12505
12716
|
});
|
|
12506
12717
|
documentOverlayStore.createIndex(DbDocumentOverlayCollectionPathOverlayIndex, DbDocumentOverlayCollectionPathOverlayIndexPath, { unique: false });
|
|
12507
12718
|
documentOverlayStore.createIndex(DbDocumentOverlayCollectionGroupOverlayIndex, DbDocumentOverlayCollectionGroupOverlayIndexPath, { unique: false });
|
|
12719
|
+
}
|
|
12720
|
+
function extractKey(remoteDoc) {
|
|
12721
|
+
if (remoteDoc.document) {
|
|
12722
|
+
return new DocumentKey(ResourcePath.fromString(remoteDoc.document.name).popFirst(5));
|
|
12723
|
+
}
|
|
12724
|
+
else if (remoteDoc.noDocument) {
|
|
12725
|
+
return DocumentKey.fromSegments(remoteDoc.noDocument.path);
|
|
12726
|
+
}
|
|
12727
|
+
else if (remoteDoc.unknownDocument) {
|
|
12728
|
+
return DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
|
|
12729
|
+
}
|
|
12730
|
+
else {
|
|
12731
|
+
return fail();
|
|
12732
|
+
}
|
|
12508
12733
|
}
|
|
12509
12734
|
|
|
12510
12735
|
/**
|
|
@@ -13000,7 +13225,7 @@ class IndexedDbPersistence {
|
|
|
13000
13225
|
return this.remoteDocumentCache;
|
|
13001
13226
|
}
|
|
13002
13227
|
getIndexManager(user) {
|
|
13003
|
-
return new IndexedDbIndexManager(user);
|
|
13228
|
+
return new IndexedDbIndexManager(user, this.serializer.remoteSerializer.databaseId);
|
|
13004
13229
|
}
|
|
13005
13230
|
getDocumentOverlayCache(user) {
|
|
13006
13231
|
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
@@ -13347,18 +13572,17 @@ class LocalDocumentsView {
|
|
|
13347
13572
|
*
|
|
13348
13573
|
* @param transaction - The persistence transaction.
|
|
13349
13574
|
* @param query - The query to match documents against.
|
|
13350
|
-
* @param
|
|
13351
|
-
* documents that have been read since this snapshot version (exclusive).
|
|
13575
|
+
* @param offset - Read time and key to start scanning by (exclusive).
|
|
13352
13576
|
*/
|
|
13353
|
-
getDocumentsMatchingQuery(transaction, query,
|
|
13577
|
+
getDocumentsMatchingQuery(transaction, query, offset) {
|
|
13354
13578
|
if (isDocumentQuery$1(query)) {
|
|
13355
13579
|
return this.getDocumentsMatchingDocumentQuery(transaction, query.path);
|
|
13356
13580
|
}
|
|
13357
13581
|
else if (isCollectionGroupQuery(query)) {
|
|
13358
|
-
return this.getDocumentsMatchingCollectionGroupQuery(transaction, query,
|
|
13582
|
+
return this.getDocumentsMatchingCollectionGroupQuery(transaction, query, offset);
|
|
13359
13583
|
}
|
|
13360
13584
|
else {
|
|
13361
|
-
return this.getDocumentsMatchingCollectionQuery(transaction, query,
|
|
13585
|
+
return this.getDocumentsMatchingCollectionQuery(transaction, query, offset);
|
|
13362
13586
|
}
|
|
13363
13587
|
}
|
|
13364
13588
|
getDocumentsMatchingDocumentQuery(transaction, docPath) {
|
|
@@ -13371,7 +13595,7 @@ class LocalDocumentsView {
|
|
|
13371
13595
|
return result;
|
|
13372
13596
|
});
|
|
13373
13597
|
}
|
|
13374
|
-
getDocumentsMatchingCollectionGroupQuery(transaction, query,
|
|
13598
|
+
getDocumentsMatchingCollectionGroupQuery(transaction, query, offset) {
|
|
13375
13599
|
const collectionId = query.collectionGroup;
|
|
13376
13600
|
let results = documentMap();
|
|
13377
13601
|
return this.indexManager
|
|
@@ -13381,7 +13605,7 @@ class LocalDocumentsView {
|
|
|
13381
13605
|
// collectionId and aggregate the results.
|
|
13382
13606
|
return PersistencePromise.forEach(parents, (parent) => {
|
|
13383
13607
|
const collectionQuery = asCollectionQueryAtPath(query, parent.child(collectionId));
|
|
13384
|
-
return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery,
|
|
13608
|
+
return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery, offset).next(r => {
|
|
13385
13609
|
r.forEach((key, doc) => {
|
|
13386
13610
|
results = results.insert(key, doc);
|
|
13387
13611
|
});
|
|
@@ -13389,11 +13613,11 @@ class LocalDocumentsView {
|
|
|
13389
13613
|
}).next(() => results);
|
|
13390
13614
|
});
|
|
13391
13615
|
}
|
|
13392
|
-
getDocumentsMatchingCollectionQuery(transaction, query,
|
|
13616
|
+
getDocumentsMatchingCollectionQuery(transaction, query, offset) {
|
|
13393
13617
|
// Query the remote documents and overlay mutations.
|
|
13394
13618
|
let results;
|
|
13395
13619
|
return this.remoteDocumentCache
|
|
13396
|
-
.
|
|
13620
|
+
.getAllFromCollection(transaction, query.path, offset)
|
|
13397
13621
|
.next(queryResults => {
|
|
13398
13622
|
results = queryResults;
|
|
13399
13623
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
@@ -13479,11 +13703,12 @@ class LocalStoreImpl {
|
|
|
13479
13703
|
// TODO(wuandy): Evaluate if TargetId can be part of Target.
|
|
13480
13704
|
this.targetIdByTarget = new ObjectMap(t => canonifyTarget(t), targetEquals);
|
|
13481
13705
|
/**
|
|
13482
|
-
*
|
|
13706
|
+
* A per collection group index of the last read time processed by
|
|
13707
|
+
* `getNewDocumentChanges()`.
|
|
13483
13708
|
*
|
|
13484
13709
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
13485
13710
|
*/
|
|
13486
|
-
this.
|
|
13711
|
+
this.collectionGroupReadTime = new Map();
|
|
13487
13712
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
13488
13713
|
this.targetCache = persistence.getTargetCache();
|
|
13489
13714
|
this.bundleCache = persistence.getBundleCache();
|
|
@@ -14018,6 +14243,7 @@ function localStoreExecuteQuery(localStore, query, usePreviousResults) {
|
|
|
14018
14243
|
? lastLimboFreeSnapshotVersion
|
|
14019
14244
|
: SnapshotVersion.min(), usePreviousResults ? remoteKeys : documentKeySet()))
|
|
14020
14245
|
.next(documents => {
|
|
14246
|
+
setMaxReadTime(localStoreImpl, queryCollectionGroup(query), documents);
|
|
14021
14247
|
return { documents, remoteKeys };
|
|
14022
14248
|
});
|
|
14023
14249
|
});
|
|
@@ -14095,28 +14321,32 @@ function localStoreGetCachedTarget(localStore, targetId) {
|
|
|
14095
14321
|
* since the prior call.
|
|
14096
14322
|
*/
|
|
14097
14323
|
// PORTING NOTE: Multi-Tab only.
|
|
14098
|
-
function localStoreGetNewDocumentChanges(localStore) {
|
|
14324
|
+
function localStoreGetNewDocumentChanges(localStore, collectionGroup) {
|
|
14099
14325
|
const localStoreImpl = debugCast(localStore);
|
|
14326
|
+
// Get the current maximum read time for the collection. This should always
|
|
14327
|
+
// exist, but to reduce the chance for regressions we default to
|
|
14328
|
+
// SnapshotVersion.Min()
|
|
14329
|
+
// TODO(indexing): Consider removing the default value.
|
|
14330
|
+
const readTime = localStoreImpl.collectionGroupReadTime.get(collectionGroup) ||
|
|
14331
|
+
SnapshotVersion.min();
|
|
14100
14332
|
return localStoreImpl.persistence
|
|
14101
|
-
.runTransaction('Get new document changes', 'readonly', txn =>
|
|
14102
|
-
|
|
14103
|
-
|
|
14333
|
+
.runTransaction('Get new document changes', 'readonly', txn => localStoreImpl.remoteDocuments.getAllFromCollectionGroup(txn, collectionGroup, newIndexOffsetSuccessorFromReadTime(readTime, INITIAL_LARGEST_BATCH_ID),
|
|
14334
|
+
/* limit= */ Number.MAX_SAFE_INTEGER))
|
|
14335
|
+
.then(changedDocs => {
|
|
14336
|
+
setMaxReadTime(localStoreImpl, collectionGroup, changedDocs);
|
|
14104
14337
|
return changedDocs;
|
|
14105
14338
|
});
|
|
14106
14339
|
}
|
|
14107
|
-
/**
|
|
14108
|
-
* Reads the newest document change from persistence and moves the internal
|
|
14109
|
-
* synchronization marker forward so that calls to `getNewDocumentChanges()`
|
|
14110
|
-
* only return changes that happened after client initialization.
|
|
14111
|
-
*/
|
|
14340
|
+
/** Sets the collection group's maximum read time from the given documents. */
|
|
14112
14341
|
// PORTING NOTE: Multi-Tab only.
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
.
|
|
14117
|
-
|
|
14118
|
-
|
|
14342
|
+
function setMaxReadTime(localStoreImpl, collectionGroup, changedDocs) {
|
|
14343
|
+
let readTime = SnapshotVersion.min();
|
|
14344
|
+
changedDocs.forEach((_, doc) => {
|
|
14345
|
+
if (doc.readTime.compareTo(readTime) > 0) {
|
|
14346
|
+
readTime = doc.readTime;
|
|
14347
|
+
}
|
|
14119
14348
|
});
|
|
14349
|
+
localStoreImpl.collectionGroupReadTime.set(collectionGroup, readTime);
|
|
14120
14350
|
}
|
|
14121
14351
|
/**
|
|
14122
14352
|
* Creates a new target using the given bundle name, which will be used to
|
|
@@ -14799,7 +15029,7 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
14799
15029
|
});
|
|
14800
15030
|
return PersistencePromise.resolve(results);
|
|
14801
15031
|
}
|
|
14802
|
-
|
|
15032
|
+
getAllFromCollection(transaction, collectionPath, offset) {
|
|
14803
15033
|
let results = mutableDocumentMap();
|
|
14804
15034
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
14805
15035
|
// the documents we need to match the query against.
|
|
@@ -14814,13 +15044,19 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
14814
15044
|
// Exclude entries from subcollections.
|
|
14815
15045
|
continue;
|
|
14816
15046
|
}
|
|
14817
|
-
if (document
|
|
15047
|
+
if (indexOffsetComparator(newIndexOffsetFromDocument(document), offset) <= 0) {
|
|
15048
|
+
// The document sorts before the offset.
|
|
14818
15049
|
continue;
|
|
14819
15050
|
}
|
|
14820
15051
|
results = results.insert(document.key, document.mutableCopy());
|
|
14821
15052
|
}
|
|
14822
15053
|
return PersistencePromise.resolve(results);
|
|
14823
15054
|
}
|
|
15055
|
+
getAllFromCollectionGroup(transaction, collectionGroup, offset, limti) {
|
|
15056
|
+
// This method should only be called from the IndexBackfiller if persistence
|
|
15057
|
+
// is enabled.
|
|
15058
|
+
fail();
|
|
15059
|
+
}
|
|
14824
15060
|
forEachDocumentKey(transaction, f) {
|
|
14825
15061
|
return PersistencePromise.forEach(this.docs, (key) => f(key));
|
|
14826
15062
|
}
|
|
@@ -15273,7 +15509,7 @@ class QueryEngine {
|
|
|
15273
15509
|
}
|
|
15274
15510
|
// Retrieve all results for documents that were updated since the last
|
|
15275
15511
|
// limbo-document free remote snapshot.
|
|
15276
|
-
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, lastLimboFreeSnapshotVersion).next(updatedResults => {
|
|
15512
|
+
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, newIndexOffsetSuccessorFromReadTime(lastLimboFreeSnapshotVersion, INITIAL_LARGEST_BATCH_ID)).next(updatedResults => {
|
|
15277
15513
|
// We merge `previousResults` into `updateResults`, since
|
|
15278
15514
|
// `updateResults` is already a DocumentMap. If a document is
|
|
15279
15515
|
// contained in both lists, then its contents are the same.
|
|
@@ -15335,7 +15571,7 @@ class QueryEngine {
|
|
|
15335
15571
|
if (getLogLevel() <= LogLevel.DEBUG) {
|
|
15336
15572
|
logDebug('QueryEngine', 'Using full collection scan to execute query:', stringifyQuery(query));
|
|
15337
15573
|
}
|
|
15338
|
-
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query,
|
|
15574
|
+
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, IndexOffset.min());
|
|
15339
15575
|
}
|
|
15340
15576
|
}
|
|
15341
15577
|
|
|
@@ -15396,8 +15632,9 @@ function createWebStorageOnlineStateKey(persistenceKey) {
|
|
|
15396
15632
|
// The WebStorage prefix that plays as a event to indicate the remote documents
|
|
15397
15633
|
// might have changed due to some secondary tabs loading a bundle.
|
|
15398
15634
|
// format of the key is:
|
|
15399
|
-
//
|
|
15400
|
-
|
|
15635
|
+
// firestore_bundle_loaded_v2_<persistenceKey>
|
|
15636
|
+
// The version ending with "v2" stores the list of modified collection groups.
|
|
15637
|
+
const BUNDLE_LOADED_KEY_PREFIX = 'firestore_bundle_loaded_v2';
|
|
15401
15638
|
function createBundleLoadedKey(persistenceKey) {
|
|
15402
15639
|
return `${BUNDLE_LOADED_KEY_PREFIX}_${persistenceKey}`;
|
|
15403
15640
|
}
|
|
@@ -15775,8 +16012,8 @@ class WebStorageSharedClientState {
|
|
|
15775
16012
|
setOnlineState(onlineState) {
|
|
15776
16013
|
this.persistOnlineState(onlineState);
|
|
15777
16014
|
}
|
|
15778
|
-
notifyBundleLoaded() {
|
|
15779
|
-
this.persistBundleLoadedState();
|
|
16015
|
+
notifyBundleLoaded(collectionGroups) {
|
|
16016
|
+
this.persistBundleLoadedState(collectionGroups);
|
|
15780
16017
|
}
|
|
15781
16018
|
shutdown() {
|
|
15782
16019
|
if (this.started) {
|
|
@@ -15860,7 +16097,8 @@ class WebStorageSharedClientState {
|
|
|
15860
16097
|
}
|
|
15861
16098
|
}
|
|
15862
16099
|
else if (storageEvent.key === this.bundleLoadedKey) {
|
|
15863
|
-
|
|
16100
|
+
const collectionGroups = this.fromWebStoreBundleLoadedState(storageEvent.newValue);
|
|
16101
|
+
await Promise.all(collectionGroups.map(cg => this.syncEngine.synchronizeWithChangedDocuments(cg)));
|
|
15864
16102
|
}
|
|
15865
16103
|
});
|
|
15866
16104
|
}
|
|
@@ -15892,8 +16130,9 @@ class WebStorageSharedClientState {
|
|
|
15892
16130
|
const targetMetadata = new QueryTargetMetadata(targetId, state, error);
|
|
15893
16131
|
this.setItem(targetKey, targetMetadata.toWebStorageJSON());
|
|
15894
16132
|
}
|
|
15895
|
-
persistBundleLoadedState() {
|
|
15896
|
-
|
|
16133
|
+
persistBundleLoadedState(collectionGroups) {
|
|
16134
|
+
const json = JSON.stringify(Array.from(collectionGroups));
|
|
16135
|
+
this.setItem(this.bundleLoadedKey, json);
|
|
15897
16136
|
}
|
|
15898
16137
|
/**
|
|
15899
16138
|
* Parses a client state key in WebStorage. Returns null if the key does not
|
|
@@ -15937,6 +16176,9 @@ class WebStorageSharedClientState {
|
|
|
15937
16176
|
fromWebStorageOnlineState(value) {
|
|
15938
16177
|
return SharedOnlineState.fromWebStorageEntry(value);
|
|
15939
16178
|
}
|
|
16179
|
+
fromWebStoreBundleLoadedState(value) {
|
|
16180
|
+
return JSON.parse(value);
|
|
16181
|
+
}
|
|
15940
16182
|
async handleMutationBatchEvent(mutationBatch) {
|
|
15941
16183
|
if (mutationBatch.user.uid !== this.currentUser.uid) {
|
|
15942
16184
|
logDebug(LOG_TAG$a, `Ignoring mutation for non-active user ${mutationBatch.user.uid}`);
|
|
@@ -16053,7 +16295,7 @@ class MemorySharedClientState {
|
|
|
16053
16295
|
}
|
|
16054
16296
|
shutdown() { }
|
|
16055
16297
|
writeSequenceNumber(sequenceNumber) { }
|
|
16056
|
-
notifyBundleLoaded() {
|
|
16298
|
+
notifyBundleLoaded(collectionGroups) {
|
|
16057
16299
|
// No op.
|
|
16058
16300
|
}
|
|
16059
16301
|
}
|
|
@@ -21353,29 +21595,6 @@ class LocalViewChanges {
|
|
|
21353
21595
|
}
|
|
21354
21596
|
}
|
|
21355
21597
|
|
|
21356
|
-
/**
|
|
21357
|
-
* @license
|
|
21358
|
-
* Copyright 2020 Google LLC
|
|
21359
|
-
*
|
|
21360
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
21361
|
-
* you may not use this file except in compliance with the License.
|
|
21362
|
-
* You may obtain a copy of the License at
|
|
21363
|
-
*
|
|
21364
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
21365
|
-
*
|
|
21366
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
21367
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21368
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21369
|
-
* See the License for the specific language governing permissions and
|
|
21370
|
-
* limitations under the License.
|
|
21371
|
-
*/
|
|
21372
|
-
class BundleLoadResult {
|
|
21373
|
-
constructor(progress, changedDocs) {
|
|
21374
|
-
this.progress = progress;
|
|
21375
|
-
this.changedDocs = changedDocs;
|
|
21376
|
-
}
|
|
21377
|
-
}
|
|
21378
|
-
|
|
21379
21598
|
/**
|
|
21380
21599
|
* @license
|
|
21381
21600
|
* Copyright 2020 Google LLC
|
|
@@ -21430,6 +21649,8 @@ class BundleLoader {
|
|
|
21430
21649
|
this.queries = [];
|
|
21431
21650
|
/** Batched documents to be saved into storage */
|
|
21432
21651
|
this.documents = [];
|
|
21652
|
+
/** The collection groups affected by this bundle. */
|
|
21653
|
+
this.collectionGroups = new Set();
|
|
21433
21654
|
this.progress = bundleInitialProgress(bundleMetadata);
|
|
21434
21655
|
}
|
|
21435
21656
|
/**
|
|
@@ -21449,6 +21670,8 @@ class BundleLoader {
|
|
|
21449
21670
|
if (!element.payload.documentMetadata.exists) {
|
|
21450
21671
|
++documentsLoaded;
|
|
21451
21672
|
}
|
|
21673
|
+
const path = ResourcePath.fromString(element.payload.documentMetadata.name);
|
|
21674
|
+
this.collectionGroups.add(path.get(path.length - 2));
|
|
21452
21675
|
}
|
|
21453
21676
|
else if (element.payload.document) {
|
|
21454
21677
|
this.documents[this.documents.length - 1].document =
|
|
@@ -21479,13 +21702,17 @@ class BundleLoader {
|
|
|
21479
21702
|
* Update the progress to 'Success' and return the updated progress.
|
|
21480
21703
|
*/
|
|
21481
21704
|
async complete() {
|
|
21482
|
-
const
|
|
21705
|
+
const changedDocs = await localStoreApplyBundledDocuments(this.localStore, new BundleConverterImpl(this.serializer), this.documents, this.bundleMetadata.id);
|
|
21483
21706
|
const queryDocumentMap = this.getQueryDocumentMapping(this.documents);
|
|
21484
21707
|
for (const q of this.queries) {
|
|
21485
21708
|
await localStoreSaveNamedQuery(this.localStore, q, queryDocumentMap.get(q.name));
|
|
21486
21709
|
}
|
|
21487
21710
|
this.progress.taskState = 'Success';
|
|
21488
|
-
return
|
|
21711
|
+
return {
|
|
21712
|
+
progress: this.progress,
|
|
21713
|
+
changedCollectionGroups: this.collectionGroups,
|
|
21714
|
+
changedDocs
|
|
21715
|
+
};
|
|
21489
21716
|
}
|
|
21490
21717
|
}
|
|
21491
21718
|
/**
|
|
@@ -22564,9 +22791,9 @@ async function synchronizeViewAndComputeSnapshot(syncEngine, queryView) {
|
|
|
22564
22791
|
* snapshots if needed.
|
|
22565
22792
|
*/
|
|
22566
22793
|
// PORTING NOTE: Multi-Tab only.
|
|
22567
|
-
async function syncEngineSynchronizeWithChangedDocuments(syncEngine) {
|
|
22794
|
+
async function syncEngineSynchronizeWithChangedDocuments(syncEngine, collectionGroup) {
|
|
22568
22795
|
const syncEngineImpl = debugCast(syncEngine);
|
|
22569
|
-
return localStoreGetNewDocumentChanges(syncEngineImpl.localStore).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
|
|
22796
|
+
return localStoreGetNewDocumentChanges(syncEngineImpl.localStore, collectionGroup).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
|
|
22570
22797
|
}
|
|
22571
22798
|
/** Applies a mutation state to an existing batch. */
|
|
22572
22799
|
// PORTING NOTE: Multi-Tab only.
|
|
@@ -22731,11 +22958,12 @@ async function syncEngineApplyTargetState(syncEngine, targetId, state, error) {
|
|
|
22731
22958
|
logDebug(LOG_TAG$3, 'Ignoring unexpected query state notification.');
|
|
22732
22959
|
return;
|
|
22733
22960
|
}
|
|
22734
|
-
|
|
22961
|
+
const query = syncEngineImpl.queriesByTarget.get(targetId);
|
|
22962
|
+
if (query && query.length > 0) {
|
|
22735
22963
|
switch (state) {
|
|
22736
22964
|
case 'current':
|
|
22737
22965
|
case 'not-current': {
|
|
22738
|
-
const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore);
|
|
22966
|
+
const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore, queryCollectionGroup(query[0]));
|
|
22739
22967
|
const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(targetId, state === 'current');
|
|
22740
22968
|
await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes, synthesizedRemoteEvent);
|
|
22741
22969
|
break;
|
|
@@ -22818,10 +23046,11 @@ function syncEngineEnsureWriteCallbacks(syncEngine) {
|
|
|
22818
23046
|
function syncEngineLoadBundle(syncEngine, bundleReader, task) {
|
|
22819
23047
|
const syncEngineImpl = debugCast(syncEngine);
|
|
22820
23048
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
22821
|
-
loadBundleImpl(syncEngineImpl, bundleReader, task).then(
|
|
22822
|
-
syncEngineImpl.sharedClientState.notifyBundleLoaded();
|
|
23049
|
+
loadBundleImpl(syncEngineImpl, bundleReader, task).then(collectionGroups => {
|
|
23050
|
+
syncEngineImpl.sharedClientState.notifyBundleLoaded(collectionGroups);
|
|
22823
23051
|
});
|
|
22824
23052
|
}
|
|
23053
|
+
/** Loads a bundle and returns the list of affected collection groups. */
|
|
22825
23054
|
async function loadBundleImpl(syncEngine, reader, task) {
|
|
22826
23055
|
try {
|
|
22827
23056
|
const metadata = await reader.getMetadata();
|
|
@@ -22829,7 +23058,7 @@ async function loadBundleImpl(syncEngine, reader, task) {
|
|
|
22829
23058
|
if (skip) {
|
|
22830
23059
|
await reader.close();
|
|
22831
23060
|
task._completeWith(bundleSuccessProgress(metadata));
|
|
22832
|
-
return;
|
|
23061
|
+
return Promise.resolve(new Set());
|
|
22833
23062
|
}
|
|
22834
23063
|
task._updateProgress(bundleInitialProgress(metadata));
|
|
22835
23064
|
const loader = new BundleLoader(metadata, syncEngine.localStore, reader.serializer);
|
|
@@ -22843,18 +23072,17 @@ async function loadBundleImpl(syncEngine, reader, task) {
|
|
|
22843
23072
|
element = await reader.nextElement();
|
|
22844
23073
|
}
|
|
22845
23074
|
const result = await loader.complete();
|
|
22846
|
-
// TODO(b/160876443): This currently raises snapshots with
|
|
22847
|
-
// `fromCache=false` if users already listen to some queries and bundles
|
|
22848
|
-
// has newer version.
|
|
22849
23075
|
await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, result.changedDocs,
|
|
22850
23076
|
/* remoteEvent */ undefined);
|
|
22851
23077
|
// Save metadata, so loading the same bundle will skip.
|
|
22852
23078
|
await localStoreSaveBundle(syncEngine.localStore, metadata);
|
|
22853
23079
|
task._completeWith(result.progress);
|
|
23080
|
+
return Promise.resolve(result.changedCollectionGroups);
|
|
22854
23081
|
}
|
|
22855
23082
|
catch (e) {
|
|
22856
23083
|
logWarn(LOG_TAG$3, `Loading bundle failed with ${e}`);
|
|
22857
23084
|
task._failWith(e);
|
|
23085
|
+
return Promise.resolve(new Set());
|
|
22858
23086
|
}
|
|
22859
23087
|
}
|
|
22860
23088
|
|
|
@@ -22923,7 +23151,6 @@ class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentProvider {
|
|
|
22923
23151
|
}
|
|
22924
23152
|
async initialize(cfg) {
|
|
22925
23153
|
await super.initialize(cfg);
|
|
22926
|
-
await localStoreSynchronizeLastDocumentChangeReadTime(this.localStore);
|
|
22927
23154
|
await this.onlineComponentProvider.initialize(this, cfg);
|
|
22928
23155
|
// Enqueue writes from a previous session
|
|
22929
23156
|
await syncEngineEnsureWriteCallbacks(this.onlineComponentProvider.syncEngine);
|