linkgress-orm 0.0.2 → 0.1.0
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/LICENSE +21 -21
- package/README.md +196 -196
- package/dist/entity/db-column.d.ts +38 -1
- package/dist/entity/db-column.d.ts.map +1 -1
- package/dist/entity/db-column.js.map +1 -1
- package/dist/entity/db-context.d.ts +429 -50
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +884 -203
- package/dist/entity/db-context.js.map +1 -1
- package/dist/entity/entity-base.d.ts +8 -0
- package/dist/entity/entity-base.d.ts.map +1 -1
- package/dist/entity/entity-base.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/migration/db-schema-manager.js +77 -77
- package/dist/migration/enum-migrator.js +6 -6
- package/dist/query/collection-strategy.factory.d.ts.map +1 -1
- package/dist/query/collection-strategy.factory.js +7 -3
- package/dist/query/collection-strategy.factory.js.map +1 -1
- package/dist/query/collection-strategy.interface.d.ts +12 -6
- package/dist/query/collection-strategy.interface.d.ts.map +1 -1
- package/dist/query/conditions.d.ts +178 -24
- package/dist/query/conditions.d.ts.map +1 -1
- package/dist/query/conditions.js +165 -4
- package/dist/query/conditions.js.map +1 -1
- package/dist/query/cte-builder.d.ts +21 -5
- package/dist/query/cte-builder.d.ts.map +1 -1
- package/dist/query/cte-builder.js +31 -7
- package/dist/query/cte-builder.js.map +1 -1
- package/dist/query/grouped-query.d.ts +185 -8
- package/dist/query/grouped-query.d.ts.map +1 -1
- package/dist/query/grouped-query.js +516 -30
- package/dist/query/grouped-query.js.map +1 -1
- package/dist/query/join-builder.d.ts +5 -4
- package/dist/query/join-builder.d.ts.map +1 -1
- package/dist/query/join-builder.js +11 -33
- package/dist/query/join-builder.js.map +1 -1
- package/dist/query/query-builder.d.ts +89 -20
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +317 -168
- package/dist/query/query-builder.js.map +1 -1
- package/dist/query/query-utils.d.ts +45 -0
- package/dist/query/query-utils.d.ts.map +1 -0
- package/dist/query/query-utils.js +103 -0
- package/dist/query/query-utils.js.map +1 -0
- package/dist/query/sql-utils.d.ts +83 -0
- package/dist/query/sql-utils.d.ts.map +1 -0
- package/dist/query/sql-utils.js +218 -0
- package/dist/query/sql-utils.js.map +1 -0
- package/dist/query/strategies/cte-collection-strategy.d.ts +85 -0
- package/dist/query/strategies/cte-collection-strategy.d.ts.map +1 -0
- package/dist/query/strategies/cte-collection-strategy.js +338 -0
- package/dist/query/strategies/cte-collection-strategy.js.map +1 -0
- package/dist/query/strategies/lateral-collection-strategy.d.ts +59 -0
- package/dist/query/strategies/lateral-collection-strategy.d.ts.map +1 -0
- package/dist/query/strategies/lateral-collection-strategy.js +243 -0
- package/dist/query/strategies/lateral-collection-strategy.js.map +1 -0
- package/dist/query/strategies/temptable-collection-strategy.d.ts +21 -0
- package/dist/query/strategies/temptable-collection-strategy.d.ts.map +1 -1
- package/dist/query/strategies/temptable-collection-strategy.js +216 -94
- package/dist/query/strategies/temptable-collection-strategy.js.map +1 -1
- package/dist/query/subquery.d.ts +24 -1
- package/dist/query/subquery.d.ts.map +1 -1
- package/dist/query/subquery.js +38 -2
- package/dist/query/subquery.js.map +1 -1
- package/package.json +1 -1
- package/dist/query/strategies/jsonb-collection-strategy.d.ts +0 -51
- package/dist/query/strategies/jsonb-collection-strategy.d.ts.map +0 -1
- package/dist/query/strategies/jsonb-collection-strategy.js +0 -210
- package/dist/query/strategies/jsonb-collection-strategy.js.map +0 -1
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CteCollectionStrategy = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* CTE-based collection strategy
|
|
6
|
+
*
|
|
7
|
+
* This is the current/default strategy that uses PostgreSQL CTEs with jsonb_agg
|
|
8
|
+
* to aggregate related records into JSONB arrays.
|
|
9
|
+
*
|
|
10
|
+
* Benefits:
|
|
11
|
+
* - Single query execution
|
|
12
|
+
* - No temp table management
|
|
13
|
+
* - Works well for moderate data sizes
|
|
14
|
+
*
|
|
15
|
+
* SQL Pattern:
|
|
16
|
+
* ```sql
|
|
17
|
+
* WITH "cte_0" AS (
|
|
18
|
+
* SELECT
|
|
19
|
+
* "user_id" as parent_id,
|
|
20
|
+
* jsonb_agg(
|
|
21
|
+
* jsonb_build_object('id', "id", 'title', "title")
|
|
22
|
+
* ORDER BY "views" DESC
|
|
23
|
+
* ) as data
|
|
24
|
+
* FROM (
|
|
25
|
+
* SELECT "user_id", "id", "title", "views"
|
|
26
|
+
* FROM "posts"
|
|
27
|
+
* WHERE "views" > $1
|
|
28
|
+
* ORDER BY "views" DESC
|
|
29
|
+
* ) sub
|
|
30
|
+
* GROUP BY "user_id"
|
|
31
|
+
* )
|
|
32
|
+
* SELECT ... COALESCE("cte_0".data, '[]'::jsonb) as "posts" ...
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
class CteCollectionStrategy {
|
|
36
|
+
getType() {
|
|
37
|
+
return 'cte';
|
|
38
|
+
}
|
|
39
|
+
requiresParentIds() {
|
|
40
|
+
// JSONB strategy doesn't need parent IDs upfront - it aggregates for all parents
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
buildAggregation(config, context) {
|
|
44
|
+
const cteName = `cte_${config.counter}`;
|
|
45
|
+
let cteSQL;
|
|
46
|
+
let selectExpression;
|
|
47
|
+
switch (config.aggregationType) {
|
|
48
|
+
case 'jsonb':
|
|
49
|
+
cteSQL = this.buildJsonbAggregation(config, cteName, context);
|
|
50
|
+
selectExpression = `COALESCE("${cteName}".data, ${config.defaultValue})`;
|
|
51
|
+
break;
|
|
52
|
+
case 'array':
|
|
53
|
+
cteSQL = this.buildArrayAggregation(config, cteName, context);
|
|
54
|
+
selectExpression = `COALESCE("${cteName}".data, ${config.defaultValue})`;
|
|
55
|
+
break;
|
|
56
|
+
case 'count':
|
|
57
|
+
case 'min':
|
|
58
|
+
case 'max':
|
|
59
|
+
case 'sum':
|
|
60
|
+
cteSQL = this.buildScalarAggregation(config, cteName, context);
|
|
61
|
+
selectExpression = `COALESCE("${cteName}".data, ${config.defaultValue})`;
|
|
62
|
+
break;
|
|
63
|
+
default:
|
|
64
|
+
throw new Error(`Unknown aggregation type: ${config.aggregationType}`);
|
|
65
|
+
}
|
|
66
|
+
// Store CTE in context
|
|
67
|
+
context.ctes.set(cteName, { sql: cteSQL, params: [] });
|
|
68
|
+
return {
|
|
69
|
+
sql: cteSQL,
|
|
70
|
+
params: context.allParams,
|
|
71
|
+
tableName: cteName,
|
|
72
|
+
joinClause: `LEFT JOIN "${cteName}" ON "${config.sourceTable}"."id" = "${cteName}".parent_id`,
|
|
73
|
+
selectExpression,
|
|
74
|
+
isCTE: true,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Helper to collect all leaf fields from a potentially nested structure
|
|
79
|
+
* Returns array of { alias, expression } for SELECT clause (flattened with unique aliases)
|
|
80
|
+
*/
|
|
81
|
+
collectLeafFields(fields, prefix = '') {
|
|
82
|
+
const result = [];
|
|
83
|
+
for (const field of fields) {
|
|
84
|
+
const fullAlias = prefix ? `${prefix}__${field.alias}` : field.alias;
|
|
85
|
+
if (field.nested) {
|
|
86
|
+
// Recurse into nested fields
|
|
87
|
+
result.push(...this.collectLeafFields(field.nested, fullAlias));
|
|
88
|
+
}
|
|
89
|
+
else if (field.expression) {
|
|
90
|
+
// Leaf field
|
|
91
|
+
result.push({ alias: fullAlias, expression: field.expression });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Helper to build jsonb_build_object expression (handles nested structures)
|
|
98
|
+
*/
|
|
99
|
+
buildJsonbObject(fields, prefix = '') {
|
|
100
|
+
const parts = [];
|
|
101
|
+
for (const field of fields) {
|
|
102
|
+
if (field.nested) {
|
|
103
|
+
// Nested object - recurse
|
|
104
|
+
const nestedJsonb = this.buildJsonbObject(field.nested, prefix ? `${prefix}__${field.alias}` : field.alias);
|
|
105
|
+
parts.push(`'${field.alias}', ${nestedJsonb}`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Leaf field - reference the aliased column from subquery
|
|
109
|
+
const fullAlias = prefix ? `${prefix}__${field.alias}` : field.alias;
|
|
110
|
+
parts.push(`'${field.alias}', "${fullAlias}"`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return `jsonb_build_object(${parts.join(', ')})`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Build JSONB aggregation CTE
|
|
117
|
+
*
|
|
118
|
+
* When LIMIT/OFFSET is specified, uses ROW_NUMBER() window function to correctly
|
|
119
|
+
* apply pagination per parent row (not globally).
|
|
120
|
+
*/
|
|
121
|
+
buildJsonbAggregation(config, cteName, context) {
|
|
122
|
+
const { selectedFields, targetTable, foreignKey, whereClause, orderByClause, limitValue, offsetValue, isDistinct } = config;
|
|
123
|
+
// Collect all leaf fields for the SELECT clause
|
|
124
|
+
const leafFields = this.collectLeafFields(selectedFields);
|
|
125
|
+
// Build the JSONB fields for jsonb_build_object (handles nested structures)
|
|
126
|
+
const jsonbObjectExpr = this.buildJsonbObject(selectedFields);
|
|
127
|
+
// Build WHERE clause
|
|
128
|
+
const whereSQL = whereClause ? `WHERE ${whereClause}` : '';
|
|
129
|
+
// Build DISTINCT clause
|
|
130
|
+
const distinctClause = isDistinct ? 'DISTINCT ' : '';
|
|
131
|
+
// If LIMIT or OFFSET is specified, use ROW_NUMBER() for per-parent pagination
|
|
132
|
+
if (limitValue !== undefined || offsetValue !== undefined) {
|
|
133
|
+
return this.buildJsonbAggregationWithRowNumber(config, leafFields, jsonbObjectExpr, whereSQL, distinctClause);
|
|
134
|
+
}
|
|
135
|
+
// No LIMIT/OFFSET - use simple aggregation
|
|
136
|
+
// Build the subquery SELECT fields
|
|
137
|
+
const allSelectFields = [
|
|
138
|
+
`"${foreignKey}" as "__fk_${foreignKey}"`,
|
|
139
|
+
...leafFields.map(f => {
|
|
140
|
+
if (f.expression !== `"${f.alias}"`) {
|
|
141
|
+
return `${f.expression} as "${f.alias}"`;
|
|
142
|
+
}
|
|
143
|
+
return f.expression;
|
|
144
|
+
}),
|
|
145
|
+
];
|
|
146
|
+
// Build ORDER BY clause
|
|
147
|
+
const orderBySQL = orderByClause ? `ORDER BY ${orderByClause}` : '';
|
|
148
|
+
// Build the jsonb_agg ORDER BY clause
|
|
149
|
+
const jsonbAggOrderBy = orderByClause ? ` ORDER BY ${orderByClause}` : '';
|
|
150
|
+
const cteSQL = `
|
|
151
|
+
SELECT
|
|
152
|
+
"__fk_${foreignKey}" as parent_id,
|
|
153
|
+
jsonb_agg(
|
|
154
|
+
${jsonbObjectExpr}${jsonbAggOrderBy}
|
|
155
|
+
) as data
|
|
156
|
+
FROM (
|
|
157
|
+
SELECT ${distinctClause}${allSelectFields.join(', ')}
|
|
158
|
+
FROM "${targetTable}"
|
|
159
|
+
${whereSQL}
|
|
160
|
+
${orderBySQL}
|
|
161
|
+
) sub
|
|
162
|
+
GROUP BY "__fk_${foreignKey}"
|
|
163
|
+
`.trim();
|
|
164
|
+
return cteSQL;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Build JSONB aggregation with ROW_NUMBER() for per-parent LIMIT/OFFSET
|
|
168
|
+
*
|
|
169
|
+
* SQL Pattern:
|
|
170
|
+
* ```sql
|
|
171
|
+
* SELECT parent_id, jsonb_agg(jsonb_build_object(...)) as data
|
|
172
|
+
* FROM (
|
|
173
|
+
* SELECT *, ROW_NUMBER() OVER (PARTITION BY foreign_key ORDER BY ...) as __rn
|
|
174
|
+
* FROM (SELECT ... FROM table WHERE ...) inner_sub
|
|
175
|
+
* ) sub
|
|
176
|
+
* WHERE __rn > offset AND __rn <= offset + limit
|
|
177
|
+
* GROUP BY parent_id
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
buildJsonbAggregationWithRowNumber(config, leafFields, jsonbObjectExpr, whereSQL, distinctClause) {
|
|
181
|
+
const { targetTable, foreignKey, orderByClause, limitValue, offsetValue } = config;
|
|
182
|
+
// Build the innermost SELECT fields
|
|
183
|
+
const innerSelectFields = [
|
|
184
|
+
`"${foreignKey}" as "__fk_${foreignKey}"`,
|
|
185
|
+
...leafFields.map(f => {
|
|
186
|
+
if (f.expression !== `"${f.alias}"`) {
|
|
187
|
+
return `${f.expression} as "${f.alias}"`;
|
|
188
|
+
}
|
|
189
|
+
return f.expression;
|
|
190
|
+
}),
|
|
191
|
+
];
|
|
192
|
+
// Build ORDER BY for ROW_NUMBER() - use the order clause or default to foreign key
|
|
193
|
+
const rowNumberOrderBy = orderByClause || `"__fk_${foreignKey}"`;
|
|
194
|
+
// Build the row number filter condition
|
|
195
|
+
const offset = offsetValue || 0;
|
|
196
|
+
let rowNumberFilter;
|
|
197
|
+
if (limitValue !== undefined) {
|
|
198
|
+
// Both LIMIT and potentially OFFSET
|
|
199
|
+
rowNumberFilter = `WHERE "__rn" > ${offset} AND "__rn" <= ${offset + limitValue}`;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Only OFFSET (no LIMIT)
|
|
203
|
+
rowNumberFilter = `WHERE "__rn" > ${offset}`;
|
|
204
|
+
}
|
|
205
|
+
const cteSQL = `
|
|
206
|
+
SELECT
|
|
207
|
+
"__fk_${foreignKey}" as parent_id,
|
|
208
|
+
jsonb_agg(
|
|
209
|
+
${jsonbObjectExpr}
|
|
210
|
+
) as data
|
|
211
|
+
FROM (
|
|
212
|
+
SELECT *, ROW_NUMBER() OVER (PARTITION BY "__fk_${foreignKey}" ORDER BY ${rowNumberOrderBy}) as "__rn"
|
|
213
|
+
FROM (
|
|
214
|
+
SELECT ${distinctClause}${innerSelectFields.join(', ')}
|
|
215
|
+
FROM "${targetTable}"
|
|
216
|
+
${whereSQL}
|
|
217
|
+
) inner_sub
|
|
218
|
+
) sub
|
|
219
|
+
${rowNumberFilter}
|
|
220
|
+
GROUP BY "__fk_${foreignKey}"
|
|
221
|
+
`.trim();
|
|
222
|
+
return cteSQL;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Build array aggregation CTE (for toNumberList/toStringList)
|
|
226
|
+
*
|
|
227
|
+
* When LIMIT/OFFSET is specified, uses ROW_NUMBER() window function to correctly
|
|
228
|
+
* apply pagination per parent row (not globally).
|
|
229
|
+
*/
|
|
230
|
+
buildArrayAggregation(config, cteName, context) {
|
|
231
|
+
const { arrayField, targetTable, foreignKey, whereClause, orderByClause, limitValue, offsetValue, isDistinct } = config;
|
|
232
|
+
if (!arrayField) {
|
|
233
|
+
throw new Error('arrayField is required for array aggregation');
|
|
234
|
+
}
|
|
235
|
+
// Build WHERE clause
|
|
236
|
+
const whereSQL = whereClause ? `WHERE ${whereClause}` : '';
|
|
237
|
+
// Build DISTINCT clause
|
|
238
|
+
const distinctClause = isDistinct ? 'DISTINCT ' : '';
|
|
239
|
+
// If LIMIT or OFFSET is specified, use ROW_NUMBER() for per-parent pagination
|
|
240
|
+
if (limitValue !== undefined || offsetValue !== undefined) {
|
|
241
|
+
return this.buildArrayAggregationWithRowNumber(config, whereSQL, distinctClause);
|
|
242
|
+
}
|
|
243
|
+
// No LIMIT/OFFSET - use simple aggregation
|
|
244
|
+
// Build ORDER BY clause
|
|
245
|
+
const orderBySQL = orderByClause ? `ORDER BY ${orderByClause}` : '';
|
|
246
|
+
// Build the array_agg ORDER BY clause
|
|
247
|
+
const arrayAggOrderBy = orderByClause ? ` ORDER BY ${orderByClause}` : '';
|
|
248
|
+
const cteSQL = `
|
|
249
|
+
SELECT
|
|
250
|
+
"__fk_${foreignKey}" as parent_id,
|
|
251
|
+
array_agg(
|
|
252
|
+
"${arrayField}"${arrayAggOrderBy}
|
|
253
|
+
) as data
|
|
254
|
+
FROM (
|
|
255
|
+
SELECT ${distinctClause}"__fk_${foreignKey}", "${arrayField}"
|
|
256
|
+
FROM (
|
|
257
|
+
SELECT "${foreignKey}" as "__fk_${foreignKey}", "${arrayField}"
|
|
258
|
+
FROM "${targetTable}"
|
|
259
|
+
${whereSQL}
|
|
260
|
+
${orderBySQL}
|
|
261
|
+
) inner_sub
|
|
262
|
+
) sub
|
|
263
|
+
GROUP BY "__fk_${foreignKey}"
|
|
264
|
+
`.trim();
|
|
265
|
+
return cteSQL;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Build array aggregation with ROW_NUMBER() for per-parent LIMIT/OFFSET
|
|
269
|
+
*/
|
|
270
|
+
buildArrayAggregationWithRowNumber(config, whereSQL, distinctClause) {
|
|
271
|
+
const { arrayField, targetTable, foreignKey, orderByClause, limitValue, offsetValue } = config;
|
|
272
|
+
// Build ORDER BY for ROW_NUMBER() - use the order clause or default to foreign key
|
|
273
|
+
const rowNumberOrderBy = orderByClause || `"__fk_${foreignKey}"`;
|
|
274
|
+
// Build the row number filter condition
|
|
275
|
+
const offset = offsetValue || 0;
|
|
276
|
+
let rowNumberFilter;
|
|
277
|
+
if (limitValue !== undefined) {
|
|
278
|
+
rowNumberFilter = `WHERE "__rn" > ${offset} AND "__rn" <= ${offset + limitValue}`;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
rowNumberFilter = `WHERE "__rn" > ${offset}`;
|
|
282
|
+
}
|
|
283
|
+
const cteSQL = `
|
|
284
|
+
SELECT
|
|
285
|
+
"__fk_${foreignKey}" as parent_id,
|
|
286
|
+
array_agg(
|
|
287
|
+
"${arrayField}"
|
|
288
|
+
) as data
|
|
289
|
+
FROM (
|
|
290
|
+
SELECT *, ROW_NUMBER() OVER (PARTITION BY "__fk_${foreignKey}" ORDER BY ${rowNumberOrderBy}) as "__rn"
|
|
291
|
+
FROM (
|
|
292
|
+
SELECT ${distinctClause}"${foreignKey}" as "__fk_${foreignKey}", "${arrayField}"
|
|
293
|
+
FROM "${targetTable}"
|
|
294
|
+
${whereSQL}
|
|
295
|
+
) inner_sub
|
|
296
|
+
) sub
|
|
297
|
+
${rowNumberFilter}
|
|
298
|
+
GROUP BY "__fk_${foreignKey}"
|
|
299
|
+
`.trim();
|
|
300
|
+
return cteSQL;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Build scalar aggregation CTE (COUNT, MIN, MAX, SUM)
|
|
304
|
+
*/
|
|
305
|
+
buildScalarAggregation(config, cteName, context) {
|
|
306
|
+
const { aggregationType, aggregateField, targetTable, foreignKey, whereClause } = config;
|
|
307
|
+
// Build WHERE clause
|
|
308
|
+
const whereSQL = whereClause ? `WHERE ${whereClause}` : '';
|
|
309
|
+
// Build aggregation expression
|
|
310
|
+
let aggregateExpression;
|
|
311
|
+
switch (aggregationType) {
|
|
312
|
+
case 'count':
|
|
313
|
+
aggregateExpression = 'COUNT(*)';
|
|
314
|
+
break;
|
|
315
|
+
case 'min':
|
|
316
|
+
case 'max':
|
|
317
|
+
case 'sum':
|
|
318
|
+
if (!aggregateField) {
|
|
319
|
+
throw new Error(`${aggregationType.toUpperCase()} requires an aggregate field`);
|
|
320
|
+
}
|
|
321
|
+
aggregateExpression = `${aggregationType.toUpperCase()}("${aggregateField}")`;
|
|
322
|
+
break;
|
|
323
|
+
default:
|
|
324
|
+
throw new Error(`Unknown aggregation type: ${aggregationType}`);
|
|
325
|
+
}
|
|
326
|
+
const cteSQL = `
|
|
327
|
+
SELECT
|
|
328
|
+
"${foreignKey}" as parent_id,
|
|
329
|
+
${aggregateExpression} as data
|
|
330
|
+
FROM "${targetTable}"
|
|
331
|
+
${whereSQL}
|
|
332
|
+
GROUP BY "${foreignKey}"
|
|
333
|
+
`.trim();
|
|
334
|
+
return cteSQL;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
exports.CteCollectionStrategy = CteCollectionStrategy;
|
|
338
|
+
//# sourceMappingURL=cte-collection-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cte-collection-strategy.js","sourceRoot":"","sources":["../../../src/query/strategies/cte-collection-strategy.ts"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAa,qBAAqB;IAChC,OAAO;QACL,OAAO,KAAK,CAAC;IACf,CAAC;IAED,iBAAiB;QACf,iFAAiF;QACjF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,gBAAgB,CACd,MAAmC,EACnC,OAAqB;QAErB,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC;QAExC,IAAI,MAAc,CAAC;QACnB,IAAI,gBAAwB,CAAC;QAE7B,QAAQ,MAAM,CAAC,eAAe,EAAE,CAAC;YAC/B,KAAK,OAAO;gBACV,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9D,gBAAgB,GAAG,aAAa,OAAO,WAAW,MAAM,CAAC,YAAY,GAAG,CAAC;gBACzE,MAAM;YAER,KAAK,OAAO;gBACV,MAAM,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9D,gBAAgB,GAAG,aAAa,OAAO,WAAW,MAAM,CAAC,YAAY,GAAG,CAAC;gBACzE,MAAM;YAER,KAAK,OAAO,CAAC;YACb,KAAK,KAAK,CAAC;YACX,KAAK,KAAK,CAAC;YACX,KAAK,KAAK;gBACR,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC/D,gBAAgB,GAAG,aAAa,OAAO,WAAW,MAAM,CAAC,YAAY,GAAG,CAAC;gBACzE,MAAM;YAER;gBACE,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;QAC3E,CAAC;QAED,uBAAuB;QACvB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;QAEvD,OAAO;YACL,GAAG,EAAE,MAAM;YACX,MAAM,EAAE,OAAO,CAAC,SAAS;YACzB,SAAS,EAAE,OAAO;YAClB,UAAU,EAAE,cAAc,OAAO,SAAS,MAAM,CAAC,WAAW,aAAa,OAAO,aAAa;YAC7F,gBAAgB;YAChB,KAAK,EAAE,IAAI;SACZ,CAAC;IACJ,CAAC;IAED;;;OAGG;IACK,iBAAiB,CAAC,MAAuB,EAAE,SAAiB,EAAE;QACpE,MAAM,MAAM,GAAiD,EAAE,CAAC;QAChE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;YACrE,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjB,6BAA6B;gBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;YAClE,CAAC;iBAAM,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBAC5B,aAAa;gBACb,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,MAAuB,EAAE,SAAiB,EAAE;QACnE,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjB,0BAA0B;gBAC1B,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC5G,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,MAAM,WAAW,EAAE,CAAC,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,0DAA0D;gBAC1D,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;gBACrE,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,OAAO,SAAS,GAAG,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,OAAO,sBAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACK,qBAAqB,CAC3B,MAAmC,EACnC,OAAe,EACf,OAAqB;QAErB,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;QAE5H,gDAAgD;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;QAE1D,4EAA4E;QAC5E,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC;QAE9D,qBAAqB;QACrB,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3D,wBAAwB;QACxB,MAAM,cAAc,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAErD,8EAA8E;QAC9E,IAAI,UAAU,KAAK,SAAS,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,kCAAkC,CAC5C,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,CAC9D,CAAC;QACJ,CAAC;QAED,2CAA2C;QAC3C,mCAAmC;QACnC,MAAM,eAAe,GAAG;YACtB,IAAI,UAAU,cAAc,UAAU,GAAG;YACzC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBACpB,IAAI,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBACpC,OAAO,GAAG,CAAC,CAAC,UAAU,QAAQ,CAAC,CAAC,KAAK,GAAG,CAAC;gBAC3C,CAAC;gBACD,OAAO,CAAC,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC;SACH,CAAC;QAEF,wBAAwB;QACxB,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,YAAY,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAEpE,sCAAsC;QACtC,MAAM,eAAe,GAAG,aAAa,CAAC,CAAC,CAAC,aAAa,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAE1E,MAAM,MAAM,GAAG;;UAET,UAAU;;MAEd,eAAe,GAAG,eAAe;;;WAG5B,cAAc,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;UAC5C,WAAW;IACjB,QAAQ;IACR,UAAU;;iBAEG,UAAU;KACtB,CAAC,IAAI,EAAE,CAAC;QAET,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;OAaG;IACK,kCAAkC,CACxC,MAAmC,EACnC,UAAwD,EACxD,eAAuB,EACvB,QAAgB,EAChB,cAAsB;QAEtB,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAEnF,oCAAoC;QACpC,MAAM,iBAAiB,GAAG;YACxB,IAAI,UAAU,cAAc,UAAU,GAAG;YACzC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;gBACpB,IAAI,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBACpC,OAAO,GAAG,CAAC,CAAC,UAAU,QAAQ,CAAC,CAAC,KAAK,GAAG,CAAC;gBAC3C,CAAC;gBACD,OAAO,CAAC,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC;SACH,CAAC;QAEF,mFAAmF;QACnF,MAAM,gBAAgB,GAAG,aAAa,IAAI,SAAS,UAAU,GAAG,CAAC;QAEjE,wCAAwC;QACxC,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,CAAC;QAChC,IAAI,eAAuB,CAAC;QAC5B,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,oCAAoC;YACpC,eAAe,GAAG,kBAAkB,MAAM,kBAAkB,MAAM,GAAG,UAAU,EAAE,CAAC;QACpF,CAAC;aAAM,CAAC;YACN,yBAAyB;YACzB,eAAe,GAAG,kBAAkB,MAAM,EAAE,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG;;UAET,UAAU;;MAEd,eAAe;;;oDAG+B,UAAU,cAAc,gBAAgB;;aAE/E,cAAc,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9C,WAAW;MACjB,QAAQ;;;EAGZ,eAAe;iBACA,UAAU;KACtB,CAAC,IAAI,EAAE,CAAC;QAET,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;OAKG;IACK,qBAAqB,CAC3B,MAAmC,EACnC,OAAe,EACf,OAAqB;QAErB,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;QAExH,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,CAAC;QAED,qBAAqB;QACrB,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3D,wBAAwB;QACxB,MAAM,cAAc,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAErD,8EAA8E;QAC9E,IAAI,UAAU,KAAK,SAAS,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC,kCAAkC,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QACnF,CAAC;QAED,2CAA2C;QAC3C,wBAAwB;QACxB,MAAM,UAAU,GAAG,aAAa,CAAC,CAAC,CAAC,YAAY,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAEpE,sCAAsC;QACtC,MAAM,eAAe,GAAG,aAAa,CAAC,CAAC,CAAC,aAAa,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAE1E,MAAM,MAAM,GAAG;;UAET,UAAU;;OAEb,UAAU,IAAI,eAAe;;;WAGzB,cAAc,SAAS,UAAU,OAAO,UAAU;;cAE/C,UAAU,cAAc,UAAU,OAAO,UAAU;YACrD,WAAW;MACjB,QAAQ;MACR,UAAU;;;iBAGC,UAAU;KACtB,CAAC,IAAI,EAAE,CAAC;QAET,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,kCAAkC,CACxC,MAAmC,EACnC,QAAgB,EAChB,cAAsB;QAEtB,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAE/F,mFAAmF;QACnF,MAAM,gBAAgB,GAAG,aAAa,IAAI,SAAS,UAAU,GAAG,CAAC;QAEjE,wCAAwC;QACxC,MAAM,MAAM,GAAG,WAAW,IAAI,CAAC,CAAC;QAChC,IAAI,eAAuB,CAAC;QAC5B,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,eAAe,GAAG,kBAAkB,MAAM,kBAAkB,MAAM,GAAG,UAAU,EAAE,CAAC;QACpF,CAAC;aAAM,CAAC;YACN,eAAe,GAAG,kBAAkB,MAAM,EAAE,CAAC;QAC/C,CAAC;QAED,MAAM,MAAM,GAAG;;UAET,UAAU;;OAEb,UAAU;;;oDAGmC,UAAU,cAAc,gBAAgB;;aAE/E,cAAc,IAAI,UAAU,cAAc,UAAU,OAAO,UAAU;YACtE,WAAW;MACjB,QAAQ;;;EAGZ,eAAe;iBACA,UAAU;KACtB,CAAC,IAAI,EAAE,CAAC;QAET,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,sBAAsB,CAC5B,MAAmC,EACnC,OAAe,EACf,OAAqB;QAErB,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAEzF,qBAAqB;QACrB,MAAM,QAAQ,GAAG,WAAW,CAAC,CAAC,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3D,+BAA+B;QAC/B,IAAI,mBAA2B,CAAC;QAChC,QAAQ,eAAe,EAAE,CAAC;YACxB,KAAK,OAAO;gBACV,mBAAmB,GAAG,UAAU,CAAC;gBACjC,MAAM;YACR,KAAK,KAAK,CAAC;YACX,KAAK,KAAK,CAAC;YACX,KAAK,KAAK;gBACR,IAAI,CAAC,cAAc,EAAE,CAAC;oBACpB,MAAM,IAAI,KAAK,CAAC,GAAG,eAAe,CAAC,WAAW,EAAE,8BAA8B,CAAC,CAAC;gBAClF,CAAC;gBACD,mBAAmB,GAAG,GAAG,eAAe,CAAC,WAAW,EAAE,KAAK,cAAc,IAAI,CAAC;gBAC9E,MAAM;YACR;gBACE,MAAM,IAAI,KAAK,CAAC,6BAA6B,eAAe,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,MAAM,GAAG;;KAEd,UAAU;IACX,mBAAmB;QACf,WAAW;EACjB,QAAQ;YACE,UAAU;KACjB,CAAC,IAAI,EAAE,CAAC;QAET,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAnXD,sDAmXC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ICollectionStrategy, CollectionStrategyType, CollectionAggregationConfig, CollectionAggregationResult } from '../collection-strategy.interface';
|
|
2
|
+
import { QueryContext } from '../query-builder';
|
|
3
|
+
/**
|
|
4
|
+
* LATERAL JOIN-based collection strategy
|
|
5
|
+
*
|
|
6
|
+
* This strategy uses PostgreSQL LATERAL joins to efficiently fetch related records
|
|
7
|
+
* for each parent row. LATERAL allows the subquery to reference columns from
|
|
8
|
+
* preceding FROM items, enabling per-row subqueries.
|
|
9
|
+
*
|
|
10
|
+
* Benefits:
|
|
11
|
+
* - Single query execution (like CTE)
|
|
12
|
+
* - Can be more efficient for queries with LIMIT/OFFSET per parent
|
|
13
|
+
* - Better query plan for certain data distributions
|
|
14
|
+
* - Natural support for correlated subqueries
|
|
15
|
+
*
|
|
16
|
+
* Trade-offs:
|
|
17
|
+
* - May be slower than CTE for large result sets without LIMIT
|
|
18
|
+
* - Query plan depends heavily on indexes
|
|
19
|
+
*
|
|
20
|
+
* SQL Pattern:
|
|
21
|
+
* ```sql
|
|
22
|
+
* SELECT
|
|
23
|
+
* "users"."id", "users"."username",
|
|
24
|
+
* COALESCE("lateral_0".data, '[]'::jsonb) as "posts"
|
|
25
|
+
* FROM "users"
|
|
26
|
+
* LEFT JOIN LATERAL (
|
|
27
|
+
* SELECT jsonb_agg(
|
|
28
|
+
* jsonb_build_object('id', "id", 'title', "title")
|
|
29
|
+
* ORDER BY "views" DESC
|
|
30
|
+
* ) as data
|
|
31
|
+
* FROM (
|
|
32
|
+
* SELECT "id", "title", "views"
|
|
33
|
+
* FROM "posts"
|
|
34
|
+
* WHERE "posts"."user_id" = "users"."id"
|
|
35
|
+
* AND "views" > $1
|
|
36
|
+
* ORDER BY "views" DESC
|
|
37
|
+
* LIMIT 10
|
|
38
|
+
* ) sub
|
|
39
|
+
* ) "lateral_0" ON true
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class LateralCollectionStrategy implements ICollectionStrategy {
|
|
43
|
+
getType(): CollectionStrategyType;
|
|
44
|
+
requiresParentIds(): boolean;
|
|
45
|
+
buildAggregation(config: CollectionAggregationConfig, context: QueryContext): CollectionAggregationResult;
|
|
46
|
+
/**
|
|
47
|
+
* Build JSONB aggregation using LATERAL
|
|
48
|
+
*/
|
|
49
|
+
private buildJsonbAggregation;
|
|
50
|
+
/**
|
|
51
|
+
* Build array aggregation using LATERAL (for toNumberList/toStringList)
|
|
52
|
+
*/
|
|
53
|
+
private buildArrayAggregation;
|
|
54
|
+
/**
|
|
55
|
+
* Build scalar aggregation using LATERAL (COUNT, MIN, MAX, SUM)
|
|
56
|
+
*/
|
|
57
|
+
private buildScalarAggregation;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=lateral-collection-strategy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lateral-collection-strategy.d.ts","sourceRoot":"","sources":["../../../src/query/strategies/lateral-collection-strategy.ts"],"names":[],"mappings":"AACA,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,2BAA2B,EAC3B,2BAA2B,EAE5B,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,OAAO,IAAI,sBAAsB;IAIjC,iBAAiB,IAAI,OAAO;IAK5B,gBAAgB,CACd,MAAM,EAAE,2BAA2B,EACnC,OAAO,EAAE,YAAY,GACpB,2BAA2B;IA2C9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA6F7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkD7B;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAuC/B"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LateralCollectionStrategy = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* LATERAL JOIN-based collection strategy
|
|
6
|
+
*
|
|
7
|
+
* This strategy uses PostgreSQL LATERAL joins to efficiently fetch related records
|
|
8
|
+
* for each parent row. LATERAL allows the subquery to reference columns from
|
|
9
|
+
* preceding FROM items, enabling per-row subqueries.
|
|
10
|
+
*
|
|
11
|
+
* Benefits:
|
|
12
|
+
* - Single query execution (like CTE)
|
|
13
|
+
* - Can be more efficient for queries with LIMIT/OFFSET per parent
|
|
14
|
+
* - Better query plan for certain data distributions
|
|
15
|
+
* - Natural support for correlated subqueries
|
|
16
|
+
*
|
|
17
|
+
* Trade-offs:
|
|
18
|
+
* - May be slower than CTE for large result sets without LIMIT
|
|
19
|
+
* - Query plan depends heavily on indexes
|
|
20
|
+
*
|
|
21
|
+
* SQL Pattern:
|
|
22
|
+
* ```sql
|
|
23
|
+
* SELECT
|
|
24
|
+
* "users"."id", "users"."username",
|
|
25
|
+
* COALESCE("lateral_0".data, '[]'::jsonb) as "posts"
|
|
26
|
+
* FROM "users"
|
|
27
|
+
* LEFT JOIN LATERAL (
|
|
28
|
+
* SELECT jsonb_agg(
|
|
29
|
+
* jsonb_build_object('id', "id", 'title', "title")
|
|
30
|
+
* ORDER BY "views" DESC
|
|
31
|
+
* ) as data
|
|
32
|
+
* FROM (
|
|
33
|
+
* SELECT "id", "title", "views"
|
|
34
|
+
* FROM "posts"
|
|
35
|
+
* WHERE "posts"."user_id" = "users"."id"
|
|
36
|
+
* AND "views" > $1
|
|
37
|
+
* ORDER BY "views" DESC
|
|
38
|
+
* LIMIT 10
|
|
39
|
+
* ) sub
|
|
40
|
+
* ) "lateral_0" ON true
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
class LateralCollectionStrategy {
|
|
44
|
+
getType() {
|
|
45
|
+
return 'lateral';
|
|
46
|
+
}
|
|
47
|
+
requiresParentIds() {
|
|
48
|
+
// LATERAL doesn't need parent IDs upfront - it correlates with each parent row
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
buildAggregation(config, context) {
|
|
52
|
+
const lateralAlias = `lateral_${config.counter}`;
|
|
53
|
+
let lateralSQL;
|
|
54
|
+
let selectExpression;
|
|
55
|
+
switch (config.aggregationType) {
|
|
56
|
+
case 'jsonb':
|
|
57
|
+
lateralSQL = this.buildJsonbAggregation(config, lateralAlias, context);
|
|
58
|
+
selectExpression = `COALESCE("${lateralAlias}".data, ${config.defaultValue})`;
|
|
59
|
+
break;
|
|
60
|
+
case 'array':
|
|
61
|
+
lateralSQL = this.buildArrayAggregation(config, lateralAlias, context);
|
|
62
|
+
selectExpression = `COALESCE("${lateralAlias}".data, ${config.defaultValue})`;
|
|
63
|
+
break;
|
|
64
|
+
case 'count':
|
|
65
|
+
case 'min':
|
|
66
|
+
case 'max':
|
|
67
|
+
case 'sum':
|
|
68
|
+
lateralSQL = this.buildScalarAggregation(config, lateralAlias, context);
|
|
69
|
+
selectExpression = `COALESCE("${lateralAlias}".data, ${config.defaultValue})`;
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
throw new Error(`Unknown aggregation type: ${config.aggregationType}`);
|
|
73
|
+
}
|
|
74
|
+
// For LATERAL, we don't use CTEs - instead we inline the subquery in the JOIN
|
|
75
|
+
// The join clause includes the entire LATERAL subquery
|
|
76
|
+
const joinClause = `LEFT JOIN LATERAL (${lateralSQL}) "${lateralAlias}" ON true`;
|
|
77
|
+
return {
|
|
78
|
+
sql: lateralSQL,
|
|
79
|
+
params: context.allParams,
|
|
80
|
+
tableName: lateralAlias,
|
|
81
|
+
joinClause,
|
|
82
|
+
selectExpression,
|
|
83
|
+
isCTE: false, // LATERAL is not a CTE - it's an inline subquery
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Build JSONB aggregation using LATERAL
|
|
88
|
+
*/
|
|
89
|
+
buildJsonbAggregation(config, lateralAlias, context) {
|
|
90
|
+
const { selectedFields, targetTable, foreignKey, sourceTable, whereClause, orderByClause, limitValue, offsetValue, isDistinct } = config;
|
|
91
|
+
// Helper to collect all leaf fields from a potentially nested structure
|
|
92
|
+
const collectLeafFields = (fields, prefix = '') => {
|
|
93
|
+
const result = [];
|
|
94
|
+
for (const field of fields) {
|
|
95
|
+
const fullAlias = prefix ? `${prefix}__${field.alias}` : field.alias;
|
|
96
|
+
if (field.nested) {
|
|
97
|
+
result.push(...collectLeafFields(field.nested, fullAlias));
|
|
98
|
+
}
|
|
99
|
+
else if (field.expression) {
|
|
100
|
+
result.push({ alias: fullAlias, expression: field.expression });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
};
|
|
105
|
+
// Helper to build jsonb_build_object expression (handles nested structures)
|
|
106
|
+
const buildJsonbObject = (fields, prefix = '') => {
|
|
107
|
+
const parts = [];
|
|
108
|
+
for (const field of fields) {
|
|
109
|
+
if (field.nested) {
|
|
110
|
+
const nestedJsonb = buildJsonbObject(field.nested, prefix ? `${prefix}__${field.alias}` : field.alias);
|
|
111
|
+
parts.push(`'${field.alias}', ${nestedJsonb}`);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const fullAlias = prefix ? `${prefix}__${field.alias}` : field.alias;
|
|
115
|
+
parts.push(`'${field.alias}', "${fullAlias}"`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return `jsonb_build_object(${parts.join(', ')})`;
|
|
119
|
+
};
|
|
120
|
+
// Collect all leaf fields for the SELECT clause
|
|
121
|
+
const leafFields = collectLeafFields(selectedFields);
|
|
122
|
+
// Build the subquery SELECT fields (no foreign key needed since we correlate with parent)
|
|
123
|
+
const allSelectFields = leafFields.map(f => {
|
|
124
|
+
if (f.expression !== `"${f.alias}"`) {
|
|
125
|
+
return `${f.expression} as "${f.alias}"`;
|
|
126
|
+
}
|
|
127
|
+
return f.expression;
|
|
128
|
+
});
|
|
129
|
+
// Build the JSONB fields for jsonb_build_object
|
|
130
|
+
const jsonbObjectExpr = buildJsonbObject(selectedFields);
|
|
131
|
+
// Build WHERE clause - LATERAL correlates with parent via foreign key
|
|
132
|
+
// The correlation is: target.foreignKey = source.id
|
|
133
|
+
let whereSQL = `WHERE "${targetTable}"."${foreignKey}" = "${sourceTable}"."id"`;
|
|
134
|
+
if (whereClause) {
|
|
135
|
+
whereSQL += ` AND ${whereClause}`;
|
|
136
|
+
}
|
|
137
|
+
// Build ORDER BY clause
|
|
138
|
+
const orderBySQL = orderByClause ? `ORDER BY ${orderByClause}` : '';
|
|
139
|
+
// Build LIMIT/OFFSET
|
|
140
|
+
let limitOffsetClause = '';
|
|
141
|
+
if (limitValue !== undefined) {
|
|
142
|
+
limitOffsetClause = `LIMIT ${limitValue}`;
|
|
143
|
+
}
|
|
144
|
+
if (offsetValue !== undefined) {
|
|
145
|
+
limitOffsetClause += ` OFFSET ${offsetValue}`;
|
|
146
|
+
}
|
|
147
|
+
// Build DISTINCT clause
|
|
148
|
+
const distinctClause = isDistinct ? 'DISTINCT ' : '';
|
|
149
|
+
// Note: We don't add ORDER BY inside jsonb_agg because:
|
|
150
|
+
// 1. The inner subquery already applies ORDER BY before LIMIT/OFFSET
|
|
151
|
+
// 2. Column aliases in the subquery may differ from original column names
|
|
152
|
+
// The order is preserved from the inner query's ORDER BY
|
|
153
|
+
const lateralSQL = `
|
|
154
|
+
SELECT jsonb_agg(
|
|
155
|
+
${jsonbObjectExpr}
|
|
156
|
+
) as data
|
|
157
|
+
FROM (
|
|
158
|
+
SELECT ${distinctClause}${allSelectFields.join(', ')}
|
|
159
|
+
FROM "${targetTable}"
|
|
160
|
+
${whereSQL}
|
|
161
|
+
${orderBySQL}
|
|
162
|
+
${limitOffsetClause}
|
|
163
|
+
) sub
|
|
164
|
+
`.trim();
|
|
165
|
+
return lateralSQL;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build array aggregation using LATERAL (for toNumberList/toStringList)
|
|
169
|
+
*/
|
|
170
|
+
buildArrayAggregation(config, lateralAlias, context) {
|
|
171
|
+
const { arrayField, targetTable, foreignKey, sourceTable, whereClause, orderByClause, limitValue, offsetValue, isDistinct } = config;
|
|
172
|
+
if (!arrayField) {
|
|
173
|
+
throw new Error('arrayField is required for array aggregation');
|
|
174
|
+
}
|
|
175
|
+
// Build WHERE clause with LATERAL correlation
|
|
176
|
+
let whereSQL = `WHERE "${targetTable}"."${foreignKey}" = "${sourceTable}"."id"`;
|
|
177
|
+
if (whereClause) {
|
|
178
|
+
whereSQL += ` AND ${whereClause}`;
|
|
179
|
+
}
|
|
180
|
+
// Build ORDER BY clause
|
|
181
|
+
const orderBySQL = orderByClause ? `ORDER BY ${orderByClause}` : '';
|
|
182
|
+
// Build LIMIT/OFFSET
|
|
183
|
+
let limitOffsetClause = '';
|
|
184
|
+
if (limitValue !== undefined) {
|
|
185
|
+
limitOffsetClause = `LIMIT ${limitValue}`;
|
|
186
|
+
}
|
|
187
|
+
if (offsetValue !== undefined) {
|
|
188
|
+
limitOffsetClause += ` OFFSET ${offsetValue}`;
|
|
189
|
+
}
|
|
190
|
+
// Build DISTINCT clause
|
|
191
|
+
const distinctClause = isDistinct ? 'DISTINCT ' : '';
|
|
192
|
+
// Note: We don't add ORDER BY inside array_agg because the inner subquery already sorts
|
|
193
|
+
const lateralSQL = `
|
|
194
|
+
SELECT array_agg(
|
|
195
|
+
"${arrayField}"
|
|
196
|
+
) as data
|
|
197
|
+
FROM (
|
|
198
|
+
SELECT ${distinctClause}"${arrayField}"
|
|
199
|
+
FROM "${targetTable}"
|
|
200
|
+
${whereSQL}
|
|
201
|
+
${orderBySQL}
|
|
202
|
+
${limitOffsetClause}
|
|
203
|
+
) sub
|
|
204
|
+
`.trim();
|
|
205
|
+
return lateralSQL;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Build scalar aggregation using LATERAL (COUNT, MIN, MAX, SUM)
|
|
209
|
+
*/
|
|
210
|
+
buildScalarAggregation(config, lateralAlias, context) {
|
|
211
|
+
const { aggregationType, aggregateField, targetTable, foreignKey, sourceTable, whereClause } = config;
|
|
212
|
+
// Build WHERE clause with LATERAL correlation
|
|
213
|
+
let whereSQL = `WHERE "${targetTable}"."${foreignKey}" = "${sourceTable}"."id"`;
|
|
214
|
+
if (whereClause) {
|
|
215
|
+
whereSQL += ` AND ${whereClause}`;
|
|
216
|
+
}
|
|
217
|
+
// Build aggregation expression
|
|
218
|
+
let aggregateExpression;
|
|
219
|
+
switch (aggregationType) {
|
|
220
|
+
case 'count':
|
|
221
|
+
aggregateExpression = 'COUNT(*)';
|
|
222
|
+
break;
|
|
223
|
+
case 'min':
|
|
224
|
+
case 'max':
|
|
225
|
+
case 'sum':
|
|
226
|
+
if (!aggregateField) {
|
|
227
|
+
throw new Error(`${aggregationType.toUpperCase()} requires an aggregate field`);
|
|
228
|
+
}
|
|
229
|
+
aggregateExpression = `${aggregationType.toUpperCase()}("${aggregateField}")`;
|
|
230
|
+
break;
|
|
231
|
+
default:
|
|
232
|
+
throw new Error(`Unknown aggregation type: ${aggregationType}`);
|
|
233
|
+
}
|
|
234
|
+
const lateralSQL = `
|
|
235
|
+
SELECT ${aggregateExpression} as data
|
|
236
|
+
FROM "${targetTable}"
|
|
237
|
+
${whereSQL}
|
|
238
|
+
`.trim();
|
|
239
|
+
return lateralSQL;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.LateralCollectionStrategy = LateralCollectionStrategy;
|
|
243
|
+
//# sourceMappingURL=lateral-collection-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lateral-collection-strategy.js","sourceRoot":"","sources":["../../../src/query/strategies/lateral-collection-strategy.ts"],"names":[],"mappings":"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"}
|