@tanstack/db
Version:
A reactive client store for building super fast apps on sync
188 lines (187 loc) • 6.23 kB
JavaScript
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const dbIvm = require("@tanstack/db-ivm");
const arrayUtils = require("../utils/array-utils.cjs");
const btree = require("../utils/btree.cjs");
const comparison = require("../utils/comparison.cjs");
const errorMessage = require("../error-message.cjs");
const baseIndex = require("./base-index.cjs");
class BTreeIndex extends baseIndex.BaseIndex {
constructor() {
super(...arguments);
this.ay = new btree.BTree(
(a, b) => this.compareFn(a.value, b.value)
);
this.ax = /* @__PURE__ */ new Map();
this.aK = /* @__PURE__ */ new Set();
}
add(key, item) {
const value = this.evaluateAddedValue(key, item);
const exact = comparison.normalizeValue(value);
this.dF(key, value, exact);
this.addRangeValue(value);
this.aK.add(key);
}
dF(key, value, exact) {
const group = this.ax.get(exact);
if (group) {
group.keys.set(key, value);
group.buckets?.get(exact).add(key);
return;
}
const ordered = this.ay.get({ value });
if (ordered) {
ordered.buckets ??= /* @__PURE__ */ new Map([
[comparison.normalizeValue(ordered.value), new Set(ordered.keys.keys())]
]);
ordered.buckets.set(exact, /* @__PURE__ */ new Set([key]));
ordered.keys.set(key, value);
this.ax.set(exact, ordered);
} else {
const created = {
value,
keys: /* @__PURE__ */ new Map([[key, value]]),
representativeKey: key
};
this.ay.set(created, created);
this.ax.set(exact, created);
}
}
remove(key, item) {
let value;
try {
value = this.evaluateIndexExpression(item);
} catch (error) {
baseIndex.warnRemovalFailure(key, error);
return;
}
const exact = comparison.normalizeValue(value);
this.cf(key, exact);
this.removeRangeValue(value);
this.aK.delete(key);
}
cf(key, exact) {
const group = this.ax.get(exact);
if (!group?.keys.has(key)) return;
const bucket = group.buckets?.get(exact);
if (group.buckets && !bucket?.has(key)) return;
if (group.keys.size === 1) {
this.ay.delete(group);
}
bucket?.delete(key);
group.keys.delete(key);
if (bucket?.size === 0 || group.keys.size === 0) {
this.ax.delete(exact);
group.buckets?.delete(exact);
}
if (group.keys.size && group.representativeKey === key) {
const [nextKey, nextValue] = (group.nextRepresentative ??= group.keys.entries()).next().value;
group.representativeKey = nextKey;
group.value = nextValue;
}
}
update(key, oldItem, newItem) {
let oldValue;
let newValue;
try {
oldValue = this.evaluateIndexExpression(oldItem);
newValue = this.evaluateIndexExpression(newItem);
} catch {
this.remove(key, oldItem);
this.add(key, newItem);
return;
}
const oldExact = comparison.normalizeValue(oldValue);
const newExact = comparison.normalizeValue(newValue);
const group = this.ax.get(newExact);
const containsKey = group?.buckets ? group.buckets.get(newExact)?.has(key) : group?.keys.has(key);
if (comparison.areSameValueZeroEqual(oldExact, newExact) && containsKey) {
group.keys.set(key, newValue);
if (group.representativeKey === key) group.value = newValue;
this.removeRangeValue(oldValue);
this.addRangeValue(newValue);
return;
}
this.cf(key, oldExact);
this.removeRangeValue(oldValue);
this.dF(key, newValue, newExact);
this.addRangeValue(newValue);
this.aK.add(key);
}
build(entries) {
this.clear();
for (const [key, item] of entries) this.add(key, item);
}
clear() {
this.ay.clear();
this.ax.clear();
this.aK.clear();
this.clearRangeValues();
}
lookup(operation, value) {
const result = this.lookupSupportedOperation(operation, value);
if (result !== void 0) return result;
throw new Error(
errorMessage.devBuild() && process.env.NODE_ENV !== `production` ? `Operation ${operation} not supported by BTreeIndex` : errorMessage.codedMessage(101, { operation })
);
}
get keyCount() {
return this.aK.size;
}
equalityLookup(value) {
return new Set(this.exactBucketKeys(value));
}
exactBucketKeys(value) {
const exact = comparison.normalizeValue(value);
const group = this.ax.get(exact);
return group?.buckets ? group.buckets.get(exact) : group?.keys.keys();
}
rangeQuery(options = {}) {
const result = /* @__PURE__ */ new Set();
if (this.ay.size === 0) return result;
const { from, to, fromInclusive = true, toInclusive = true } = options;
const low = "from" in options ? { value: from } : this.ay.minKey();
const high = "to" in options ? { value: to } : this.ay.maxKey();
this.ay.forRange(
low,
high,
!("to" in options) || toInclusive,
(sortKey, group) => {
if ("from" in options && !fromInclusive && this.compareFn(sortKey.value, from) === 0)
return;
for (const key of group.keys.keys()) result.add(key);
}
);
return result;
}
eb(n, from, filterFn, reversed) {
const result = [];
let pair = reversed ? this.ay.nextLowerPair(from) : this.ay.nextHigherPair(from);
while (pair && result.length < n) {
for (const key of [...pair[1].keys.keys()].sort(
reversed ? arrayUtils.compareKeysReversed : dbIvm.compareKeys
)) {
if (filterFn?.(key) ?? true) result.push(key);
if (result.length >= n) break;
}
if (result.length >= n) break;
pair = reversed ? this.ay.nextLowerPair(pair[0]) : this.ay.nextHigherPair(pair[0]);
}
return result;
}
take(n, from, filterFn) {
return this.eb(n, { value: from }, filterFn, false);
}
takeFromStart(n, filterFn) {
return this.eb(n, void 0, filterFn, false);
}
takeReversed(n, from, filterFn) {
return this.eb(n, { value: from }, filterFn, true);
}
takeReversedFromEnd(n, filterFn) {
return this.eb(n, void 0, filterFn, true);
}
}
baseIndex.builtInIndexResolverNames.set(BTreeIndex, "BTreeIndex");
exports.BTreeIndex = BTreeIndex;
//# sourceMappingURL=btree-index.cjs.map