UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1,436 lines (1,435 loc) 49 kB
"use strict"; Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const dbIvm = require("@tanstack/db-ivm"); const optimizer = require("../optimizer.cjs"); const errors = require("../../errors.cjs"); const virtualProps = require("../../virtual-props.cjs"); const ir = require("../ir.cjs"); const autoIndex = require("../../indexes/auto-index.cjs"); const utils = require("../../utils.cjs"); const evaluators = require("./evaluators.cjs"); const joins = require("./joins.cjs"); const groupBy = require("./group-by.cjs"); const lazyTargets = require("./lazy-targets.cjs"); const orderBy = require("./order-by.cjs"); const select = require("./select.cjs"); const INCLUDES_ROUTING = /* @__PURE__ */ Symbol(`includesRouting`); const INCLUDES_PUBLIC_KEY = /* @__PURE__ */ Symbol(`includesPublicKey`); const FN_SELECT_STATE = /* @__PURE__ */ Symbol(`fnSelectState`); const SKIP_INCLUDE = /* @__PURE__ */ Symbol(`skipInclude`); function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache = /* @__PURE__ */ new WeakMap(), queryMapping = /* @__PURE__ */ new WeakMap(), parentKeyStream, childCorrelationField) { const cachedResult = parentKeyStream === void 0 ? cache.get(rawQuery) : void 0; if (cachedResult) { return cachedResult; } validateQueryStructure(rawQuery); const { optimizedQuery, sourceWhereClauses } = optimizer.optimizeQuery(rawQuery); let query = optimizedQuery; queryMapping.set(query, rawQuery); mapNestedQueries(query, rawQuery, queryMapping); const allInputs = { ...inputs }; bindSourceInputs(rawQuery, allInputs); const aliasToCollectionId = {}; const aliasRemapping = {}; const sources = {}; const { alias: mainSource, collectionId: mainCollectionId, pipeline: initialPipeline, sources: fromSources, sourceIncludes, directIncludes, isUnionFrom } = processFromClause( query.from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses ); Object.assign(sources, fromSources); let pipeline = initialPipeline; const childCorrelationAlias = childCorrelationField?.path[0]; const joinsParentAfterJoins = !isUnionFrom && parentKeyStream !== void 0 && childCorrelationField !== void 0 && childCorrelationAlias !== mainSource; if (!isUnionFrom && parentKeyStream && childCorrelationField && !joinsParentAfterJoins) { const mainInput = sources[mainSource]; let filteredMainInput = mainInput; const childFieldPath = childCorrelationField.path.slice(1); const childRekeyed = mainInput.pipe( dbIvm.map(([key, row]) => { const correlationValue = getNestedValue(row, childFieldPath); return [correlationValue, [key, row]]; }) ); const joined = childRekeyed.pipe(dbIvm.join(parentKeyStream, `inner`)); filteredMainInput = joined.pipe( dbIvm.filter(([_correlationValue, [childSide]]) => { return childSide != null; }), dbIvm.map(([correlationValue, [childSide, parentSide]]) => { const [childKey, childRow] = childSide; const tagged = { ...childRow, __correlationKey: correlationValue, [INCLUDES_PUBLIC_KEY]: childKey }; if (parentSide != null) { tagged.__parentContext = parentSide; } const effectiveKey = parentSide != null ? dbIvm.serializeValue([childKey, parentSide]) : childKey; return [effectiveKey, tagged]; }) ); sources[mainSource] = filteredMainInput; pipeline = wrapInputWithAlias(filteredMainInput, mainSource); } if (query.join && query.join.length > 0) { pipeline = joins.processJoins( pipeline, query.join, sources, mainCollectionId, mainSource, allInputs, cache, queryMapping, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, rawQuery, compileQuery, aliasToCollectionId, aliasRemapping, sourceWhereClauses, parentKeyStream !== void 0 && !joinsParentAfterJoins ); } if (joinsParentAfterJoins) { const compiledChildCorrelation = evaluators.compileExpression(childCorrelationField); pipeline = pipeline.pipe( dbIvm.map( ([key, row]) => [compiledChildCorrelation(row), [key, row]] ), dbIvm.join(parentKeyStream, `inner`), dbIvm.filter(([_correlationValue, [childSide]]) => childSide != null), dbIvm.map(([correlationValue, [childSide, parentSide]]) => { const [childKey, row] = childSide; const namespaced = { ...row }; namespaced[mainSource] = { ...namespaced[mainSource], __correlationKey: correlationValue, [INCLUDES_PUBLIC_KEY]: childKey }; if (parentSide != null) { Object.assign(namespaced, parentSide); namespaced.__parentContext = parentSide; } const effectiveKey = parentSide != null ? dbIvm.serializeValue([childKey, parentSide]) : childKey; return [effectiveKey, namespaced]; }) ); } if (query.where && query.where.length > 0) { for (const where of query.where) { const whereExpression = ir.getWhereExpression(where); const compiledWhere = evaluators.compileExpression(whereExpression); pipeline = pipeline.pipe( dbIvm.filter(([_key, namespacedRow]) => { return evaluators.toBooleanPredicate(compiledWhere(namespacedRow)); }) ); } } if (query.fnWhere && query.fnWhere.length > 0) { for (const fnWhere of query.fnWhere) { pipeline = pipeline.pipe( dbIvm.filter(([_key, namespacedRow]) => { return evaluators.toBooleanPredicate(fnWhere(namespacedRow)); }) ); } } const includesResults = !query.select ? [...directIncludes] : []; const includesRoutingFns = []; for (const { sourceAlias, include } of sourceIncludes) { const projectedPaths = query.select != null ? findProjectedSourceIncludePaths( query.select, sourceAlias, include.resultPath ) : query.fnSelect ? [] : [ { path: [sourceAlias, ...include.resultPath], guards: [] } ]; if (projectedPaths.length === 0) { continue; } for (const { path: resultPath, guards } of projectedPaths) { const fieldName = getUniqueIncludesRoutingKey( `${sourceAlias}.${resultPath.join(`.`)}`, includesRoutingFns ); const compiledGuards = guards.map((guard) => ({ condition: evaluators.compileExpression(guard.condition), expected: guard.expected })); includesResults.push({ ...include, fieldName, resultPath }); includesRoutingFns.push({ fieldName, getRouting: (nsRow) => { if (!matchesConditionalSelectGuards(compiledGuards, nsRow)) { return { active: false, correlationKey: null, parentContext: null }; } return nsRow[sourceAlias]?.[INCLUDES_ROUTING]?.[include.fieldName] ?? { active: false, correlationKey: null, parentContext: null }; } }); } } if (query.select && directIncludes.length > 0) { for (const include of directIncludes) { const projectedPaths = findProjectedResultIncludePaths( query.select, include.resultPath ); for (const { path: resultPath, guards } of projectedPaths) { const fieldName = getUniqueIncludesRoutingKey( resultPath.join(`.`), includesRoutingFns ); const compiledGuards = guards.map((guard) => ({ condition: evaluators.compileExpression(guard.condition), expected: guard.expected })); includesResults.push({ ...include, fieldName, resultPath }); includesRoutingFns.push({ fieldName, getRouting: (nsRow) => { if (!matchesConditionalSelectGuards(compiledGuards, nsRow)) { return { active: false, correlationKey: null, parentContext: null }; } return nsRow[INCLUDES_ROUTING]?.[include.fieldName] ?? { active: false, correlationKey: null, parentContext: null }; } }); } } } if (query.select) { const includesEntries = extractIncludesFromSelect(query.select); if (includesEntries.length > 0) { query = { ...query, select: { ...query.select } }; } for (const { key, path, subquery, guards } of includesEntries) { const fieldName = getUniqueIncludesRoutingKey(key, includesRoutingFns); const compiledCorrelation = evaluators.compileExpression(subquery.correlationField); const compiledGuards = guards.map((guard) => ({ condition: evaluators.compileExpression(guard.condition), expected: guard.expected })); let parentKeys; if (subquery.parentProjection && subquery.parentProjection.length > 0) { const compiledProjections = subquery.parentProjection.map((ref) => ({ alias: ref.path[0], field: ref.path.slice(1), compiled: evaluators.compileExpression(ref) })); parentKeys = pipeline.pipe( dbIvm.map(([_key, nsRow]) => { if (!matchesConditionalSelectGuards(compiledGuards, nsRow)) { return [SKIP_INCLUDE, null]; } const parentContext = {}; for (const proj of compiledProjections) { if (!parentContext[proj.alias]) { parentContext[proj.alias] = {}; } const value = proj.compiled(nsRow); let target = parentContext[proj.alias]; for (let i = 0; i < proj.field.length - 1; i++) { if (!target[proj.field[i]]) { target[proj.field[i]] = {}; } target = target[proj.field[i]]; } target[proj.field[proj.field.length - 1]] = value; } return [compiledCorrelation(nsRow), parentContext]; }) ); } else { parentKeys = pipeline.pipe( dbIvm.map(([_key, nsRow]) => { if (!matchesConditionalSelectGuards(compiledGuards, nsRow)) { return [SKIP_INCLUDE, null]; } return [compiledCorrelation(nsRow), null]; }) ); } parentKeys = parentKeys.pipe( dbIvm.filter(([correlationValue]) => correlationValue !== SKIP_INCLUDE) ); parentKeys = parentKeys.pipe( dbIvm.reduce( (values) => values.map(([v, mult]) => [v, mult > 0 ? 1 : 0]) ) ); const childSourceAlias = subquery.childCorrelationField.path[0]; const directChildCollection = subquery.query.from.type === `collectionRef` ? subquery.query.from.collection : void 0; const lazyTargets$1 = lazyTargets.getLazyLoadTargets( subquery.query, subquery.query.from, childSourceAlias, subquery.childCorrelationField, directChildCollection, aliasRemapping ); if (lazyTargets$1.length > 0) { for (const target of lazyTargets$1) { lazySources.add(target.sourceId); } for (const target of lazyTargets$1) { const targetFieldName = target.path[0]; if (targetFieldName) { autoIndex.ensureIndexForField(targetFieldName, target.path, target.collection); } } const initialKeys = getStaticDemandKeys( rawQuery, subquery.correlationField ); const demandPlans = lazyTargets$1.map( (target) => joins.registerLazyDemandPlan(callbacks, target, initialKeys) ); const demandWeights = /* @__PURE__ */ new Map(); parentKeys = parentKeys.pipe( dbIvm.tap((data) => { for (const [[correlationValue], weight] of data.getInner()) { if (correlationValue == null) continue; const encoded = dbIvm.serializeValue(correlationValue); const previous = demandWeights.get(encoded); const nextWeight = (previous?.weight ?? 0) + weight; if (nextWeight === 0) { demandWeights.delete(encoded); } else { demandWeights.set(encoded, { key: correlationValue, weight: nextWeight }); } } const keys = new Set( [...demandWeights.values()].filter(({ weight }) => weight > 0).map(({ key: demandedKey }) => demandedKey) ); for (let index = 0; index < lazyTargets$1.length; index++) { const target = lazyTargets$1[index]; const plan = demandPlans[index]; callbacks[target.sourceId]?.setDemand?.(plan, keys); } }) ); } const childQuery = subquery.parentFilters && subquery.parentFilters.length > 0 ? { ...subquery.query, where: [ ...subquery.query.where || [], ...subquery.parentFilters ] } : subquery.query; const childResult = compileQuery( childQuery, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, parentKeys, subquery.childCorrelationField ); Object.assign(aliasToCollectionId, childResult.aliasToCollectionId); Object.assign(aliasRemapping, childResult.aliasRemapping); for (const [alias, whereClause] of childResult.sourceWhereClauses) { sourceWhereClauses.set(alias, whereClause); } includesResults.push({ pipeline: childResult.pipeline, fieldName, resultPath: path, correlationField: subquery.correlationField, childCorrelationField: subquery.childCorrelationField, hasOrderBy: !!(subquery.query.orderBy && subquery.query.orderBy.length > 0), childCompilationResult: childResult, parentProjection: subquery.parentProjection, materialization: subquery.materialization, scalarField: subquery.scalarField }); if (subquery.parentProjection && subquery.parentProjection.length > 0) { const compiledProjs = subquery.parentProjection.map((ref) => ({ alias: ref.path[0], field: ref.path.slice(1), compiled: evaluators.compileExpression(ref) })); const compiledCorr = compiledCorrelation; const compiledRoutingGuards = compiledGuards; includesRoutingFns.push({ fieldName, getRouting: (nsRow) => { if (!matchesConditionalSelectGuards(compiledRoutingGuards, nsRow)) { return { active: false, correlationKey: null, parentContext: null }; } const parentContext = {}; for (const proj of compiledProjs) { if (!parentContext[proj.alias]) { parentContext[proj.alias] = {}; } const value = proj.compiled(nsRow); let target = parentContext[proj.alias]; for (let i = 0; i < proj.field.length - 1; i++) { if (!target[proj.field[i]]) { target[proj.field[i]] = {}; } target = target[proj.field[i]]; } target[proj.field[proj.field.length - 1]] = value; } return { active: true, correlationKey: compiledCorr(nsRow), parentContext }; } }); } else { const compiledRoutingGuards = compiledGuards; includesRoutingFns.push({ fieldName, getRouting: (nsRow) => { if (!matchesConditionalSelectGuards(compiledRoutingGuards, nsRow)) { return { active: false, correlationKey: null, parentContext: null }; } return { active: true, correlationKey: compiledCorrelation(nsRow), parentContext: null }; } }); } query = { ...query, select: replaceIncludesInSelect(query.select, path) }; } } if (query.distinct && !query.fnSelect && !query.select && query.from.type !== `unionAll`) { throw new errors.DistinctRequiresSelectError(); } if (query.fnSelect && query.groupBy && query.groupBy.length > 0) { throw new errors.FnSelectWithGroupByError(); } if (query.fnSelect) { pipeline = pipeline.pipe( dbIvm.map(([key, namespacedRow]) => { const selectResults = query.fnSelect(namespacedRow); let selected = selectResults; if (selectResults && typeof selectResults === `object`) { selected = Array.isArray(selectResults) ? [...selectResults] : { ...selectResults }; const routing = namespacedRow[INCLUDES_ROUTING]; if (routing) { selected[INCLUDES_ROUTING] = routing; } if (directIncludes.length > 0) { Object.defineProperty(selected, FN_SELECT_STATE, { value: { sourceRow: namespacedRow, fnSelect: query.fnSelect }, enumerable: true, configurable: true }); } } return [ key, { ...namespacedRow, $selected: selected } ]; }) ); } else if (query.select) { pipeline = select.processSelect(pipeline, query.select); } else { pipeline = pipeline.pipe( dbIvm.map(([key, namespacedRow]) => { const selectResults = !isUnionFrom && !query.join && !query.groupBy ? namespacedRow[mainSource] : namespacedRow; return [ key, { ...namespacedRow, $selected: selectResults } ]; }) ); } if (includesRoutingFns.length > 0) { pipeline = pipeline.pipe( dbIvm.map(([key, namespacedRow]) => { const routing = {}; for (const { fieldName, getRouting } of includesRoutingFns) { routing[fieldName] = getRouting(namespacedRow); } const selected = Array.isArray(namespacedRow.$selected) ? [...namespacedRow.$selected] : { ...namespacedRow.$selected }; selected[INCLUDES_ROUTING] = routing; return [key, { ...namespacedRow, $selected: selected }]; }) ); } const groupByMainSource = parentKeyStream ? mainSource : void 0; if (query.groupBy && query.groupBy.length > 0) { pipeline = groupBy.processGroupBy( pipeline, query.groupBy, query.having, query.select, query.fnHaving, mainCollectionId, groupByMainSource ); } else if (query.select) { const hasAggregates = Object.values(query.select).some( (expr) => expr.type === `agg` || groupBy.containsAggregate(expr) ); if (hasAggregates) { pipeline = groupBy.processGroupBy( pipeline, [], // Empty group by means single group query.having, query.select, query.fnHaving, mainCollectionId, groupByMainSource ); } } if (query.having && (!query.groupBy || query.groupBy.length === 0)) { const hasAggregates = query.select ? Object.values(query.select).some((expr) => expr.type === `agg`) : false; if (!hasAggregates) { throw new errors.HavingRequiresGroupByError(); } } if (query.fnHaving && query.fnHaving.length > 0 && (!query.groupBy || query.groupBy.length === 0)) { for (const fnHaving of query.fnHaving) { pipeline = pipeline.pipe( dbIvm.filter(([_key, namespacedRow]) => { return fnHaving(namespacedRow); }) ); } } if (!query.select || !groupBy.containsAggregate(query.select)) { pipeline = canonicalizeSelectedRows( pipeline, query, mainSource, parentKeyStream !== void 0 ); } const keyedSourceWhereClauses = keyWhereClausesBySource( rawQuery, sourceWhereClauses, aliasRemapping ); if (query.distinct) { pipeline = pipeline.pipe(dbIvm.distinct(([_key, row]) => row.$selected)); } if (query.orderBy && query.orderBy.length > 0) { const includesGroupKeyFn = parentKeyStream && (query.limit !== void 0 || query.offset !== void 0) ? (_key, row) => { const correlationKey = row?.[mainSource]?.__correlationKey ?? row?.__correlationKey; const parentContext = row?.__parentContext; if (parentContext != null) { return dbIvm.serializeValue([correlationKey, parentContext]); } return correlationKey; } : void 0; const orderedPipeline = orderBy.processOrderBy( rawQuery, pipeline, query.orderBy, query.select || {}, collections[mainCollectionId], optimizableOrderByCollections, setWindowFn, query.limit, query.offset, includesGroupKeyFn ); const resultPipeline2 = orderedPipeline.pipe( dbIvm.map(([key, [row, orderByIndex]]) => { const raw = row.$selected; const finalResults = attachVirtualPropsToSelected( unwrapValue(raw), row ); if (parentKeyStream) { const correlationKey = row[mainSource]?.__correlationKey ?? row.__correlationKey; const parentContext = row.__parentContext ?? null; const publicKey = getIncludesPublicKey(row, mainSource, key); const routedResults = stripInternalCorrelation(finalResults); return [ key, [ routedResults, orderByIndex, correlationKey, parentContext, publicKey ] ]; } return [key, [finalResults, orderByIndex]]; }) ); const compilationResult2 = { collectionId: mainCollectionId, pipeline: resultPipeline2, sourceWhereClauses: keyedSourceWhereClauses, aliasToCollectionId, aliasRemapping, includes: includesResults.length > 0 ? includesResults : void 0 }; if (parentKeyStream === void 0) cache.set(rawQuery, compilationResult2); return compilationResult2; } else if (query.limit !== void 0 || query.offset !== void 0) { throw new errors.LimitOffsetRequireOrderByError(); } const resultPipeline = pipeline.pipe( dbIvm.map(([key, row]) => { const raw = row.$selected; const finalResults = attachVirtualPropsToSelected( unwrapValue(raw), row ); if (parentKeyStream) { const correlationKey = row[mainSource]?.__correlationKey ?? row.__correlationKey; const parentContext = row.__parentContext ?? null; const publicKey = getIncludesPublicKey(row, mainSource, key); const routedResults = stripInternalCorrelation(finalResults); return [ key, [routedResults, void 0, correlationKey, parentContext, publicKey] ]; } return [key, [finalResults, void 0]]; }) ); const compilationResult = { collectionId: mainCollectionId, pipeline: resultPipeline, sourceWhereClauses: keyedSourceWhereClauses, aliasToCollectionId, aliasRemapping, includes: includesResults.length > 0 ? includesResults : void 0 }; if (parentKeyStream === void 0) cache.set(rawQuery, compilationResult); return compilationResult; } function keyWhereClausesBySource(query, clauses, aliasRemapping) { const sources = ir.collectCollectionSources(query); const sourceIds = new Set(sources.map(({ sourceId }) => sourceId)); const result = /* @__PURE__ */ new Map(); for (const [key, clause] of clauses) { if (sourceIds.has(key)) { result.set(key, clause); continue; } const alias = aliasRemapping[key] ?? key; for (const source of sources) { if (source.alias === alias) result.set(source.sourceId, clause); } } return result; } function bindSourceInputs(query, inputs) { for (const source of ir.collectCollectionSources(query)) { const input = inputs[source.sourceId] ?? inputs[source.alias]; if (!input) continue; inputs[source.sourceId] = input; inputs[source.alias] = input; } } function canonicalizeSelectedRows(pipeline, query, mainSource, isIncludedRelation) { const compiledOrder = (query.orderBy ?? []).map( ({ expression }) => evaluators.compileExpression(expression) ); const signature = (row) => ({ value: row.$selected, routing: row.$selected?.[INCLUDES_ROUTING], outerCorrelation: isIncludedRelation ? row[mainSource]?.__correlationKey : void 0, parentContext: isIncludedRelation ? row.__parentContext ?? row[mainSource]?.__parentContext ?? null : void 0, order: compiledOrder.map((evaluate) => evaluate(row)) }); return pipeline.pipe( dbIvm.reduce((values) => { const totalMultiplicity = values.reduce( (total, [, multiplicity]) => total + multiplicity, 0 ); if (totalMultiplicity === 0) return []; if (totalMultiplicity < 0) { throw new Error(`Query row has negative multiplicity`); } const visible = values.find(([, multiplicity]) => multiplicity > 0)?.[0]; if (!visible) throw new Error(`Query row has no positive contributor`); const visibleSignature = signature(visible); for (const [candidate, multiplicity] of values) { if (multiplicity > 0 && !utils.deepEquals(visibleSignature, signature(candidate))) { throw new Error( `Query contributors with the same row key are not congruent` ); } } return [[visible, 1]]; }) ); } function collectDirectCollectionAliases(query) { const aliases = /* @__PURE__ */ new Set(); for (const source of getFromSources(query.from)) { if (source.type === `collectionRef`) { aliases.add(source.alias); } } if (query.join) { for (const joinClause of query.join) { if (joinClause.from.type === `collectionRef`) { aliases.add(joinClause.from.alias); } } } return aliases; } function validateQueryStructure(query, parentCollectionAliases = /* @__PURE__ */ new Set()) { const currentLevelAliases = collectDirectCollectionAliases(query); for (const alias of currentLevelAliases) { if (parentCollectionAliases.has(alias)) { throw new errors.DuplicateAliasInSubqueryError( alias, Array.from(parentCollectionAliases) ); } } const combinedAliases = /* @__PURE__ */ new Set([ ...parentCollectionAliases, ...currentLevelAliases ]); if (query.from.type === `unionAll`) { for (const branch of query.from.queries) { validateQueryStructure(branch, combinedAliases); } } else { for (const source of getFromSources(query.from)) { if (source.type === `queryRef`) { validateQueryStructure(source.query, combinedAliases); } } } if (query.join) { for (const joinClause of query.join) { if (joinClause.from.type === `queryRef`) { validateQueryStructure(joinClause.from.query, combinedAliases); } } } if (query.select) { for (const { subquery } of extractIncludesFromSelect(query.select)) { validateQueryStructure(subquery.query, combinedAliases); } } } function processFromClause(from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses) { if (from.type === `unionAll`) { return processUnionAll( from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses ); } if (from.type !== `unionFrom`) { const { alias, input, collectionId, sourceIncludes: sourceIncludes2 } = processFrom( from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses ); return { alias, pipeline: wrapInputWithAlias(input, alias), collectionId, sources: { [alias]: input }, sourceIncludes: sourceIncludes2, directIncludes: [], isUnionFrom: false }; } if (from.sources.length === 0) { throw new errors.UnsupportedFromTypeError(`empty unionFrom`); } const sources = {}; const sourceIncludes = []; let pipeline; let mainAlias = ``; let mainCollectionId = ``; for (const source of from.sources) { const { alias, input, collectionId, sourceIncludes: childSourceIncludes } = processFrom( source, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses ); if (!mainAlias) { mainAlias = alias; mainCollectionId = collectionId; } sources[alias] = input; sourceIncludes.push(...childSourceIncludes); const branch = wrapInputWithAlias(input, alias).pipe( dbIvm.map(([key, row]) => { return [`${alias}:${encodeKeyForUnionBranch(key)}`, row]; }) ); pipeline = pipeline ? pipeline.pipe(dbIvm.concat(branch)) : branch; } return { alias: mainAlias, pipeline, collectionId: mainCollectionId, sources, sourceIncludes, directIncludes: [], isUnionFrom: true }; } function processUnionAll(from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses) { if (from.queries.length === 0) { throw new errors.UnsupportedFromTypeError(`empty unionAll`); } const sources = {}; const sourceIncludes = []; const directIncludes = []; let pipeline; let mainCollectionId = ``; const branchAliases = /* @__PURE__ */ new Set(); for (let index = 0; index < from.queries.length; index++) { const branch = from.queries[index]; for (const source of getAllSources(branch)) { if (branchAliases.has(source.alias)) { throw new Error( `Duplicate source alias "${source.alias}" in unionAll query branches. Use distinct aliases in each branch before passing them to unionAll().` ); } branchAliases.add(source.alias); } const branchResult = compileQuery( branch, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping ); if (!mainCollectionId) { mainCollectionId = branchResult.collectionId; } Object.assign(aliasToCollectionId, branchResult.aliasToCollectionId); Object.assign(aliasRemapping, branchResult.aliasRemapping); directIncludes.push(...branchResult.includes ?? []); Object.assign(sources, allInputs); for (const [alias, where] of branchResult.sourceWhereClauses) { sourceWhereClauses.set(alias, where); } const branchPipeline = branchResult.pipeline.pipe( dbIvm.map(([key, [row]]) => { return [`${index}:${encodeKeyForUnionBranch(key)}`, row]; }) ); pipeline = pipeline ? pipeline.pipe(dbIvm.concat(branchPipeline)) : branchPipeline; } return { alias: ``, pipeline, collectionId: mainCollectionId, sources, sourceIncludes, directIncludes, isUnionFrom: true }; } function wrapInputWithAlias(input, alias) { return input.pipe( dbIvm.map(([key, row]) => { const { __parentContext, ...cleanRow } = row; const nsRow = { [alias]: cleanRow }; if (__parentContext) { Object.assign(nsRow, __parentContext); nsRow.__parentContext = __parentContext; } return [key, nsRow]; }) ); } function encodeKeyForUnionBranch(key) { if (typeof key === `string`) { return `string:${key}`; } if (typeof key === `number`) { return `number:${String(key)}`; } if (typeof key === `bigint`) { return `bigint:${String(key)}`; } return `${typeof key}:${JSON.stringify(key)}`; } function processFrom(from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping, aliasToCollectionId, aliasRemapping, sourceWhereClauses) { switch (from.type) { case `collectionRef`: { const input = allInputs[from.sourceId] ?? allInputs[from.alias]; if (!input) { throw new errors.CollectionInputNotFoundError( from.alias, from.collection.id, Object.keys(allInputs) ); } aliasToCollectionId[from.alias] = from.collection.id; return { alias: from.alias, input, collectionId: from.collection.id, sourceIncludes: [] }; } case `queryRef`: { const originalQuery = queryMapping.get(from.query) || from.query; const subQueryResult = compileQuery( originalQuery, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, setWindowFn, cache, queryMapping ); Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId); Object.assign(aliasRemapping, subQueryResult.aliasRemapping); const isUserDefinedSubquery = queryMapping.has(from.query); const subqueryFromAlias = getFirstFromAlias(from.query.from); const isOptimizerCreated = !isUserDefinedSubquery && from.alias === subqueryFromAlias; if (!isOptimizerCreated) { for (const [alias, whereClause] of subQueryResult.sourceWhereClauses) { sourceWhereClauses.set(alias, whereClause); } } const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find( (alias) => subQueryResult.aliasToCollectionId[alias] === subQueryResult.collectionId ); if (innerAlias && innerAlias !== from.alias) { aliasRemapping[from.alias] = innerAlias; } const subQueryInput = subQueryResult.pipeline; const extractedInput = subQueryInput.pipe( dbIvm.map((data) => { const [key, [value, _orderByIndex]] = data; const unwrapped = unwrapValue(value); return [key, unwrapped]; }) ); return { alias: from.alias, input: extractedInput, collectionId: subQueryResult.collectionId, sourceIncludes: subQueryResult.includes?.map((include) => ({ sourceAlias: from.alias, include })) ?? [] }; } default: throw new errors.UnsupportedFromTypeError(from.type); } } function isValue(raw) { return raw instanceof ir.Value || raw && typeof raw === `object` && `type` in raw && raw.type === `val`; } function unwrapValue(value) { return isValue(value) ? value.value : value; } function attachVirtualPropsToSelected(selected, row) { if (!selected || typeof selected !== `object`) { return selected; } let needsMerge = false; for (const prop of virtualProps.VIRTUAL_PROP_NAMES) { if (selected[prop] == null && prop in row) { needsMerge = true; break; } } if (!needsMerge) { return selected; } const result = Array.isArray(selected) ? [...selected] : { ...selected }; for (const prop of virtualProps.VIRTUAL_PROP_NAMES) { if (selected[prop] == null && prop in row) { result[prop] = row[prop]; } } return result; } function stripInternalCorrelation(selected) { if (!selected || typeof selected !== `object` || !(`__correlationKey` in selected) && !(`__parentContext` in selected) && !(INCLUDES_PUBLIC_KEY in selected)) { return selected; } const result = Array.isArray(selected) ? [...selected] : { ...selected }; delete result.__correlationKey; delete result.__parentContext; delete result[INCLUDES_PUBLIC_KEY]; return result; } function getIncludesPublicKey(row, mainSource, fallback) { return row[mainSource]?.[INCLUDES_PUBLIC_KEY] ?? fallback; } function mapNestedQueries(optimizedQuery, originalQuery, queryMapping) { mapNestedFromQueries(optimizedQuery.from, originalQuery.from, queryMapping); if (optimizedQuery.join && originalQuery.join) { for (let i = 0; i < optimizedQuery.join.length && i < originalQuery.join.length; i++) { const optimizedJoin = optimizedQuery.join[i]; const originalJoin = originalQuery.join[i]; if (optimizedJoin.from.type === `queryRef` && originalJoin.from.type === `queryRef`) { queryMapping.set(optimizedJoin.from.query, originalJoin.from.query); mapNestedQueries( optimizedJoin.from.query, originalJoin.from.query, queryMapping ); } } } } function getFromSources(from) { if (from.type === `unionFrom`) { return from.sources; } if (from.type === `unionAll`) { return []; } return [from]; } function getAllSources(query) { return [ ...getFromSources(query.from), ...query.join?.map((join) => join.from) ?? [] ]; } function getFirstFromAlias(from) { return getFromSources(from)[0]?.alias ?? ``; } function findProjectedSourceIncludePaths(select2, sourceAlias, sourcePath) { const targetPath = [sourceAlias, ...sourcePath]; return findProjectedIncludePaths(select2, targetPath); } function findProjectedResultIncludePaths(select2, resultPath) { return findProjectedIncludePaths(select2, resultPath); } function findProjectedIncludePaths(select2, targetPath) { const resultPaths = []; const visitSelectObject = (obj, prefix, guards) => { for (const [key, value] of Object.entries(obj)) { if (key.startsWith(`__SPREAD_SENTINEL__`)) { visitSpreadSentinel(key, value, prefix, guards); continue; } visitSelectValue(value, [...prefix, key], guards); } }; const visitSpreadSentinel = (key, value, path, guards) => { const rest = key.slice(`__SPREAD_SENTINEL__`.length); const splitIndex = rest.lastIndexOf(`__`); const pathStr = splitIndex >= 0 ? rest.slice(0, splitIndex) : rest; const isRefExpr = value && typeof value === `object` && `type` in value && value.type === `ref`; const sourcePath = isRefExpr ? value.path : pathStr.split(`.`).filter(Boolean); if (pathStartsWith(targetPath, sourcePath)) { resultPaths.push({ path: [...path, ...targetPath.slice(sourcePath.length)], guards }); } }; const visitSelectValue = (value, path, guards) => { if (value instanceof ir.PropRef && pathStartsWith(targetPath, value.path)) { resultPaths.push({ path: [...path, ...targetPath.slice(value.path.length)], guards }); return; } if (value instanceof ir.ConditionalSelect) { const previousBranchGuards = []; for (const branch of value.branches) { visitSelectValue(branch.value, path, [ ...guards, ...previousBranchGuards, { condition: branch.condition, expected: true } ]); previousBranchGuards.push({ condition: branch.condition, expected: false }); } if (value.defaultValue !== void 0) { visitSelectValue(value.defaultValue, path, [ ...guards, ...previousBranchGuards ]); } return; } if (isNestedSelectObject(value)) { visitSelectObject(value, path, guards); } }; visitSelectObject(select2, [], []); return resultPaths; } function pathStartsWith(path, prefix) { return prefix.length <= path.length && prefix.every((part, i) => path[i] === part); } function mapNestedFromQueries(optimizedFrom, originalFrom, queryMapping) { if (optimizedFrom.type === `unionAll` && originalFrom.type === `unionAll`) { for (let i = 0; i < optimizedFrom.queries.length && i < originalFrom.queries.length; i++) { const optimizedBranch = optimizedFrom.queries[i]; const originalBranch = originalFrom.queries[i]; queryMapping.set(optimizedBranch, originalBranch); mapNestedQueries(optimizedBranch, originalBranch, queryMapping); } return; } const optimizedSources = getFromSources(optimizedFrom); const originalSources = getFromSources(originalFrom); for (let i = 0; i < optimizedSources.length && i < originalSources.length; i++) { const optimizedSource = optimizedSources[i]; const originalSource = originalSources[i]; if (optimizedSource.type === `queryRef` && originalSource.type === `queryRef`) { queryMapping.set(optimizedSource.query, originalSource.query); mapNestedQueries( optimizedSource.query, originalSource.query, queryMapping ); } } } function extractIncludesFromSelect(select2) { const results = []; for (const [key, value] of Object.entries(select2)) { if (key.startsWith(`__SPREAD_SENTINEL__`)) continue; if (value instanceof ir.IncludesSubquery) { results.push({ key: getIncludesRoutingKey([key], results), path: [key], subquery: value, guards: [] }); } else if (value instanceof ir.ConditionalSelect) { collectIncludesFromConditionalSelect(value, [key], [], results); } else if (isNestedSelectObject(value)) { assertNoNestedIncludes(value, key); } } return results; } function collectIncludesFromConditionalSelect(conditional, prefixPath, guards, results) { const previousBranchGuards = []; for (const branch of conditional.branches) { collectIncludesFromSelectValue( branch.value, prefixPath, [ ...guards, ...previousBranchGuards, { condition: branch.condition, expected: true } ], results ); previousBranchGuards.push({ condition: branch.condition, expected: false }); } if (conditional.defaultValue !== void 0) { collectIncludesFromSelectValue( conditional.defaultValue, prefixPath, [...guards, ...previousBranchGuards], results ); } } function collectIncludesFromSelectValue(value, prefixPath, guards, results) { if (value instanceof ir.IncludesSubquery) { const key = getIncludesRoutingKey(prefixPath, results); results.push({ key, path: prefixPath, subquery: value, guards }); return; } if (value instanceof ir.ConditionalSelect) { collectIncludesFromConditionalSelect(value, prefixPath, guards, results); return; } if (!isNestedSelectObject(value)) { return; } for (const [key, child] of Object.entries(value)) { if (key.startsWith(`__SPREAD_SENTINEL__`)) continue; collectIncludesFromSelectValue(child, [...prefixPath, key], guards, results); } } function getIncludesRoutingKey(path, entries) { return getUniqueIncludesRoutingKey(path.join(`.`), entries); } function getUniqueIncludesRoutingKey(baseKey, entries) { const hasKey = (key2) => entries.some((entry) => (entry.key ?? entry.fieldName) === key2); if (!hasKey(baseKey)) { return baseKey; } let suffix = entries.length; let key = `${baseKey}#${suffix}`; while (hasKey(key)) { suffix++; key = `${baseKey}#${suffix}`; } return key; } function isNestedSelectObject(value) { return value != null && typeof value === `object` && !Array.isArray(value) && !ir.isExpressionLike(value) && value.__refProxy !== true; } function assertNoNestedIncludes(obj, parentPath) { for (const [key, value] of Object.entries(obj)) { if (key.startsWith(`__SPREAD_SENTINEL__`)) continue; if (value instanceof ir.IncludesSubquery) { throw new Error( `Includes subqueries must be at the top level of select(). Found nested includes at "${parentPath}.${key}".` ); } if (isNestedSelectObject(value)) { assertNoNestedIncludes(value, `${parentPath}.${key}`); } } } function replaceIncludesInSelect(select2, path) { return replaceIncludesInSelectValue(select2, path, new ir.Value(null)).value; } function replaceIncludesInSelectValue(value, path, replacement) { if (path.length === 0) { return replaceIncludesValue(value, replacement); } if (value instanceof ir.ConditionalSelect) { return replaceIncludesInConditionalSelect(value, path, replacement); } if (!isNestedSelectObject(value)) { return { value, replaced: false }; } if (path.length === 1) { const field = path[0]; const result2 = replaceIncludesValue(value[field], replacement); if (!result2.replaced) { return { value, replaced: false }; } return { value: { ...value, [field]: result2.value }, replaced: true }; } const [head, ...rest] = path; const result = replaceIncludesInSelectValue(value[head], rest, replacement); if (!result.replaced) { return { value, replaced: false }; } return { value: { ...value, [head]: result.value }, replaced: true }; } function replaceIncludesValue(value, replacement) { if (value instanceof ir.IncludesSubquery) { return { value: replacement, replaced: true }; } if (value instanceof ir.ConditionalSelect) { return replaceIncludesInConditionalSelect(value, [], replacement); } return { value, replaced: false }; } function replaceIncludesInConditionalSelect(conditional, path, replacement) { let replaced = false; const branches = conditional.branches.map((branch) => { const result = path.length === 0 ? replaceIncludesValue(branch.value, replacement) : replaceIncludesInSelectValue(branch.value, path, replacement); if (!result.replaced) { return branch; } replaced = true; return { ...branch, value: result.value }; }); let defaultValue = conditional.defaultValue; if (conditional.defaultValue !== void 0) { const result = path.length === 0 ? replaceIncludesValue(conditional.defaultValue, replacement) : replaceIncludesInSelectValue( conditional.defaultValue, path, replacement ); if (result.replaced) { replaced = true; defaultValue = result.value; } } if (!replaced) { return { value: conditional, replaced: false }; } return { value: new ir.ConditionalSelect(branches, defaultValue), replaced: true }; } function getNestedValue(obj, path) { let value = obj; for (const segment of path) { if (value == null) return value; value = value[segment]; } return value; } function matchesConditionalSelectGuards(guards, row) { return guards.every( (guard) => evaluators.isCaseWhenConditionTrue(guard.condition(row)) === guard.expected ); } function getStaticDemandKeys(query, ref) { const constraints = []; const visit = (expression) => { if (expression.type !== `func`) return; if (expression.name === `and`) { expression.args.forEach(visit); return; } if (expression.name !== `eq` && expression.name !== `in`) return; const [left, right] = expression.args; const value = left?.type === `ref` && pathsEqual(left.path, ref.path) && right instanceof ir.Value ? right.value : right?.type === `ref` && pathsEqual(right.path, ref.path) && left instanceof ir.Value ? left.value : void 0; if (value === void 0) return; constraints.push(new Set(Array.isArray(value) ? value : [value])); }; query.where?.forEach((where) => visit(ir.getWhereExpression(where))); if (constraints.length === 0) return /* @__PURE__ */ new Set(); return new Set( [...constraints[0]].filter( (value) => constraints.slice(1).every((constraint) => constraint.has(value)) ) ); } function pathsEqual(left, right) { return left.length === right.length && left.every((segment, index) => segment === right[index]); } exports.FN_SELECT_STATE = FN_SELECT_STATE; exports.INCLUDES_PUBLIC_KEY = INCLUDES_PUBLIC_KEY; exports.INCLUDES_ROUTING = INCLUDES_ROUTING; exports.compileQuery = compileQuery; //# sourceMappingURL=index.cjs.map