in packages/firestore/src/core/target.ts [381:461]
export function targetGetUpperBound(
target: Target,
fieldIndex: FieldIndex
): Bound | null {
const values: ProtoValue[] = [];
let inclusive = true;
// For each segment, retrieve an upper bound if there is a suitable filter or
// endAt.
for (const segment of fieldIndexGetDirectionalSegments(fieldIndex)) {
let segmentValue: ProtoValue | undefined = undefined;
let segmentInclusive = true;
// Process all filters to find a value for the current field segment
for (const fieldFilter of targetGetFieldFiltersForPath(
target,
segment.fieldPath
)) {
let filterValue: ProtoValue | undefined = undefined;
let filterInclusive = true;
switch (fieldFilter.op) {
case Operator.GREATER_THAN_OR_EQUAL:
case Operator.GREATER_THAN:
filterValue = valuesGetUpperBound(fieldFilter.value);
filterInclusive = false;
break;
case Operator.EQUAL:
case Operator.IN:
case Operator.LESS_THAN_OR_EQUAL:
filterValue = fieldFilter.value;
break;
case Operator.LESS_THAN:
filterValue = fieldFilter.value;
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) }
};
break;
default:
// Remaining filters cannot be used as upper bounds.
}
if (valuesMin(segmentValue, filterValue) === filterValue) {
segmentValue = filterValue;
segmentInclusive = filterInclusive;
}
}
// If there is a endAt bound, compare the values against the existing
// boundary to see if we can narrow the scope.
if (target.endAt !== null) {
for (let i = 0; i < target.orderBy.length; ++i) {
const orderBy = target.orderBy[i];
if (orderBy.field.isEqual(segment.fieldPath)) {
const cursorValue = target.endAt.position[i];
if (valuesMin(segmentValue, cursorValue) === cursorValue) {
segmentValue = cursorValue;
segmentInclusive = target.endAt.inclusive;
}
break;
}
}
}
if (segmentValue === undefined) {
// No lower bound exists
return null;
}
values.push(segmentValue);
inclusive &&= segmentInclusive;
}
return new Bound(values, inclusive);
}