lib/query-range.js
/**
QueryRange represents a LIMIT + OFFSET pair and provides high-level methods
for comparing, copying and joining ranges. It also provides syntax sugar
around infinity (the absence of a defined LIMIT or OFFSET).
*/
export default class QueryRange {
static infinite() {
return new QueryRange({limit: null, offset: null});
}
static rangeWithUnion(a, b) {
if (a.isInfinite() || b.isInfinite()) {
return QueryRange.infinite();
}
if (!a.isContiguousWith(b)) {
throw new Error('You cannot union ranges which do not touch or intersect.');
}
return new QueryRange({
start: Math.min(a.start, b.start),
end: Math.max(a.end, b.end),
});
}
static rangesBySubtracting(a, b) {
if (!b) {
return [];
}
if (a.isInfinite() || b.isInfinite()) {
throw new Error("You cannot subtract infinite ranges.");
}
const uncovered = []
if (b.start > a.start) {
uncovered.push(new QueryRange({start: a.start, end: Math.min(a.end, b.start)}))
}
if (b.end < a.end) {
uncovered.push(new QueryRange({start: Math.max(a.start, b.end), end: a.end}));
}
return uncovered;
}
get start() {
return this.offset;
}
get end() {
return this.offset + this.limit;
}
constructor({limit, offset, start, end} = {}) {
this.limit = limit;
this.offset = offset;
if ((start !== undefined) && (offset === undefined)) {
this.offset = start;
}
if ((end !== undefined) && (limit === undefined)) {
this.limit = end - this.offset;
}
if (this.limit === undefined) {
throw new Error("You must specify a limit");
}
if (this.offset === undefined) {
throw new Error("You must specify an offset");
}
}
clone() {
const {limit, offset} = this;
return new QueryRange({limit, offset});
}
isInfinite() {
return (this.limit === null) && (this.offset === null);
}
isEqual(b) {
return (this.start === b.start) && (this.end === b.end);
}
// Returns true if joining the two ranges would not result in empty space.
// ie: they intersect or touch
isContiguousWith(b) {
if (this.isInfinite() || b.isInfinite()) {
return true;
}
return ((this.start <= b.start) && (b.start <= this.end)) || ((this.start <= b.end) && (b.end <= this.end));
}
toString() {
return `QueryRange{${this.start} - ${this.end}}`;
}
}