@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.cjs.js
CHANGED
|
@@ -33,7 +33,7 @@ var grpc__namespace = /*#__PURE__*/_interopNamespace(grpc);
|
|
|
33
33
|
var protoLoader__namespace = /*#__PURE__*/_interopNamespace(protoLoader);
|
|
34
34
|
|
|
35
35
|
const name = "@firebase/firestore";
|
|
36
|
-
const version$1 = "3.4.
|
|
36
|
+
const version$1 = "3.4.7-canary.05dc9d6a0";
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* @license
|
|
@@ -86,7 +86,7 @@ User.GOOGLE_CREDENTIALS = new User('google-credentials-uid');
|
|
|
86
86
|
User.FIRST_PARTY = new User('first-party-uid');
|
|
87
87
|
User.MOCK_USER = new User('mock-user');
|
|
88
88
|
|
|
89
|
-
const version = "9.6.
|
|
89
|
+
const version = "9.6.10-canary.05dc9d6a0";
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* @license
|
|
@@ -1225,6 +1225,24 @@ function decodeResourcePath(path) {
|
|
|
1225
1225
|
return new ResourcePath(segments);
|
|
1226
1226
|
}
|
|
1227
1227
|
|
|
1228
|
+
/**
|
|
1229
|
+
* @license
|
|
1230
|
+
* Copyright 2022 Google LLC
|
|
1231
|
+
*
|
|
1232
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
1233
|
+
* you may not use this file except in compliance with the License.
|
|
1234
|
+
* You may obtain a copy of the License at
|
|
1235
|
+
*
|
|
1236
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
1237
|
+
*
|
|
1238
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
1239
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
1240
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
1241
|
+
* See the License for the specific language governing permissions and
|
|
1242
|
+
* limitations under the License.
|
|
1243
|
+
*/
|
|
1244
|
+
const DbRemoteDocumentStore$1 = 'remoteDocuments';
|
|
1245
|
+
|
|
1228
1246
|
/**
|
|
1229
1247
|
* @license
|
|
1230
1248
|
* Copyright 2022 Google LLC
|
|
@@ -1295,26 +1313,36 @@ function newDbDocumentMutationKey(userId, path, batchId) {
|
|
|
1295
1313
|
*/
|
|
1296
1314
|
const DbDocumentMutationPlaceholder = {};
|
|
1297
1315
|
const DbDocumentMutationStore = 'documentMutations';
|
|
1298
|
-
const DbRemoteDocumentStore = '
|
|
1316
|
+
const DbRemoteDocumentStore = 'remoteDocumentsV14';
|
|
1299
1317
|
/**
|
|
1300
|
-
*
|
|
1301
|
-
*
|
|
1302
|
-
*
|
|
1303
|
-
* This index is used to provide a changelog for Multi-Tab.
|
|
1318
|
+
* The primary key of the remote documents store, which allows for efficient
|
|
1319
|
+
* access by collection path and read time.
|
|
1304
1320
|
*/
|
|
1305
|
-
const
|
|
1306
|
-
|
|
1321
|
+
const DbRemoteDocumentKeyPath = [
|
|
1322
|
+
'prefixPath',
|
|
1323
|
+
'collectionGroup',
|
|
1324
|
+
'readTime',
|
|
1325
|
+
'documentId'
|
|
1326
|
+
];
|
|
1327
|
+
/** An index that provides access to documents by key. */
|
|
1328
|
+
const DbRemoteDocumentDocumentKeyIndex = 'documentKeyIndex';
|
|
1329
|
+
const DbRemoteDocumentDocumentKeyIndexPath = [
|
|
1330
|
+
'prefixPath',
|
|
1331
|
+
'collectionGroup',
|
|
1332
|
+
'documentId'
|
|
1333
|
+
];
|
|
1307
1334
|
/**
|
|
1308
|
-
* An index that provides access to documents
|
|
1335
|
+
* An index that provides access to documents by collection group and read
|
|
1309
1336
|
* time.
|
|
1310
1337
|
*
|
|
1311
|
-
* This index is used
|
|
1312
|
-
* documents in a collection.
|
|
1338
|
+
* This index is used by the index backfiller.
|
|
1313
1339
|
*/
|
|
1314
|
-
const
|
|
1315
|
-
const
|
|
1316
|
-
'
|
|
1317
|
-
'readTime'
|
|
1340
|
+
const DbRemoteDocumentCollectionGroupIndex = 'collectionGroupIndex';
|
|
1341
|
+
const DbRemoteDocumentCollectionGroupIndexPath = [
|
|
1342
|
+
'collectionGroup',
|
|
1343
|
+
'readTime',
|
|
1344
|
+
'prefixPath',
|
|
1345
|
+
'documentId'
|
|
1318
1346
|
];
|
|
1319
1347
|
const DbRemoteDocumentGlobalStore = 'remoteDocumentGlobal';
|
|
1320
1348
|
const DbRemoteDocumentGlobalKey = 'remoteDocumentGlobalKey';
|
|
@@ -1390,13 +1418,14 @@ const DbIndexEntryKeyPath = [
|
|
|
1390
1418
|
'uid',
|
|
1391
1419
|
'arrayValue',
|
|
1392
1420
|
'directionalValue',
|
|
1421
|
+
'orderedDocumentKey',
|
|
1393
1422
|
'documentKey'
|
|
1394
1423
|
];
|
|
1395
1424
|
const DbIndexEntryDocumentKeyIndex = 'documentKeyIndex';
|
|
1396
1425
|
const DbIndexEntryDocumentKeyIndexPath = [
|
|
1397
1426
|
'indexId',
|
|
1398
1427
|
'uid',
|
|
1399
|
-
'
|
|
1428
|
+
'orderedDocumentKey'
|
|
1400
1429
|
];
|
|
1401
1430
|
/** Name of the IndexedDb object store. */
|
|
1402
1431
|
const DbDocumentOverlayStore = 'documentOverlays';
|
|
@@ -1422,7 +1451,7 @@ const V1_STORES = [
|
|
|
1422
1451
|
DbMutationQueueStore,
|
|
1423
1452
|
DbMutationBatchStore,
|
|
1424
1453
|
DbDocumentMutationStore,
|
|
1425
|
-
DbRemoteDocumentStore,
|
|
1454
|
+
DbRemoteDocumentStore$1,
|
|
1426
1455
|
DbTargetStore,
|
|
1427
1456
|
DbPrimaryClientStore,
|
|
1428
1457
|
DbTargetGlobalStore,
|
|
@@ -1437,14 +1466,33 @@ const V8_STORES = [...V6_STORES, DbCollectionParentStore];
|
|
|
1437
1466
|
const V11_STORES = [...V8_STORES, DbBundleStore, DbNamedQueryStore];
|
|
1438
1467
|
const V12_STORES = [...V11_STORES, DbDocumentOverlayStore];
|
|
1439
1468
|
const V13_STORES = [
|
|
1440
|
-
|
|
1469
|
+
DbMutationQueueStore,
|
|
1470
|
+
DbMutationBatchStore,
|
|
1471
|
+
DbDocumentMutationStore,
|
|
1472
|
+
DbRemoteDocumentStore,
|
|
1473
|
+
DbTargetStore,
|
|
1474
|
+
DbPrimaryClientStore,
|
|
1475
|
+
DbTargetGlobalStore,
|
|
1476
|
+
DbTargetDocumentStore,
|
|
1477
|
+
DbClientMetadataStore,
|
|
1478
|
+
DbRemoteDocumentGlobalStore,
|
|
1479
|
+
DbCollectionParentStore,
|
|
1480
|
+
DbBundleStore,
|
|
1481
|
+
DbNamedQueryStore,
|
|
1482
|
+
DbDocumentOverlayStore
|
|
1483
|
+
];
|
|
1484
|
+
const V14_STORES = [
|
|
1485
|
+
...V13_STORES,
|
|
1441
1486
|
DbIndexConfigurationStore,
|
|
1442
1487
|
DbIndexStateStore,
|
|
1443
1488
|
DbIndexEntryStore
|
|
1444
1489
|
];
|
|
1445
1490
|
/** Returns the object stores for the provided schema. */
|
|
1446
1491
|
function getObjectStores(schemaVersion) {
|
|
1447
|
-
if (schemaVersion ===
|
|
1492
|
+
if (schemaVersion === 14) {
|
|
1493
|
+
return V14_STORES;
|
|
1494
|
+
}
|
|
1495
|
+
else if (schemaVersion === 13) {
|
|
1448
1496
|
return V13_STORES;
|
|
1449
1497
|
}
|
|
1450
1498
|
else if (schemaVersion === 12) {
|
|
@@ -2263,9 +2311,7 @@ class SimpleDbStore {
|
|
|
2263
2311
|
cursor.continue(controller.skipToKey);
|
|
2264
2312
|
}
|
|
2265
2313
|
};
|
|
2266
|
-
}).next(() =>
|
|
2267
|
-
return PersistencePromise.waitFor(results);
|
|
2268
|
-
});
|
|
2314
|
+
}).next(() => PersistencePromise.waitFor(results));
|
|
2269
2315
|
}
|
|
2270
2316
|
options(indexOrRange, range) {
|
|
2271
2317
|
let indexName = undefined;
|
|
@@ -2655,6 +2701,9 @@ class SnapshotVersion {
|
|
|
2655
2701
|
static min() {
|
|
2656
2702
|
return new SnapshotVersion(new Timestamp(0, 0));
|
|
2657
2703
|
}
|
|
2704
|
+
static max() {
|
|
2705
|
+
return new SnapshotVersion(new Timestamp(253402300799, 1e9 - 1));
|
|
2706
|
+
}
|
|
2658
2707
|
compareTo(other) {
|
|
2659
2708
|
return this.timestamp._compareTo(other.timestamp);
|
|
2660
2709
|
}
|
|
@@ -4171,6 +4220,17 @@ function fieldIndexGetArraySegment(fieldIndex) {
|
|
|
4171
4220
|
function fieldIndexGetDirectionalSegments(fieldIndex) {
|
|
4172
4221
|
return fieldIndex.fields.filter(s => s.kind !== 2 /* CONTAINS */);
|
|
4173
4222
|
}
|
|
4223
|
+
/**
|
|
4224
|
+
* Returns the order of the document key component for the given index.
|
|
4225
|
+
*
|
|
4226
|
+
* PORTING NOTE: This is only used in the Web IndexedDb implementation.
|
|
4227
|
+
*/
|
|
4228
|
+
function fieldIndexGetKeyOrder(fieldIndex) {
|
|
4229
|
+
const directionalSegments = fieldIndexGetDirectionalSegments(fieldIndex);
|
|
4230
|
+
return directionalSegments.length === 0
|
|
4231
|
+
? 0 /* ASCENDING */
|
|
4232
|
+
: directionalSegments[directionalSegments.length - 1].kind;
|
|
4233
|
+
}
|
|
4174
4234
|
/** Returns a debug representation of the field index */
|
|
4175
4235
|
function fieldIndexToString(fieldIndex) {
|
|
4176
4236
|
return `id=${fieldIndex.indexId}|cg=${fieldIndex.collectionGroup}|f=${fieldIndex.fields.map(f => `${f.fieldPath}:${f.kind}`).join(',')}`;
|
|
@@ -4206,6 +4266,28 @@ class IndexState {
|
|
|
4206
4266
|
return new IndexState(INITIAL_SEQUENCE_NUMBER, IndexOffset.min());
|
|
4207
4267
|
}
|
|
4208
4268
|
}
|
|
4269
|
+
/**
|
|
4270
|
+
* Creates an offset that matches all documents with a read time higher than
|
|
4271
|
+
* `readTime`.
|
|
4272
|
+
*/
|
|
4273
|
+
function newIndexOffsetSuccessorFromReadTime(readTime, largestBatchId) {
|
|
4274
|
+
// We want to create an offset that matches all documents with a read time
|
|
4275
|
+
// greater than the provided read time. To do so, we technically need to
|
|
4276
|
+
// create an offset for `(readTime, MAX_DOCUMENT_KEY)`. While we could use
|
|
4277
|
+
// Unicode codepoints to generate MAX_DOCUMENT_KEY, it is much easier to use
|
|
4278
|
+
// `(readTime + 1, DocumentKey.empty())` since `> DocumentKey.empty()` matches
|
|
4279
|
+
// all valid document IDs.
|
|
4280
|
+
const successorSeconds = readTime.toTimestamp().seconds;
|
|
4281
|
+
const successorNanos = readTime.toTimestamp().nanoseconds + 1;
|
|
4282
|
+
const successor = SnapshotVersion.fromTimestamp(successorNanos === 1e9
|
|
4283
|
+
? new Timestamp(successorSeconds + 1, 0)
|
|
4284
|
+
: new Timestamp(successorSeconds, successorNanos));
|
|
4285
|
+
return new IndexOffset(successor, DocumentKey.empty(), largestBatchId);
|
|
4286
|
+
}
|
|
4287
|
+
/** Creates a new offset based on the provided document. */
|
|
4288
|
+
function newIndexOffsetFromDocument(document) {
|
|
4289
|
+
return new IndexOffset(document.readTime, document.key, INITIAL_LARGEST_BATCH_ID);
|
|
4290
|
+
}
|
|
4209
4291
|
/**
|
|
4210
4292
|
* Stores the latest read time, document and batch ID that were processed for an
|
|
4211
4293
|
* index.
|
|
@@ -4230,10 +4312,25 @@ class IndexOffset {
|
|
|
4230
4312
|
this.documentKey = documentKey;
|
|
4231
4313
|
this.largestBatchId = largestBatchId;
|
|
4232
4314
|
}
|
|
4233
|
-
/**
|
|
4315
|
+
/** Returns an offset that sorts before all regular offsets. */
|
|
4234
4316
|
static min() {
|
|
4235
4317
|
return new IndexOffset(SnapshotVersion.min(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
|
|
4236
4318
|
}
|
|
4319
|
+
/** Returns an offset that sorts after all regular offsets. */
|
|
4320
|
+
static max() {
|
|
4321
|
+
return new IndexOffset(SnapshotVersion.max(), DocumentKey.empty(), INITIAL_LARGEST_BATCH_ID);
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4324
|
+
function indexOffsetComparator(left, right) {
|
|
4325
|
+
let cmp = left.readTime.compareTo(right.readTime);
|
|
4326
|
+
if (cmp !== 0) {
|
|
4327
|
+
return cmp;
|
|
4328
|
+
}
|
|
4329
|
+
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
|
|
4330
|
+
if (cmp !== 0) {
|
|
4331
|
+
return cmp;
|
|
4332
|
+
}
|
|
4333
|
+
return primitiveComparator(left.largestBatchId, right.largestBatchId);
|
|
4237
4334
|
}
|
|
4238
4335
|
|
|
4239
4336
|
/**
|
|
@@ -4434,58 +4531,15 @@ function targetGetLowerBound(target, fieldIndex) {
|
|
|
4434
4531
|
// For each segment, retrieve a lower bound if there is a suitable filter or
|
|
4435
4532
|
// startAt.
|
|
4436
4533
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
let filterValue = undefined;
|
|
4442
|
-
let filterInclusive = true;
|
|
4443
|
-
switch (fieldFilter.op) {
|
|
4444
|
-
case "<" /* LESS_THAN */:
|
|
4445
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4446
|
-
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
4447
|
-
break;
|
|
4448
|
-
case "==" /* EQUAL */:
|
|
4449
|
-
case "in" /* IN */:
|
|
4450
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4451
|
-
filterValue = fieldFilter.value;
|
|
4452
|
-
break;
|
|
4453
|
-
case ">" /* GREATER_THAN */:
|
|
4454
|
-
filterValue = fieldFilter.value;
|
|
4455
|
-
filterInclusive = false;
|
|
4456
|
-
break;
|
|
4457
|
-
case "!=" /* NOT_EQUAL */:
|
|
4458
|
-
case "not-in" /* NOT_IN */:
|
|
4459
|
-
filterValue = MIN_VALUE;
|
|
4460
|
-
break;
|
|
4461
|
-
// Remaining filters cannot be used as lower bounds.
|
|
4462
|
-
}
|
|
4463
|
-
if (valuesMax(segmentValue, filterValue) === filterValue) {
|
|
4464
|
-
segmentValue = filterValue;
|
|
4465
|
-
segmentInclusive = filterInclusive;
|
|
4466
|
-
}
|
|
4467
|
-
}
|
|
4468
|
-
// If there is a startAt bound, compare the values against the existing
|
|
4469
|
-
// boundary to see if we can narrow the scope.
|
|
4470
|
-
if (target.startAt !== null) {
|
|
4471
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4472
|
-
const orderBy = target.orderBy[i];
|
|
4473
|
-
if (orderBy.field.isEqual(segment.fieldPath)) {
|
|
4474
|
-
const cursorValue = target.startAt.position[i];
|
|
4475
|
-
if (valuesMax(segmentValue, cursorValue) === cursorValue) {
|
|
4476
|
-
segmentValue = cursorValue;
|
|
4477
|
-
segmentInclusive = target.startAt.inclusive;
|
|
4478
|
-
}
|
|
4479
|
-
break;
|
|
4480
|
-
}
|
|
4481
|
-
}
|
|
4482
|
-
}
|
|
4483
|
-
if (segmentValue === undefined) {
|
|
4534
|
+
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
4535
|
+
? targetGetLowerBoundForField(target, segment.fieldPath, target.startAt)
|
|
4536
|
+
: targetGetUpperBoundForField(target, segment.fieldPath, target.startAt);
|
|
4537
|
+
if (!segmentBound.value) {
|
|
4484
4538
|
// No lower bound exists
|
|
4485
4539
|
return null;
|
|
4486
4540
|
}
|
|
4487
|
-
values.push(
|
|
4488
|
-
inclusive && (inclusive =
|
|
4541
|
+
values.push(segmentBound.value);
|
|
4542
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
4489
4543
|
}
|
|
4490
4544
|
return new Bound(values, inclusive);
|
|
4491
4545
|
}
|
|
@@ -4500,62 +4554,125 @@ function targetGetUpperBound(target, fieldIndex) {
|
|
|
4500
4554
|
// For each segment, retrieve an upper bound if there is a suitable filter or
|
|
4501
4555
|
// endAt.
|
|
4502
4556
|
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
let filterValue = undefined;
|
|
4508
|
-
let filterInclusive = true;
|
|
4509
|
-
switch (fieldFilter.op) {
|
|
4510
|
-
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4511
|
-
case ">" /* GREATER_THAN */:
|
|
4512
|
-
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
4513
|
-
filterInclusive = false;
|
|
4514
|
-
break;
|
|
4515
|
-
case "==" /* EQUAL */:
|
|
4516
|
-
case "in" /* IN */:
|
|
4517
|
-
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4518
|
-
filterValue = fieldFilter.value;
|
|
4519
|
-
break;
|
|
4520
|
-
case "<" /* LESS_THAN */:
|
|
4521
|
-
filterValue = fieldFilter.value;
|
|
4522
|
-
filterInclusive = false;
|
|
4523
|
-
break;
|
|
4524
|
-
case "!=" /* NOT_EQUAL */:
|
|
4525
|
-
case "not-in" /* NOT_IN */:
|
|
4526
|
-
filterValue = MAX_VALUE;
|
|
4527
|
-
break;
|
|
4528
|
-
// Remaining filters cannot be used as upper bounds.
|
|
4529
|
-
}
|
|
4530
|
-
if (valuesMin(segmentValue, filterValue) === filterValue) {
|
|
4531
|
-
segmentValue = filterValue;
|
|
4532
|
-
segmentInclusive = filterInclusive;
|
|
4533
|
-
}
|
|
4534
|
-
}
|
|
4535
|
-
// If there is a endAt bound, compare the values against the existing
|
|
4536
|
-
// boundary to see if we can narrow the scope.
|
|
4537
|
-
if (target.endAt !== null) {
|
|
4538
|
-
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4539
|
-
const orderBy = target.orderBy[i];
|
|
4540
|
-
if (orderBy.field.isEqual(segment.fieldPath)) {
|
|
4541
|
-
const cursorValue = target.endAt.position[i];
|
|
4542
|
-
if (valuesMin(segmentValue, cursorValue) === cursorValue) {
|
|
4543
|
-
segmentValue = cursorValue;
|
|
4544
|
-
segmentInclusive = target.endAt.inclusive;
|
|
4545
|
-
}
|
|
4546
|
-
break;
|
|
4547
|
-
}
|
|
4548
|
-
}
|
|
4549
|
-
}
|
|
4550
|
-
if (segmentValue === undefined) {
|
|
4557
|
+
const segmentBound = segment.kind === 0 /* ASCENDING */
|
|
4558
|
+
? targetGetUpperBoundForField(target, segment.fieldPath, target.endAt)
|
|
4559
|
+
: targetGetLowerBoundForField(target, segment.fieldPath, target.endAt);
|
|
4560
|
+
if (!segmentBound.value) {
|
|
4551
4561
|
// No upper bound exists
|
|
4552
4562
|
return null;
|
|
4553
4563
|
}
|
|
4554
|
-
values.push(
|
|
4555
|
-
inclusive && (inclusive =
|
|
4564
|
+
values.push(segmentBound.value);
|
|
4565
|
+
inclusive && (inclusive = segmentBound.inclusive);
|
|
4556
4566
|
}
|
|
4557
4567
|
return new Bound(values, inclusive);
|
|
4558
4568
|
}
|
|
4569
|
+
/**
|
|
4570
|
+
* Returns the value to use as the lower bound for ascending index segment at
|
|
4571
|
+
* the provided `fieldPath` (or the upper bound for an descending segment).
|
|
4572
|
+
*/
|
|
4573
|
+
function targetGetLowerBoundForField(target, fieldPath, bound) {
|
|
4574
|
+
let value = undefined;
|
|
4575
|
+
let inclusive = true;
|
|
4576
|
+
// Process all filters to find a value for the current field segment
|
|
4577
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
4578
|
+
let filterValue = undefined;
|
|
4579
|
+
let filterInclusive = true;
|
|
4580
|
+
switch (fieldFilter.op) {
|
|
4581
|
+
case "<" /* LESS_THAN */:
|
|
4582
|
+
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4583
|
+
filterValue = valuesGetLowerBound(fieldFilter.value);
|
|
4584
|
+
break;
|
|
4585
|
+
case "==" /* EQUAL */:
|
|
4586
|
+
case "in" /* IN */:
|
|
4587
|
+
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4588
|
+
filterValue = fieldFilter.value;
|
|
4589
|
+
break;
|
|
4590
|
+
case ">" /* GREATER_THAN */:
|
|
4591
|
+
filterValue = fieldFilter.value;
|
|
4592
|
+
filterInclusive = false;
|
|
4593
|
+
break;
|
|
4594
|
+
case "!=" /* NOT_EQUAL */:
|
|
4595
|
+
case "not-in" /* NOT_IN */:
|
|
4596
|
+
filterValue = MIN_VALUE;
|
|
4597
|
+
break;
|
|
4598
|
+
// Remaining filters cannot be used as lower bounds.
|
|
4599
|
+
}
|
|
4600
|
+
if (valuesMax(value, filterValue) === filterValue) {
|
|
4601
|
+
value = filterValue;
|
|
4602
|
+
inclusive = filterInclusive;
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
// If there is an additional bound, compare the values against the existing
|
|
4606
|
+
// range to see if we can narrow the scope.
|
|
4607
|
+
if (bound !== null) {
|
|
4608
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4609
|
+
const orderBy = target.orderBy[i];
|
|
4610
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
4611
|
+
const cursorValue = bound.position[i];
|
|
4612
|
+
if (valuesMax(value, cursorValue) === cursorValue) {
|
|
4613
|
+
value = cursorValue;
|
|
4614
|
+
inclusive = bound.inclusive;
|
|
4615
|
+
}
|
|
4616
|
+
break;
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
return { value, inclusive };
|
|
4621
|
+
}
|
|
4622
|
+
/**
|
|
4623
|
+
* Returns the value to use as the upper bound for ascending index segment at
|
|
4624
|
+
* the provided `fieldPath` (or the lower bound for an descending segment).
|
|
4625
|
+
*/
|
|
4626
|
+
function targetGetUpperBoundForField(target, fieldPath, bound) {
|
|
4627
|
+
let value = undefined;
|
|
4628
|
+
let inclusive = true;
|
|
4629
|
+
// Process all filters to find a value for the current field segment
|
|
4630
|
+
for (const fieldFilter of targetGetFieldFiltersForPath(target, fieldPath)) {
|
|
4631
|
+
let filterValue = undefined;
|
|
4632
|
+
let filterInclusive = true;
|
|
4633
|
+
switch (fieldFilter.op) {
|
|
4634
|
+
case ">=" /* GREATER_THAN_OR_EQUAL */:
|
|
4635
|
+
case ">" /* GREATER_THAN */:
|
|
4636
|
+
filterValue = valuesGetUpperBound(fieldFilter.value);
|
|
4637
|
+
filterInclusive = false;
|
|
4638
|
+
break;
|
|
4639
|
+
case "==" /* EQUAL */:
|
|
4640
|
+
case "in" /* IN */:
|
|
4641
|
+
case "<=" /* LESS_THAN_OR_EQUAL */:
|
|
4642
|
+
filterValue = fieldFilter.value;
|
|
4643
|
+
break;
|
|
4644
|
+
case "<" /* LESS_THAN */:
|
|
4645
|
+
filterValue = fieldFilter.value;
|
|
4646
|
+
filterInclusive = false;
|
|
4647
|
+
break;
|
|
4648
|
+
case "!=" /* NOT_EQUAL */:
|
|
4649
|
+
case "not-in" /* NOT_IN */:
|
|
4650
|
+
filterValue = MAX_VALUE;
|
|
4651
|
+
break;
|
|
4652
|
+
// Remaining filters cannot be used as upper bounds.
|
|
4653
|
+
}
|
|
4654
|
+
if (valuesMin(value, filterValue) === filterValue) {
|
|
4655
|
+
value = filterValue;
|
|
4656
|
+
inclusive = filterInclusive;
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
// If there is an additional bound, compare the values against the existing
|
|
4660
|
+
// range to see if we can narrow the scope.
|
|
4661
|
+
if (bound !== null) {
|
|
4662
|
+
for (let i = 0; i < target.orderBy.length; ++i) {
|
|
4663
|
+
const orderBy = target.orderBy[i];
|
|
4664
|
+
if (orderBy.field.isEqual(fieldPath)) {
|
|
4665
|
+
const cursorValue = bound.position[i];
|
|
4666
|
+
if (valuesMin(value, cursorValue) === cursorValue) {
|
|
4667
|
+
value = cursorValue;
|
|
4668
|
+
inclusive = bound.inclusive;
|
|
4669
|
+
}
|
|
4670
|
+
break;
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
return { value, inclusive };
|
|
4675
|
+
}
|
|
4559
4676
|
class Filter {
|
|
4560
4677
|
}
|
|
4561
4678
|
class FieldFilter extends Filter {
|
|
@@ -5126,6 +5243,18 @@ function queryMatchesBounds(query, doc) {
|
|
|
5126
5243
|
}
|
|
5127
5244
|
return true;
|
|
5128
5245
|
}
|
|
5246
|
+
/**
|
|
5247
|
+
* Returns the collection group that this query targets.
|
|
5248
|
+
*
|
|
5249
|
+
* PORTING NOTE: This is only used in the Web SDK to facilitate multi-tab
|
|
5250
|
+
* synchronization for query results.
|
|
5251
|
+
*/
|
|
5252
|
+
function queryCollectionGroup(query) {
|
|
5253
|
+
return (query.collectionGroup ||
|
|
5254
|
+
(query.path.length % 2 === 1
|
|
5255
|
+
? query.path.lastSegment()
|
|
5256
|
+
: query.path.get(query.path.length - 2)));
|
|
5257
|
+
}
|
|
5129
5258
|
/**
|
|
5130
5259
|
* Returns a new comparator function that can be used to compare two documents
|
|
5131
5260
|
* based on the Query's ordering constraint.
|
|
@@ -8659,40 +8788,33 @@ function fromDbRemoteDocument(localSerializer, remoteDoc) {
|
|
|
8659
8788
|
}
|
|
8660
8789
|
/** Encodes a document for storage locally. */
|
|
8661
8790
|
function toDbRemoteDocument(localSerializer, document) {
|
|
8662
|
-
const
|
|
8663
|
-
const
|
|
8791
|
+
const key = document.key;
|
|
8792
|
+
const remoteDoc = {
|
|
8793
|
+
prefixPath: key.getCollectionPath().popLast().toArray(),
|
|
8794
|
+
collectionGroup: key.collectionGroup,
|
|
8795
|
+
documentId: key.path.lastSegment(),
|
|
8796
|
+
readTime: toDbTimestampKey(document.readTime),
|
|
8797
|
+
hasCommittedMutations: document.hasCommittedMutations
|
|
8798
|
+
};
|
|
8664
8799
|
if (document.isFoundDocument()) {
|
|
8665
|
-
|
|
8666
|
-
const hasCommittedMutations = document.hasCommittedMutations;
|
|
8667
|
-
return {
|
|
8668
|
-
document: doc,
|
|
8669
|
-
hasCommittedMutations,
|
|
8670
|
-
readTime,
|
|
8671
|
-
parentPath
|
|
8672
|
-
};
|
|
8800
|
+
remoteDoc.document = toDocument(localSerializer.remoteSerializer, document);
|
|
8673
8801
|
}
|
|
8674
8802
|
else if (document.isNoDocument()) {
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
noDocument: { path, readTime: toDbTimestamp(document.version) },
|
|
8679
|
-
hasCommittedMutations,
|
|
8680
|
-
readTime,
|
|
8681
|
-
parentPath
|
|
8803
|
+
remoteDoc.noDocument = {
|
|
8804
|
+
path: key.path.toArray(),
|
|
8805
|
+
readTime: toDbTimestamp(document.version)
|
|
8682
8806
|
};
|
|
8683
8807
|
}
|
|
8684
8808
|
else if (document.isUnknownDocument()) {
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
hasCommittedMutations: true,
|
|
8689
|
-
readTime,
|
|
8690
|
-
parentPath
|
|
8809
|
+
remoteDoc.unknownDocument = {
|
|
8810
|
+
path: key.path.toArray(),
|
|
8811
|
+
version: toDbTimestamp(document.version)
|
|
8691
8812
|
};
|
|
8692
8813
|
}
|
|
8693
8814
|
else {
|
|
8694
8815
|
return fail();
|
|
8695
8816
|
}
|
|
8817
|
+
return remoteDoc;
|
|
8696
8818
|
}
|
|
8697
8819
|
function toDbTimestampKey(snapshotVersion) {
|
|
8698
8820
|
const timestamp = snapshotVersion.toTimestamp();
|
|
@@ -9954,8 +10076,9 @@ const EMPTY_VALUE = new Uint8Array(0);
|
|
|
9954
10076
|
* data as it supports multi-tab access.
|
|
9955
10077
|
*/
|
|
9956
10078
|
class IndexedDbIndexManager {
|
|
9957
|
-
constructor(user) {
|
|
10079
|
+
constructor(user, databaseId) {
|
|
9958
10080
|
this.user = user;
|
|
10081
|
+
this.databaseId = databaseId;
|
|
9959
10082
|
/**
|
|
9960
10083
|
* An in-memory copy of the index entries we've already written since the SDK
|
|
9961
10084
|
* launched. Used to avoid re-writing the same entry repeatedly.
|
|
@@ -10051,7 +10174,8 @@ class IndexedDbIndexManager {
|
|
|
10051
10174
|
return PersistencePromise.resolve(null);
|
|
10052
10175
|
}
|
|
10053
10176
|
else {
|
|
10054
|
-
let
|
|
10177
|
+
let existingKeys = documentKeySet();
|
|
10178
|
+
const result = [];
|
|
10055
10179
|
return PersistencePromise.forEach(indexes, (index, subTarget) => {
|
|
10056
10180
|
logDebug(LOG_TAG$f, `Using index ${fieldIndexToString(index)} to execute ${canonifyTarget(target)}`);
|
|
10057
10181
|
const arrayValues = targetGetArrayValues(subTarget, index);
|
|
@@ -10067,7 +10191,11 @@ class IndexedDbIndexManager {
|
|
|
10067
10191
|
.loadFirst(indexRange, target.limit)
|
|
10068
10192
|
.next(entries => {
|
|
10069
10193
|
entries.forEach(entry => {
|
|
10070
|
-
|
|
10194
|
+
const documentKey = DocumentKey.fromSegments(entry.documentKey);
|
|
10195
|
+
if (!existingKeys.has(documentKey)) {
|
|
10196
|
+
existingKeys = existingKeys.add(documentKey);
|
|
10197
|
+
result.push(documentKey);
|
|
10198
|
+
}
|
|
10071
10199
|
});
|
|
10072
10200
|
});
|
|
10073
10201
|
});
|
|
@@ -10166,6 +10294,15 @@ class IndexedDbIndexManager {
|
|
|
10166
10294
|
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(value, encoder.forKind(0 /* ASCENDING */));
|
|
10167
10295
|
return encoder.encodedBytes();
|
|
10168
10296
|
}
|
|
10297
|
+
/**
|
|
10298
|
+
* Returns an encoded form of the document key that sorts based on the key
|
|
10299
|
+
* ordering of the field index.
|
|
10300
|
+
*/
|
|
10301
|
+
encodeDirectionalKey(fieldIndex, documentKey) {
|
|
10302
|
+
const encoder = new IndexByteEncoder();
|
|
10303
|
+
FirestoreIndexValueWriter.INSTANCE.writeIndexValue(refValue(this.databaseId, documentKey), encoder.forKind(fieldIndexGetKeyOrder(fieldIndex)));
|
|
10304
|
+
return encoder.encodedBytes();
|
|
10305
|
+
}
|
|
10169
10306
|
/**
|
|
10170
10307
|
* Encodes the given field values according to the specification in `target`.
|
|
10171
10308
|
* For IN queries, a list of possible values is returned.
|
|
@@ -10289,7 +10426,7 @@ class IndexedDbIndexManager {
|
|
|
10289
10426
|
return this.getExistingIndexEntries(transaction, key, fieldIndex).next(existingEntries => {
|
|
10290
10427
|
const newEntries = this.computeIndexEntries(doc, fieldIndex);
|
|
10291
10428
|
if (!existingEntries.isEqual(newEntries)) {
|
|
10292
|
-
return this.updateEntries(transaction, doc, existingEntries, newEntries);
|
|
10429
|
+
return this.updateEntries(transaction, doc, fieldIndex, existingEntries, newEntries);
|
|
10293
10430
|
}
|
|
10294
10431
|
return PersistencePromise.resolve();
|
|
10295
10432
|
});
|
|
@@ -10297,24 +10434,26 @@ class IndexedDbIndexManager {
|
|
|
10297
10434
|
});
|
|
10298
10435
|
});
|
|
10299
10436
|
}
|
|
10300
|
-
addIndexEntry(transaction, document, indexEntry) {
|
|
10437
|
+
addIndexEntry(transaction, document, fieldIndex, indexEntry) {
|
|
10301
10438
|
const indexEntries = indexEntriesStore(transaction);
|
|
10302
10439
|
return indexEntries.put({
|
|
10303
10440
|
indexId: indexEntry.indexId,
|
|
10304
10441
|
uid: this.uid,
|
|
10305
10442
|
arrayValue: indexEntry.arrayValue,
|
|
10306
10443
|
directionalValue: indexEntry.directionalValue,
|
|
10307
|
-
|
|
10444
|
+
orderedDocumentKey: this.encodeDirectionalKey(fieldIndex, document.key),
|
|
10445
|
+
documentKey: document.key.path.toArray()
|
|
10308
10446
|
});
|
|
10309
10447
|
}
|
|
10310
|
-
deleteIndexEntry(transaction, document, indexEntry) {
|
|
10448
|
+
deleteIndexEntry(transaction, document, fieldIndex, indexEntry) {
|
|
10311
10449
|
const indexEntries = indexEntriesStore(transaction);
|
|
10312
10450
|
return indexEntries.delete([
|
|
10313
10451
|
indexEntry.indexId,
|
|
10314
10452
|
this.uid,
|
|
10315
10453
|
indexEntry.arrayValue,
|
|
10316
10454
|
indexEntry.directionalValue,
|
|
10317
|
-
|
|
10455
|
+
this.encodeDirectionalKey(fieldIndex, document.key),
|
|
10456
|
+
document.key.path.toArray()
|
|
10318
10457
|
]);
|
|
10319
10458
|
}
|
|
10320
10459
|
getExistingIndexEntries(transaction, documentKey, fieldIndex) {
|
|
@@ -10326,7 +10465,7 @@ class IndexedDbIndexManager {
|
|
|
10326
10465
|
range: IDBKeyRange.only([
|
|
10327
10466
|
fieldIndex.indexId,
|
|
10328
10467
|
this.uid,
|
|
10329
|
-
|
|
10468
|
+
this.encodeDirectionalKey(fieldIndex, documentKey)
|
|
10330
10469
|
])
|
|
10331
10470
|
}, (_, entry) => {
|
|
10332
10471
|
results = results.add(new IndexEntry(fieldIndex.indexId, documentKey, entry.arrayValue, entry.directionalValue));
|
|
@@ -10359,15 +10498,15 @@ class IndexedDbIndexManager {
|
|
|
10359
10498
|
* that are no longer referenced in `newEntries` and adding all newly added
|
|
10360
10499
|
* entries.
|
|
10361
10500
|
*/
|
|
10362
|
-
updateEntries(transaction, document, existingEntries, newEntries) {
|
|
10501
|
+
updateEntries(transaction, document, fieldIndex, existingEntries, newEntries) {
|
|
10363
10502
|
logDebug(LOG_TAG$f, "Updating index entries for document '%s'", document.key);
|
|
10364
10503
|
const promises = [];
|
|
10365
10504
|
diffSortedSets(existingEntries, newEntries, indexEntryComparator,
|
|
10366
10505
|
/* onAdd= */ entry => {
|
|
10367
|
-
promises.push(this.addIndexEntry(transaction, document, entry));
|
|
10506
|
+
promises.push(this.addIndexEntry(transaction, document, fieldIndex, entry));
|
|
10368
10507
|
},
|
|
10369
10508
|
/* onRemove= */ entry => {
|
|
10370
|
-
promises.push(this.deleteIndexEntry(transaction, document, entry));
|
|
10509
|
+
promises.push(this.deleteIndexEntry(transaction, document, fieldIndex, entry));
|
|
10371
10510
|
});
|
|
10372
10511
|
return PersistencePromise.waitFor(promises);
|
|
10373
10512
|
}
|
|
@@ -10424,13 +10563,15 @@ class IndexedDbIndexManager {
|
|
|
10424
10563
|
this.uid,
|
|
10425
10564
|
bounds[i].arrayValue,
|
|
10426
10565
|
bounds[i].directionalValue,
|
|
10427
|
-
|
|
10566
|
+
EMPTY_VALUE,
|
|
10567
|
+
[]
|
|
10428
10568
|
], [
|
|
10429
10569
|
bounds[i + 1].indexId,
|
|
10430
10570
|
this.uid,
|
|
10431
10571
|
bounds[i + 1].arrayValue,
|
|
10432
10572
|
bounds[i + 1].directionalValue,
|
|
10433
|
-
|
|
10573
|
+
EMPTY_VALUE,
|
|
10574
|
+
[]
|
|
10434
10575
|
]));
|
|
10435
10576
|
}
|
|
10436
10577
|
return ranges;
|
|
@@ -11844,7 +11985,7 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11844
11985
|
*/
|
|
11845
11986
|
addEntry(transaction, key, doc) {
|
|
11846
11987
|
const documentStore = remoteDocumentsStore(transaction);
|
|
11847
|
-
return documentStore.put(
|
|
11988
|
+
return documentStore.put(doc);
|
|
11848
11989
|
}
|
|
11849
11990
|
/**
|
|
11850
11991
|
* Removes a document from the cache.
|
|
@@ -11852,10 +11993,9 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11852
11993
|
* All calls of `removeEntry` are required to go through the RemoteDocumentChangeBuffer
|
|
11853
11994
|
* returned by `newChangeBuffer()` to ensure proper accounting of metadata.
|
|
11854
11995
|
*/
|
|
11855
|
-
removeEntry(transaction, documentKey) {
|
|
11996
|
+
removeEntry(transaction, documentKey, readTime) {
|
|
11856
11997
|
const store = remoteDocumentsStore(transaction);
|
|
11857
|
-
|
|
11858
|
-
return store.delete(key);
|
|
11998
|
+
return store.delete(dbReadTimeKey(documentKey, readTime));
|
|
11859
11999
|
}
|
|
11860
12000
|
/**
|
|
11861
12001
|
* Updates the current cache size.
|
|
@@ -11870,11 +12010,15 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11870
12010
|
});
|
|
11871
12011
|
}
|
|
11872
12012
|
getEntry(transaction, documentKey) {
|
|
12013
|
+
let doc = MutableDocument.newInvalidDocument(documentKey);
|
|
11873
12014
|
return remoteDocumentsStore(transaction)
|
|
11874
|
-
.
|
|
11875
|
-
|
|
11876
|
-
|
|
11877
|
-
})
|
|
12015
|
+
.iterate({
|
|
12016
|
+
index: DbRemoteDocumentDocumentKeyIndex,
|
|
12017
|
+
range: IDBKeyRange.only(dbKey(documentKey))
|
|
12018
|
+
}, (_, dbRemoteDoc) => {
|
|
12019
|
+
doc = this.maybeDecodeDocument(documentKey, dbRemoteDoc);
|
|
12020
|
+
})
|
|
12021
|
+
.next(() => doc);
|
|
11878
12022
|
}
|
|
11879
12023
|
/**
|
|
11880
12024
|
* Looks up an entry in the cache.
|
|
@@ -11883,15 +12027,21 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11883
12027
|
* @returns The cached document entry and its size.
|
|
11884
12028
|
*/
|
|
11885
12029
|
getSizedEntry(transaction, documentKey) {
|
|
12030
|
+
let result = {
|
|
12031
|
+
size: 0,
|
|
12032
|
+
document: MutableDocument.newInvalidDocument(documentKey)
|
|
12033
|
+
};
|
|
11886
12034
|
return remoteDocumentsStore(transaction)
|
|
11887
|
-
.
|
|
11888
|
-
|
|
11889
|
-
|
|
11890
|
-
|
|
11891
|
-
|
|
12035
|
+
.iterate({
|
|
12036
|
+
index: DbRemoteDocumentDocumentKeyIndex,
|
|
12037
|
+
range: IDBKeyRange.only(dbKey(documentKey))
|
|
12038
|
+
}, (_, dbRemoteDoc) => {
|
|
12039
|
+
result = {
|
|
12040
|
+
document: this.maybeDecodeDocument(documentKey, dbRemoteDoc),
|
|
11892
12041
|
size: dbDocumentSize(dbRemoteDoc)
|
|
11893
12042
|
};
|
|
11894
|
-
})
|
|
12043
|
+
})
|
|
12044
|
+
.next(() => result);
|
|
11895
12045
|
}
|
|
11896
12046
|
getEntries(transaction, documentKeys) {
|
|
11897
12047
|
let results = mutableDocumentMap();
|
|
@@ -11922,14 +12072,20 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11922
12072
|
if (documentKeys.isEmpty()) {
|
|
11923
12073
|
return PersistencePromise.resolve();
|
|
11924
12074
|
}
|
|
11925
|
-
|
|
11926
|
-
|
|
12075
|
+
let sortedKeys = new SortedSet(dbKeyComparator);
|
|
12076
|
+
documentKeys.forEach(e => (sortedKeys = sortedKeys.add(e)));
|
|
12077
|
+
const range = IDBKeyRange.bound(dbKey(sortedKeys.first()), dbKey(sortedKeys.last()));
|
|
12078
|
+
const keyIter = sortedKeys.getIterator();
|
|
11927
12079
|
let nextKey = keyIter.getNext();
|
|
11928
12080
|
return remoteDocumentsStore(transaction)
|
|
11929
|
-
.iterate({ range }, (
|
|
11930
|
-
const potentialKey = DocumentKey.fromSegments(
|
|
12081
|
+
.iterate({ index: DbRemoteDocumentDocumentKeyIndex, range }, (_, dbRemoteDoc, control) => {
|
|
12082
|
+
const potentialKey = DocumentKey.fromSegments([
|
|
12083
|
+
...dbRemoteDoc.prefixPath,
|
|
12084
|
+
dbRemoteDoc.collectionGroup,
|
|
12085
|
+
dbRemoteDoc.documentId
|
|
12086
|
+
]);
|
|
11931
12087
|
// Go through keys not found in cache.
|
|
11932
|
-
while (nextKey &&
|
|
12088
|
+
while (nextKey && dbKeyComparator(nextKey, potentialKey) < 0) {
|
|
11933
12089
|
callback(nextKey, null);
|
|
11934
12090
|
nextKey = keyIter.getNext();
|
|
11935
12091
|
}
|
|
@@ -11940,7 +12096,7 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11940
12096
|
}
|
|
11941
12097
|
// Skip to the next key (if there is one).
|
|
11942
12098
|
if (nextKey) {
|
|
11943
|
-
control.skip(nextKey
|
|
12099
|
+
control.skip(dbKey(nextKey));
|
|
11944
12100
|
}
|
|
11945
12101
|
else {
|
|
11946
12102
|
control.done();
|
|
@@ -11955,41 +12111,44 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
11955
12111
|
}
|
|
11956
12112
|
});
|
|
11957
12113
|
}
|
|
11958
|
-
|
|
11959
|
-
|
|
11960
|
-
|
|
11961
|
-
|
|
11962
|
-
|
|
11963
|
-
|
|
11964
|
-
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
const readTimeKey = toDbTimestampKey(sinceReadTime);
|
|
11974
|
-
iterationOptions.range = IDBKeyRange.lowerBound([collectionKey, readTimeKey],
|
|
11975
|
-
/* open= */ true);
|
|
11976
|
-
iterationOptions.index = DbRemoteDocumentCollectionReadTimeIndex;
|
|
11977
|
-
}
|
|
12114
|
+
getAllFromCollection(transaction, collection, offset) {
|
|
12115
|
+
const startKey = [
|
|
12116
|
+
collection.popLast().toArray(),
|
|
12117
|
+
collection.lastSegment(),
|
|
12118
|
+
toDbTimestampKey(offset.readTime),
|
|
12119
|
+
offset.documentKey.path.isEmpty()
|
|
12120
|
+
? ''
|
|
12121
|
+
: offset.documentKey.path.lastSegment()
|
|
12122
|
+
];
|
|
12123
|
+
const endKey = [
|
|
12124
|
+
collection.popLast().toArray(),
|
|
12125
|
+
collection.lastSegment(),
|
|
12126
|
+
[Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
|
|
12127
|
+
''
|
|
12128
|
+
];
|
|
11978
12129
|
return remoteDocumentsStore(transaction)
|
|
11979
|
-
.
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
// more than one segment longer than the query path.
|
|
11985
|
-
if (key.length !== immediateChildrenPathLength) {
|
|
11986
|
-
return;
|
|
11987
|
-
}
|
|
11988
|
-
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(key), dbRemoteDoc);
|
|
11989
|
-
if (collection.isPrefixOf(document.key.path)) {
|
|
12130
|
+
.loadAll(IDBKeyRange.bound(startKey, endKey, true))
|
|
12131
|
+
.next(dbRemoteDocs => {
|
|
12132
|
+
let results = mutableDocumentMap();
|
|
12133
|
+
for (const dbRemoteDoc of dbRemoteDocs) {
|
|
12134
|
+
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
|
|
11990
12135
|
results = results.insert(document.key, document);
|
|
11991
12136
|
}
|
|
11992
|
-
|
|
12137
|
+
return results;
|
|
12138
|
+
});
|
|
12139
|
+
}
|
|
12140
|
+
getAllFromCollectionGroup(transaction, collectionGroup, offset, limit) {
|
|
12141
|
+
let results = mutableDocumentMap();
|
|
12142
|
+
const startKey = dbCollectionGroupKey(collectionGroup, offset);
|
|
12143
|
+
const endKey = dbCollectionGroupKey(collectionGroup, IndexOffset.max());
|
|
12144
|
+
return remoteDocumentsStore(transaction)
|
|
12145
|
+
.iterate({
|
|
12146
|
+
index: DbRemoteDocumentCollectionGroupIndex,
|
|
12147
|
+
range: IDBKeyRange.bound(startKey, endKey, true)
|
|
12148
|
+
}, (_, dbRemoteDoc, control) => {
|
|
12149
|
+
const document = this.maybeDecodeDocument(DocumentKey.fromSegments(dbRemoteDoc.prefixPath.concat(dbRemoteDoc.collectionGroup, dbRemoteDoc.documentId)), dbRemoteDoc);
|
|
12150
|
+
results = results.insert(document.key, document);
|
|
12151
|
+
if (results.size === limit) {
|
|
11993
12152
|
control.done();
|
|
11994
12153
|
}
|
|
11995
12154
|
})
|
|
@@ -12033,50 +12192,6 @@ class IndexedDbRemoteDocumentCacheImpl {
|
|
|
12033
12192
|
function newIndexedDbRemoteDocumentCache(serializer) {
|
|
12034
12193
|
return new IndexedDbRemoteDocumentCacheImpl(serializer);
|
|
12035
12194
|
}
|
|
12036
|
-
/**
|
|
12037
|
-
* Returns the set of documents that have changed since the specified read
|
|
12038
|
-
* time.
|
|
12039
|
-
*/
|
|
12040
|
-
// PORTING NOTE: This is only used for multi-tab synchronization.
|
|
12041
|
-
function remoteDocumentCacheGetNewDocumentChanges(remoteDocumentCache, transaction, sinceReadTime) {
|
|
12042
|
-
const remoteDocumentCacheImpl = debugCast(remoteDocumentCache);
|
|
12043
|
-
let changedDocs = mutableDocumentMap();
|
|
12044
|
-
let lastReadTime = toDbTimestampKey(sinceReadTime);
|
|
12045
|
-
const documentsStore = remoteDocumentsStore(transaction);
|
|
12046
|
-
const range = IDBKeyRange.lowerBound(lastReadTime, true);
|
|
12047
|
-
return documentsStore
|
|
12048
|
-
.iterate({ index: DbRemoteDocumentReadTimeIndex, range }, (_, dbRemoteDoc) => {
|
|
12049
|
-
// Unlike `getEntry()` and others, `getNewDocumentChanges()` parses
|
|
12050
|
-
// the documents directly since we want to keep sentinel deletes.
|
|
12051
|
-
const doc = fromDbRemoteDocument(remoteDocumentCacheImpl.serializer, dbRemoteDoc);
|
|
12052
|
-
changedDocs = changedDocs.insert(doc.key, doc);
|
|
12053
|
-
lastReadTime = dbRemoteDoc.readTime;
|
|
12054
|
-
})
|
|
12055
|
-
.next(() => {
|
|
12056
|
-
return {
|
|
12057
|
-
changedDocs,
|
|
12058
|
-
readTime: fromDbTimestampKey(lastReadTime)
|
|
12059
|
-
};
|
|
12060
|
-
});
|
|
12061
|
-
}
|
|
12062
|
-
/**
|
|
12063
|
-
* Returns the read time of the most recently read document in the cache, or
|
|
12064
|
-
* SnapshotVersion.min() if not available.
|
|
12065
|
-
*/
|
|
12066
|
-
// PORTING NOTE: This is only used for multi-tab synchronization.
|
|
12067
|
-
function remoteDocumentCacheGetLastReadTime(transaction) {
|
|
12068
|
-
const documentsStore = remoteDocumentsStore(transaction);
|
|
12069
|
-
// If there are no existing entries, we return SnapshotVersion.min().
|
|
12070
|
-
let readTime = SnapshotVersion.min();
|
|
12071
|
-
return documentsStore
|
|
12072
|
-
.iterate({ index: DbRemoteDocumentReadTimeIndex, reverse: true }, (key, dbRemoteDoc, control) => {
|
|
12073
|
-
if (dbRemoteDoc.readTime) {
|
|
12074
|
-
readTime = fromDbTimestampKey(dbRemoteDoc.readTime);
|
|
12075
|
-
}
|
|
12076
|
-
control.done();
|
|
12077
|
-
})
|
|
12078
|
-
.next(() => readTime);
|
|
12079
|
-
}
|
|
12080
12195
|
/**
|
|
12081
12196
|
* Handles the details of adding and updating documents in the IndexedDbRemoteDocumentCache.
|
|
12082
12197
|
*
|
|
@@ -12094,24 +12209,26 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12094
12209
|
super();
|
|
12095
12210
|
this.documentCache = documentCache;
|
|
12096
12211
|
this.trackRemovals = trackRemovals;
|
|
12097
|
-
// A map of document sizes prior to applying the changes in
|
|
12098
|
-
this
|
|
12212
|
+
// A map of document sizes and read times prior to applying the changes in
|
|
12213
|
+
// this buffer.
|
|
12214
|
+
this.documentStates = new ObjectMap(key => key.toString(), (l, r) => l.isEqual(r));
|
|
12099
12215
|
}
|
|
12100
12216
|
applyChanges(transaction) {
|
|
12101
12217
|
const promises = [];
|
|
12102
12218
|
let sizeDelta = 0;
|
|
12103
12219
|
let collectionParents = new SortedSet((l, r) => primitiveComparator(l.canonicalString(), r.canonicalString()));
|
|
12104
12220
|
this.changes.forEach((key, documentChange) => {
|
|
12105
|
-
const
|
|
12221
|
+
const previousDoc = this.documentStates.get(key);
|
|
12222
|
+
promises.push(this.documentCache.removeEntry(transaction, key, previousDoc.readTime));
|
|
12106
12223
|
if (documentChange.isValidDocument()) {
|
|
12107
12224
|
const doc = toDbRemoteDocument(this.documentCache.serializer, documentChange);
|
|
12108
12225
|
collectionParents = collectionParents.add(key.path.popLast());
|
|
12109
12226
|
const size = dbDocumentSize(doc);
|
|
12110
|
-
sizeDelta += size -
|
|
12227
|
+
sizeDelta += size - previousDoc.size;
|
|
12111
12228
|
promises.push(this.documentCache.addEntry(transaction, key, doc));
|
|
12112
12229
|
}
|
|
12113
12230
|
else {
|
|
12114
|
-
sizeDelta -=
|
|
12231
|
+
sizeDelta -= previousDoc.size;
|
|
12115
12232
|
if (this.trackRemovals) {
|
|
12116
12233
|
// In order to track removals, we store a "sentinel delete" in the
|
|
12117
12234
|
// RemoteDocumentCache. This entry is represented by a NoDocument
|
|
@@ -12120,9 +12237,6 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12120
12237
|
const deletedDoc = toDbRemoteDocument(this.documentCache.serializer, documentChange.convertToNoDocument(SnapshotVersion.min()));
|
|
12121
12238
|
promises.push(this.documentCache.addEntry(transaction, key, deletedDoc));
|
|
12122
12239
|
}
|
|
12123
|
-
else {
|
|
12124
|
-
promises.push(this.documentCache.removeEntry(transaction, key));
|
|
12125
|
-
}
|
|
12126
12240
|
}
|
|
12127
12241
|
});
|
|
12128
12242
|
collectionParents.forEach(parent => {
|
|
@@ -12136,7 +12250,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12136
12250
|
return this.documentCache
|
|
12137
12251
|
.getSizedEntry(transaction, documentKey)
|
|
12138
12252
|
.next(getResult => {
|
|
12139
|
-
this.
|
|
12253
|
+
this.documentStates.set(documentKey, {
|
|
12254
|
+
size: getResult.size,
|
|
12255
|
+
readTime: getResult.document.readTime
|
|
12256
|
+
});
|
|
12140
12257
|
return getResult.document;
|
|
12141
12258
|
});
|
|
12142
12259
|
}
|
|
@@ -12150,7 +12267,10 @@ class IndexedDbRemoteDocumentChangeBuffer extends RemoteDocumentChangeBuffer {
|
|
|
12150
12267
|
// keys to `DocumentSizeEntry`s. This is to allow returning the
|
|
12151
12268
|
// `MutableDocumentMap` directly, without a conversion.
|
|
12152
12269
|
sizeMap.forEach((documentKey, size) => {
|
|
12153
|
-
this.
|
|
12270
|
+
this.documentStates.set(documentKey, {
|
|
12271
|
+
size,
|
|
12272
|
+
readTime: documents.get(documentKey).readTime
|
|
12273
|
+
});
|
|
12154
12274
|
});
|
|
12155
12275
|
return documents;
|
|
12156
12276
|
});
|
|
@@ -12165,8 +12285,71 @@ function documentGlobalStore(txn) {
|
|
|
12165
12285
|
function remoteDocumentsStore(txn) {
|
|
12166
12286
|
return getStore(txn, DbRemoteDocumentStore);
|
|
12167
12287
|
}
|
|
12168
|
-
|
|
12169
|
-
|
|
12288
|
+
/**
|
|
12289
|
+
* Returns a key that can be used for document lookups on the
|
|
12290
|
+
* `DbRemoteDocumentDocumentKeyIndex` index.
|
|
12291
|
+
*/
|
|
12292
|
+
function dbKey(documentKey) {
|
|
12293
|
+
const path = documentKey.path.toArray();
|
|
12294
|
+
return [
|
|
12295
|
+
/* prefix path */ path.slice(0, path.length - 2),
|
|
12296
|
+
/* collection id */ path[path.length - 2],
|
|
12297
|
+
/* document id */ path[path.length - 1]
|
|
12298
|
+
];
|
|
12299
|
+
}
|
|
12300
|
+
/**
|
|
12301
|
+
* Returns a key that can be used for document lookups via the primary key of
|
|
12302
|
+
* the DbRemoteDocument object store.
|
|
12303
|
+
*/
|
|
12304
|
+
function dbReadTimeKey(documentKey, readTime) {
|
|
12305
|
+
const path = documentKey.path.toArray();
|
|
12306
|
+
return [
|
|
12307
|
+
/* prefix path */ path.slice(0, path.length - 2),
|
|
12308
|
+
/* collection id */ path[path.length - 2],
|
|
12309
|
+
toDbTimestampKey(readTime),
|
|
12310
|
+
/* document id */ path[path.length - 1]
|
|
12311
|
+
];
|
|
12312
|
+
}
|
|
12313
|
+
/**
|
|
12314
|
+
* Returns a key that can be used for document lookups on the
|
|
12315
|
+
* `DbRemoteDocumentDocumentCollectionGroupIndex` index.
|
|
12316
|
+
*/
|
|
12317
|
+
function dbCollectionGroupKey(collectionGroup, offset) {
|
|
12318
|
+
const path = offset.documentKey.path.toArray();
|
|
12319
|
+
return [
|
|
12320
|
+
/* collection id */ collectionGroup,
|
|
12321
|
+
toDbTimestampKey(offset.readTime),
|
|
12322
|
+
/* prefix path */ path.slice(0, path.length - 2),
|
|
12323
|
+
/* document id */ path.length > 0 ? path[path.length - 1] : ''
|
|
12324
|
+
];
|
|
12325
|
+
}
|
|
12326
|
+
/**
|
|
12327
|
+
* Comparator that compares document keys according to the primary key sorting
|
|
12328
|
+
* used by the `DbRemoteDocumentDocument` store (by prefix path, collection id
|
|
12329
|
+
* and then document ID).
|
|
12330
|
+
*
|
|
12331
|
+
* Visible for testing.
|
|
12332
|
+
*/
|
|
12333
|
+
function dbKeyComparator(l, r) {
|
|
12334
|
+
const left = l.path.toArray();
|
|
12335
|
+
const right = r.path.toArray();
|
|
12336
|
+
// The ordering is based on https://chromium.googlesource.com/chromium/blink/+/fe5c21fef94dae71c1c3344775b8d8a7f7e6d9ec/Source/modules/indexeddb/IDBKey.cpp#74
|
|
12337
|
+
let cmp = 0;
|
|
12338
|
+
for (let i = 0; i < left.length - 2 && i < right.length - 2; ++i) {
|
|
12339
|
+
cmp = primitiveComparator(left[i], right[i]);
|
|
12340
|
+
if (cmp) {
|
|
12341
|
+
return cmp;
|
|
12342
|
+
}
|
|
12343
|
+
}
|
|
12344
|
+
cmp = primitiveComparator(left.length, right.length);
|
|
12345
|
+
if (cmp) {
|
|
12346
|
+
return cmp;
|
|
12347
|
+
}
|
|
12348
|
+
cmp = primitiveComparator(left[left.length - 2], right[right.length - 2]);
|
|
12349
|
+
if (cmp) {
|
|
12350
|
+
return cmp;
|
|
12351
|
+
}
|
|
12352
|
+
return primitiveComparator(left[left.length - 1], right[right.length - 1]);
|
|
12170
12353
|
}
|
|
12171
12354
|
/**
|
|
12172
12355
|
* Schema Version for the Web client:
|
|
@@ -12187,9 +12370,11 @@ function dbKey(docKey) {
|
|
|
12187
12370
|
* 10. Rewrite the canonical IDs to the explicit Protobuf-based format.
|
|
12188
12371
|
* 11. Add bundles and named_queries for bundle support.
|
|
12189
12372
|
* 12. Add document overlays.
|
|
12190
|
-
* 13.
|
|
12373
|
+
* 13. Rewrite the keys of the remote document cache to allow for efficient
|
|
12374
|
+
* document lookup via `getAll()`.
|
|
12375
|
+
* 14. Add indexing support.
|
|
12191
12376
|
*/
|
|
12192
|
-
const SCHEMA_VERSION =
|
|
12377
|
+
const SCHEMA_VERSION = 13;
|
|
12193
12378
|
|
|
12194
12379
|
/**
|
|
12195
12380
|
* @license
|
|
@@ -12225,7 +12410,7 @@ class SchemaConverter {
|
|
|
12225
12410
|
createPrimaryClientStore(db);
|
|
12226
12411
|
createMutationQueue(db);
|
|
12227
12412
|
createQueryCache(db);
|
|
12228
|
-
|
|
12413
|
+
createLegacyRemoteDocumentCache(db);
|
|
12229
12414
|
}
|
|
12230
12415
|
// Migration 2 to populate the targetGlobal object no longer needed since
|
|
12231
12416
|
// migration 3 unconditionally clears it.
|
|
@@ -12274,7 +12459,8 @@ class SchemaConverter {
|
|
|
12274
12459
|
// to the DbRemoteDocument object store itself. Since the previous change
|
|
12275
12460
|
// log only contained transient data, we can drop its object store.
|
|
12276
12461
|
dropRemoteDocumentChangesStore(db);
|
|
12277
|
-
|
|
12462
|
+
// Note: Schema version 9 used to create a read time index for the
|
|
12463
|
+
// RemoteDocumentCache. This is now done with schema version 13.
|
|
12278
12464
|
});
|
|
12279
12465
|
}
|
|
12280
12466
|
if (fromVersion < 10 && toVersion >= 10) {
|
|
@@ -12292,6 +12478,12 @@ class SchemaConverter {
|
|
|
12292
12478
|
});
|
|
12293
12479
|
}
|
|
12294
12480
|
if (fromVersion < 13 && toVersion >= 13) {
|
|
12481
|
+
p = p
|
|
12482
|
+
.next(() => createRemoteDocumentCache(db))
|
|
12483
|
+
.next(() => this.rewriteRemoteDocumentCache(db, simpleDbTransaction))
|
|
12484
|
+
.next(() => db.deleteObjectStore(DbRemoteDocumentStore$1));
|
|
12485
|
+
}
|
|
12486
|
+
if (fromVersion < 14 && toVersion >= 14) {
|
|
12295
12487
|
p = p.next(() => {
|
|
12296
12488
|
createFieldIndex(db);
|
|
12297
12489
|
});
|
|
@@ -12301,7 +12493,7 @@ class SchemaConverter {
|
|
|
12301
12493
|
addDocumentGlobal(txn) {
|
|
12302
12494
|
let byteSize = 0;
|
|
12303
12495
|
return txn
|
|
12304
|
-
.store(DbRemoteDocumentStore)
|
|
12496
|
+
.store(DbRemoteDocumentStore$1)
|
|
12305
12497
|
.iterate((_, doc) => {
|
|
12306
12498
|
byteSize += dbDocumentSize(doc);
|
|
12307
12499
|
})
|
|
@@ -12336,7 +12528,7 @@ class SchemaConverter {
|
|
|
12336
12528
|
*/
|
|
12337
12529
|
ensureSequenceNumbers(txn) {
|
|
12338
12530
|
const documentTargetStore = txn.store(DbTargetDocumentStore);
|
|
12339
|
-
const documentsStore = txn.store(DbRemoteDocumentStore);
|
|
12531
|
+
const documentsStore = txn.store(DbRemoteDocumentStore$1);
|
|
12340
12532
|
const globalTargetStore = txn.store(DbTargetGlobalStore);
|
|
12341
12533
|
return globalTargetStore.get(DbTargetGlobalKey).next(metadata => {
|
|
12342
12534
|
const writeSentinelKey = (path) => {
|
|
@@ -12383,7 +12575,7 @@ class SchemaConverter {
|
|
|
12383
12575
|
};
|
|
12384
12576
|
// Index existing remote documents.
|
|
12385
12577
|
return txn
|
|
12386
|
-
.store(DbRemoteDocumentStore)
|
|
12578
|
+
.store(DbRemoteDocumentStore$1)
|
|
12387
12579
|
.iterate({ keysOnly: true }, (pathSegments, _) => {
|
|
12388
12580
|
const path = new ResourcePath(pathSegments);
|
|
12389
12581
|
return addEntry(path.popLast());
|
|
@@ -12406,6 +12598,27 @@ class SchemaConverter {
|
|
|
12406
12598
|
return targetStore.put(updatedDbTarget);
|
|
12407
12599
|
});
|
|
12408
12600
|
}
|
|
12601
|
+
rewriteRemoteDocumentCache(db, transaction) {
|
|
12602
|
+
const legacyRemoteDocumentStore = transaction.store(DbRemoteDocumentStore$1);
|
|
12603
|
+
const writes = [];
|
|
12604
|
+
return legacyRemoteDocumentStore
|
|
12605
|
+
.iterate((_, legacyDocument) => {
|
|
12606
|
+
const remoteDocumentStore = transaction.store(DbRemoteDocumentStore);
|
|
12607
|
+
const path = extractKey(legacyDocument).path.toArray();
|
|
12608
|
+
const dbRemoteDocument = {
|
|
12609
|
+
prefixPath: path.slice(0, path.length - 2),
|
|
12610
|
+
collectionGroup: path[path.length - 2],
|
|
12611
|
+
documentId: path[path.length - 1],
|
|
12612
|
+
readTime: legacyDocument.readTime || [0, 0],
|
|
12613
|
+
unknownDocument: legacyDocument.unknownDocument,
|
|
12614
|
+
noDocument: legacyDocument.noDocument,
|
|
12615
|
+
document: legacyDocument.document,
|
|
12616
|
+
hasCommittedMutations: !!legacyDocument.hasCommittedMutations
|
|
12617
|
+
};
|
|
12618
|
+
writes.push(remoteDocumentStore.put(dbRemoteDocument));
|
|
12619
|
+
})
|
|
12620
|
+
.next(() => PersistencePromise.waitFor(writes));
|
|
12621
|
+
}
|
|
12409
12622
|
}
|
|
12410
12623
|
function sentinelKey(path) {
|
|
12411
12624
|
return [0, encodeResourcePath(path)];
|
|
@@ -12442,8 +12655,15 @@ function upgradeMutationBatchSchemaAndMigrateData(db, txn) {
|
|
|
12442
12655
|
return PersistencePromise.waitFor(writeAll);
|
|
12443
12656
|
});
|
|
12444
12657
|
}
|
|
12658
|
+
function createLegacyRemoteDocumentCache(db) {
|
|
12659
|
+
db.createObjectStore(DbRemoteDocumentStore$1);
|
|
12660
|
+
}
|
|
12445
12661
|
function createRemoteDocumentCache(db) {
|
|
12446
|
-
db.createObjectStore(DbRemoteDocumentStore
|
|
12662
|
+
const remoteDocumentStore = db.createObjectStore(DbRemoteDocumentStore, {
|
|
12663
|
+
keyPath: DbRemoteDocumentKeyPath
|
|
12664
|
+
});
|
|
12665
|
+
remoteDocumentStore.createIndex(DbRemoteDocumentDocumentKeyIndex, DbRemoteDocumentDocumentKeyIndexPath);
|
|
12666
|
+
remoteDocumentStore.createIndex(DbRemoteDocumentCollectionGroupIndex, DbRemoteDocumentCollectionGroupIndexPath);
|
|
12447
12667
|
}
|
|
12448
12668
|
function createDocumentGlobalStore(db) {
|
|
12449
12669
|
db.createObjectStore(DbRemoteDocumentGlobalStore);
|
|
@@ -12485,15 +12705,6 @@ function writeEmptyTargetGlobalEntry(txn) {
|
|
|
12485
12705
|
};
|
|
12486
12706
|
return globalStore.put(DbTargetGlobalKey, metadata);
|
|
12487
12707
|
}
|
|
12488
|
-
/**
|
|
12489
|
-
* Creates indices on the RemoteDocuments store used for both multi-tab
|
|
12490
|
-
* and Index-Free queries.
|
|
12491
|
-
*/
|
|
12492
|
-
function createRemoteDocumentReadTimeIndex(txn) {
|
|
12493
|
-
const remoteDocumentStore = txn.objectStore(DbRemoteDocumentStore);
|
|
12494
|
-
remoteDocumentStore.createIndex(DbRemoteDocumentReadTimeIndex, DbRemoteDocumentReadTimeIndexPath, { unique: false });
|
|
12495
|
-
remoteDocumentStore.createIndex(DbRemoteDocumentCollectionReadTimeIndex, DbRemoteDocumentCollectionReadTimeIndexPath, { unique: false });
|
|
12496
|
-
}
|
|
12497
12708
|
function createClientMetadataStore(db) {
|
|
12498
12709
|
db.createObjectStore(DbClientMetadataStore, {
|
|
12499
12710
|
keyPath: DbClientMetadataKeyPath
|
|
@@ -12510,11 +12721,11 @@ function createNamedQueriesStore(db) {
|
|
|
12510
12721
|
});
|
|
12511
12722
|
}
|
|
12512
12723
|
function createFieldIndex(db) {
|
|
12513
|
-
const
|
|
12724
|
+
const indexConfigurationStore = db.createObjectStore(DbIndexConfigurationStore, {
|
|
12514
12725
|
keyPath: DbIndexConfigurationKeyPath,
|
|
12515
12726
|
autoIncrement: true
|
|
12516
12727
|
});
|
|
12517
|
-
|
|
12728
|
+
indexConfigurationStore.createIndex(DbIndexConfigurationCollectionGroupIndex, DbIndexConfigurationCollectionGroupIndexPath, { unique: false });
|
|
12518
12729
|
const indexStateStore = db.createObjectStore(DbIndexStateStore, {
|
|
12519
12730
|
keyPath: DbIndexStateKeyPath
|
|
12520
12731
|
});
|
|
@@ -12530,6 +12741,20 @@ function createDocumentOverlayStore(db) {
|
|
|
12530
12741
|
});
|
|
12531
12742
|
documentOverlayStore.createIndex(DbDocumentOverlayCollectionPathOverlayIndex, DbDocumentOverlayCollectionPathOverlayIndexPath, { unique: false });
|
|
12532
12743
|
documentOverlayStore.createIndex(DbDocumentOverlayCollectionGroupOverlayIndex, DbDocumentOverlayCollectionGroupOverlayIndexPath, { unique: false });
|
|
12744
|
+
}
|
|
12745
|
+
function extractKey(remoteDoc) {
|
|
12746
|
+
if (remoteDoc.document) {
|
|
12747
|
+
return new DocumentKey(ResourcePath.fromString(remoteDoc.document.name).popFirst(5));
|
|
12748
|
+
}
|
|
12749
|
+
else if (remoteDoc.noDocument) {
|
|
12750
|
+
return DocumentKey.fromSegments(remoteDoc.noDocument.path);
|
|
12751
|
+
}
|
|
12752
|
+
else if (remoteDoc.unknownDocument) {
|
|
12753
|
+
return DocumentKey.fromSegments(remoteDoc.unknownDocument.path);
|
|
12754
|
+
}
|
|
12755
|
+
else {
|
|
12756
|
+
return fail();
|
|
12757
|
+
}
|
|
12533
12758
|
}
|
|
12534
12759
|
|
|
12535
12760
|
/**
|
|
@@ -13025,7 +13250,7 @@ class IndexedDbPersistence {
|
|
|
13025
13250
|
return this.remoteDocumentCache;
|
|
13026
13251
|
}
|
|
13027
13252
|
getIndexManager(user) {
|
|
13028
|
-
return new IndexedDbIndexManager(user);
|
|
13253
|
+
return new IndexedDbIndexManager(user, this.serializer.remoteSerializer.databaseId);
|
|
13029
13254
|
}
|
|
13030
13255
|
getDocumentOverlayCache(user) {
|
|
13031
13256
|
return IndexedDbDocumentOverlayCache.forUser(this.serializer, user);
|
|
@@ -13372,18 +13597,17 @@ class LocalDocumentsView {
|
|
|
13372
13597
|
*
|
|
13373
13598
|
* @param transaction - The persistence transaction.
|
|
13374
13599
|
* @param query - The query to match documents against.
|
|
13375
|
-
* @param
|
|
13376
|
-
* documents that have been read since this snapshot version (exclusive).
|
|
13600
|
+
* @param offset - Read time and key to start scanning by (exclusive).
|
|
13377
13601
|
*/
|
|
13378
|
-
getDocumentsMatchingQuery(transaction, query,
|
|
13602
|
+
getDocumentsMatchingQuery(transaction, query, offset) {
|
|
13379
13603
|
if (isDocumentQuery$1(query)) {
|
|
13380
13604
|
return this.getDocumentsMatchingDocumentQuery(transaction, query.path);
|
|
13381
13605
|
}
|
|
13382
13606
|
else if (isCollectionGroupQuery(query)) {
|
|
13383
|
-
return this.getDocumentsMatchingCollectionGroupQuery(transaction, query,
|
|
13607
|
+
return this.getDocumentsMatchingCollectionGroupQuery(transaction, query, offset);
|
|
13384
13608
|
}
|
|
13385
13609
|
else {
|
|
13386
|
-
return this.getDocumentsMatchingCollectionQuery(transaction, query,
|
|
13610
|
+
return this.getDocumentsMatchingCollectionQuery(transaction, query, offset);
|
|
13387
13611
|
}
|
|
13388
13612
|
}
|
|
13389
13613
|
getDocumentsMatchingDocumentQuery(transaction, docPath) {
|
|
@@ -13396,7 +13620,7 @@ class LocalDocumentsView {
|
|
|
13396
13620
|
return result;
|
|
13397
13621
|
});
|
|
13398
13622
|
}
|
|
13399
|
-
getDocumentsMatchingCollectionGroupQuery(transaction, query,
|
|
13623
|
+
getDocumentsMatchingCollectionGroupQuery(transaction, query, offset) {
|
|
13400
13624
|
const collectionId = query.collectionGroup;
|
|
13401
13625
|
let results = documentMap();
|
|
13402
13626
|
return this.indexManager
|
|
@@ -13406,7 +13630,7 @@ class LocalDocumentsView {
|
|
|
13406
13630
|
// collectionId and aggregate the results.
|
|
13407
13631
|
return PersistencePromise.forEach(parents, (parent) => {
|
|
13408
13632
|
const collectionQuery = asCollectionQueryAtPath(query, parent.child(collectionId));
|
|
13409
|
-
return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery,
|
|
13633
|
+
return this.getDocumentsMatchingCollectionQuery(transaction, collectionQuery, offset).next(r => {
|
|
13410
13634
|
r.forEach((key, doc) => {
|
|
13411
13635
|
results = results.insert(key, doc);
|
|
13412
13636
|
});
|
|
@@ -13414,11 +13638,11 @@ class LocalDocumentsView {
|
|
|
13414
13638
|
}).next(() => results);
|
|
13415
13639
|
});
|
|
13416
13640
|
}
|
|
13417
|
-
getDocumentsMatchingCollectionQuery(transaction, query,
|
|
13641
|
+
getDocumentsMatchingCollectionQuery(transaction, query, offset) {
|
|
13418
13642
|
// Query the remote documents and overlay mutations.
|
|
13419
13643
|
let results;
|
|
13420
13644
|
return this.remoteDocumentCache
|
|
13421
|
-
.
|
|
13645
|
+
.getAllFromCollection(transaction, query.path, offset)
|
|
13422
13646
|
.next(queryResults => {
|
|
13423
13647
|
results = queryResults;
|
|
13424
13648
|
return this.mutationQueue.getAllMutationBatchesAffectingQuery(transaction, query);
|
|
@@ -13504,11 +13728,12 @@ class LocalStoreImpl {
|
|
|
13504
13728
|
// TODO(wuandy): Evaluate if TargetId can be part of Target.
|
|
13505
13729
|
this.targetIdByTarget = new ObjectMap(t => canonifyTarget(t), targetEquals);
|
|
13506
13730
|
/**
|
|
13507
|
-
*
|
|
13731
|
+
* A per collection group index of the last read time processed by
|
|
13732
|
+
* `getNewDocumentChanges()`.
|
|
13508
13733
|
*
|
|
13509
13734
|
* PORTING NOTE: This is only used for multi-tab synchronization.
|
|
13510
13735
|
*/
|
|
13511
|
-
this.
|
|
13736
|
+
this.collectionGroupReadTime = new Map();
|
|
13512
13737
|
this.remoteDocuments = persistence.getRemoteDocumentCache();
|
|
13513
13738
|
this.targetCache = persistence.getTargetCache();
|
|
13514
13739
|
this.bundleCache = persistence.getBundleCache();
|
|
@@ -14043,6 +14268,7 @@ function localStoreExecuteQuery(localStore, query, usePreviousResults) {
|
|
|
14043
14268
|
? lastLimboFreeSnapshotVersion
|
|
14044
14269
|
: SnapshotVersion.min(), usePreviousResults ? remoteKeys : documentKeySet()))
|
|
14045
14270
|
.next(documents => {
|
|
14271
|
+
setMaxReadTime(localStoreImpl, queryCollectionGroup(query), documents);
|
|
14046
14272
|
return { documents, remoteKeys };
|
|
14047
14273
|
});
|
|
14048
14274
|
});
|
|
@@ -14120,28 +14346,32 @@ function localStoreGetCachedTarget(localStore, targetId) {
|
|
|
14120
14346
|
* since the prior call.
|
|
14121
14347
|
*/
|
|
14122
14348
|
// PORTING NOTE: Multi-Tab only.
|
|
14123
|
-
function localStoreGetNewDocumentChanges(localStore) {
|
|
14349
|
+
function localStoreGetNewDocumentChanges(localStore, collectionGroup) {
|
|
14124
14350
|
const localStoreImpl = debugCast(localStore);
|
|
14351
|
+
// Get the current maximum read time for the collection. This should always
|
|
14352
|
+
// exist, but to reduce the chance for regressions we default to
|
|
14353
|
+
// SnapshotVersion.Min()
|
|
14354
|
+
// TODO(indexing): Consider removing the default value.
|
|
14355
|
+
const readTime = localStoreImpl.collectionGroupReadTime.get(collectionGroup) ||
|
|
14356
|
+
SnapshotVersion.min();
|
|
14125
14357
|
return localStoreImpl.persistence
|
|
14126
|
-
.runTransaction('Get new document changes', 'readonly', txn =>
|
|
14127
|
-
|
|
14128
|
-
|
|
14358
|
+
.runTransaction('Get new document changes', 'readonly', txn => localStoreImpl.remoteDocuments.getAllFromCollectionGroup(txn, collectionGroup, newIndexOffsetSuccessorFromReadTime(readTime, INITIAL_LARGEST_BATCH_ID),
|
|
14359
|
+
/* limit= */ Number.MAX_SAFE_INTEGER))
|
|
14360
|
+
.then(changedDocs => {
|
|
14361
|
+
setMaxReadTime(localStoreImpl, collectionGroup, changedDocs);
|
|
14129
14362
|
return changedDocs;
|
|
14130
14363
|
});
|
|
14131
14364
|
}
|
|
14132
|
-
/**
|
|
14133
|
-
* Reads the newest document change from persistence and moves the internal
|
|
14134
|
-
* synchronization marker forward so that calls to `getNewDocumentChanges()`
|
|
14135
|
-
* only return changes that happened after client initialization.
|
|
14136
|
-
*/
|
|
14365
|
+
/** Sets the collection group's maximum read time from the given documents. */
|
|
14137
14366
|
// PORTING NOTE: Multi-Tab only.
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
.
|
|
14142
|
-
|
|
14143
|
-
|
|
14367
|
+
function setMaxReadTime(localStoreImpl, collectionGroup, changedDocs) {
|
|
14368
|
+
let readTime = SnapshotVersion.min();
|
|
14369
|
+
changedDocs.forEach((_, doc) => {
|
|
14370
|
+
if (doc.readTime.compareTo(readTime) > 0) {
|
|
14371
|
+
readTime = doc.readTime;
|
|
14372
|
+
}
|
|
14144
14373
|
});
|
|
14374
|
+
localStoreImpl.collectionGroupReadTime.set(collectionGroup, readTime);
|
|
14145
14375
|
}
|
|
14146
14376
|
/**
|
|
14147
14377
|
* Creates a new target using the given bundle name, which will be used to
|
|
@@ -14824,7 +15054,7 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
14824
15054
|
});
|
|
14825
15055
|
return PersistencePromise.resolve(results);
|
|
14826
15056
|
}
|
|
14827
|
-
|
|
15057
|
+
getAllFromCollection(transaction, collectionPath, offset) {
|
|
14828
15058
|
let results = mutableDocumentMap();
|
|
14829
15059
|
// Documents are ordered by key, so we can use a prefix scan to narrow down
|
|
14830
15060
|
// the documents we need to match the query against.
|
|
@@ -14839,13 +15069,19 @@ class MemoryRemoteDocumentCacheImpl {
|
|
|
14839
15069
|
// Exclude entries from subcollections.
|
|
14840
15070
|
continue;
|
|
14841
15071
|
}
|
|
14842
|
-
if (document
|
|
15072
|
+
if (indexOffsetComparator(newIndexOffsetFromDocument(document), offset) <= 0) {
|
|
15073
|
+
// The document sorts before the offset.
|
|
14843
15074
|
continue;
|
|
14844
15075
|
}
|
|
14845
15076
|
results = results.insert(document.key, document.mutableCopy());
|
|
14846
15077
|
}
|
|
14847
15078
|
return PersistencePromise.resolve(results);
|
|
14848
15079
|
}
|
|
15080
|
+
getAllFromCollectionGroup(transaction, collectionGroup, offset, limti) {
|
|
15081
|
+
// This method should only be called from the IndexBackfiller if persistence
|
|
15082
|
+
// is enabled.
|
|
15083
|
+
fail();
|
|
15084
|
+
}
|
|
14849
15085
|
forEachDocumentKey(transaction, f) {
|
|
14850
15086
|
return PersistencePromise.forEach(this.docs, (key) => f(key));
|
|
14851
15087
|
}
|
|
@@ -15298,7 +15534,7 @@ class QueryEngine {
|
|
|
15298
15534
|
}
|
|
15299
15535
|
// Retrieve all results for documents that were updated since the last
|
|
15300
15536
|
// limbo-document free remote snapshot.
|
|
15301
|
-
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, lastLimboFreeSnapshotVersion).next(updatedResults => {
|
|
15537
|
+
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, newIndexOffsetSuccessorFromReadTime(lastLimboFreeSnapshotVersion, INITIAL_LARGEST_BATCH_ID)).next(updatedResults => {
|
|
15302
15538
|
// We merge `previousResults` into `updateResults`, since
|
|
15303
15539
|
// `updateResults` is already a DocumentMap. If a document is
|
|
15304
15540
|
// contained in both lists, then its contents are the same.
|
|
@@ -15360,7 +15596,7 @@ class QueryEngine {
|
|
|
15360
15596
|
if (getLogLevel() <= logger.LogLevel.DEBUG) {
|
|
15361
15597
|
logDebug('QueryEngine', 'Using full collection scan to execute query:', stringifyQuery(query));
|
|
15362
15598
|
}
|
|
15363
|
-
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query,
|
|
15599
|
+
return this.localDocumentsView.getDocumentsMatchingQuery(transaction, query, IndexOffset.min());
|
|
15364
15600
|
}
|
|
15365
15601
|
}
|
|
15366
15602
|
|
|
@@ -15421,8 +15657,9 @@ function createWebStorageOnlineStateKey(persistenceKey) {
|
|
|
15421
15657
|
// The WebStorage prefix that plays as a event to indicate the remote documents
|
|
15422
15658
|
// might have changed due to some secondary tabs loading a bundle.
|
|
15423
15659
|
// format of the key is:
|
|
15424
|
-
//
|
|
15425
|
-
|
|
15660
|
+
// firestore_bundle_loaded_v2_<persistenceKey>
|
|
15661
|
+
// The version ending with "v2" stores the list of modified collection groups.
|
|
15662
|
+
const BUNDLE_LOADED_KEY_PREFIX = 'firestore_bundle_loaded_v2';
|
|
15426
15663
|
function createBundleLoadedKey(persistenceKey) {
|
|
15427
15664
|
return `${BUNDLE_LOADED_KEY_PREFIX}_${persistenceKey}`;
|
|
15428
15665
|
}
|
|
@@ -15800,8 +16037,8 @@ class WebStorageSharedClientState {
|
|
|
15800
16037
|
setOnlineState(onlineState) {
|
|
15801
16038
|
this.persistOnlineState(onlineState);
|
|
15802
16039
|
}
|
|
15803
|
-
notifyBundleLoaded() {
|
|
15804
|
-
this.persistBundleLoadedState();
|
|
16040
|
+
notifyBundleLoaded(collectionGroups) {
|
|
16041
|
+
this.persistBundleLoadedState(collectionGroups);
|
|
15805
16042
|
}
|
|
15806
16043
|
shutdown() {
|
|
15807
16044
|
if (this.started) {
|
|
@@ -15885,7 +16122,8 @@ class WebStorageSharedClientState {
|
|
|
15885
16122
|
}
|
|
15886
16123
|
}
|
|
15887
16124
|
else if (storageEvent.key === this.bundleLoadedKey) {
|
|
15888
|
-
|
|
16125
|
+
const collectionGroups = this.fromWebStoreBundleLoadedState(storageEvent.newValue);
|
|
16126
|
+
await Promise.all(collectionGroups.map(cg => this.syncEngine.synchronizeWithChangedDocuments(cg)));
|
|
15889
16127
|
}
|
|
15890
16128
|
});
|
|
15891
16129
|
}
|
|
@@ -15917,8 +16155,9 @@ class WebStorageSharedClientState {
|
|
|
15917
16155
|
const targetMetadata = new QueryTargetMetadata(targetId, state, error);
|
|
15918
16156
|
this.setItem(targetKey, targetMetadata.toWebStorageJSON());
|
|
15919
16157
|
}
|
|
15920
|
-
persistBundleLoadedState() {
|
|
15921
|
-
|
|
16158
|
+
persistBundleLoadedState(collectionGroups) {
|
|
16159
|
+
const json = JSON.stringify(Array.from(collectionGroups));
|
|
16160
|
+
this.setItem(this.bundleLoadedKey, json);
|
|
15922
16161
|
}
|
|
15923
16162
|
/**
|
|
15924
16163
|
* Parses a client state key in WebStorage. Returns null if the key does not
|
|
@@ -15962,6 +16201,9 @@ class WebStorageSharedClientState {
|
|
|
15962
16201
|
fromWebStorageOnlineState(value) {
|
|
15963
16202
|
return SharedOnlineState.fromWebStorageEntry(value);
|
|
15964
16203
|
}
|
|
16204
|
+
fromWebStoreBundleLoadedState(value) {
|
|
16205
|
+
return JSON.parse(value);
|
|
16206
|
+
}
|
|
15965
16207
|
async handleMutationBatchEvent(mutationBatch) {
|
|
15966
16208
|
if (mutationBatch.user.uid !== this.currentUser.uid) {
|
|
15967
16209
|
logDebug(LOG_TAG$a, `Ignoring mutation for non-active user ${mutationBatch.user.uid}`);
|
|
@@ -16078,7 +16320,7 @@ class MemorySharedClientState {
|
|
|
16078
16320
|
}
|
|
16079
16321
|
shutdown() { }
|
|
16080
16322
|
writeSequenceNumber(sequenceNumber) { }
|
|
16081
|
-
notifyBundleLoaded() {
|
|
16323
|
+
notifyBundleLoaded(collectionGroups) {
|
|
16082
16324
|
// No op.
|
|
16083
16325
|
}
|
|
16084
16326
|
}
|
|
@@ -21378,29 +21620,6 @@ class LocalViewChanges {
|
|
|
21378
21620
|
}
|
|
21379
21621
|
}
|
|
21380
21622
|
|
|
21381
|
-
/**
|
|
21382
|
-
* @license
|
|
21383
|
-
* Copyright 2020 Google LLC
|
|
21384
|
-
*
|
|
21385
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
21386
|
-
* you may not use this file except in compliance with the License.
|
|
21387
|
-
* You may obtain a copy of the License at
|
|
21388
|
-
*
|
|
21389
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
21390
|
-
*
|
|
21391
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
21392
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
21393
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21394
|
-
* See the License for the specific language governing permissions and
|
|
21395
|
-
* limitations under the License.
|
|
21396
|
-
*/
|
|
21397
|
-
class BundleLoadResult {
|
|
21398
|
-
constructor(progress, changedDocs) {
|
|
21399
|
-
this.progress = progress;
|
|
21400
|
-
this.changedDocs = changedDocs;
|
|
21401
|
-
}
|
|
21402
|
-
}
|
|
21403
|
-
|
|
21404
21623
|
/**
|
|
21405
21624
|
* @license
|
|
21406
21625
|
* Copyright 2020 Google LLC
|
|
@@ -21455,6 +21674,8 @@ class BundleLoader {
|
|
|
21455
21674
|
this.queries = [];
|
|
21456
21675
|
/** Batched documents to be saved into storage */
|
|
21457
21676
|
this.documents = [];
|
|
21677
|
+
/** The collection groups affected by this bundle. */
|
|
21678
|
+
this.collectionGroups = new Set();
|
|
21458
21679
|
this.progress = bundleInitialProgress(bundleMetadata);
|
|
21459
21680
|
}
|
|
21460
21681
|
/**
|
|
@@ -21474,6 +21695,8 @@ class BundleLoader {
|
|
|
21474
21695
|
if (!element.payload.documentMetadata.exists) {
|
|
21475
21696
|
++documentsLoaded;
|
|
21476
21697
|
}
|
|
21698
|
+
const path = ResourcePath.fromString(element.payload.documentMetadata.name);
|
|
21699
|
+
this.collectionGroups.add(path.get(path.length - 2));
|
|
21477
21700
|
}
|
|
21478
21701
|
else if (element.payload.document) {
|
|
21479
21702
|
this.documents[this.documents.length - 1].document =
|
|
@@ -21504,13 +21727,17 @@ class BundleLoader {
|
|
|
21504
21727
|
* Update the progress to 'Success' and return the updated progress.
|
|
21505
21728
|
*/
|
|
21506
21729
|
async complete() {
|
|
21507
|
-
const
|
|
21730
|
+
const changedDocs = await localStoreApplyBundledDocuments(this.localStore, new BundleConverterImpl(this.serializer), this.documents, this.bundleMetadata.id);
|
|
21508
21731
|
const queryDocumentMap = this.getQueryDocumentMapping(this.documents);
|
|
21509
21732
|
for (const q of this.queries) {
|
|
21510
21733
|
await localStoreSaveNamedQuery(this.localStore, q, queryDocumentMap.get(q.name));
|
|
21511
21734
|
}
|
|
21512
21735
|
this.progress.taskState = 'Success';
|
|
21513
|
-
return
|
|
21736
|
+
return {
|
|
21737
|
+
progress: this.progress,
|
|
21738
|
+
changedCollectionGroups: this.collectionGroups,
|
|
21739
|
+
changedDocs
|
|
21740
|
+
};
|
|
21514
21741
|
}
|
|
21515
21742
|
}
|
|
21516
21743
|
/**
|
|
@@ -22589,9 +22816,9 @@ async function synchronizeViewAndComputeSnapshot(syncEngine, queryView) {
|
|
|
22589
22816
|
* snapshots if needed.
|
|
22590
22817
|
*/
|
|
22591
22818
|
// PORTING NOTE: Multi-Tab only.
|
|
22592
|
-
async function syncEngineSynchronizeWithChangedDocuments(syncEngine) {
|
|
22819
|
+
async function syncEngineSynchronizeWithChangedDocuments(syncEngine, collectionGroup) {
|
|
22593
22820
|
const syncEngineImpl = debugCast(syncEngine);
|
|
22594
|
-
return localStoreGetNewDocumentChanges(syncEngineImpl.localStore).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
|
|
22821
|
+
return localStoreGetNewDocumentChanges(syncEngineImpl.localStore, collectionGroup).then(changes => syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes));
|
|
22595
22822
|
}
|
|
22596
22823
|
/** Applies a mutation state to an existing batch. */
|
|
22597
22824
|
// PORTING NOTE: Multi-Tab only.
|
|
@@ -22756,11 +22983,12 @@ async function syncEngineApplyTargetState(syncEngine, targetId, state, error) {
|
|
|
22756
22983
|
logDebug(LOG_TAG$3, 'Ignoring unexpected query state notification.');
|
|
22757
22984
|
return;
|
|
22758
22985
|
}
|
|
22759
|
-
|
|
22986
|
+
const query = syncEngineImpl.queriesByTarget.get(targetId);
|
|
22987
|
+
if (query && query.length > 0) {
|
|
22760
22988
|
switch (state) {
|
|
22761
22989
|
case 'current':
|
|
22762
22990
|
case 'not-current': {
|
|
22763
|
-
const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore);
|
|
22991
|
+
const changes = await localStoreGetNewDocumentChanges(syncEngineImpl.localStore, queryCollectionGroup(query[0]));
|
|
22764
22992
|
const synthesizedRemoteEvent = RemoteEvent.createSynthesizedRemoteEventForCurrentChange(targetId, state === 'current');
|
|
22765
22993
|
await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngineImpl, changes, synthesizedRemoteEvent);
|
|
22766
22994
|
break;
|
|
@@ -22843,10 +23071,11 @@ function syncEngineEnsureWriteCallbacks(syncEngine) {
|
|
|
22843
23071
|
function syncEngineLoadBundle(syncEngine, bundleReader, task) {
|
|
22844
23072
|
const syncEngineImpl = debugCast(syncEngine);
|
|
22845
23073
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
22846
|
-
loadBundleImpl(syncEngineImpl, bundleReader, task).then(
|
|
22847
|
-
syncEngineImpl.sharedClientState.notifyBundleLoaded();
|
|
23074
|
+
loadBundleImpl(syncEngineImpl, bundleReader, task).then(collectionGroups => {
|
|
23075
|
+
syncEngineImpl.sharedClientState.notifyBundleLoaded(collectionGroups);
|
|
22848
23076
|
});
|
|
22849
23077
|
}
|
|
23078
|
+
/** Loads a bundle and returns the list of affected collection groups. */
|
|
22850
23079
|
async function loadBundleImpl(syncEngine, reader, task) {
|
|
22851
23080
|
try {
|
|
22852
23081
|
const metadata = await reader.getMetadata();
|
|
@@ -22854,7 +23083,7 @@ async function loadBundleImpl(syncEngine, reader, task) {
|
|
|
22854
23083
|
if (skip) {
|
|
22855
23084
|
await reader.close();
|
|
22856
23085
|
task._completeWith(bundleSuccessProgress(metadata));
|
|
22857
|
-
return;
|
|
23086
|
+
return Promise.resolve(new Set());
|
|
22858
23087
|
}
|
|
22859
23088
|
task._updateProgress(bundleInitialProgress(metadata));
|
|
22860
23089
|
const loader = new BundleLoader(metadata, syncEngine.localStore, reader.serializer);
|
|
@@ -22868,18 +23097,17 @@ async function loadBundleImpl(syncEngine, reader, task) {
|
|
|
22868
23097
|
element = await reader.nextElement();
|
|
22869
23098
|
}
|
|
22870
23099
|
const result = await loader.complete();
|
|
22871
|
-
// TODO(b/160876443): This currently raises snapshots with
|
|
22872
|
-
// `fromCache=false` if users already listen to some queries and bundles
|
|
22873
|
-
// has newer version.
|
|
22874
23100
|
await syncEngineEmitNewSnapsAndNotifyLocalStore(syncEngine, result.changedDocs,
|
|
22875
23101
|
/* remoteEvent */ undefined);
|
|
22876
23102
|
// Save metadata, so loading the same bundle will skip.
|
|
22877
23103
|
await localStoreSaveBundle(syncEngine.localStore, metadata);
|
|
22878
23104
|
task._completeWith(result.progress);
|
|
23105
|
+
return Promise.resolve(result.changedCollectionGroups);
|
|
22879
23106
|
}
|
|
22880
23107
|
catch (e) {
|
|
22881
23108
|
logWarn(LOG_TAG$3, `Loading bundle failed with ${e}`);
|
|
22882
23109
|
task._failWith(e);
|
|
23110
|
+
return Promise.resolve(new Set());
|
|
22883
23111
|
}
|
|
22884
23112
|
}
|
|
22885
23113
|
|
|
@@ -22948,7 +23176,6 @@ class IndexedDbOfflineComponentProvider extends MemoryOfflineComponentProvider {
|
|
|
22948
23176
|
}
|
|
22949
23177
|
async initialize(cfg) {
|
|
22950
23178
|
await super.initialize(cfg);
|
|
22951
|
-
await localStoreSynchronizeLastDocumentChangeReadTime(this.localStore);
|
|
22952
23179
|
await this.onlineComponentProvider.initialize(this, cfg);
|
|
22953
23180
|
// Enqueue writes from a previous session
|
|
22954
23181
|
await syncEngineEnsureWriteCallbacks(this.onlineComponentProvider.syncEngine);
|