@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1,436 lines (1,435 loc) • 48.7 kB
JavaScript
import { map, join, filter, serializeValue, reduce, tap, distinct, concat } from "@tanstack/db-ivm";
import { optimizeQuery } from "../optimizer.js";
import { DistinctRequiresSelectError, FnSelectWithGroupByError, HavingRequiresGroupByError, LimitOffsetRequireOrderByError, DuplicateAliasInSubqueryError, UnsupportedFromTypeError, CollectionInputNotFoundError } from "../../errors.js";
import { VIRTUAL_PROP_NAMES } from "../../virtual-props.js";
import { getWhereExpression, collectCollectionSources, IncludesSubquery, ConditionalSelect, Value, isExpressionLike, PropRef } from "../ir.js";
import { ensureIndexForField } from "../../indexes/auto-index.js";
import { deepEquals } from "../../utils.js";
import { compileExpression, toBooleanPredicate, isCaseWhenConditionTrue } from "./evaluators.js";
import { processJoins, registerLazyDemandPlan } from "./joins.js";
import { processGroupBy, containsAggregate } from "./group-by.js";
import { getLazyLoadTargets } from "./lazy-targets.js";
import { processOrderBy } from "./order-by.js";
import { processSelect } from "./select.js";
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 } = 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(
map(([key, row]) => {
const correlationValue = getNestedValue(row, childFieldPath);
return [correlationValue, [key, row]];
})
);
const joined = childRekeyed.pipe(join(parentKeyStream, `inner`));
filteredMainInput = joined.pipe(
filter(([_correlationValue, [childSide]]) => {
return childSide != null;
}),
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 ? serializeValue([childKey, parentSide]) : childKey;
return [effectiveKey, tagged];
})
);
sources[mainSource] = filteredMainInput;
pipeline = wrapInputWithAlias(filteredMainInput, mainSource);
}
if (query.join && query.join.length > 0) {
pipeline = 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 = compileExpression(childCorrelationField);
pipeline = pipeline.pipe(
map(
([key, row]) => [compiledChildCorrelation(row), [key, row]]
),
join(parentKeyStream, `inner`),
filter(([_correlationValue, [childSide]]) => childSide != null),
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 ? serializeValue([childKey, parentSide]) : childKey;
return [effectiveKey, namespaced];
})
);
}
if (query.where && query.where.length > 0) {
for (const where of query.where) {
const whereExpression = getWhereExpression(where);
const compiledWhere = compileExpression(whereExpression);
pipeline = pipeline.pipe(
filter(([_key, namespacedRow]) => {
return toBooleanPredicate(compiledWhere(namespacedRow));
})
);
}
}
if (query.fnWhere && query.fnWhere.length > 0) {
for (const fnWhere of query.fnWhere) {
pipeline = pipeline.pipe(
filter(([_key, namespacedRow]) => {
return 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: 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: 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 = compileExpression(subquery.correlationField);
const compiledGuards = guards.map((guard) => ({
condition: 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: compileExpression(ref)
}));
parentKeys = pipeline.pipe(
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(
map(([_key, nsRow]) => {
if (!matchesConditionalSelectGuards(compiledGuards, nsRow)) {
return [SKIP_INCLUDE, null];
}
return [compiledCorrelation(nsRow), null];
})
);
}
parentKeys = parentKeys.pipe(
filter(([correlationValue]) => correlationValue !== SKIP_INCLUDE)
);
parentKeys = parentKeys.pipe(
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 = getLazyLoadTargets(
subquery.query,
subquery.query.from,
childSourceAlias,
subquery.childCorrelationField,
directChildCollection,
aliasRemapping
);
if (lazyTargets.length > 0) {
for (const target of lazyTargets) {
lazySources.add(target.sourceId);
}
for (const target of lazyTargets) {
const targetFieldName = target.path[0];
if (targetFieldName) {
ensureIndexForField(targetFieldName, target.path, target.collection);
}
}
const initialKeys = getStaticDemandKeys(
rawQuery,
subquery.correlationField
);
const demandPlans = lazyTargets.map(
(target) => registerLazyDemandPlan(callbacks, target, initialKeys)
);
const demandWeights = /* @__PURE__ */ new Map();
parentKeys = parentKeys.pipe(
tap((data) => {
for (const [[correlationValue], weight] of data.getInner()) {
if (correlationValue == null) continue;
const encoded = 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.length; index++) {
const target = lazyTargets[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: 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 DistinctRequiresSelectError();
}
if (query.fnSelect && query.groupBy && query.groupBy.length > 0) {
throw new FnSelectWithGroupByError();
}
if (query.fnSelect) {
pipeline = pipeline.pipe(
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 = processSelect(pipeline, query.select);
} else {
pipeline = pipeline.pipe(
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(
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 = 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` || containsAggregate(expr)
);
if (hasAggregates) {
pipeline = 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 HavingRequiresGroupByError();
}
}
if (query.fnHaving && query.fnHaving.length > 0 && (!query.groupBy || query.groupBy.length === 0)) {
for (const fnHaving of query.fnHaving) {
pipeline = pipeline.pipe(
filter(([_key, namespacedRow]) => {
return fnHaving(namespacedRow);
})
);
}
}
if (!query.select || !containsAggregate(query.select)) {
pipeline = canonicalizeSelectedRows(
pipeline,
query,
mainSource,
parentKeyStream !== void 0
);
}
const keyedSourceWhereClauses = keyWhereClausesBySource(
rawQuery,
sourceWhereClauses,
aliasRemapping
);
if (query.distinct) {
pipeline = pipeline.pipe(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 serializeValue([correlationKey, parentContext]);
}
return correlationKey;
} : void 0;
const orderedPipeline = processOrderBy(
rawQuery,
pipeline,
query.orderBy,
query.select || {},
collections[mainCollectionId],
optimizableOrderByCollections,
setWindowFn,
query.limit,
query.offset,
includesGroupKeyFn
);
const resultPipeline2 = orderedPipeline.pipe(
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 LimitOffsetRequireOrderByError();
}
const resultPipeline = pipeline.pipe(
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 = 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 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 }) => 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(
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 && !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 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 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(
map(([key, row]) => {
return [`${alias}:${encodeKeyForUnionBranch(key)}`, row];
})
);
pipeline = pipeline ? pipeline.pipe(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 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(
map(([key, [row]]) => {
return [`${index}:${encodeKeyForUnionBranch(key)}`, row];
})
);
pipeline = pipeline ? pipeline.pipe(concat(branchPipeline)) : branchPipeline;
}
return {
alias: ``,
pipeline,
collectionId: mainCollectionId,
sources,
sourceIncludes,
directIncludes,
isUnionFrom: true
};
}
function wrapInputWithAlias(input, alias) {
return input.pipe(
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 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(
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 UnsupportedFromTypeError(from.type);
}
}
function isValue(raw) {
return raw instanceof 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 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 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((join2) => join2.from) ?? []
];
}
function getFirstFromAlias(from) {
return getFromSources(from)[0]?.alias ?? ``;
}
function findProjectedSourceIncludePaths(select, sourceAlias, sourcePath) {
const targetPath = [sourceAlias, ...sourcePath];
return findProjectedIncludePaths(select, targetPath);
}
function findProjectedResultIncludePaths(select, resultPath) {
return findProjectedIncludePaths(select, resultPath);
}
function findProjectedIncludePaths(select, 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 PropRef && pathStartsWith(targetPath, value.path)) {
resultPaths.push({
path: [...path, ...targetPath.slice(value.path.length)],
guards
});
return;
}
if (value instanceof 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(select, [], []);
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(select) {
const results = [];
for (const [key, value] of Object.entries(select)) {
if (key.startsWith(`__SPREAD_SENTINEL__`)) continue;
if (value instanceof IncludesSubquery) {
results.push({
key: getIncludesRoutingKey([key], results),
path: [key],
subquery: value,
guards: []
});
} else if (value instanceof 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 IncludesSubquery) {
const key = getIncludesRoutingKey(prefixPath, results);
results.push({ key, path: prefixPath, subquery: value, guards });
return;
}
if (value instanceof 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) && !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 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(select, path) {
return replaceIncludesInSelectValue(select, path, new Value(null)).value;
}
function replaceIncludesInSelectValue(value, path, replacement) {
if (path.length === 0) {
return replaceIncludesValue(value, replacement);
}
if (value instanceof 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 IncludesSubquery) {
return { value: replacement, replaced: true };
}
if (value instanceof 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 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) => 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 Value ? right.value : right?.type === `ref` && pathsEqual(right.path, ref.path) && left instanceof Value ? left.value : void 0;
if (value === void 0) return;
constraints.push(new Set(Array.isArray(value) ? value : [value]));
};
query.where?.forEach((where) => visit(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]);
}
export {
FN_SELECT_STATE,
INCLUDES_PUBLIC_KEY,
INCLUDES_ROUTING,
compileQuery
};
//# sourceMappingURL=index.js.map