Skip to content

Commit 6c5576f

Browse files
committed
Refactor getMinOffsetFromFieldIndexes to follow Android implementation. Add comments.
1 parent 665d71f commit 6c5576f

File tree

3 files changed

+77
-42
lines changed

3 files changed

+77
-42
lines changed

packages/firestore/src/local/indexeddb_index_manager.ts

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import { FieldPath, ResourcePath } from '../model/path';
5151
import { TargetIndexMatcher } from '../model/target_index_matcher';
5252
import { isArray, refValue } from '../model/values';
5353
import { Value as ProtoValue } from '../protos/firestore_proto_api';
54-
import {debugAssert, hardAssert} from '../util/assert';
54+
import { debugAssert, fail, hardAssert } from '../util/assert';
5555
import { logDebug } from '../util/log';
5656
import { immediateSuccessor, primitiveComparator } from '../util/misc';
5757
import { ObjectMap } from '../util/obj_map';
@@ -970,53 +970,22 @@ export class IndexedDbIndexManager implements IndexManager {
970970
transaction: PersistenceTransaction,
971971
collectionGroup: string
972972
): PersistencePromise<IndexOffset> {
973-
return this.getFieldIndexes(transaction, collectionGroup)
974-
.next(fieldIndexes => this.getMinOffsetFromFieldIndexes(fieldIndexes));
975-
}
976-
977-
getMinOffsetFromFieldIndexes(fieldIndexes: FieldIndex[]): IndexOffset {
978-
hardAssert(
979-
fieldIndexes.length !== 0,
980-
"Found empty index group when looking for least recent index offset.");
981-
982-
let minOffset: IndexOffset = fieldIndexes[0].indexState.offset;
983-
let maxBatchId: number = minOffset.largestBatchId;
984-
for (const fieldIndex of fieldIndexes) {
985-
const newOffset: IndexOffset = fieldIndex.indexState.offset;
986-
if (indexOffsetComparator(newOffset, minOffset) < 0) {
987-
minOffset = newOffset;
988-
}
989-
if (maxBatchId < newOffset.largestBatchId) {
990-
maxBatchId = newOffset.largestBatchId;
991-
}
992-
}
993-
return new IndexOffset(
994-
minOffset.readTime,
995-
minOffset.documentKey,
996-
maxBatchId
973+
return this.getFieldIndexes(transaction, collectionGroup).next(
974+
getMinOffsetFromFieldIndexes
997975
);
998976
}
999977

1000978
getMinOffset(
1001979
transaction: PersistenceTransaction,
1002980
target: Target
1003981
): PersistencePromise<IndexOffset> {
1004-
let offset: IndexOffset | undefined;
1005-
return PersistencePromise.forEach(
982+
return PersistencePromise.mapArray(
1006983
this.getSubTargets(target),
1007-
(subTarget: Target) => {
1008-
return this.getFieldIndex(transaction, subTarget).next(index => {
1009-
if (!index) {
1010-
offset = IndexOffset.min();
1011-
} else if (
1012-
!offset ||
1013-
indexOffsetComparator(index.indexState.offset, offset) < 0
1014-
) {
1015-
offset = index.indexState.offset;
1016-
}
1017-
});
1018-
}
1019-
).next(() => offset!);
984+
(subTarget: Target) =>
985+
this.getFieldIndex(transaction, subTarget).next(index =>
986+
!index ? fail('Target cannot be served from index') : index
987+
)
988+
).next(getMinOffsetFromFieldIndexes);
1020989
}
1021990
}
1022991

