@tanstack/db 0.4.7 → 0.4.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/collection/index.cjs.map +1 -1
- package/dist/cjs/collection/index.d.cts +2 -1
- package/dist/cjs/collection/lifecycle.cjs +2 -3
- package/dist/cjs/collection/lifecycle.cjs.map +1 -1
- package/dist/cjs/collection/state.cjs +22 -33
- package/dist/cjs/collection/state.cjs.map +1 -1
- package/dist/cjs/collection/state.d.cts +6 -2
- package/dist/cjs/collection/sync.cjs +4 -3
- package/dist/cjs/collection/sync.cjs.map +1 -1
- package/dist/cjs/errors.cjs +51 -17
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +38 -8
- package/dist/cjs/index.cjs +8 -4
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/indexes/auto-index.cjs +0 -3
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/query/builder/types.d.cts +1 -1
- package/dist/cjs/query/compiler/index.cjs +42 -19
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +33 -8
- package/dist/cjs/query/compiler/joins.cjs +88 -66
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +5 -2
- package/dist/cjs/query/compiler/order-by.cjs +2 -0
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +322 -46
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +98 -7
- package/dist/cjs/query/live/collection-registry.cjs +16 -0
- package/dist/cjs/query/live/collection-registry.cjs.map +1 -0
- package/dist/cjs/query/live/collection-registry.d.cts +26 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +57 -58
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
- package/dist/cjs/query/live-query-collection.cjs +11 -5
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +10 -3
- package/dist/cjs/query/optimizer.cjs +44 -7
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/query/optimizer.d.cts +4 -4
- package/dist/cjs/scheduler.cjs +137 -0
- package/dist/cjs/scheduler.cjs.map +1 -0
- package/dist/cjs/scheduler.d.cts +56 -0
- package/dist/cjs/transactions.cjs +7 -1
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +3 -5
- package/dist/esm/collection/index.d.ts +2 -1
- package/dist/esm/collection/index.js.map +1 -1
- package/dist/esm/collection/lifecycle.js +2 -3
- package/dist/esm/collection/lifecycle.js.map +1 -1
- package/dist/esm/collection/state.d.ts +6 -2
- package/dist/esm/collection/state.js +22 -33
- package/dist/esm/collection/state.js.map +1 -1
- package/dist/esm/collection/sync.js +4 -3
- package/dist/esm/collection/sync.js.map +1 -1
- package/dist/esm/errors.d.ts +38 -8
- package/dist/esm/errors.js +52 -18
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +9 -5
- package/dist/esm/indexes/auto-index.js +0 -3
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +1 -1
- package/dist/esm/query/compiler/index.d.ts +33 -8
- package/dist/esm/query/compiler/index.js +42 -19
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +5 -2
- package/dist/esm/query/compiler/joins.js +90 -68
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -0
- package/dist/esm/query/compiler/order-by.js +2 -0
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +98 -7
- package/dist/esm/query/live/collection-config-builder.js +322 -46
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-registry.d.ts +26 -0
- package/dist/esm/query/live/collection-registry.js +16 -0
- package/dist/esm/query/live/collection-registry.js.map +1 -0
- package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
- package/dist/esm/query/live/collection-subscriber.js +57 -58
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +10 -3
- package/dist/esm/query/live-query-collection.js +11 -5
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.d.ts +4 -4
- package/dist/esm/query/optimizer.js +44 -7
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/scheduler.d.ts +56 -0
- package/dist/esm/scheduler.js +137 -0
- package/dist/esm/scheduler.js.map +1 -0
- package/dist/esm/transactions.js +7 -1
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +3 -5
- package/package.json +2 -2
- package/src/collection/index.ts +1 -1
- package/src/collection/lifecycle.ts +3 -4
- package/src/collection/state.ts +52 -48
- package/src/collection/sync.ts +7 -6
- package/src/errors.ts +79 -13
- package/src/indexes/auto-index.ts +0 -8
- package/src/query/builder/types.ts +1 -1
- package/src/query/compiler/index.ts +115 -32
- package/src/query/compiler/joins.ts +180 -127
- package/src/query/compiler/order-by.ts +7 -0
- package/src/query/compiler/select.ts +2 -3
- package/src/query/live/collection-config-builder.ts +542 -71
- package/src/query/live/collection-registry.ts +47 -0
- package/src/query/live/collection-subscriber.ts +87 -105
- package/src/query/live-query-collection.ts +39 -14
- package/src/query/optimizer.ts +85 -15
- package/src/scheduler.ts +198 -0
- package/src/transactions.ts +12 -1
- package/src/types.ts +3 -5
|
@@ -9,18 +9,20 @@ const joins = require("./joins.cjs");
|
|
|
9
9
|
const groupBy = require("./group-by.cjs");
|
|
10
10
|
const orderBy = require("./order-by.cjs");
|
|
11
11
|
const select = require("./select.cjs");
|
|
12
|
-
function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks,
|
|
12
|
+
function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, cache = /* @__PURE__ */ new WeakMap(), queryMapping = /* @__PURE__ */ new WeakMap()) {
|
|
13
13
|
const cachedResult = cache.get(rawQuery);
|
|
14
14
|
if (cachedResult) {
|
|
15
15
|
return cachedResult;
|
|
16
16
|
}
|
|
17
|
-
const { optimizedQuery: query,
|
|
17
|
+
const { optimizedQuery: query, sourceWhereClauses } = optimizer.optimizeQuery(rawQuery);
|
|
18
18
|
queryMapping.set(query, rawQuery);
|
|
19
19
|
mapNestedQueries(query, rawQuery, queryMapping);
|
|
20
20
|
const allInputs = { ...inputs };
|
|
21
|
-
const
|
|
21
|
+
const aliasToCollectionId = {};
|
|
22
|
+
const aliasRemapping = {};
|
|
23
|
+
const sources = {};
|
|
22
24
|
const {
|
|
23
|
-
alias:
|
|
25
|
+
alias: mainSource,
|
|
24
26
|
input: mainInput,
|
|
25
27
|
collectionId: mainCollectionId
|
|
26
28
|
} = processFrom(
|
|
@@ -29,15 +31,17 @@ function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, l
|
|
|
29
31
|
collections,
|
|
30
32
|
subscriptions,
|
|
31
33
|
callbacks,
|
|
32
|
-
|
|
34
|
+
lazySources,
|
|
33
35
|
optimizableOrderByCollections,
|
|
34
36
|
cache,
|
|
35
|
-
queryMapping
|
|
37
|
+
queryMapping,
|
|
38
|
+
aliasToCollectionId,
|
|
39
|
+
aliasRemapping
|
|
36
40
|
);
|
|
37
|
-
|
|
41
|
+
sources[mainSource] = mainInput;
|
|
38
42
|
let pipeline = mainInput.pipe(
|
|
39
43
|
dbIvm.map(([key, row]) => {
|
|
40
|
-
const ret = [key, { [
|
|
44
|
+
const ret = [key, { [mainSource]: row }];
|
|
41
45
|
return ret;
|
|
42
46
|
})
|
|
43
47
|
);
|
|
@@ -45,19 +49,21 @@ function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, l
|
|
|
45
49
|
pipeline = joins.processJoins(
|
|
46
50
|
pipeline,
|
|
47
51
|
query.join,
|
|
48
|
-
|
|
52
|
+
sources,
|
|
49
53
|
mainCollectionId,
|
|
50
|
-
|
|
54
|
+
mainSource,
|
|
51
55
|
allInputs,
|
|
52
56
|
cache,
|
|
53
57
|
queryMapping,
|
|
54
58
|
collections,
|
|
55
59
|
subscriptions,
|
|
56
60
|
callbacks,
|
|
57
|
-
|
|
61
|
+
lazySources,
|
|
58
62
|
optimizableOrderByCollections,
|
|
59
63
|
rawQuery,
|
|
60
|
-
compileQuery
|
|
64
|
+
compileQuery,
|
|
65
|
+
aliasToCollectionId,
|
|
66
|
+
aliasRemapping
|
|
61
67
|
);
|
|
62
68
|
}
|
|
63
69
|
if (query.where && query.where.length > 0) {
|
|
@@ -101,7 +107,7 @@ function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, l
|
|
|
101
107
|
} else {
|
|
102
108
|
pipeline = pipeline.pipe(
|
|
103
109
|
dbIvm.map(([key, namespacedRow]) => {
|
|
104
|
-
const selectResults = !query.join && !query.groupBy ? namespacedRow[
|
|
110
|
+
const selectResults = !query.join && !query.groupBy ? namespacedRow[mainSource] : namespacedRow;
|
|
105
111
|
return [
|
|
106
112
|
key,
|
|
107
113
|
{
|
|
@@ -175,7 +181,9 @@ function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, l
|
|
|
175
181
|
const compilationResult2 = {
|
|
176
182
|
collectionId: mainCollectionId,
|
|
177
183
|
pipeline: result2,
|
|
178
|
-
|
|
184
|
+
sourceWhereClauses,
|
|
185
|
+
aliasToCollectionId,
|
|
186
|
+
aliasRemapping
|
|
179
187
|
};
|
|
180
188
|
cache.set(rawQuery, compilationResult2);
|
|
181
189
|
return compilationResult2;
|
|
@@ -193,18 +201,25 @@ function compileQuery(rawQuery, inputs, collections, subscriptions, callbacks, l
|
|
|
193
201
|
const compilationResult = {
|
|
194
202
|
collectionId: mainCollectionId,
|
|
195
203
|
pipeline: result,
|
|
196
|
-
|
|
204
|
+
sourceWhereClauses,
|
|
205
|
+
aliasToCollectionId,
|
|
206
|
+
aliasRemapping
|
|
197
207
|
};
|
|
198
208
|
cache.set(rawQuery, compilationResult);
|
|
199
209
|
return compilationResult;
|
|
200
210
|
}
|
|
201
|
-
function processFrom(from, allInputs, collections, subscriptions, callbacks,
|
|
211
|
+
function processFrom(from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, cache, queryMapping, aliasToCollectionId, aliasRemapping) {
|
|
202
212
|
switch (from.type) {
|
|
203
213
|
case `collectionRef`: {
|
|
204
|
-
const input = allInputs[from.
|
|
214
|
+
const input = allInputs[from.alias];
|
|
205
215
|
if (!input) {
|
|
206
|
-
throw new errors.CollectionInputNotFoundError(
|
|
216
|
+
throw new errors.CollectionInputNotFoundError(
|
|
217
|
+
from.alias,
|
|
218
|
+
from.collection.id,
|
|
219
|
+
Object.keys(allInputs)
|
|
220
|
+
);
|
|
207
221
|
}
|
|
222
|
+
aliasToCollectionId[from.alias] = from.collection.id;
|
|
208
223
|
return { alias: from.alias, input, collectionId: from.collection.id };
|
|
209
224
|
}
|
|
210
225
|
case `queryRef`: {
|
|
@@ -215,11 +230,19 @@ function processFrom(from, allInputs, collections, subscriptions, callbacks, laz
|
|
|
215
230
|
collections,
|
|
216
231
|
subscriptions,
|
|
217
232
|
callbacks,
|
|
218
|
-
|
|
233
|
+
lazySources,
|
|
219
234
|
optimizableOrderByCollections,
|
|
220
235
|
cache,
|
|
221
236
|
queryMapping
|
|
222
237
|
);
|
|
238
|
+
Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId);
|
|
239
|
+
Object.assign(aliasRemapping, subQueryResult.aliasRemapping);
|
|
240
|
+
const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(
|
|
241
|
+
(alias) => subQueryResult.aliasToCollectionId[alias] === subQueryResult.collectionId
|
|
242
|
+
);
|
|
243
|
+
if (innerAlias && innerAlias !== from.alias) {
|
|
244
|
+
aliasRemapping[from.alias] = innerAlias;
|
|
245
|
+
}
|
|
223
246
|
const subQueryInput = subQueryResult.pipeline;
|
|
224
247
|
const extractedInput = subQueryInput.pipe(
|
|
225
248
|
dbIvm.map((data) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../../../src/query/compiler/index.ts"],"sourcesContent":["import { distinct, filter, map } from \"@tanstack/db-ivm\"\nimport { optimizeQuery } from \"../optimizer.js\"\nimport {\n CollectionInputNotFoundError,\n DistinctRequiresSelectError,\n HavingRequiresGroupByError,\n LimitOffsetRequireOrderByError,\n UnsupportedFromTypeError,\n} from \"../../errors.js\"\nimport { PropRef, Value as ValClass, getWhereExpression } from \"../ir.js\"\nimport { compileExpression } from \"./evaluators.js\"\nimport { processJoins } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processSelect } from \"./select.js\"\nimport type { CollectionSubscription } from \"../../collection/subscription.js\"\nimport type { OrderByOptimizationInfo } from \"./order-by.js\"\nimport type {\n BasicExpression,\n CollectionRef,\n QueryIR,\n QueryRef,\n} from \"../ir.js\"\nimport type { LazyCollectionCallbacks } from \"./joins.js\"\nimport type { Collection } from \"../../collection/index.js\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n ResultStream,\n} from \"../../types.js\"\nimport type { QueryCache, QueryMapping } from \"./types.js\"\n\n/**\n * Result of query compilation including both the pipeline and collection-specific WHERE clauses\n */\nexport interface CompilationResult {\n /** The ID of the main collection */\n collectionId: string\n /** The compiled query pipeline */\n pipeline: ResultStream\n /** Map of collection aliases to their WHERE clauses for index optimization */\n collectionWhereClauses: Map<string, BasicExpression<boolean>>\n}\n\n/**\n * Compiles a query2 IR into a D2 pipeline\n * @param rawQuery The query IR to compile\n * @param inputs Mapping of collection names to input streams\n * @param cache Optional cache for compiled subqueries (used internally for recursion)\n * @param queryMapping Optional mapping from optimized queries to original queries\n * @returns A CompilationResult with the pipeline and collection WHERE clauses\n */\nexport function compileQuery(\n rawQuery: QueryIR,\n inputs: Record<string, KeyedStream>,\n collections: Record<string, Collection<any, any, any, any, any>>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazyCollections: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n cache: QueryCache = new WeakMap(),\n queryMapping: QueryMapping = new WeakMap()\n): CompilationResult {\n // Check if the original raw query has already been compiled\n const cachedResult = cache.get(rawQuery)\n if (cachedResult) {\n return cachedResult\n }\n\n // Optimize the query before compilation\n const { optimizedQuery: query, collectionWhereClauses } =\n optimizeQuery(rawQuery)\n\n // Create mapping from optimized query to original for caching\n queryMapping.set(query, rawQuery)\n mapNestedQueries(query, rawQuery, queryMapping)\n\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Create a map of table aliases to inputs\n const tables: Record<string, KeyedStream> = {}\n\n // Process the FROM clause to get the main table\n const {\n alias: mainTableAlias,\n input: mainInput,\n collectionId: mainCollectionId,\n } = processFrom(\n query.from,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazyCollections,\n optimizableOrderByCollections,\n cache,\n queryMapping\n )\n tables[mainTableAlias] = mainInput\n\n // Prepare the initial pipeline with the main table wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = mainInput.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainTableAlias]: row }] as [\n string,\n Record<string, typeof row>,\n ]\n return ret\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join && query.join.length > 0) {\n pipeline = processJoins(\n pipeline,\n query.join,\n tables,\n mainCollectionId,\n mainTableAlias,\n allInputs,\n cache,\n queryMapping,\n collections,\n subscriptions,\n callbacks,\n lazyCollections,\n optimizableOrderByCollections,\n rawQuery,\n compileQuery\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where && query.where.length > 0) {\n // Apply each WHERE condition as a filter (they are ANDed together)\n for (const where of query.where) {\n const whereExpression = getWhereExpression(where)\n const compiledWhere = compileExpression(whereExpression)\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return compiledWhere(namespacedRow)\n })\n )\n }\n }\n\n // Process functional WHERE clauses if they exist\n if (query.fnWhere && query.fnWhere.length > 0) {\n for (const fnWhere of query.fnWhere) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnWhere(namespacedRow)\n })\n )\n }\n }\n\n if (query.distinct && !query.fnSelect && !query.select) {\n throw new DistinctRequiresSelectError()\n }\n\n // Process the SELECT clause early - always create __select_results\n // This eliminates duplication and allows for DISTINCT implementation\n if (query.fnSelect) {\n // Handle functional select - apply the function to transform the row\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults = query.fnSelect!(namespacedRow)\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [string, typeof namespacedRow & { __select_results: any }]\n })\n )\n } else if (query.select) {\n pipeline = processSelect(pipeline, query.select, allInputs)\n } else {\n // If no SELECT clause, create __select_results with the main table data\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults =\n !query.join && !query.groupBy\n ? namespacedRow[mainTableAlias]\n : namespacedRow\n\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [string, typeof namespacedRow & { __select_results: any }]\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy && query.groupBy.length > 0) {\n pipeline = processGroupBy(\n pipeline,\n query.groupBy,\n query.having,\n query.select,\n query.fnHaving\n )\n } else if (query.select) {\n // Check if SELECT contains aggregates but no GROUP BY (implicit single-group aggregation)\n const hasAggregates = Object.values(query.select).some(\n (expr) => expr.type === `agg`\n )\n if (hasAggregates) {\n // Handle implicit single-group aggregation\n pipeline = processGroupBy(\n pipeline,\n [], // Empty group by means single group\n query.having,\n query.select,\n query.fnHaving\n )\n }\n }\n\n // Process the HAVING clause if it exists (only applies after GROUP BY)\n if (query.having && (!query.groupBy || query.groupBy.length === 0)) {\n // Check if we have aggregates in SELECT that would trigger implicit grouping\n const hasAggregates = query.select\n ? Object.values(query.select).some((expr) => expr.type === `agg`)\n : false\n\n if (!hasAggregates) {\n throw new HavingRequiresGroupByError()\n }\n }\n\n // Process functional HAVING clauses outside of GROUP BY (treat as additional WHERE filters)\n if (\n query.fnHaving &&\n query.fnHaving.length > 0 &&\n (!query.groupBy || query.groupBy.length === 0)\n ) {\n // If there's no GROUP BY but there are fnHaving clauses, apply them as filters\n for (const fnHaving of query.fnHaving) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n // Process the DISTINCT clause if it exists\n if (query.distinct) {\n pipeline = pipeline.pipe(distinct(([_key, row]) => row.__select_results))\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy && query.orderBy.length > 0) {\n const orderedPipeline = processOrderBy(\n rawQuery,\n pipeline,\n query.orderBy,\n query.select || {},\n collections[mainCollectionId]!,\n optimizableOrderByCollections,\n query.limit,\n query.offset\n )\n\n // Final step: extract the __select_results and include orderBy index\n const resultPipeline = orderedPipeline.pipe(\n map(([key, [row, orderByIndex]]) => {\n // Extract the final results from __select_results and include orderBy index\n const raw = (row as any).__select_results\n const finalResults = unwrapValue(raw)\n return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]\n })\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n collectionWhereClauses,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new LimitOffsetRequireOrderByError()\n }\n\n // Final step: extract the __select_results and return tuple format (no orderBy)\n const resultPipeline: ResultStream = pipeline.pipe(\n map(([key, row]) => {\n // Extract the final results from __select_results and return [key, [results, undefined]]\n const raw = (row as any).__select_results\n const finalResults = unwrapValue(raw)\n return [key, [finalResults, undefined]] as [\n unknown,\n [any, string | undefined],\n ]\n })\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n collectionWhereClauses,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n}\n\n/**\n * Processes the FROM clause to extract the main table alias and input stream\n */\nfunction processFrom(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n collections: Record<string, Collection>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazyCollections: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n cache: QueryCache,\n queryMapping: QueryMapping\n): { alias: string; input: KeyedStream; collectionId: string } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.collection.id]\n if (!input) {\n throw new CollectionInputNotFoundError(from.collection.id)\n }\n return { alias: from.alias, input, collectionId: from.collection.id }\n }\n case `queryRef`: {\n // Find the original query for caching purposes\n const originalQuery = queryMapping.get(from.query) || from.query\n\n // Recursively compile the sub-query with cache\n const subQueryResult = compileQuery(\n originalQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazyCollections,\n optimizableOrderByCollections,\n cache,\n queryMapping\n )\n\n // Extract the pipeline from the compilation result\n const subQueryInput = subQueryResult.pipeline\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n // Unwrap Value expressions that might have leaked through as the entire row\n const unwrapped = unwrapValue(value)\n return [key, unwrapped] as [unknown, any]\n })\n )\n\n return {\n alias: from.alias,\n input: extractedInput,\n collectionId: subQueryResult.collectionId,\n }\n }\n default:\n throw new UnsupportedFromTypeError((from as any).type)\n }\n}\n\n// Helper to check if a value is a Value expression\nfunction isValue(raw: any): boolean {\n return (\n raw instanceof ValClass ||\n (raw && typeof raw === `object` && `type` in raw && raw.type === `val`)\n )\n}\n\n// Helper to unwrap a Value expression or return the value itself\nfunction unwrapValue(value: any): any {\n return isValue(value) ? value.value : value\n}\n\n/**\n * Recursively maps optimized subqueries to their original queries for proper caching.\n * This ensures that when we encounter the same QueryRef object in different contexts,\n * we can find the original query to check the cache.\n */\nfunction mapNestedQueries(\n optimizedQuery: QueryIR,\n originalQuery: QueryIR,\n queryMapping: QueryMapping\n): void {\n // Map the FROM clause if it's a QueryRef\n if (\n optimizedQuery.from.type === `queryRef` &&\n originalQuery.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedQuery.from.query, originalQuery.from.query)\n // Recursively map nested queries\n mapNestedQueries(\n optimizedQuery.from.query,\n originalQuery.from.query,\n queryMapping\n )\n }\n\n // Map JOIN clauses if they exist\n if (optimizedQuery.join && originalQuery.join) {\n for (\n let i = 0;\n i < optimizedQuery.join.length && i < originalQuery.join.length;\n i++\n ) {\n const optimizedJoin = optimizedQuery.join[i]!\n const originalJoin = originalQuery.join[i]!\n\n if (\n optimizedJoin.from.type === `queryRef` &&\n originalJoin.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedJoin.from.query, originalJoin.from.query)\n // Recursively map nested queries in joins\n mapNestedQueries(\n optimizedJoin.from.query,\n originalJoin.from.query,\n queryMapping\n )\n }\n }\n }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n\nexport type CompileQueryFn = typeof compileQuery\n"],"names":["optimizeQuery","map","processJoins","getWhereExpression","compileExpression","filter","DistinctRequiresSelectError","processSelect","processGroupBy","HavingRequiresGroupByError","distinct","processOrderBy","resultPipeline","result","compilationResult","LimitOffsetRequireOrderByError","CollectionInputNotFoundError","UnsupportedFromTypeError","ValClass"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/query/compiler/index.ts"],"sourcesContent":["import { distinct, filter, map } from \"@tanstack/db-ivm\"\nimport { optimizeQuery } from \"../optimizer.js\"\nimport {\n CollectionInputNotFoundError,\n DistinctRequiresSelectError,\n HavingRequiresGroupByError,\n LimitOffsetRequireOrderByError,\n UnsupportedFromTypeError,\n} from \"../../errors.js\"\nimport { PropRef, Value as ValClass, getWhereExpression } from \"../ir.js\"\nimport { compileExpression } from \"./evaluators.js\"\nimport { processJoins } from \"./joins.js\"\nimport { processGroupBy } from \"./group-by.js\"\nimport { processOrderBy } from \"./order-by.js\"\nimport { processSelect } from \"./select.js\"\nimport type { CollectionSubscription } from \"../../collection/subscription.js\"\nimport type { OrderByOptimizationInfo } from \"./order-by.js\"\nimport type {\n BasicExpression,\n CollectionRef,\n QueryIR,\n QueryRef,\n} from \"../ir.js\"\nimport type { LazyCollectionCallbacks } from \"./joins.js\"\nimport type { Collection } from \"../../collection/index.js\"\nimport type {\n KeyedStream,\n NamespacedAndKeyedStream,\n ResultStream,\n} from \"../../types.js\"\nimport type { QueryCache, QueryMapping } from \"./types.js\"\n\n/**\n * Result of query compilation including both the pipeline and source-specific WHERE clauses\n */\nexport interface CompilationResult {\n /** The ID of the main collection */\n collectionId: string\n\n /** The compiled query pipeline (D2 stream) */\n pipeline: ResultStream\n\n /** Map of source aliases to their WHERE clauses for index optimization */\n sourceWhereClauses: Map<string, BasicExpression<boolean>>\n\n /**\n * Maps each source alias to its collection ID. Enables per-alias subscriptions for self-joins.\n * Example: `{ employee: 'employees-col-id', manager: 'employees-col-id' }`\n */\n aliasToCollectionId: Record<string, string>\n\n /**\n * Flattened mapping from outer alias to innermost alias for subqueries.\n * Always provides one-hop lookups, never recursive chains.\n *\n * Example: `{ activeUser: 'user' }` when `.from({ activeUser: subquery })`\n * where the subquery uses `.from({ user: collection })`.\n *\n * For deeply nested subqueries, the mapping goes directly to the innermost alias:\n * `{ author: 'user' }` (not `{ author: 'activeUser' }`), so `aliasRemapping[alias]`\n * always resolves in a single lookup.\n *\n * Used to resolve subscriptions during lazy loading when join aliases differ from\n * the inner aliases where collection subscriptions were created.\n */\n aliasRemapping: Record<string, string>\n}\n\n/**\n * Compiles a query IR into a D2 pipeline\n * @param rawQuery The query IR to compile\n * @param inputs Mapping of source aliases to input streams (e.g., `{ employee: input1, manager: input2 }`)\n * @param collections Mapping of collection IDs to Collection instances\n * @param subscriptions Mapping of source aliases to CollectionSubscription instances\n * @param callbacks Mapping of source aliases to lazy loading callbacks\n * @param lazySources Set of source aliases that should load data lazily\n * @param optimizableOrderByCollections Map of collection IDs to order-by optimization info\n * @param cache Optional cache for compiled subqueries (used internally for recursion)\n * @param queryMapping Optional mapping from optimized queries to original queries\n * @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata\n */\nexport function compileQuery(\n rawQuery: QueryIR,\n inputs: Record<string, KeyedStream>,\n collections: Record<string, Collection<any, any, any, any, any>>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n cache: QueryCache = new WeakMap(),\n queryMapping: QueryMapping = new WeakMap()\n): CompilationResult {\n // Check if the original raw query has already been compiled\n const cachedResult = cache.get(rawQuery)\n if (cachedResult) {\n return cachedResult\n }\n\n // Optimize the query before compilation\n const { optimizedQuery: query, sourceWhereClauses } = optimizeQuery(rawQuery)\n\n // Create mapping from optimized query to original for caching\n queryMapping.set(query, rawQuery)\n mapNestedQueries(query, rawQuery, queryMapping)\n\n // Create a copy of the inputs map to avoid modifying the original\n const allInputs = { ...inputs }\n\n // Track alias to collection id relationships discovered during compilation.\n // This includes all user-declared aliases plus inner aliases from subqueries.\n const aliasToCollectionId: Record<string, string> = {}\n\n // Track alias remapping for subqueries (outer alias → inner alias)\n // e.g., when .join({ activeUser: subquery }) where subquery uses .from({ user: collection })\n // we store: aliasRemapping['activeUser'] = 'user'\n const aliasRemapping: Record<string, string> = {}\n\n // Create a map of source aliases to input streams.\n // Inputs MUST be keyed by alias (e.g., `{ employee: input1, manager: input2 }`),\n // not by collection ID. This enables per-alias subscriptions where different aliases\n // of the same collection (e.g., self-joins) maintain independent filtered streams.\n const sources: Record<string, KeyedStream> = {}\n\n // Process the FROM clause to get the main source\n const {\n alias: mainSource,\n input: mainInput,\n collectionId: mainCollectionId,\n } = processFrom(\n query.from,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n cache,\n queryMapping,\n aliasToCollectionId,\n aliasRemapping\n )\n sources[mainSource] = mainInput\n\n // Prepare the initial pipeline with the main source wrapped in its alias\n let pipeline: NamespacedAndKeyedStream = mainInput.pipe(\n map(([key, row]) => {\n // Initialize the record with a nested structure\n const ret = [key, { [mainSource]: row }] as [\n string,\n Record<string, typeof row>,\n ]\n return ret\n })\n )\n\n // Process JOIN clauses if they exist\n if (query.join && query.join.length > 0) {\n pipeline = processJoins(\n pipeline,\n query.join,\n sources,\n mainCollectionId,\n mainSource,\n allInputs,\n cache,\n queryMapping,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n rawQuery,\n compileQuery,\n aliasToCollectionId,\n aliasRemapping\n )\n }\n\n // Process the WHERE clause if it exists\n if (query.where && query.where.length > 0) {\n // Apply each WHERE condition as a filter (they are ANDed together)\n for (const where of query.where) {\n const whereExpression = getWhereExpression(where)\n const compiledWhere = compileExpression(whereExpression)\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return compiledWhere(namespacedRow)\n })\n )\n }\n }\n\n // Process functional WHERE clauses if they exist\n if (query.fnWhere && query.fnWhere.length > 0) {\n for (const fnWhere of query.fnWhere) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnWhere(namespacedRow)\n })\n )\n }\n }\n\n if (query.distinct && !query.fnSelect && !query.select) {\n throw new DistinctRequiresSelectError()\n }\n\n // Process the SELECT clause early - always create __select_results\n // This eliminates duplication and allows for DISTINCT implementation\n if (query.fnSelect) {\n // Handle functional select - apply the function to transform the row\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults = query.fnSelect!(namespacedRow)\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [string, typeof namespacedRow & { __select_results: any }]\n })\n )\n } else if (query.select) {\n pipeline = processSelect(pipeline, query.select, allInputs)\n } else {\n // If no SELECT clause, create __select_results with the main table data\n pipeline = pipeline.pipe(\n map(([key, namespacedRow]) => {\n const selectResults =\n !query.join && !query.groupBy\n ? namespacedRow[mainSource]\n : namespacedRow\n\n return [\n key,\n {\n ...namespacedRow,\n __select_results: selectResults,\n },\n ] as [string, typeof namespacedRow & { __select_results: any }]\n })\n )\n }\n\n // Process the GROUP BY clause if it exists\n if (query.groupBy && query.groupBy.length > 0) {\n pipeline = processGroupBy(\n pipeline,\n query.groupBy,\n query.having,\n query.select,\n query.fnHaving\n )\n } else if (query.select) {\n // Check if SELECT contains aggregates but no GROUP BY (implicit single-group aggregation)\n const hasAggregates = Object.values(query.select).some(\n (expr) => expr.type === `agg`\n )\n if (hasAggregates) {\n // Handle implicit single-group aggregation\n pipeline = processGroupBy(\n pipeline,\n [], // Empty group by means single group\n query.having,\n query.select,\n query.fnHaving\n )\n }\n }\n\n // Process the HAVING clause if it exists (only applies after GROUP BY)\n if (query.having && (!query.groupBy || query.groupBy.length === 0)) {\n // Check if we have aggregates in SELECT that would trigger implicit grouping\n const hasAggregates = query.select\n ? Object.values(query.select).some((expr) => expr.type === `agg`)\n : false\n\n if (!hasAggregates) {\n throw new HavingRequiresGroupByError()\n }\n }\n\n // Process functional HAVING clauses outside of GROUP BY (treat as additional WHERE filters)\n if (\n query.fnHaving &&\n query.fnHaving.length > 0 &&\n (!query.groupBy || query.groupBy.length === 0)\n ) {\n // If there's no GROUP BY but there are fnHaving clauses, apply them as filters\n for (const fnHaving of query.fnHaving) {\n pipeline = pipeline.pipe(\n filter(([_key, namespacedRow]) => {\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n // Process the DISTINCT clause if it exists\n if (query.distinct) {\n pipeline = pipeline.pipe(distinct(([_key, row]) => row.__select_results))\n }\n\n // Process orderBy parameter if it exists\n if (query.orderBy && query.orderBy.length > 0) {\n const orderedPipeline = processOrderBy(\n rawQuery,\n pipeline,\n query.orderBy,\n query.select || {},\n collections[mainCollectionId]!,\n optimizableOrderByCollections,\n query.limit,\n query.offset\n )\n\n // Final step: extract the __select_results and include orderBy index\n const resultPipeline = orderedPipeline.pipe(\n map(([key, [row, orderByIndex]]) => {\n // Extract the final results from __select_results and include orderBy index\n const raw = (row as any).__select_results\n const finalResults = unwrapValue(raw)\n return [key, [finalResults, orderByIndex]] as [unknown, [any, string]]\n })\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n } else if (query.limit !== undefined || query.offset !== undefined) {\n // If there's a limit or offset without orderBy, throw an error\n throw new LimitOffsetRequireOrderByError()\n }\n\n // Final step: extract the __select_results and return tuple format (no orderBy)\n const resultPipeline: ResultStream = pipeline.pipe(\n map(([key, row]) => {\n // Extract the final results from __select_results and return [key, [results, undefined]]\n const raw = (row as any).__select_results\n const finalResults = unwrapValue(raw)\n return [key, [finalResults, undefined]] as [\n unknown,\n [any, string | undefined],\n ]\n })\n )\n\n const result = resultPipeline\n // Cache the result before returning (use original query as key)\n const compilationResult = {\n collectionId: mainCollectionId,\n pipeline: result,\n sourceWhereClauses,\n aliasToCollectionId,\n aliasRemapping,\n }\n cache.set(rawQuery, compilationResult)\n\n return compilationResult\n}\n\n/**\n * Processes the FROM clause, handling direct collection references and subqueries.\n * Populates `aliasToCollectionId` and `aliasRemapping` for per-alias subscription tracking.\n */\nfunction processFrom(\n from: CollectionRef | QueryRef,\n allInputs: Record<string, KeyedStream>,\n collections: Record<string, Collection>,\n subscriptions: Record<string, CollectionSubscription>,\n callbacks: Record<string, LazyCollectionCallbacks>,\n lazySources: Set<string>,\n optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>,\n cache: QueryCache,\n queryMapping: QueryMapping,\n aliasToCollectionId: Record<string, string>,\n aliasRemapping: Record<string, string>\n): { alias: string; input: KeyedStream; collectionId: string } {\n switch (from.type) {\n case `collectionRef`: {\n const input = allInputs[from.alias]\n if (!input) {\n throw new CollectionInputNotFoundError(\n from.alias,\n from.collection.id,\n Object.keys(allInputs)\n )\n }\n aliasToCollectionId[from.alias] = from.collection.id\n return { alias: from.alias, input, collectionId: from.collection.id }\n }\n case `queryRef`: {\n // Find the original query for caching purposes\n const originalQuery = queryMapping.get(from.query) || from.query\n\n // Recursively compile the sub-query with cache\n const subQueryResult = compileQuery(\n originalQuery,\n allInputs,\n collections,\n subscriptions,\n callbacks,\n lazySources,\n optimizableOrderByCollections,\n cache,\n queryMapping\n )\n\n // Pull up alias mappings from subquery to parent scope.\n // This includes both the innermost alias-to-collection mappings AND\n // any existing remappings from nested subquery levels.\n Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId)\n Object.assign(aliasRemapping, subQueryResult.aliasRemapping)\n\n // Create a FLATTENED remapping from outer alias to innermost alias.\n // For nested subqueries, this ensures one-hop lookups (not recursive chains).\n //\n // Example with 3-level nesting:\n // Inner: .from({ user: usersCollection })\n // Middle: .from({ activeUser: innerSubquery }) → creates: activeUser → user\n // Outer: .from({ author: middleSubquery }) → creates: author → user (not author → activeUser)\n //\n // The key insight: We search through the PULLED-UP aliasToCollectionId (which contains\n // the innermost 'user' alias), so we always map directly to the deepest level.\n // This means aliasRemapping[alias] is always a single lookup, never recursive.\n // Needed for subscription resolution during lazy loading.\n const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(\n (alias) =>\n subQueryResult.aliasToCollectionId[alias] ===\n subQueryResult.collectionId\n )\n if (innerAlias && innerAlias !== from.alias) {\n aliasRemapping[from.alias] = innerAlias\n }\n\n // Extract the pipeline from the compilation result\n const subQueryInput = subQueryResult.pipeline\n\n // Subqueries may return [key, [value, orderByIndex]] (with ORDER BY) or [key, value] (without ORDER BY)\n // We need to extract just the value for use in parent queries\n const extractedInput = subQueryInput.pipe(\n map((data: any) => {\n const [key, [value, _orderByIndex]] = data\n // Unwrap Value expressions that might have leaked through as the entire row\n const unwrapped = unwrapValue(value)\n return [key, unwrapped] as [unknown, any]\n })\n )\n\n return {\n alias: from.alias,\n input: extractedInput,\n collectionId: subQueryResult.collectionId,\n }\n }\n default:\n throw new UnsupportedFromTypeError((from as any).type)\n }\n}\n\n// Helper to check if a value is a Value expression\nfunction isValue(raw: any): boolean {\n return (\n raw instanceof ValClass ||\n (raw && typeof raw === `object` && `type` in raw && raw.type === `val`)\n )\n}\n\n// Helper to unwrap a Value expression or return the value itself\nfunction unwrapValue(value: any): any {\n return isValue(value) ? value.value : value\n}\n\n/**\n * Recursively maps optimized subqueries to their original queries for proper caching.\n * This ensures that when we encounter the same QueryRef object in different contexts,\n * we can find the original query to check the cache.\n */\nfunction mapNestedQueries(\n optimizedQuery: QueryIR,\n originalQuery: QueryIR,\n queryMapping: QueryMapping\n): void {\n // Map the FROM clause if it's a QueryRef\n if (\n optimizedQuery.from.type === `queryRef` &&\n originalQuery.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedQuery.from.query, originalQuery.from.query)\n // Recursively map nested queries\n mapNestedQueries(\n optimizedQuery.from.query,\n originalQuery.from.query,\n queryMapping\n )\n }\n\n // Map JOIN clauses if they exist\n if (optimizedQuery.join && originalQuery.join) {\n for (\n let i = 0;\n i < optimizedQuery.join.length && i < originalQuery.join.length;\n i++\n ) {\n const optimizedJoin = optimizedQuery.join[i]!\n const originalJoin = originalQuery.join[i]!\n\n if (\n optimizedJoin.from.type === `queryRef` &&\n originalJoin.from.type === `queryRef`\n ) {\n queryMapping.set(optimizedJoin.from.query, originalJoin.from.query)\n // Recursively map nested queries in joins\n mapNestedQueries(\n optimizedJoin.from.query,\n originalJoin.from.query,\n queryMapping\n )\n }\n }\n }\n}\n\nfunction getRefFromAlias(\n query: QueryIR,\n alias: string\n): CollectionRef | QueryRef | void {\n if (query.from.alias === alias) {\n return query.from\n }\n\n for (const join of query.join || []) {\n if (join.from.alias === alias) {\n return join.from\n }\n }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n query: QueryIR,\n ref: PropRef<any>,\n collection: Collection\n): { collection: Collection; path: Array<string> } | void {\n if (ref.path.length === 0) {\n return\n }\n\n if (ref.path.length === 1) {\n // This field should be part of this collection\n const field = ref.path[0]!\n // is it part of the select clause?\n if (query.select) {\n const selectedField = query.select[field]\n if (selectedField && selectedField.type === `ref`) {\n return followRef(query, selectedField, collection)\n }\n }\n\n // Either this field is not part of the select clause\n // and thus it must be part of the collection itself\n // or it is part of the select but is not a reference\n // so we can stop here and don't have to follow it\n return { collection, path: [field] }\n }\n\n if (ref.path.length > 1) {\n // This is a nested field\n const [alias, ...rest] = ref.path\n const aliasRef = getRefFromAlias(query, alias!)\n if (!aliasRef) {\n return\n }\n\n if (aliasRef.type === `queryRef`) {\n return followRef(aliasRef.query, new PropRef(rest), collection)\n } else {\n // This is a reference to a collection\n // we can't follow it further\n // so the field must be on the collection itself\n return { collection: aliasRef.collection, path: rest }\n }\n }\n}\n\nexport type CompileQueryFn = typeof compileQuery\n"],"names":["optimizeQuery","map","processJoins","getWhereExpression","compileExpression","filter","DistinctRequiresSelectError","processSelect","processGroupBy","HavingRequiresGroupByError","distinct","processOrderBy","resultPipeline","result","compilationResult","LimitOffsetRequireOrderByError","CollectionInputNotFoundError","UnsupportedFromTypeError","ValClass"],"mappings":";;;;;;;;;;;AAiFO,SAAS,aACd,UACA,QACA,aACA,eACA,WACA,aACA,+BACA,4BAAwB,QAAA,GACxB,eAA6B,oBAAI,WACd;AAEnB,QAAM,eAAe,MAAM,IAAI,QAAQ;AACvC,MAAI,cAAc;AAChB,WAAO;AAAA,EACT;AAGA,QAAM,EAAE,gBAAgB,OAAO,mBAAA,IAAuBA,UAAAA,cAAc,QAAQ;AAG5E,eAAa,IAAI,OAAO,QAAQ;AAChC,mBAAiB,OAAO,UAAU,YAAY;AAG9C,QAAM,YAAY,EAAE,GAAG,OAAA;AAIvB,QAAM,sBAA8C,CAAA;AAKpD,QAAM,iBAAyC,CAAA;AAM/C,QAAM,UAAuC,CAAA;AAG7C,QAAM;AAAA,IACJ,OAAO;AAAA,IACP,OAAO;AAAA,IACP,cAAc;AAAA,EAAA,IACZ;AAAA,IACF,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEF,UAAQ,UAAU,IAAI;AAGtB,MAAI,WAAqC,UAAU;AAAA,IACjDC,MAAAA,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AAElB,YAAM,MAAM,CAAC,KAAK,EAAE,CAAC,UAAU,GAAG,KAAK;AAIvC,aAAO;AAAA,IACT,CAAC;AAAA,EAAA;AAIH,MAAI,MAAM,QAAQ,MAAM,KAAK,SAAS,GAAG;AACvC,eAAWC,MAAAA;AAAAA,MACT;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAGA,MAAI,MAAM,SAAS,MAAM,MAAM,SAAS,GAAG;AAEzC,eAAW,SAAS,MAAM,OAAO;AAC/B,YAAM,kBAAkBC,GAAAA,mBAAmB,KAAK;AAChD,YAAM,gBAAgBC,WAAAA,kBAAkB,eAAe;AACvD,iBAAW,SAAS;AAAA,QAClBC,MAAAA,OAAO,CAAC,CAAC,MAAM,aAAa,MAAM;AAChC,iBAAO,cAAc,aAAa;AAAA,QACpC,CAAC;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AAGA,MAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC7C,eAAW,WAAW,MAAM,SAAS;AACnC,iBAAW,SAAS;AAAA,QAClBA,MAAAA,OAAO,CAAC,CAAC,MAAM,aAAa,MAAM;AAChC,iBAAO,QAAQ,aAAa;AAAA,QAC9B,CAAC;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AAEA,MAAI,MAAM,YAAY,CAAC,MAAM,YAAY,CAAC,MAAM,QAAQ;AACtD,UAAM,IAAIC,OAAAA,4BAAA;AAAA,EACZ;AAIA,MAAI,MAAM,UAAU;AAElB,eAAW,SAAS;AAAA,MAClBL,MAAAA,IAAI,CAAC,CAAC,KAAK,aAAa,MAAM;AAC5B,cAAM,gBAAgB,MAAM,SAAU,aAAa;AACnD,eAAO;AAAA,UACL;AAAA,UACA;AAAA,YACE,GAAG;AAAA,YACH,kBAAkB;AAAA,UAAA;AAAA,QACpB;AAAA,MAEJ,CAAC;AAAA,IAAA;AAAA,EAEL,WAAW,MAAM,QAAQ;AACvB,eAAWM,OAAAA,cAAc,UAAU,MAAM,MAAiB;AAAA,EAC5D,OAAO;AAEL,eAAW,SAAS;AAAA,MAClBN,MAAAA,IAAI,CAAC,CAAC,KAAK,aAAa,MAAM;AAC5B,cAAM,gBACJ,CAAC,MAAM,QAAQ,CAAC,MAAM,UAClB,cAAc,UAAU,IACxB;AAEN,eAAO;AAAA,UACL;AAAA,UACA;AAAA,YACE,GAAG;AAAA,YACH,kBAAkB;AAAA,UAAA;AAAA,QACpB;AAAA,MAEJ,CAAC;AAAA,IAAA;AAAA,EAEL;AAGA,MAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC7C,eAAWO,QAAAA;AAAAA,MACT;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAAA,EAEV,WAAW,MAAM,QAAQ;AAEvB,UAAM,gBAAgB,OAAO,OAAO,MAAM,MAAM,EAAE;AAAA,MAChD,CAAC,SAAS,KAAK,SAAS;AAAA,IAAA;AAE1B,QAAI,eAAe;AAEjB,iBAAWA,QAAAA;AAAAA,QACT;AAAA,QACA,CAAA;AAAA;AAAA,QACA,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,MAAA;AAAA,IAEV;AAAA,EACF;AAGA,MAAI,MAAM,WAAW,CAAC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAAI;AAElE,UAAM,gBAAgB,MAAM,SACxB,OAAO,OAAO,MAAM,MAAM,EAAE,KAAK,CAAC,SAAS,KAAK,SAAS,KAAK,IAC9D;AAEJ,QAAI,CAAC,eAAe;AAClB,YAAM,IAAIC,OAAAA,2BAAA;AAAA,IACZ;AAAA,EACF;AAGA,MACE,MAAM,YACN,MAAM,SAAS,SAAS,MACvB,CAAC,MAAM,WAAW,MAAM,QAAQ,WAAW,IAC5C;AAEA,eAAW,YAAY,MAAM,UAAU;AACrC,iBAAW,SAAS;AAAA,QAClBJ,MAAAA,OAAO,CAAC,CAAC,MAAM,aAAa,MAAM;AAChC,iBAAO,SAAS,aAAa;AAAA,QAC/B,CAAC;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AAGA,MAAI,MAAM,UAAU;AAClB,eAAW,SAAS,KAAKK,eAAS,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,gBAAgB,CAAC;AAAA,EAC1E;AAGA,MAAI,MAAM,WAAW,MAAM,QAAQ,SAAS,GAAG;AAC7C,UAAM,kBAAkBC,QAAAA;AAAAA,MACtB;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,MAAM,UAAU,CAAA;AAAA,MAChB,YAAY,gBAAgB;AAAA,MAC5B;AAAA,MACA,MAAM;AAAA,MACN,MAAM;AAAA,IAAA;AAIR,UAAMC,kBAAiB,gBAAgB;AAAA,MACrCX,MAAAA,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,CAAC,MAAM;AAElC,cAAM,MAAO,IAAY;AACzB,cAAM,eAAe,YAAY,GAAG;AACpC,eAAO,CAAC,KAAK,CAAC,cAAc,YAAY,CAAC;AAAA,MAC3C,CAAC;AAAA,IAAA;AAGH,UAAMY,UAASD;AAEf,UAAME,qBAAoB;AAAA,MACxB,cAAc;AAAA,MACd,UAAUD;AAAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAEF,UAAM,IAAI,UAAUC,kBAAiB;AAErC,WAAOA;AAAAA,EACT,WAAW,MAAM,UAAU,UAAa,MAAM,WAAW,QAAW;AAElE,UAAM,IAAIC,OAAAA,+BAAA;AAAA,EACZ;AAGA,QAAM,iBAA+B,SAAS;AAAA,IAC5Cd,MAAAA,IAAI,CAAC,CAAC,KAAK,GAAG,MAAM;AAElB,YAAM,MAAO,IAAY;AACzB,YAAM,eAAe,YAAY,GAAG;AACpC,aAAO,CAAC,KAAK,CAAC,cAAc,MAAS,CAAC;AAAA,IAIxC,CAAC;AAAA,EAAA;AAGH,QAAM,SAAS;AAEf,QAAM,oBAAoB;AAAA,IACxB,cAAc;AAAA,IACd,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEF,QAAM,IAAI,UAAU,iBAAiB;AAErC,SAAO;AACT;AAMA,SAAS,YACP,MACA,WACA,aACA,eACA,WACA,aACA,+BACA,OACA,cACA,qBACA,gBAC6D;AAC7D,UAAQ,KAAK,MAAA;AAAA,IACX,KAAK,iBAAiB;AACpB,YAAM,QAAQ,UAAU,KAAK,KAAK;AAClC,UAAI,CAAC,OAAO;AACV,cAAM,IAAIe,OAAAA;AAAAA,UACR,KAAK;AAAA,UACL,KAAK,WAAW;AAAA,UAChB,OAAO,KAAK,SAAS;AAAA,QAAA;AAAA,MAEzB;AACA,0BAAoB,KAAK,KAAK,IAAI,KAAK,WAAW;AAClD,aAAO,EAAE,OAAO,KAAK,OAAO,OAAO,cAAc,KAAK,WAAW,GAAA;AAAA,IACnE;AAAA,IACA,KAAK,YAAY;AAEf,YAAM,gBAAgB,aAAa,IAAI,KAAK,KAAK,KAAK,KAAK;AAG3D,YAAM,iBAAiB;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAMF,aAAO,OAAO,qBAAqB,eAAe,mBAAmB;AACrE,aAAO,OAAO,gBAAgB,eAAe,cAAc;AAc3D,YAAM,aAAa,OAAO,KAAK,eAAe,mBAAmB,EAAE;AAAA,QACjE,CAAC,UACC,eAAe,oBAAoB,KAAK,MACxC,eAAe;AAAA,MAAA;AAEnB,UAAI,cAAc,eAAe,KAAK,OAAO;AAC3C,uBAAe,KAAK,KAAK,IAAI;AAAA,MAC/B;AAGA,YAAM,gBAAgB,eAAe;AAIrC,YAAM,iBAAiB,cAAc;AAAA,QACnCf,MAAAA,IAAI,CAAC,SAAc;AACjB,gBAAM,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,IAAI;AAEtC,gBAAM,YAAY,YAAY,KAAK;AACnC,iBAAO,CAAC,KAAK,SAAS;AAAA,QACxB,CAAC;AAAA,MAAA;AAGH,aAAO;AAAA,QACL,OAAO,KAAK;AAAA,QACZ,OAAO;AAAA,QACP,cAAc,eAAe;AAAA,MAAA;AAAA,IAEjC;AAAA,IACA;AACE,YAAM,IAAIgB,OAAAA,yBAA0B,KAAa,IAAI;AAAA,EAAA;AAE3D;AAGA,SAAS,QAAQ,KAAmB;AAClC,SACE,eAAeC,GAAAA,SACd,OAAO,OAAO,QAAQ,YAAY,UAAU,OAAO,IAAI,SAAS;AAErE;AAGA,SAAS,YAAY,OAAiB;AACpC,SAAO,QAAQ,KAAK,IAAI,MAAM,QAAQ;AACxC;AAOA,SAAS,iBACP,gBACA,eACA,cACM;AAEN,MACE,eAAe,KAAK,SAAS,cAC7B,cAAc,KAAK,SAAS,YAC5B;AACA,iBAAa,IAAI,eAAe,KAAK,OAAO,cAAc,KAAK,KAAK;AAEpE;AAAA,MACE,eAAe,KAAK;AAAA,MACpB,cAAc,KAAK;AAAA,MACnB;AAAA,IAAA;AAAA,EAEJ;AAGA,MAAI,eAAe,QAAQ,cAAc,MAAM;AAC7C,aACM,IAAI,GACR,IAAI,eAAe,KAAK,UAAU,IAAI,cAAc,KAAK,QACzD,KACA;AACA,YAAM,gBAAgB,eAAe,KAAK,CAAC;AAC3C,YAAM,eAAe,cAAc,KAAK,CAAC;AAEzC,UACE,cAAc,KAAK,SAAS,cAC5B,aAAa,KAAK,SAAS,YAC3B;AACA,qBAAa,IAAI,cAAc,KAAK,OAAO,aAAa,KAAK,KAAK;AAElE;AAAA,UACE,cAAc,KAAK;AAAA,UACnB,aAAa,KAAK;AAAA,UAClB;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAAA,EACF;AACF;;"}
|
|
@@ -6,25 +6,50 @@ import { Collection } from '../../collection/index.js';
|
|
|
6
6
|
import { KeyedStream, ResultStream } from '../../types.js';
|
|
7
7
|
import { QueryCache, QueryMapping } from './types.js';
|
|
8
8
|
/**
|
|
9
|
-
* Result of query compilation including both the pipeline and
|
|
9
|
+
* Result of query compilation including both the pipeline and source-specific WHERE clauses
|
|
10
10
|
*/
|
|
11
11
|
export interface CompilationResult {
|
|
12
12
|
/** The ID of the main collection */
|
|
13
13
|
collectionId: string;
|
|
14
|
-
/** The compiled query pipeline */
|
|
14
|
+
/** The compiled query pipeline (D2 stream) */
|
|
15
15
|
pipeline: ResultStream;
|
|
16
|
-
/** Map of
|
|
17
|
-
|
|
16
|
+
/** Map of source aliases to their WHERE clauses for index optimization */
|
|
17
|
+
sourceWhereClauses: Map<string, BasicExpression<boolean>>;
|
|
18
|
+
/**
|
|
19
|
+
* Maps each source alias to its collection ID. Enables per-alias subscriptions for self-joins.
|
|
20
|
+
* Example: `{ employee: 'employees-col-id', manager: 'employees-col-id' }`
|
|
21
|
+
*/
|
|
22
|
+
aliasToCollectionId: Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Flattened mapping from outer alias to innermost alias for subqueries.
|
|
25
|
+
* Always provides one-hop lookups, never recursive chains.
|
|
26
|
+
*
|
|
27
|
+
* Example: `{ activeUser: 'user' }` when `.from({ activeUser: subquery })`
|
|
28
|
+
* where the subquery uses `.from({ user: collection })`.
|
|
29
|
+
*
|
|
30
|
+
* For deeply nested subqueries, the mapping goes directly to the innermost alias:
|
|
31
|
+
* `{ author: 'user' }` (not `{ author: 'activeUser' }`), so `aliasRemapping[alias]`
|
|
32
|
+
* always resolves in a single lookup.
|
|
33
|
+
*
|
|
34
|
+
* Used to resolve subscriptions during lazy loading when join aliases differ from
|
|
35
|
+
* the inner aliases where collection subscriptions were created.
|
|
36
|
+
*/
|
|
37
|
+
aliasRemapping: Record<string, string>;
|
|
18
38
|
}
|
|
19
39
|
/**
|
|
20
|
-
* Compiles a
|
|
40
|
+
* Compiles a query IR into a D2 pipeline
|
|
21
41
|
* @param rawQuery The query IR to compile
|
|
22
|
-
* @param inputs Mapping of
|
|
42
|
+
* @param inputs Mapping of source aliases to input streams (e.g., `{ employee: input1, manager: input2 }`)
|
|
43
|
+
* @param collections Mapping of collection IDs to Collection instances
|
|
44
|
+
* @param subscriptions Mapping of source aliases to CollectionSubscription instances
|
|
45
|
+
* @param callbacks Mapping of source aliases to lazy loading callbacks
|
|
46
|
+
* @param lazySources Set of source aliases that should load data lazily
|
|
47
|
+
* @param optimizableOrderByCollections Map of collection IDs to order-by optimization info
|
|
23
48
|
* @param cache Optional cache for compiled subqueries (used internally for recursion)
|
|
24
49
|
* @param queryMapping Optional mapping from optimized queries to original queries
|
|
25
|
-
* @returns A CompilationResult with the pipeline
|
|
50
|
+
* @returns A CompilationResult with the pipeline, source WHERE clauses, and alias metadata
|
|
26
51
|
*/
|
|
27
|
-
export declare function compileQuery(rawQuery: QueryIR, inputs: Record<string, KeyedStream>, collections: Record<string, Collection<any, any, any, any, any>>, subscriptions: Record<string, CollectionSubscription>, callbacks: Record<string, LazyCollectionCallbacks>,
|
|
52
|
+
export declare function compileQuery(rawQuery: QueryIR, inputs: Record<string, KeyedStream>, collections: Record<string, Collection<any, any, any, any, any>>, subscriptions: Record<string, CollectionSubscription>, callbacks: Record<string, LazyCollectionCallbacks>, lazySources: Set<string>, optimizableOrderByCollections: Record<string, OrderByOptimizationInfo>, cache?: QueryCache, queryMapping?: QueryMapping): CompilationResult;
|
|
28
53
|
/**
|
|
29
54
|
* Follows the given reference in a query
|
|
30
55
|
* until its finds the root field the reference points to.
|
|
@@ -6,32 +6,35 @@ const autoIndex = require("../../indexes/auto-index.cjs");
|
|
|
6
6
|
const ir = require("../ir.cjs");
|
|
7
7
|
const functions = require("../builder/functions.cjs");
|
|
8
8
|
const evaluators = require("./evaluators.cjs");
|
|
9
|
-
function processJoins(pipeline, joinClauses,
|
|
9
|
+
function processJoins(pipeline, joinClauses, sources, mainCollectionId, mainSource, allInputs, cache, queryMapping, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, rawQuery, onCompileSubquery, aliasToCollectionId, aliasRemapping) {
|
|
10
10
|
let resultPipeline = pipeline;
|
|
11
11
|
for (const joinClause of joinClauses) {
|
|
12
12
|
resultPipeline = processJoin(
|
|
13
13
|
resultPipeline,
|
|
14
14
|
joinClause,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
sources,
|
|
16
|
+
mainCollectionId,
|
|
17
|
+
mainSource,
|
|
18
18
|
allInputs,
|
|
19
19
|
cache,
|
|
20
20
|
queryMapping,
|
|
21
21
|
collections,
|
|
22
22
|
subscriptions,
|
|
23
23
|
callbacks,
|
|
24
|
-
|
|
24
|
+
lazySources,
|
|
25
25
|
optimizableOrderByCollections,
|
|
26
26
|
rawQuery,
|
|
27
|
-
onCompileSubquery
|
|
27
|
+
onCompileSubquery,
|
|
28
|
+
aliasToCollectionId,
|
|
29
|
+
aliasRemapping
|
|
28
30
|
);
|
|
29
31
|
}
|
|
30
32
|
return resultPipeline;
|
|
31
33
|
}
|
|
32
|
-
function processJoin(pipeline, joinClause,
|
|
34
|
+
function processJoin(pipeline, joinClause, sources, mainCollectionId, mainSource, allInputs, cache, queryMapping, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, rawQuery, onCompileSubquery, aliasToCollectionId, aliasRemapping) {
|
|
35
|
+
const isCollectionRef = joinClause.from.type === `collectionRef`;
|
|
33
36
|
const {
|
|
34
|
-
alias:
|
|
37
|
+
alias: joinedSource,
|
|
35
38
|
input: joinedInput,
|
|
36
39
|
collectionId: joinedCollectionId
|
|
37
40
|
} = processJoinSource(
|
|
@@ -40,32 +43,37 @@ function processJoin(pipeline, joinClause, tables, mainTableId, mainTableAlias,
|
|
|
40
43
|
collections,
|
|
41
44
|
subscriptions,
|
|
42
45
|
callbacks,
|
|
43
|
-
|
|
46
|
+
lazySources,
|
|
44
47
|
optimizableOrderByCollections,
|
|
45
48
|
cache,
|
|
46
49
|
queryMapping,
|
|
47
|
-
onCompileSubquery
|
|
50
|
+
onCompileSubquery,
|
|
51
|
+
aliasToCollectionId,
|
|
52
|
+
aliasRemapping
|
|
48
53
|
);
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
sources[joinedSource] = joinedInput;
|
|
55
|
+
if (isCollectionRef) {
|
|
56
|
+
aliasToCollectionId[joinedSource] = joinedCollectionId;
|
|
57
|
+
}
|
|
58
|
+
const mainCollection = collections[mainCollectionId];
|
|
51
59
|
const joinedCollection = collections[joinedCollectionId];
|
|
52
60
|
if (!mainCollection) {
|
|
53
|
-
throw new errors.JoinCollectionNotFoundError(
|
|
61
|
+
throw new errors.JoinCollectionNotFoundError(mainCollectionId);
|
|
54
62
|
}
|
|
55
63
|
if (!joinedCollection) {
|
|
56
64
|
throw new errors.JoinCollectionNotFoundError(joinedCollectionId);
|
|
57
65
|
}
|
|
58
|
-
const {
|
|
66
|
+
const { activeSource, lazySource } = getActiveAndLazySources(
|
|
59
67
|
joinClause.type,
|
|
60
68
|
mainCollection,
|
|
61
69
|
joinedCollection
|
|
62
70
|
);
|
|
63
|
-
const
|
|
71
|
+
const availableSources = Object.keys(sources);
|
|
64
72
|
const { mainExpr, joinedExpr } = analyzeJoinExpressions(
|
|
65
73
|
joinClause.left,
|
|
66
74
|
joinClause.right,
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
availableSources,
|
|
76
|
+
joinedSource
|
|
69
77
|
);
|
|
70
78
|
const compiledMainExpr = evaluators.compileExpression(mainExpr);
|
|
71
79
|
const compiledJoinedExpr = evaluators.compileExpression(joinedExpr);
|
|
@@ -77,7 +85,7 @@ function processJoin(pipeline, joinClause, tables, mainTableId, mainTableAlias,
|
|
|
77
85
|
);
|
|
78
86
|
let joinedPipeline = joinedInput.pipe(
|
|
79
87
|
dbIvm.map(([currentKey, row]) => {
|
|
80
|
-
const namespacedRow = { [
|
|
88
|
+
const namespacedRow = { [joinedSource]: row };
|
|
81
89
|
const joinedKey = compiledJoinedExpr(namespacedRow);
|
|
82
90
|
return [joinedKey, [currentKey, namespacedRow]];
|
|
83
91
|
})
|
|
@@ -85,18 +93,19 @@ function processJoin(pipeline, joinClause, tables, mainTableId, mainTableAlias,
|
|
|
85
93
|
if (![`inner`, `left`, `right`, `full`].includes(joinClause.type)) {
|
|
86
94
|
throw new errors.UnsupportedJoinTypeError(joinClause.type);
|
|
87
95
|
}
|
|
88
|
-
if (
|
|
89
|
-
const lazyFrom =
|
|
96
|
+
if (activeSource) {
|
|
97
|
+
const lazyFrom = activeSource === `main` ? joinClause.from : rawQuery.from;
|
|
90
98
|
const limitedSubquery = lazyFrom.type === `queryRef` && (lazyFrom.query.limit || lazyFrom.query.offset);
|
|
91
99
|
const hasComputedJoinExpr = mainExpr.type === `func` || joinedExpr.type === `func`;
|
|
92
100
|
if (!limitedSubquery && !hasComputedJoinExpr) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
101
|
+
const lazyAlias = activeSource === `main` ? joinedSource : mainSource;
|
|
102
|
+
lazySources.add(lazyAlias);
|
|
103
|
+
const activePipeline = activeSource === `main` ? mainPipeline : joinedPipeline;
|
|
104
|
+
const lazySourceJoinExpr = activeSource === `main` ? joinedExpr : mainExpr;
|
|
96
105
|
const followRefResult = ir.followRef(
|
|
97
106
|
rawQuery,
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
lazySourceJoinExpr,
|
|
108
|
+
lazySource
|
|
100
109
|
);
|
|
101
110
|
const followRefCollection = followRefResult.collection;
|
|
102
111
|
const fieldName = followRefResult.path[0];
|
|
@@ -109,27 +118,31 @@ function processJoin(pipeline, joinClause, tables, mainTableId, mainTableAlias,
|
|
|
109
118
|
}
|
|
110
119
|
const activePipelineWithLoading = activePipeline.pipe(
|
|
111
120
|
dbIvm.tap((data) => {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
const resolvedAlias = aliasRemapping[lazyAlias] || lazyAlias;
|
|
122
|
+
const lazySourceSubscription = subscriptions[resolvedAlias];
|
|
123
|
+
if (!lazySourceSubscription) {
|
|
124
|
+
throw new errors.SubscriptionNotFoundError(
|
|
125
|
+
resolvedAlias,
|
|
126
|
+
lazyAlias,
|
|
127
|
+
lazySource.id,
|
|
128
|
+
Object.keys(subscriptions)
|
|
116
129
|
);
|
|
117
130
|
}
|
|
118
|
-
if (
|
|
131
|
+
if (lazySourceSubscription.hasLoadedInitialState()) {
|
|
119
132
|
return;
|
|
120
133
|
}
|
|
121
134
|
const joinKeys = data.getInner().map(([[joinKey]]) => joinKey);
|
|
122
135
|
const lazyJoinRef = new ir.PropRef(followRefResult.path);
|
|
123
|
-
const loaded =
|
|
136
|
+
const loaded = lazySourceSubscription.requestSnapshot({
|
|
124
137
|
where: functions.inArray(lazyJoinRef, joinKeys),
|
|
125
138
|
optimizedOnly: true
|
|
126
139
|
});
|
|
127
140
|
if (!loaded) {
|
|
128
|
-
|
|
141
|
+
lazySourceSubscription.requestSnapshot();
|
|
129
142
|
}
|
|
130
143
|
})
|
|
131
144
|
);
|
|
132
|
-
if (
|
|
145
|
+
if (activeSource === `main`) {
|
|
133
146
|
mainPipeline = activePipelineWithLoading;
|
|
134
147
|
} else {
|
|
135
148
|
joinedPipeline = activePipelineWithLoading;
|
|
@@ -138,61 +151,65 @@ function processJoin(pipeline, joinClause, tables, mainTableId, mainTableAlias,
|
|
|
138
151
|
}
|
|
139
152
|
return mainPipeline.pipe(
|
|
140
153
|
dbIvm.join(joinedPipeline, joinClause.type),
|
|
141
|
-
dbIvm.consolidate(),
|
|
142
154
|
processJoinResults(joinClause.type)
|
|
143
155
|
);
|
|
144
156
|
}
|
|
145
|
-
function analyzeJoinExpressions(left, right,
|
|
146
|
-
const
|
|
147
|
-
(alias) => alias !==
|
|
157
|
+
function analyzeJoinExpressions(left, right, allAvailableSourceAliases, joinedSource) {
|
|
158
|
+
const availableSources = allAvailableSourceAliases.filter(
|
|
159
|
+
(alias) => alias !== joinedSource
|
|
148
160
|
);
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
if (
|
|
161
|
+
const leftSourceAlias = getSourceAliasFromExpression(left);
|
|
162
|
+
const rightSourceAlias = getSourceAliasFromExpression(right);
|
|
163
|
+
if (leftSourceAlias && availableSources.includes(leftSourceAlias) && rightSourceAlias === joinedSource) {
|
|
152
164
|
return { mainExpr: left, joinedExpr: right };
|
|
153
165
|
}
|
|
154
|
-
if (
|
|
166
|
+
if (leftSourceAlias === joinedSource && rightSourceAlias && availableSources.includes(rightSourceAlias)) {
|
|
155
167
|
return { mainExpr: right, joinedExpr: left };
|
|
156
168
|
}
|
|
157
|
-
if (!
|
|
158
|
-
throw new errors.
|
|
169
|
+
if (!leftSourceAlias || !rightSourceAlias) {
|
|
170
|
+
throw new errors.InvalidJoinConditionSourceMismatchError();
|
|
159
171
|
}
|
|
160
|
-
if (
|
|
161
|
-
throw new errors.
|
|
172
|
+
if (leftSourceAlias === rightSourceAlias) {
|
|
173
|
+
throw new errors.InvalidJoinConditionSameSourceError(leftSourceAlias);
|
|
162
174
|
}
|
|
163
|
-
if (!
|
|
164
|
-
throw new errors.
|
|
175
|
+
if (!availableSources.includes(leftSourceAlias)) {
|
|
176
|
+
throw new errors.InvalidJoinConditionLeftSourceError(leftSourceAlias);
|
|
165
177
|
}
|
|
166
|
-
if (
|
|
167
|
-
throw new errors.
|
|
178
|
+
if (rightSourceAlias !== joinedSource) {
|
|
179
|
+
throw new errors.InvalidJoinConditionRightSourceError(joinedSource);
|
|
168
180
|
}
|
|
169
181
|
throw new errors.InvalidJoinCondition();
|
|
170
182
|
}
|
|
171
|
-
function
|
|
183
|
+
function getSourceAliasFromExpression(expr) {
|
|
172
184
|
switch (expr.type) {
|
|
173
185
|
case `ref`:
|
|
174
186
|
return expr.path[0] || null;
|
|
175
187
|
case `func`: {
|
|
176
|
-
const
|
|
188
|
+
const sourceAliases = /* @__PURE__ */ new Set();
|
|
177
189
|
for (const arg of expr.args) {
|
|
178
|
-
const alias =
|
|
190
|
+
const alias = getSourceAliasFromExpression(arg);
|
|
179
191
|
if (alias) {
|
|
180
|
-
|
|
192
|
+
sourceAliases.add(alias);
|
|
181
193
|
}
|
|
182
194
|
}
|
|
183
|
-
return
|
|
195
|
+
return sourceAliases.size === 1 ? Array.from(sourceAliases)[0] : null;
|
|
184
196
|
}
|
|
185
197
|
default:
|
|
186
198
|
return null;
|
|
187
199
|
}
|
|
188
200
|
}
|
|
189
|
-
function processJoinSource(from, allInputs, collections, subscriptions, callbacks,
|
|
201
|
+
function processJoinSource(from, allInputs, collections, subscriptions, callbacks, lazySources, optimizableOrderByCollections, cache, queryMapping, onCompileSubquery, aliasToCollectionId, aliasRemapping) {
|
|
190
202
|
switch (from.type) {
|
|
191
203
|
case `collectionRef`: {
|
|
192
|
-
const input = allInputs[from.
|
|
204
|
+
const input = allInputs[from.alias];
|
|
193
205
|
if (!input) {
|
|
194
|
-
throw new errors.CollectionInputNotFoundError(
|
|
206
|
+
throw new errors.CollectionInputNotFoundError(
|
|
207
|
+
from.alias,
|
|
208
|
+
from.collection.id,
|
|
209
|
+
Object.keys(allInputs)
|
|
210
|
+
);
|
|
195
211
|
}
|
|
212
|
+
aliasToCollectionId[from.alias] = from.collection.id;
|
|
196
213
|
return { alias: from.alias, input, collectionId: from.collection.id };
|
|
197
214
|
}
|
|
198
215
|
case `queryRef`: {
|
|
@@ -203,11 +220,19 @@ function processJoinSource(from, allInputs, collections, subscriptions, callback
|
|
|
203
220
|
collections,
|
|
204
221
|
subscriptions,
|
|
205
222
|
callbacks,
|
|
206
|
-
|
|
223
|
+
lazySources,
|
|
207
224
|
optimizableOrderByCollections,
|
|
208
225
|
cache,
|
|
209
226
|
queryMapping
|
|
210
227
|
);
|
|
228
|
+
Object.assign(aliasToCollectionId, subQueryResult.aliasToCollectionId);
|
|
229
|
+
Object.assign(aliasRemapping, subQueryResult.aliasRemapping);
|
|
230
|
+
const innerAlias = Object.keys(subQueryResult.aliasToCollectionId).find(
|
|
231
|
+
(alias) => subQueryResult.aliasToCollectionId[alias] === subQueryResult.collectionId
|
|
232
|
+
);
|
|
233
|
+
if (innerAlias && innerAlias !== from.alias) {
|
|
234
|
+
aliasRemapping[from.alias] = innerAlias;
|
|
235
|
+
}
|
|
211
236
|
const subQueryInput = subQueryResult.pipeline;
|
|
212
237
|
const extractedInput = subQueryInput.pipe(
|
|
213
238
|
dbIvm.map((data) => {
|
|
@@ -263,19 +288,16 @@ function processJoinResults(joinType) {
|
|
|
263
288
|
);
|
|
264
289
|
};
|
|
265
290
|
}
|
|
266
|
-
function
|
|
267
|
-
if (leftCollection.id === rightCollection.id) {
|
|
268
|
-
return { activeCollection: void 0, lazyCollection: void 0 };
|
|
269
|
-
}
|
|
291
|
+
function getActiveAndLazySources(joinType, leftCollection, rightCollection) {
|
|
270
292
|
switch (joinType) {
|
|
271
293
|
case `left`:
|
|
272
|
-
return {
|
|
294
|
+
return { activeSource: `main`, lazySource: rightCollection };
|
|
273
295
|
case `right`:
|
|
274
|
-
return {
|
|
296
|
+
return { activeSource: `joined`, lazySource: leftCollection };
|
|
275
297
|
case `inner`:
|
|
276
|
-
return leftCollection.size < rightCollection.size ? {
|
|
298
|
+
return leftCollection.size < rightCollection.size ? { activeSource: `main`, lazySource: rightCollection } : { activeSource: `joined`, lazySource: leftCollection };
|
|
277
299
|
default:
|
|
278
|
-
return {
|
|
300
|
+
return { activeSource: void 0, lazySource: void 0 };
|
|
279
301
|
}
|
|
280
302
|
}
|
|
281
303
|
exports.processJoins = processJoins;
|