Skip to content

Commit

Permalink
Implement Index Scanning (#6008)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian authored Feb 22, 2022
1 parent 9fb82cb commit 13c0895
Show file tree
Hide file tree
Showing 15 changed files with 1,576 additions and 77 deletions.
24 changes: 7 additions & 17 deletions packages/firestore/src/core/target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export function targetGetNotInValues(
target: Target,
fieldIndex: FieldIndex
): ProtoValue[] | null {
const values: ProtoValue[] = [];
const values = new Map</* fieldPath = */ string, ProtoValue>();

for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
for (const fieldFilter of targetGetFieldFiltersForPath(
Expand All @@ -272,14 +272,14 @@ export function targetGetNotInValues(
// Encode equality prefix, which is encoded in the index value before
// the inequality (e.g. `a == 'a' && b != 'b'` is encoded to
// `value != 'ab'`).
values.push(fieldFilter.value);
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
break;
case Operator.NOT_IN:
case Operator.NOT_EQUAL:
// NotIn/NotEqual is always a suffix. There cannot be any remaining
// segments and hence we can return early here.
values.push(fieldFilter.value);
return values;
values.set(segment.fieldPath.canonicalString(), fieldFilter.value);
return Array.from(values.values());
default:
// Remaining filters cannot be used as notIn bounds.
}
Expand Down Expand Up @@ -330,13 +330,8 @@ export function targetGetLowerBound(
filterInclusive = false;
break;
case Operator.NOT_EQUAL:
filterValue = MIN_VALUE;
break;
case Operator.NOT_IN:
const length = (fieldFilter.value.arrayValue!.values || []).length;
filterValue = {
arrayValue: { values: new Array(length).fill(MIN_VALUE) }
};
filterValue = MIN_VALUE;
break;
default:
// Remaining filters cannot be used as lower bounds.
Expand Down Expand Up @@ -415,13 +410,8 @@ export function targetGetUpperBound(
filterInclusive = false;
break;
case Operator.NOT_EQUAL:
filterValue = MAX_VALUE;
break;
case Operator.NOT_IN:
const length = (fieldFilter.value.arrayValue!.values || []).length;
filterValue = {
arrayValue: { values: new Array(length).fill(MIN_VALUE) }
};
filterValue = MAX_VALUE;
break;
default:
// Remaining filters cannot be used as upper bounds.
Expand Down Expand Up @@ -450,7 +440,7 @@ export function targetGetUpperBound(
}

if (segmentValue === undefined) {
// No lower bound exists
// No upper bound exists
return null;
}
values.push(segmentValue);
Expand Down
35 changes: 31 additions & 4 deletions packages/firestore/src/index/index_entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,33 @@ export class IndexEntry {
readonly arrayValue: Uint8Array,
readonly directionalValue: Uint8Array
) {}

/**
* Returns an IndexEntry entry that sorts immediately after the current
* directional value.
*/
successor(): IndexEntry {
const currentLength = this.directionalValue.length;
const newLength =
currentLength === 0 || this.directionalValue[currentLength - 1] === 255
? currentLength + 1
: currentLength;

const successor = new Uint8Array(newLength);
successor.set(this.directionalValue, 0);
if (newLength !== currentLength) {
successor.set([0], this.directionalValue.length);
} else {
++successor[successor.length - 1];
}

return new IndexEntry(
this.indexId,
this.documentKey,
this.arrayValue,
successor
);
}
}

export function indexEntryComparator(
Expand All @@ -36,20 +63,20 @@ export function indexEntryComparator(
return cmp;
}

cmp = DocumentKey.comparator(left.documentKey, right.documentKey);
cmp = compareByteArrays(left.arrayValue, right.arrayValue);
if (cmp !== 0) {
return cmp;
}

cmp = compareByteArrays(left.arrayValue, right.arrayValue);
cmp = compareByteArrays(left.directionalValue, right.directionalValue);
if (cmp !== 0) {
return cmp;
}

return compareByteArrays(left.directionalValue, right.directionalValue);
return DocumentKey.comparator(left.documentKey, right.documentKey);
}

function compareByteArrays(left: Uint8Array, right: Uint8Array): number {
export function compareByteArrays(left: Uint8Array, right: Uint8Array): number {
for (let i = 0; i < left.length && i < right.length; ++i) {
const compare = left[i] - right[i];
if (compare !== 0) {
Expand Down
5 changes: 2 additions & 3 deletions packages/firestore/src/local/index_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,12 @@ export interface IndexManager {

/**
* Returns the documents that match the given target based on the provided
* index.
* index or `null` if the target does not have a matching index.
*/
getDocumentsMatchingTarget(
transaction: PersistenceTransaction,
fieldIndex: FieldIndex,
target: Target
): PersistencePromise<DocumentKeySet>;
): PersistencePromise<DocumentKeySet | null>;

/**
* Returns the next collection group to update. Returns `null` if no group
Expand Down
Loading

0 comments on commit 13c0895

Please sign in to comment.