@@ -1062,3 +1031,23 @@ function indexStateStore(
10621031
): SimpleDbStore<DbIndexStateKey, DbIndexState> {
10631032
return getStore<DbIndexStateKey, DbIndexState>(txn, DbIndexStateStore);
10641033
}
1034+
1035+
function getMinOffsetFromFieldIndexes(fieldIndexes: FieldIndex[]): IndexOffset {
1036+
hardAssert(
1037+
fieldIndexes.length !== 0,
1038+
'Found empty index group when looking for least recent index offset.'
1039+
);
1040+
1041+
let minOffset: IndexOffset = fieldIndexes[0].indexState.offset;
1042+
let maxBatchId: number = minOffset.largestBatchId;
1043+
for (let i = 1; i < fieldIndexes.length; i++) {
1044+
const newOffset: IndexOffset = fieldIndexes[i].indexState.offset;
1045+
if (indexOffsetComparator(newOffset, minOffset) < 0) {
1046+
minOffset = newOffset;
1047+
}
1048+
if (maxBatchId < newOffset.largestBatchId) {
1049+
maxBatchId = newOffset.largestBatchId;
1050+
}
1051+
}
1052+
return new IndexOffset(minOffset.readTime, minOffset.documentKey, maxBatchId);
1053+
}

packages/firestore/src/local/local_documents_view.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,21 @@ export class LocalDocumentsView {
373373
}
374374
}
375375

376+
/**
377+
* Given a collection group, returns the next documents that follow the provided offset, along
378+
* with an updated batch ID.
379+
*
380+
* <p>The documents returned by this method are ordered by remote version from the provided
381+
* offset. If there are no more remote documents after the provided offset, documents with
382+
* mutations in order of batch id from the offset are returned. Since all documents in a batch are
383+
* returned together, the total number of documents returned can exceed {@code count}.
384+
*
385+
* @param transaction
386+
* @param collectionGroup The collection group for the documents.
387+
* @param offset The offset to index into.
388+
* @param count The number of documents to return
389+
* @return A LocalWriteResult with the documents that follow the provided offset and the last processed batch id.
390+
*/
376391
getNextDocuments(
377392
transaction: PersistenceTransaction,
378393
collectionGroup: string,
@@ -391,6 +406,10 @@ export class LocalDocumentsView {
391406
count - originalDocs.size
392407
)
393408
: PersistencePromise.resolve(newOverlayMap());
409+
// The callsite will use the largest batch ID together with the latest read time to create
410+
// a new index offset. Since we only process batch IDs if all remote documents have been read,
411+
// no overlay will increase the overall read time. This is why we only need to special case
412+
// the batch id.
394413
let largestBatchId = INITIAL_LARGEST_BATCH_ID;
395414
let modifiedDocs = originalDocs;
396415
return overlaysPromise.next(overlays => {

packages/firestore/src/local/persistence_promise.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,33 @@ export class PersistencePromise<T> {
244244
return this.waitFor(promises);
245245
}
246246

247+
/**
248+
* Concurrently map all array elements through asynchronous function.
249+
*/
250+
static mapArray<T, U>(
251+
array: T[],
252+
f: (t: T) => PersistencePromise<U>
253+
): PersistencePromise<U[]> {
254+
return new PersistencePromise<U[]>((resolve, reject) => {
255+
const expectedCount = array.length;
256+
const results: U[] = new Array(expectedCount);
257+
let resolvedCount = 0;
258+
for (let i = 0; i < expectedCount; i++) {
259+
const current = i;
260+
f(array[current]).next(
261+
result => {
262+
results[current] = result;
263+
++resolvedCount;
264+
if (resolvedCount === expectedCount) {
265+
resolve(results);
266+
}
267+
},
268+
err => reject(err)
269+
);
270+
}
271+
});
272+
}
273+
247274
/**
248275
* An alternative to recursive PersistencePromise calls, that avoids
249276
* potential memory problems from unbounded chains of promises.
@@ -255,11 +282,11 @@ export class PersistencePromise<T> {
255282
action: () => PersistencePromise<void>
256283
): PersistencePromise<void> {
257284
return new PersistencePromise<void>((resolve, reject) => {
258-
const process = () => {
285+
const process = (): void => {
259286
if (condition() === true) {
260287
action().next(() => {
261288
process();
262-
}, reject)
289+
}, reject);
263290
} else {
264291
resolve();
265292
}

0 commit comments

Comments
 (0)