linkgress-orm 0.0.3 → 0.1.1

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.
Files changed (73) hide show
  1. package/README.md +3 -3
  2. package/dist/entity/db-column.d.ts +38 -1
  3. package/dist/entity/db-column.d.ts.map +1 -1
  4. package/dist/entity/db-column.js.map +1 -1
  5. package/dist/entity/db-context.d.ts +429 -50
  6. package/dist/entity/db-context.d.ts.map +1 -1
  7. package/dist/entity/db-context.js +884 -203
  8. package/dist/entity/db-context.js.map +1 -1
  9. package/dist/entity/entity-base.d.ts +8 -0
  10. package/dist/entity/entity-base.d.ts.map +1 -1
  11. package/dist/entity/entity-base.js.map +1 -1
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -3
  15. package/dist/index.js.map +1 -1
  16. package/dist/query/collection-strategy.factory.d.ts.map +1 -1
  17. package/dist/query/collection-strategy.factory.js +7 -3
  18. package/dist/query/collection-strategy.factory.js.map +1 -1
  19. package/dist/query/collection-strategy.interface.d.ts +12 -6
  20. package/dist/query/collection-strategy.interface.d.ts.map +1 -1
  21. package/dist/query/conditions.d.ts +134 -23
  22. package/dist/query/conditions.d.ts.map +1 -1
  23. package/dist/query/conditions.js +58 -0
  24. package/dist/query/conditions.js.map +1 -1
  25. package/dist/query/cte-builder.d.ts +24 -5
  26. package/dist/query/cte-builder.d.ts.map +1 -1
  27. package/dist/query/cte-builder.js +45 -7
  28. package/dist/query/cte-builder.js.map +1 -1
  29. package/dist/query/grouped-query.d.ts +196 -8
  30. package/dist/query/grouped-query.d.ts.map +1 -1
  31. package/dist/query/grouped-query.js +586 -54
  32. package/dist/query/grouped-query.js.map +1 -1
  33. package/dist/query/join-builder.d.ts +5 -4
  34. package/dist/query/join-builder.d.ts.map +1 -1
  35. package/dist/query/join-builder.js +21 -47
  36. package/dist/query/join-builder.js.map +1 -1
  37. package/dist/query/query-builder.d.ts +118 -20
  38. package/dist/query/query-builder.d.ts.map +1 -1
  39. package/dist/query/query-builder.js +511 -280
  40. package/dist/query/query-builder.js.map +1 -1
  41. package/dist/query/query-utils.d.ts +45 -0
  42. package/dist/query/query-utils.d.ts.map +1 -0
  43. package/dist/query/query-utils.js +103 -0
  44. package/dist/query/query-utils.js.map +1 -0
  45. package/dist/query/sql-utils.d.ts +83 -0
  46. package/dist/query/sql-utils.d.ts.map +1 -0
  47. package/dist/query/sql-utils.js +218 -0
  48. package/dist/query/sql-utils.js.map +1 -0
  49. package/dist/query/strategies/cte-collection-strategy.d.ts +85 -0
  50. package/dist/query/strategies/cte-collection-strategy.d.ts.map +1 -0
  51. package/dist/query/strategies/cte-collection-strategy.js +338 -0
  52. package/dist/query/strategies/cte-collection-strategy.js.map +1 -0
  53. package/dist/query/strategies/lateral-collection-strategy.d.ts +59 -0
  54. package/dist/query/strategies/lateral-collection-strategy.d.ts.map +1 -0
  55. package/dist/query/strategies/lateral-collection-strategy.js +243 -0
  56. package/dist/query/strategies/lateral-collection-strategy.js.map +1 -0
  57. package/dist/query/strategies/temptable-collection-strategy.d.ts +21 -0
  58. package/dist/query/strategies/temptable-collection-strategy.d.ts.map +1 -1
  59. package/dist/query/strategies/temptable-collection-strategy.js +160 -38
  60. package/dist/query/strategies/temptable-collection-strategy.js.map +1 -1
  61. package/dist/query/subquery.d.ts +24 -1
  62. package/dist/query/subquery.d.ts.map +1 -1
  63. package/dist/query/subquery.js +38 -2
  64. package/dist/query/subquery.js.map +1 -1
  65. package/dist/schema/table-builder.d.ts +16 -0
  66. package/dist/schema/table-builder.d.ts.map +1 -1
  67. package/dist/schema/table-builder.js +23 -1
  68. package/dist/schema/table-builder.js.map +1 -1
  69. package/package.json +1 -1
  70. package/dist/query/strategies/jsonb-collection-strategy.d.ts +0 -51
  71. package/dist/query/strategies/jsonb-collection-strategy.d.ts.map +0 -1
  72. package/dist/query/strategies/jsonb-collection-strategy.js +0 -210
  73. 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":"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"}
@@ -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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"}