@tanstack/db
Version:
A reactive client store for building super fast apps on sync
849 lines (848 loc) • 24.2 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const ir = require("./ir.cjs");
function isWhereSubset(subset, superset) {
if (subset === void 0 && superset === void 0) {
return true;
}
if (subset === void 0 && superset !== void 0) {
return false;
}
if (superset === void 0 && subset !== void 0) {
return true;
}
return isWhereSubsetInternal(subset, superset);
}
function makeDisjunction(preds) {
if (preds.length === 0) {
return new ir.Value(false);
}
if (preds.length === 1) {
return preds[0];
}
return new ir.Func(`or`, preds);
}
function convertInToOr(inField) {
const equalities = inField.values.map(
(value) => new ir.Func(`eq`, [inField.ref, new ir.Value(value)])
);
return makeDisjunction(equalities);
}
function isWhereSubsetInternal(subset, superset) {
if (subset.type === `val` && subset.value === false) {
return true;
}
if (areExpressionsEqual(subset, superset)) {
return true;
}
if (superset.type === `func` && superset.name === `and`) {
return superset.args.every(
(arg) => isWhereSubsetInternal(subset, arg)
);
}
if (subset.type === `func` && subset.name === `and`) {
return subset.args.some(
(arg) => isWhereSubsetInternal(arg, superset)
);
}
if (subset.type === `func` && subset.name === `in`) {
const inField = extractInField(subset);
if (inField) {
return isWhereSubsetInternal(convertInToOr(inField), superset);
}
}
if (superset.type === `func` && superset.name === `in`) {
const inField = extractInField(superset);
if (inField) {
return isWhereSubsetInternal(subset, convertInToOr(inField));
}
}
if (subset.type === `func` && subset.name === `or`) {
return subset.args.every(
(arg) => isWhereSubsetInternal(arg, superset)
);
}
if (superset.type === `func` && superset.name === `or`) {
return superset.args.some(
(arg) => isWhereSubsetInternal(subset, arg)
);
}
if (subset.type === `func` && superset.type === `func`) {
const subsetFunc = subset;
const supersetFunc = superset;
const subsetField = extractComparisonField(subsetFunc);
const supersetField = extractComparisonField(supersetFunc);
if (subsetField && supersetField && areRefsEqual(subsetField.ref, supersetField.ref)) {
return isComparisonSubset(
subsetFunc,
subsetField.value,
supersetFunc,
supersetField.value
);
}
}
return false;
}
function combineWherePredicates(predicates, operation, simplifyFn) {
const emptyValue = operation === `and` ? true : false;
const identityValue = operation === `and` ? true : false;
if (predicates.length === 0) {
return { type: `val`, value: emptyValue };
}
if (predicates.length === 1) {
return predicates[0];
}
const flatPredicates = [];
for (const pred of predicates) {
if (pred.type === `func` && pred.name === operation) {
flatPredicates.push(...pred.args);
} else {
flatPredicates.push(pred);
}
}
const grouped = groupPredicatesByField(flatPredicates);
const simplified = [];
for (const [field, preds] of grouped.entries()) {
if (field === null) {
simplified.push(...preds);
} else {
const result = simplifyFn(preds);
if (result) {
simplified.push(result);
}
}
}
if (simplified.length === 0) {
return { type: `val`, value: identityValue };
}
if (simplified.length === 1) {
return simplified[0];
}
return {
type: `func`,
name: operation,
args: simplified
};
}
function unionWherePredicates(predicates) {
return combineWherePredicates(predicates, `or`, unionSameFieldPredicates);
}
function minusWherePredicates(fromPredicate, subtractPredicate) {
if (subtractPredicate === void 0) {
return fromPredicate ?? { type: `val`, value: true };
}
if (fromPredicate === void 0) {
return {
type: `func`,
name: `not`,
args: [subtractPredicate]
};
}
if (isWhereSubset(fromPredicate, subtractPredicate)) {
return { type: `val`, value: false };
}
const commonConditions = findCommonConditions(
fromPredicate,
subtractPredicate
);
if (commonConditions.length > 0) {
const fromWithoutCommon = removeConditions(fromPredicate, commonConditions);
const subtractWithoutCommon = removeConditions(
subtractPredicate,
commonConditions
);
const simplifiedDifference = minusWherePredicates(
fromWithoutCommon,
subtractWithoutCommon
);
if (simplifiedDifference !== null) {
return combineConditions([...commonConditions, simplifiedDifference]);
}
}
if (fromPredicate.type === `func` && subtractPredicate.type === `func`) {
const result = minusSameFieldPredicates(fromPredicate, subtractPredicate);
if (result !== null) {
return result;
}
}
return null;
}
function minusSameFieldPredicates(fromPred, subtractPred) {
const fromField = extractComparisonField(fromPred) || extractEqualityField(fromPred) || extractInField(fromPred);
const subtractField = extractComparisonField(subtractPred) || extractEqualityField(subtractPred) || extractInField(subtractPred);
if (!fromField || !subtractField || !areRefsEqual(fromField.ref, subtractField.ref)) {
return null;
}
if (fromPred.name === `in` && subtractPred.name === `in`) {
const fromInField = fromField;
const subtractInField = subtractField;
const remainingValues = fromInField.values.filter(
(v) => !arrayIncludesWithSet(
subtractInField.values,
v,
subtractInField.primitiveSet ?? null,
subtractInField.areAllPrimitives
)
);
if (remainingValues.length === 0) {
return { type: `val`, value: false };
}
if (remainingValues.length === 1) {
return {
type: `func`,
name: `eq`,
args: [fromField.ref, { type: `val`, value: remainingValues[0] }]
};
}
return {
type: `func`,
name: `in`,
args: [fromField.ref, { type: `val`, value: remainingValues }]
};
}
if (fromPred.name === `in` && subtractPred.name === `eq`) {
const fromInField = fromField;
const subtractValue = subtractField.value;
const remainingValues = fromInField.values.filter(
(v) => !areValuesEqual(v, subtractValue)
);
if (remainingValues.length === 0) {
return { type: `val`, value: false };
}
if (remainingValues.length === 1) {
return {
type: `func`,
name: `eq`,
args: [fromField.ref, { type: `val`, value: remainingValues[0] }]
};
}
return {
type: `func`,
name: `in`,
args: [fromField.ref, { type: `val`, value: remainingValues }]
};
}
if (fromPred.name === `eq` && subtractPred.name === `eq`) {
const fromValue = fromField.value;
const subtractValue = subtractField.value;
if (areValuesEqual(fromValue, subtractValue)) {
return { type: `val`, value: false };
}
return fromPred;
}
const fromComp = extractComparisonField(fromPred);
const subtractComp = extractComparisonField(subtractPred);
if (fromComp && subtractComp && areRefsEqual(fromComp.ref, subtractComp.ref)) {
const result = minusRangePredicates(
fromPred,
fromComp.value,
subtractPred,
subtractComp.value
);
return result;
}
return null;
}
function minusRangePredicates(fromFunc, fromValue, subtractFunc, subtractValue) {
const fromOp = fromFunc.name;
const subtractOp = subtractFunc.name;
const ref = (extractComparisonField(fromFunc) || extractEqualityField(fromFunc)).ref;
if (fromOp === `gt` && subtractOp === `gt`) {
if (fromValue < subtractValue) {
return {
type: `func`,
name: `and`,
args: [
fromFunc,
{
type: `func`,
name: `lte`,
args: [ref, { type: `val`, value: subtractValue }]
}
]
};
}
return fromFunc;
}
if (fromOp === `gte` && subtractOp === `gte`) {
if (fromValue < subtractValue) {
return {
type: `func`,
name: `and`,
args: [
fromFunc,
{
type: `func`,
name: `lt`,
args: [ref, { type: `val`, value: subtractValue }]
}
]
};
}
return fromFunc;
}
if (fromOp === `gt` && subtractOp === `gte`) {
if (fromValue < subtractValue) {
return {
type: `func`,
name: `and`,
args: [
fromFunc,
{
type: `func`,
name: `lt`,
args: [ref, { type: `val`, value: subtractValue }]
}
]
};
}
return fromFunc;
}
if (fromOp === `gte` && subtractOp === `gt`) {
if (fromValue <= subtractValue) {
return {
type: `func`,
name: `and`,
args: [
fromFunc,
{
type: `func`,
name: `lte`,
args: [ref, { type: `val`, value: subtractValue }]
}
]
};
}
return fromFunc;
}
if (fromOp === `lt` && subtractOp === `lt`) {
if (fromValue > subtractValue) {
return {
type: `func`,
name: `and`,
args: [
{
type: `func`,
name: `gte`,
args: [ref, { type: `val`, value: subtractValue }]
},
fromFunc
]
};
}
return fromFunc;
}
if (fromOp === `lte` && subtractOp === `lte`) {
if (fromValue > subtractValue) {
return {
type: `func`,
name: `and`,
args: [
{
type: `func`,
name: `gt`,
args: [ref, { type: `val`, value: subtractValue }]
},
fromFunc
]
};
}
return fromFunc;
}
if (fromOp === `lt` && subtractOp === `lte`) {
if (fromValue > subtractValue) {
return {
type: `func`,
name: `and`,
args: [
{
type: `func`,
name: `gt`,
args: [ref, { type: `val`, value: subtractValue }]
},
fromFunc
]
};
}
return fromFunc;
}
if (fromOp === `lte` && subtractOp === `lt`) {
if (fromValue >= subtractValue) {
return {
type: `func`,
name: `and`,
args: [
{
type: `func`,
name: `gte`,
args: [ref, { type: `val`, value: subtractValue }]
},
fromFunc
]
};
}
return fromFunc;
}
return null;
}
function isOrderBySubset(subset, superset) {
if (!subset || subset.length === 0) {
return true;
}
if (!superset || superset.length === 0) {
return false;
}
if (subset.length > superset.length) {
return false;
}
for (let i = 0; i < subset.length; i++) {
const subClause = subset[i];
const superClause = superset[i];
if (!areExpressionsEqual(subClause.expression, superClause.expression)) {
return false;
}
if (!areCompareOptionsEqual(
subClause.compareOptions,
superClause.compareOptions
)) {
return false;
}
}
return true;
}
function isLimitSubset(subset, superset) {
if (superset === void 0) {
return true;
}
if (subset === void 0) {
return false;
}
return subset <= superset;
}
function isOffsetLimitSubset(subset, superset) {
const subsetOffset = subset.offset ?? 0;
const supersetOffset = superset.offset ?? 0;
if (supersetOffset > subsetOffset) {
return false;
}
if (superset.limit === void 0) {
return true;
}
if (subset.limit === void 0) {
return false;
}
const subsetEnd = subsetOffset + subset.limit;
const supersetEnd = supersetOffset + superset.limit;
return subsetEnd <= supersetEnd;
}
function isPredicateSubset(subset, superset) {
if (superset.limit !== void 0) {
if (!areWhereClausesEqual(subset.where, superset.where)) {
return false;
}
return isOrderBySubset(subset.orderBy, superset.orderBy) && isOffsetLimitSubset(subset, superset);
}
return isWhereSubset(subset.where, superset.where) && isOrderBySubset(subset.orderBy, superset.orderBy) && isOffsetLimitSubset(subset, superset);
}
function areWhereClausesEqual(a, b) {
if (a === void 0 && b === void 0) {
return true;
}
if (a === void 0 || b === void 0) {
return false;
}
return areExpressionsEqual(a, b);
}
function findCommonConditions(predicate1, predicate2) {
const conditions1 = extractAllConditions(predicate1);
const conditions2 = extractAllConditions(predicate2);
const common = [];
for (const cond1 of conditions1) {
for (const cond2 of conditions2) {
if (areExpressionsEqual(cond1, cond2)) {
if (!common.some((c) => areExpressionsEqual(c, cond1))) {
common.push(cond1);
}
break;
}
}
}
return common;
}
function extractAllConditions(predicate) {
if (predicate.type === `func` && predicate.name === `and`) {
const conditions = [];
for (const arg of predicate.args) {
conditions.push(...extractAllConditions(arg));
}
return conditions;
}
return [predicate];
}
function removeConditions(predicate, conditionsToRemove) {
if (predicate.type === `func` && predicate.name === `and`) {
const remainingArgs = predicate.args.filter(
(arg) => !conditionsToRemove.some(
(cond) => areExpressionsEqual(arg, cond)
)
);
if (remainingArgs.length === 0) {
return void 0;
} else if (remainingArgs.length === 1) {
return remainingArgs[0];
} else {
return {
type: `func`,
name: `and`,
args: remainingArgs
};
}
}
return predicate;
}
function combineConditions(conditions) {
if (conditions.length === 0) {
return { type: `val`, value: true };
} else if (conditions.length === 1) {
return conditions[0];
} else {
const flattenedConditions = [];
for (const condition of conditions) {
if (condition.type === `func` && condition.name === `and`) {
flattenedConditions.push(...condition.args);
} else {
flattenedConditions.push(condition);
}
}
if (flattenedConditions.length === 1) {
return flattenedConditions[0];
} else {
return {
type: `func`,
name: `and`,
args: flattenedConditions
};
}
}
}
function findPredicateWithOperator(predicates, operator, value) {
return predicates.find((p) => {
if (p.type === `func`) {
const f = p;
const field = extractComparisonField(f);
return f.name === operator && field && areValuesEqual(field.value, value);
}
return false;
});
}
function areExpressionsEqual(a, b) {
if (a.type !== b.type) {
return false;
}
if (a.type === `val` && b.type === `val`) {
return areValuesEqual(a.value, b.value);
}
if (a.type === `ref` && b.type === `ref`) {
return areRefsEqual(a, b);
}
if (a.type === `func` && b.type === `func`) {
const aFunc = a;
const bFunc = b;
if (aFunc.name !== bFunc.name) {
return false;
}
if (aFunc.args.length !== bFunc.args.length) {
return false;
}
return aFunc.args.every(
(arg, i) => areExpressionsEqual(arg, bFunc.args[i])
);
}
return false;
}
function areValuesEqual(a, b) {
if (a === b) {
return true;
}
if (typeof a === `number` && typeof b === `number` && isNaN(a) && isNaN(b)) {
return true;
}
if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
if (typeof a === `object` && typeof b === `object` && a !== null && b !== null) {
return a === b;
}
return false;
}
function areRefsEqual(a, b) {
if (a.path.length !== b.path.length) {
return false;
}
return a.path.every((segment, i) => segment === b.path[i]);
}
function isPrimitive(value) {
return value === null || value === void 0 || typeof value === `string` || typeof value === `number` || typeof value === `boolean`;
}
function areAllPrimitives(values) {
return values.every(isPrimitive);
}
function arrayIncludesWithSet(array, value, primitiveSet, arrayIsAllPrimitives) {
if (primitiveSet) {
if (arrayIsAllPrimitives || isPrimitive(value)) {
return primitiveSet.has(value);
}
return false;
}
return array.some((v) => areValuesEqual(v, value));
}
function maxValue(a, b) {
if (a instanceof Date && b instanceof Date) {
return a.getTime() > b.getTime() ? a : b;
}
return Math.max(a, b);
}
function minValue(a, b) {
if (a instanceof Date && b instanceof Date) {
return a.getTime() < b.getTime() ? a : b;
}
return Math.min(a, b);
}
function areCompareOptionsEqual(a, b) {
return a.direction === b.direction;
}
function extractComparisonField(func) {
if ([`eq`, `gt`, `gte`, `lt`, `lte`].includes(func.name)) {
const firstArg = func.args[0];
const secondArg = func.args[1];
if (firstArg?.type === `ref` && secondArg?.type === `val`) {
return {
ref: firstArg,
value: secondArg.value
};
}
}
return null;
}
function extractEqualityField(func) {
if (func.name === `eq`) {
const firstArg = func.args[0];
const secondArg = func.args[1];
if (firstArg?.type === `ref` && secondArg?.type === `val`) {
return {
ref: firstArg,
value: secondArg.value
};
}
}
return null;
}
function extractInField(func) {
if (func.name === `in`) {
const firstArg = func.args[0];
const secondArg = func.args[1];
if (firstArg?.type === `ref` && secondArg?.type === `val` && Array.isArray(secondArg.value)) {
let values = secondArg.value;
const allPrimitives = areAllPrimitives(values);
let primitiveSet = null;
if (allPrimitives && values.length > 10) {
primitiveSet = new Set(values);
if (primitiveSet.size < values.length) {
values = Array.from(primitiveSet);
}
}
return {
ref: firstArg,
values,
areAllPrimitives: allPrimitives,
primitiveSet
};
}
}
return null;
}
function isComparisonSubset(subsetFunc, subsetValue, supersetFunc, supersetValue) {
const subOp = subsetFunc.name;
const superOp = supersetFunc.name;
if (subOp === superOp) {
if (subOp === `eq`) {
if (isPrimitive(subsetValue) && isPrimitive(supersetValue)) {
return subsetValue === supersetValue;
}
return areValuesEqual(subsetValue, supersetValue);
} else if (subOp === `gt`) {
return subsetValue >= supersetValue;
} else if (subOp === `gte`) {
return subsetValue >= supersetValue;
} else if (subOp === `lt`) {
return subsetValue <= supersetValue;
} else if (subOp === `lte`) {
return subsetValue <= supersetValue;
}
}
if (subOp === `eq` && superOp === `gt`) {
return subsetValue > supersetValue;
}
if (subOp === `eq` && superOp === `gte`) {
return subsetValue >= supersetValue;
}
if (subOp === `eq` && superOp === `lt`) {
return subsetValue < supersetValue;
}
if (subOp === `eq` && superOp === `lte`) {
return subsetValue <= supersetValue;
}
if (subOp === `gt` && superOp === `gte`) {
return subsetValue >= supersetValue;
}
if (subOp === `gte` && superOp === `gt`) {
return subsetValue > supersetValue;
}
if (subOp === `lt` && superOp === `lte`) {
return subsetValue <= supersetValue;
}
if (subOp === `lte` && superOp === `lt`) {
return subsetValue < supersetValue;
}
return false;
}
function groupPredicatesByField(predicates) {
const groups = /* @__PURE__ */ new Map();
for (const pred of predicates) {
let fieldKey = null;
if (pred.type === `func`) {
const func = pred;
const field = extractComparisonField(func) || extractEqualityField(func) || extractInField(func);
if (field) {
fieldKey = field.ref.path.join(`.`);
}
}
const group = groups.get(fieldKey) || [];
group.push(pred);
groups.set(fieldKey, group);
}
return groups;
}
function unionSameFieldPredicates(predicates) {
if (predicates.length === 1) {
return predicates[0];
}
let maxGt = null;
let maxGte = null;
let minLt = null;
let minLte = null;
const eqValues = /* @__PURE__ */ new Set();
const inValues = /* @__PURE__ */ new Set();
const otherPredicates = [];
for (const pred of predicates) {
if (pred.type === `func`) {
const func = pred;
const field = extractComparisonField(func);
if (field) {
const value = field.value;
if (func.name === `gt`) {
maxGt = maxGt === null ? value : minValue(maxGt, value);
} else if (func.name === `gte`) {
maxGte = maxGte === null ? value : minValue(maxGte, value);
} else if (func.name === `lt`) {
minLt = minLt === null ? value : maxValue(minLt, value);
} else if (func.name === `lte`) {
minLte = minLte === null ? value : maxValue(minLte, value);
} else if (func.name === `eq`) {
eqValues.add(value);
} else {
otherPredicates.push(pred);
}
} else {
const inField = extractInField(func);
if (inField) {
for (const val of inField.values) {
inValues.add(val);
}
} else {
otherPredicates.push(pred);
}
}
} else {
otherPredicates.push(pred);
}
}
if (eqValues.size > 1 || eqValues.size > 0 && inValues.size > 0) {
const allValues = [...eqValues, ...inValues];
const ref = predicates.find((p) => {
if (p.type === `func`) {
const field = extractComparisonField(p) || extractInField(p);
return field !== null;
}
return false;
});
if (ref && ref.type === `func`) {
const field = extractComparisonField(ref) || extractInField(ref);
if (field) {
return {
type: `func`,
name: `in`,
args: [
field.ref,
{ type: `val`, value: allValues }
]
};
}
}
}
const result = [];
if (maxGt !== null && maxGte !== null) {
const pred = maxGte <= maxGt ? findPredicateWithOperator(predicates, `gte`, maxGte) : findPredicateWithOperator(predicates, `gt`, maxGt);
if (pred) result.push(pred);
} else if (maxGt !== null) {
const pred = findPredicateWithOperator(predicates, `gt`, maxGt);
if (pred) result.push(pred);
} else if (maxGte !== null) {
const pred = findPredicateWithOperator(predicates, `gte`, maxGte);
if (pred) result.push(pred);
}
if (minLt !== null && minLte !== null) {
const pred = minLte >= minLt ? findPredicateWithOperator(predicates, `lte`, minLte) : findPredicateWithOperator(predicates, `lt`, minLt);
if (pred) result.push(pred);
} else if (minLt !== null) {
const pred = findPredicateWithOperator(predicates, `lt`, minLt);
if (pred) result.push(pred);
} else if (minLte !== null) {
const pred = findPredicateWithOperator(predicates, `lte`, minLte);
if (pred) result.push(pred);
}
if (eqValues.size === 1 && inValues.size === 0) {
const pred = findPredicateWithOperator(predicates, `eq`, [...eqValues][0]);
if (pred) result.push(pred);
}
if (eqValues.size === 0 && inValues.size > 0) {
result.push(
predicates.find((p) => {
if (p.type === `func`) {
return p.name === `in`;
}
return false;
})
);
}
result.push(...otherPredicates);
if (result.length === 0) {
return { type: `val`, value: true };
}
if (result.length === 1) {
return result[0];
}
return {
type: `func`,
name: `or`,
args: result
};
}
exports.isLimitSubset = isLimitSubset;
exports.isOffsetLimitSubset = isOffsetLimitSubset;
exports.isOrderBySubset = isOrderBySubset;
exports.isPredicateSubset = isPredicateSubset;
exports.isWhereSubset = isWhereSubset;
exports.minusWherePredicates = minusWherePredicates;
exports.unionWherePredicates = unionWherePredicates;
//# sourceMappingURL=predicate-utils.cjs.map