@tanstack/db-ivm
Version:
Incremental View Maintenance for TanStack DB based on Differential Dataflow
175 lines (174 loc) • 6.43 kB
JavaScript
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
var _inner, _MultiSet_instances, consolidateKeyed_fn, consolidateUnkeyed_fn;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const utils = require("./utils.cjs");
const hash = require("./hashing/hash.cjs");
const _MultiSet = class _MultiSet {
constructor(data = []) {
__privateAdd(this, _MultiSet_instances);
__privateAdd(this, _inner);
__privateSet(this, _inner, data);
}
toString(indent = false) {
return `MultiSet(${JSON.stringify(__privateGet(this, _inner), null, indent ? 2 : void 0)})`;
}
toJSON() {
return JSON.stringify(Array.from(this.getInner()));
}
static fromJSON(json) {
return new _MultiSet(JSON.parse(json));
}
/**
* Apply a function to all records in the collection.
*/
map(f) {
return new _MultiSet(
__privateGet(this, _inner).map(([data, multiplicity]) => [f(data), multiplicity])
);
}
/**
* Filter out records for which a function f(record) evaluates to False.
*/
filter(f) {
return new _MultiSet(__privateGet(this, _inner).filter(([data, _]) => f(data)));
}
/**
* Negate all multiplicities in the collection.
*/
negate() {
return new _MultiSet(
__privateGet(this, _inner).map(([data, multiplicity]) => [data, -multiplicity])
);
}
/**
* Concatenate two collections together.
*/
concat(other) {
const out = [];
utils.chunkedArrayPush(out, __privateGet(this, _inner));
utils.chunkedArrayPush(out, other.getInner());
return new _MultiSet(out);
}
/**
* Produce as output a collection that is logically equivalent to the input
* but which combines identical instances of the same record into one
* (record, multiplicity) pair.
*/
consolidate() {
var _a;
if (__privateGet(this, _inner).length > 0) {
const firstItem = (_a = __privateGet(this, _inner)[0]) == null ? void 0 : _a[0];
if (Array.isArray(firstItem) && firstItem.length === 2) {
return __privateMethod(this, _MultiSet_instances, consolidateKeyed_fn).call(this);
}
}
return __privateMethod(this, _MultiSet_instances, consolidateUnkeyed_fn).call(this);
}
extend(other) {
const otherArray = other instanceof _MultiSet ? other.getInner() : other;
utils.chunkedArrayPush(__privateGet(this, _inner), otherArray);
}
getInner() {
return __privateGet(this, _inner);
}
};
_inner = new WeakMap();
_MultiSet_instances = new WeakSet();
/**
* Private method for consolidating keyed multisets where keys are strings/numbers
* and values are compared by reference equality.
*
* This method provides significant performance improvements over the hash-based approach
* by using WeakMap for object reference tracking and avoiding expensive serialization.
*
* Special handling for join operations: When values are tuples of length 2 (common in joins),
* we unpack them and compare each element individually to maintain proper equality semantics.
*/
consolidateKeyed_fn = function() {
const consolidated = /* @__PURE__ */ new Map();
const values = /* @__PURE__ */ new Map();
const getTupleId = (tuple) => {
if (tuple.length !== 2) {
throw new Error(`Expected tuple of length 2`);
}
const [first, second] = tuple;
return `${utils.globalObjectIdGenerator.getStringId(first)}|${utils.globalObjectIdGenerator.getStringId(second)}`;
};
for (const [data, multiplicity] of __privateGet(this, _inner)) {
if (!Array.isArray(data) || data.length !== 2) {
return __privateMethod(this, _MultiSet_instances, consolidateUnkeyed_fn).call(this);
}
const [key, value] = data;
if (typeof key !== `string` && typeof key !== `number`) {
return __privateMethod(this, _MultiSet_instances, consolidateUnkeyed_fn).call(this);
}
let valueId;
if (Array.isArray(value) && value.length === 2) {
valueId = getTupleId(value);
} else {
valueId = utils.globalObjectIdGenerator.getStringId(value);
}
const compositeKey = key + `|` + valueId;
consolidated.set(
compositeKey,
(consolidated.get(compositeKey) || 0) + multiplicity
);
if (!values.has(compositeKey)) {
values.set(compositeKey, data);
}
}
const result = [];
for (const [compositeKey, multiplicity] of consolidated) {
if (multiplicity !== 0) {
result.push([values.get(compositeKey), multiplicity]);
}
}
return new _MultiSet(result);
};
/**
* Private method for consolidating unkeyed multisets using the original approach.
*/
consolidateUnkeyed_fn = function() {
const consolidated = new utils.DefaultMap(() => 0);
const values = /* @__PURE__ */ new Map();
let hasString = false;
let hasNumber = false;
let hasOther = false;
for (const [data, _] of __privateGet(this, _inner)) {
if (typeof data === `string`) {
hasString = true;
} else if (typeof data === `number`) {
hasNumber = true;
} else {
hasOther = true;
break;
}
}
const requireJson = hasOther || hasString && hasNumber;
for (const [data, multiplicity] of __privateGet(this, _inner)) {
const key = requireJson ? hash.hash(data) : data;
if (requireJson && !values.has(key)) {
values.set(key, data);
}
consolidated.update(key, (count) => count + multiplicity);
}
const result = [];
for (const [key, multiplicity] of consolidated.entries()) {
if (multiplicity !== 0) {
const parsedKey = requireJson ? values.get(key) : key;
result.push([parsedKey, multiplicity]);
}
}
return new _MultiSet(result);
};
let MultiSet = _MultiSet;
exports.MultiSet = MultiSet;
//# sourceMappingURL=multiset.cjs.map
;