Skip to content

Commit 13c0895

Browse files
Implement Index Scanning (#6008)
1 parent 9fb82cb commit 13c0895

15 files changed

+1576
-77
lines changed

packages/firestore/src/core/target.ts

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ export function targetGetNotInValues(
259259
target: Target,
260260
fieldIndex: FieldIndex
261261
): ProtoValue[] | null {
262-
const values: ProtoValue[] = [];
262+
const values = new Map</* fieldPath = */ string, ProtoValue>();
263263

264264
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
265265
for (const fieldFilter of targetGetFieldFiltersForPath(
@@ -272,14 +272,14 @@ export function targetGetNotInValues(
272272
// Encode equality prefix, which is encoded in the index value before
273273
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
274274
// `value != 'ab'`).
275-
values.push(fieldFilter.value);
275+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
276276
break;
277277
case Operator.NOT_IN:
278278
case Operator.NOT_EQUAL:
279279
// NotIn/NotEqual is always a suffix. There cannot be any remaining
280280
// segments and hence we can return early here.
281-
values.push(fieldFilter.value);
282-
return values;
281+
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
282+
return Array.from(values.values());
283283
default:
284284
// Remaining filters cannot be used as notIn bounds.
285285
}
@@ -330,13 +330,8 @@ export function targetGetLowerBound(
330330
filterInclusive = false;
331331
break;
332332
case Operator.NOT_EQUAL:
333-
filterValue = MIN_VALUE;
334-
break;
335333
case Operator.NOT_IN:
336-
const length = (fieldFilter.value.arrayValue!.values || []).length;
337-
filterValue = {
338-
arrayValue: { values: new Array(length).fill(MIN_VALUE) }
339-
};
334+
filterValue = MIN_VALUE;
340335
break;
341336
default:
342337
// Remaining filters cannot be used as lower bounds.
@@ -415,13 +410,8 @@ export function targetGetUpperBound(
415410
filterInclusive = false;
416411
break;
417412
case Operator.NOT_EQUAL:
418-
filterValue = MAX_VALUE;
419-
break;
420413
case Operator.NOT_IN:
421-
const length = (fieldFilter.value.arrayValue!.values || []).length;
422-
filterValue = {
423-
arrayValue: { values: new Array(length).fill(MIN_VALUE) }
424-
};
414+
filterValue = MAX_VALUE;
425415
break;
426416
default:
427417
// Remaining filters cannot be used as upper bounds.
@@ -450,7 +440,7 @@ export function targetGetUpperBound(
450440
}
451441

452442
if (segmentValue === undefined) {
453-
// No lower bound exists
443+
// No upper bound exists
454444
return null;
455445
}
456446
values.push(segmentValue);

packages/firestore/src/index/index_entry.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,33 @@ export class IndexEntry {
2525
readonly arrayValue: Uint8Array,
2626
readonly directionalValue: Uint8Array
2727
) {}
28+
29+
/**
30+
* Returns an IndexEntry entry that sorts immediately after the current
31+
* directional value.
32+
*/
33+
successor(): IndexEntry {
34+
const currentLength = this.directionalValue.length;
35+
const newLength =
36+
currentLength === 0 || this.directionalValue[currentLength - 1] === 255
37+
? currentLength + 1
38+
: currentLength;
39+
40+
const successor = new Uint8Array(newLength);
41+
successor.set(this.directionalValue, 0);
42+
if (newLength !== currentLength) {
43+
successor.set([0], this.directionalValue.length);
44+
} else {
45+
++successor[successor.length - 1];
46+
}
47+
48+
return new IndexEntry(
49+
this.indexId,
50+
this.documentKey,
51+
this.arrayValue,
52+
successor
53+
);
54+
}
2855
}
2956

3057
export function indexEntryComparator(
@@ -36,20 +63,20 @@ export function indexEntryComparator(
3663
return cmp;
3764
}
3865

39-
cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
66+
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
4067
if (cmp !== 0) {
4168
return cmp;
4269
}
4370

44-
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
71+
cmp = compareByteArrays(left.directionalValue, right.directionalValue);
4572
if (cmp !== 0) {
4673
return cmp;
4774
}
4875

49-
return compareByteArrays(left.directionalValue, right.directionalValue);
76+
return DocumentKey.comparator(left.documentKey, right.documentKey);
5077
}
5178

52-
function compareByteArrays(left: Uint8Array, right: Uint8Array): number {
79+
export function compareByteArrays(left: Uint8Array, right: Uint8Array): number {
5380
for (let i = 0; i < left.length && i < right.length; ++i) {
5481
const compare = left[i] - right[i];
5582
if (compare !== 0) {

packages/firestore/src/local/index_manager.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,12 @@ export interface IndexManager {
103103

104104
/**
105105
* Returns the documents that match the given target based on the provided
106-
* index.
106+
* index or `null` if the target does not have a matching index.
107107
*/
108108
getDocumentsMatchingTarget(
109109
transaction: PersistenceTransaction,
110-
fieldIndex: FieldIndex,
111110
target: Target
112-
): PersistencePromise<DocumentKeySet>;
111+
): PersistencePromise<DocumentKeySet | null>;
113112

114113
/**
115114
* Returns the next collection group to update. Returns `null` if no group

0 commit comments

Comments
 (0)