UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

271 lines (270 loc) 8.94 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); function findIndexForField(indexes, fieldPath) { for (const index of indexes.values()) { if (index.matchesField(fieldPath)) { return index; } } return void 0; } function intersectSets(sets) { if (sets.length === 0) return /* @__PURE__ */ new Set(); if (sets.length === 1) return new Set(sets[0]); let result = new Set(sets[0]); for (let i = 1; i < sets.length; i++) { const newResult = /* @__PURE__ */ new Set(); for (const item of result) { if (sets[i].has(item)) { newResult.add(item); } } result = newResult; } return result; } function unionSets(sets) { const result = /* @__PURE__ */ new Set(); for (const set of sets) { for (const item of set) { result.add(item); } } return result; } function optimizeExpressionWithIndexes(expression, indexes) { return optimizeQueryRecursive(expression, indexes); } function optimizeQueryRecursive(expression, indexes) { if (expression.type === `func`) { switch (expression.name) { case `eq`: case `gt`: case `gte`: case `lt`: case `lte`: return optimizeSimpleComparison(expression, indexes); case `and`: return optimizeAndExpression(expression, indexes); case `or`: return optimizeOrExpression(expression, indexes); case `in`: return optimizeInArrayExpression(expression, indexes); } } return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } function optimizeCompoundRangeQuery(expression, indexes) { if (expression.type !== `func` || expression.args.length < 2) { return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } const fieldOperations = /* @__PURE__ */ new Map(); for (const arg of expression.args) { if (arg.type === `func` && [`gt`, `gte`, `lt`, `lte`].includes(arg.name)) { const rangeOp = arg; if (rangeOp.args.length === 2) { const leftArg = rangeOp.args[0]; const rightArg = rangeOp.args[1]; let fieldArg = null; let valueArg = null; let operation = rangeOp.name; if (leftArg.type === `ref` && rightArg.type === `val`) { fieldArg = leftArg; valueArg = rightArg; } else if (leftArg.type === `val` && rightArg.type === `ref`) { fieldArg = rightArg; valueArg = leftArg; switch (operation) { case `gt`: operation = `lt`; break; case `gte`: operation = `lte`; break; case `lt`: operation = `gt`; break; case `lte`: operation = `gte`; break; } } if (fieldArg && valueArg) { const fieldPath = fieldArg.path; const fieldKey = fieldPath.join(`.`); const value = valueArg.value; if (!fieldOperations.has(fieldKey)) { fieldOperations.set(fieldKey, []); } fieldOperations.get(fieldKey).push({ operation, value }); } } } } for (const [fieldKey, operations] of fieldOperations) { if (operations.length >= 2) { const fieldPath = fieldKey.split(`.`); const index = findIndexForField(indexes, fieldPath); if (index && index.supports(`gt`) && index.supports(`lt`)) { let from = void 0; let to = void 0; let fromInclusive = true; let toInclusive = true; for (const { operation, value } of operations) { switch (operation) { case `gt`: if (from === void 0 || value > from) { from = value; fromInclusive = false; } break; case `gte`: if (from === void 0 || value > from) { from = value; fromInclusive = true; } break; case `lt`: if (to === void 0 || value < to) { to = value; toInclusive = false; } break; case `lte`: if (to === void 0 || value < to) { to = value; toInclusive = true; } break; } } const matchingKeys = index.rangeQuery({ from, to, fromInclusive, toInclusive }); return { canOptimize: true, matchingKeys }; } } } return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } function optimizeSimpleComparison(expression, indexes) { if (expression.type !== `func` || expression.args.length !== 2) { return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } const leftArg = expression.args[0]; const rightArg = expression.args[1]; let fieldArg = null; let valueArg = null; let operation = expression.name; if (leftArg.type === `ref` && rightArg.type === `val`) { fieldArg = leftArg; valueArg = rightArg; } else if (leftArg.type === `val` && rightArg.type === `ref`) { fieldArg = rightArg; valueArg = leftArg; switch (operation) { case `gt`: operation = `lt`; break; case `gte`: operation = `lte`; break; case `lt`: operation = `gt`; break; case `lte`: operation = `gte`; break; } } if (fieldArg && valueArg) { const fieldPath = fieldArg.path; const index = findIndexForField(indexes, fieldPath); if (index) { const queryValue = valueArg.value; const indexOperation = operation; if (!index.supports(indexOperation)) { return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } const matchingKeys = index.lookup(indexOperation, queryValue); return { canOptimize: true, matchingKeys }; } } return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } function optimizeAndExpression(expression, indexes) { if (expression.type !== `func` || expression.args.length < 2) { return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } const compoundRangeResult = optimizeCompoundRangeQuery(expression, indexes); if (compoundRangeResult.canOptimize) { return compoundRangeResult; } const results = []; for (const arg of expression.args) { const result = optimizeQueryRecursive(arg, indexes); if (result.canOptimize) { results.push(result); } } if (results.length > 0) { const allMatchingSets = results.map((r) => r.matchingKeys); const intersectedKeys = intersectSets(allMatchingSets); return { canOptimize: true, matchingKeys: intersectedKeys }; } return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } function optimizeOrExpression(expression, indexes) { if (expression.type !== `func` || expression.args.length < 2) { return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } const results = []; for (const arg of expression.args) { const result = optimizeQueryRecursive(arg, indexes); if (result.canOptimize) { results.push(result); } } if (results.length > 0) { const allMatchingSets = results.map((r) => r.matchingKeys); const unionedKeys = unionSets(allMatchingSets); return { canOptimize: true, matchingKeys: unionedKeys }; } return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } function optimizeInArrayExpression(expression, indexes) { if (expression.type !== `func` || expression.args.length !== 2) { return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } const fieldArg = expression.args[0]; const arrayArg = expression.args[1]; if (fieldArg.type === `ref` && arrayArg.type === `val` && Array.isArray(arrayArg.value)) { const fieldPath = fieldArg.path; const values = arrayArg.value; const index = findIndexForField(indexes, fieldPath); if (index) { if (index.supports(`in`)) { const matchingKeys = index.lookup(`in`, values); return { canOptimize: true, matchingKeys }; } else if (index.supports(`eq`)) { const matchingKeys = /* @__PURE__ */ new Set(); for (const value of values) { const keysForValue = index.lookup(`eq`, value); for (const key of keysForValue) { matchingKeys.add(key); } } return { canOptimize: true, matchingKeys }; } } } return { canOptimize: false, matchingKeys: /* @__PURE__ */ new Set() }; } exports.findIndexForField = findIndexForField; exports.intersectSets = intersectSets; exports.optimizeExpressionWithIndexes = optimizeExpressionWithIndexes; exports.unionSets = unionSets; //# sourceMappingURL=index-optimization.cjs.map