30
30
31
31
#include " Firestore/core/src/firebase/firestore/auth/user.h"
32
32
#include " Firestore/core/src/firebase/firestore/local/leveldb_transaction.h"
33
- #include " Firestore/core/src/firebase/firestore/model/document_key.h"
34
33
#include " Firestore/core/src/firebase/firestore/model/resource_path.h"
35
34
#include " Firestore/core/src/firebase/firestore/util/hard_assert.h"
36
35
#include " Firestore/core/src/firebase/firestore/util/string_apple.h"
46
45
using Firestore::StringView;
47
46
using firebase::firestore::auth::User;
48
47
using firebase::firestore::model::DocumentKey;
48
+ using firebase::firestore::model::DocumentKeySet;
49
49
using firebase::firestore::model::ResourcePath;
50
50
using leveldb::DB;
51
51
using leveldb::Iterator;
@@ -364,11 +364,16 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID
364
364
NSMutableArray *result = [NSMutableArray array ];
365
365
FSTLevelDBDocumentMutationKey *rowKey = [[FSTLevelDBDocumentMutationKey alloc ] init ];
366
366
for (; indexIterator->Valid (); indexIterator->Next ()) {
367
- // Only consider rows matching exactly the specific key of interest. Note that because we order
368
- // by path first, and we order terminators before path separators, we'll encounter all the
369
- // index rows for documentKey contiguously. In particular, all the rows for documentKey will
370
- // occur before any rows for documents nested in a subcollection beneath documentKey so we can
371
- // stop as soon as we hit any such row.
367
+ // Only consider rows matching exactly the specific key of interest. Index rows have this
368
+ // form (with markers in brackets):
369
+ //
370
+ // <User>user <Path>collection <Path>doc <BatchId>2 <Terminator>
371
+ // <User>user <Path>collection <Path>doc <BatchId>3 <Terminator>
372
+ // <User>user <Path>collection <Path>doc <Path>sub <Path>doc <BatchId>3 <Terminator>
373
+ //
374
+ // Note that Path markers sort after BatchId markers so this means that when searching for
375
+ // collection/doc, all the entries for it will be contiguous in the table, allowing a break
376
+ // after any mismatch.
372
377
if (!absl::StartsWith (indexIterator->key (), indexPrefix) ||
373
378
![rowKey decodeKey: indexIterator->key ()] ||
374
379
DocumentKey{rowKey.documentKey } != documentKey) {
@@ -396,6 +401,43 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID
396
401
return result;
397
402
}
398
403
404
+ - (NSArray <FSTMutationBatch *> *)allMutationBatchesAffectingDocumentKeys :
405
+ (const DocumentKeySet &)documentKeys {
406
+ NSString *userID = self.userID ;
407
+
408
+ // Take a pass through the document keys and collect the set of unique mutation batchIDs that
409
+ // affect them all. Some batches can affect more than one key.
410
+ std::set<FSTBatchID> batchIDs;
411
+
412
+ auto indexIterator = _db.currentTransaction ->NewIterator ();
413
+ FSTLevelDBDocumentMutationKey *rowKey = [[FSTLevelDBDocumentMutationKey alloc ] init ];
414
+ for (const DocumentKey &documentKey : documentKeys) {
415
+ std::string indexPrefix =
416
+ [FSTLevelDBDocumentMutationKey keyPrefixWithUserID: userID resourcePath: documentKey.path ()];
417
+ for (indexIterator->Seek (indexPrefix); indexIterator->Valid (); indexIterator->Next ()) {
418
+ // Only consider rows matching exactly the specific key of interest. Index rows have this
419
+ // form (with markers in brackets):
420
+ //
421
+ // <User>user <Path>collection <Path>doc <BatchId>2 <Terminator>
422
+ // <User>user <Path>collection <Path>doc <BatchId>3 <Terminator>
423
+ // <User>user <Path>collection <Path>doc <Path>sub <Path>doc <BatchId>3 <Terminator>
424
+ //
425
+ // Note that Path markers sort after BatchId markers so this means that when searching for
426
+ // collection/doc, all the entries for it will be contiguous in the table, allowing a break
427
+ // after any mismatch.
428
+ if (!absl::StartsWith (indexIterator->key (), indexPrefix) ||
429
+ ![rowKey decodeKey: indexIterator->key ()] ||
430
+ DocumentKey{rowKey.documentKey } != documentKey) {
431
+ break ;
432
+ }
433
+
434
+ batchIDs.insert (rowKey.batchID );
435
+ }
436
+ }
437
+
438
+ return [self allMutationBatchesWithBatchIDs: batchIDs];
439
+ }
440
+
399
441
- (NSArray <FSTMutationBatch *> *)allMutationBatchesAffectingQuery : (FSTQuery *)query {
400
442
HARD_ASSERT (![query isDocumentQuery ], " Document queries shouldn't go down this path" );
401
443
NSString *userID = self.userID ;
@@ -417,11 +459,10 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID
417
459
// index for more than a single document so the associated batchIDs will be neither necessarily
418
460
// unique nor in order. This means an efficient simultaneous scan isn't possible.
419
461
std::string indexPrefix =
420
- [FSTLevelDBDocumentMutationKey keyPrefixWithUserID: self . userID resourcePath: queryPath];
462
+ [FSTLevelDBDocumentMutationKey keyPrefixWithUserID: userID resourcePath: queryPath];
421
463
auto indexIterator = _db.currentTransaction ->NewIterator ();
422
464
indexIterator->Seek (indexPrefix);
423
465
424
- NSMutableArray *result = [NSMutableArray array ];
425
466
FSTLevelDBDocumentMutationKey *rowKey = [[FSTLevelDBDocumentMutationKey alloc ] init ];
426
467
427
468
// Collect up unique batchIDs encountered during a scan of the index. Use a set<FSTBatchID> to
@@ -430,7 +471,7 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID
430
471
// This method is faster than performing lookups of the keys with _db->Get and keeping a hash of
431
472
// batchIDs that have already been looked up. The performance difference is minor for small
432
473
// numbers of keys but > 30% faster for larger numbers of keys.
433
- std::set<FSTBatchID> uniqueBatchIds ;
474
+ std::set<FSTBatchID> uniqueBatchIDs ;
434
475
for (; indexIterator->Valid (); indexIterator->Next ()) {
435
476
if (!absl::StartsWith (indexIterator->key (), indexPrefix) ||
436
477
![rowKey decodeKey: indexIterator->key ()]) {
@@ -444,14 +485,25 @@ - (nullable FSTMutationBatch *)nextMutationBatchAfterBatchID:(FSTBatchID)batchID
444
485
continue ;
445
486
}
446
487
447
- uniqueBatchIds .insert (rowKey.batchID );
488
+ uniqueBatchIDs .insert (rowKey.batchID );
448
489
}
449
490
491
+ return [self allMutationBatchesWithBatchIDs: uniqueBatchIDs];
492
+ }
493
+
494
+ /* *
495
+ * Constructs an array of matching batches, sorted by batchID to ensure that multiple mutations
496
+ * affecting the same document key are applied in order.
497
+ */
498
+ - (NSArray <FSTMutationBatch *> *)allMutationBatchesWithBatchIDs :
499
+ (const std::set<FSTBatchID> &)batchIDs {
500
+ NSMutableArray *result = [NSMutableArray array ];
501
+ NSString *userID = self.userID ;
502
+
450
503
// Given an ordered set of unique batchIDs perform a skipping scan over the main table to find
451
504
// the mutation batches.
452
505
auto mutationIterator = _db.currentTransaction ->NewIterator ();
453
-
454
- for (FSTBatchID batchID : uniqueBatchIds) {
506
+ for (FSTBatchID batchID : batchIDs) {
455
507
std::string mutationKey = [FSTLevelDBMutationKey keyWithUserID: userID batchID: batchID];
456
508
mutationIterator->Seek (mutationKey);
457
509
if (!mutationIterator->Valid () || mutationIterator->key () != mutationKey) {
0 commit comments