UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

849 lines (848 loc) 24.2 kB
"use strict"; 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