@loomcore/api 0.1.77 → 0.1.78

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.
@@ -2,79 +2,171 @@ import { Join } from "../../operations/join.operation.js";
2
2
  import { JoinMany } from "../../operations/join-many.operation.js";
3
3
  import { JoinThrough } from "../../operations/join-through.operation.js";
4
4
  import { JoinThroughMany } from "../../operations/join-through-many.operation.js";
5
+ function resolveLocalField(localField, mainTableName, operations) {
6
+ if (!localField.includes('.')) {
7
+ return mainTableName
8
+ ? `"${mainTableName}"."${localField}"`
9
+ : `"${localField}"`;
10
+ }
11
+ const [alias, field] = localField.split('.');
12
+ const objectJoin = operations.find(op => op instanceof JoinThrough && op.as === alias);
13
+ if (objectJoin) {
14
+ return `(${alias}.aggregated->>'${field}')::integer`;
15
+ }
16
+ const arrayJoin = operations.find(op => (op instanceof JoinMany || op instanceof JoinThroughMany) && op.as === alias);
17
+ if (arrayJoin) {
18
+ return `(${alias}.aggregated->>'${field}')::integer`;
19
+ }
20
+ return `${alias}."${field}"`;
21
+ }
22
+ function findEnrichmentTarget(operation, operations) {
23
+ if (!operation.localField.includes('.')) {
24
+ return null;
25
+ }
26
+ const [alias, field] = operation.localField.split('.');
27
+ const target = operations.find(op => (op instanceof JoinMany || op instanceof JoinThroughMany) && op.as === alias);
28
+ if (target && operations.indexOf(target) < operations.indexOf(operation)) {
29
+ return { target, field };
30
+ }
31
+ return null;
32
+ }
5
33
  export function buildJoinClauses(operations, mainTableName) {
6
34
  let joinClauses = '';
7
- const joinThroughOperations = operations.filter(op => op instanceof JoinThrough);
8
35
  const processedAliases = new Set();
9
- const aliasesToSkip = new Set();
36
+ const enrichedAliases = new Set();
37
+ const joinOperations = operations.filter(op => op instanceof Join);
38
+ const targetsToEnrich = new Set();
39
+ const enrichmentsByTarget = new Map();
10
40
  for (const operation of operations) {
11
- if ((operation instanceof JoinThroughMany || operation instanceof JoinMany) && operation.localField.includes('.')) {
12
- const [tableAlias] = operation.localField.split('.');
13
- const referencedJoinThroughMany = operations
14
- .filter(op => op instanceof JoinThroughMany)
15
- .find(jtm => jtm.as === tableAlias);
16
- const referencedJoinMany = operations
17
- .filter(op => op instanceof JoinMany)
18
- .find(jm => jm.as === tableAlias);
19
- if (referencedJoinThroughMany || referencedJoinMany) {
20
- const referencedJoin = referencedJoinThroughMany || referencedJoinMany;
21
- const referencedIndex = operations.indexOf(referencedJoin);
22
- const currentIndex = operations.indexOf(operation);
23
- if (referencedIndex < currentIndex) {
24
- aliasesToSkip.add(tableAlias);
41
+ if (operation instanceof JoinMany || operation instanceof JoinThroughMany) {
42
+ const enrichment = findEnrichmentTarget(operation, operations);
43
+ if (enrichment) {
44
+ const { target } = enrichment;
45
+ targetsToEnrich.add(target.as);
46
+ if (!enrichmentsByTarget.has(target.as)) {
47
+ enrichmentsByTarget.set(target.as, []);
25
48
  }
49
+ enrichmentsByTarget.get(target.as).push({ operation, field: enrichment.field });
26
50
  }
27
51
  }
28
52
  }
53
+ for (const [targetAlias, enrichments] of enrichmentsByTarget.entries()) {
54
+ enrichments.sort((a, b) => operations.indexOf(a.operation) - operations.indexOf(b.operation));
55
+ }
29
56
  for (const operation of operations) {
57
+ if (processedAliases.has(operation.as)) {
58
+ continue;
59
+ }
60
+ const isEnrichmentOp = (operation instanceof JoinMany || operation instanceof JoinThroughMany) &&
61
+ findEnrichmentTarget(operation, operations) !== null;
62
+ if (targetsToEnrich.has(operation.as) && !isEnrichmentOp) {
63
+ continue;
64
+ }
30
65
  if (operation instanceof Join) {
31
- let localFieldRef;
32
- if (operation.localField.includes('.')) {
33
- const [tableAlias, columnName] = operation.localField.split('.');
34
- const referencedJoinThrough = joinThroughOperations.find(jt => jt.as === tableAlias);
35
- if (referencedJoinThrough) {
36
- localFieldRef = `(${tableAlias}.aggregated->>'${columnName}')::integer`;
37
- }
38
- else {
39
- localFieldRef = `${tableAlias}."${columnName}"`;
40
- }
41
- }
42
- else {
43
- localFieldRef = mainTableName
44
- ? `"${mainTableName}"."${operation.localField}"`
45
- : `"${operation.localField}"`;
46
- }
66
+ const localFieldRef = resolveLocalField(operation.localField, mainTableName, operations);
47
67
  joinClauses += ` LEFT JOIN "${operation.from}" AS ${operation.as} ON ${localFieldRef} = "${operation.as}"."${operation.foreignField}"`;
68
+ processedAliases.add(operation.as);
48
69
  }
49
70
  else if (operation instanceof JoinMany) {
50
- let localFieldRef;
51
- if (operation.localField.includes('.')) {
52
- const [tableAlias, columnName] = operation.localField.split('.');
53
- localFieldRef = `${tableAlias}."${columnName}"`;
71
+ const enrichment = findEnrichmentTarget(operation, operations);
72
+ if (enrichment) {
73
+ const enrichments = enrichmentsByTarget.get(enrichment.target.as);
74
+ const isFirstEnrichment = enrichments[0].operation === operation;
75
+ if (isFirstEnrichment && !enrichedAliases.has(enrichment.target.as)) {
76
+ const { target, field } = enrichment;
77
+ const targetLocalFieldRef = target.localField.includes('.')
78
+ ? resolveLocalField(target.localField, mainTableName, operations)
79
+ : (mainTableName ? `"${mainTableName}"."${target.localField}"` : `"${target.localField}"`);
80
+ let enrichmentJoins = '';
81
+ let jsonBuildObjects = '';
82
+ for (const enrich of enrichments) {
83
+ const enrichField = enrich.field;
84
+ const enrichOp = enrich.operation;
85
+ const enrichAlias = `enrich_${enrichOp.as}`;
86
+ const nestedJoin = joinOperations.find((j) => {
87
+ if (!j.localField.includes('.'))
88
+ return false;
89
+ const [referencedAlias] = j.localField.split('.');
90
+ const referencedJoin = operations.find(op => (op instanceof Join || op instanceof JoinMany || op instanceof JoinThroughMany) &&
91
+ op.as === referencedAlias);
92
+ if (referencedJoin instanceof Join && referencedJoin.from === enrichOp.from) {
93
+ return true;
94
+ }
95
+ if (enrichOp instanceof JoinThroughMany && referencedJoin instanceof Join && referencedJoin.from === enrichOp.from) {
96
+ return true;
97
+ }
98
+ return false;
99
+ });
100
+ if (nestedJoin) {
101
+ const nestedField = nestedJoin.localField.split('.')[1];
102
+ enrichmentJoins += ` LEFT JOIN LATERAL (
103
+ SELECT COALESCE(
104
+ JSON_AGG(
105
+ enrich_elem.value || jsonb_build_object('${nestedJoin.as}', nested_join_data.${nestedJoin.as})
106
+ ),
107
+ '[]'::json
108
+ ) AS enriched
109
+ FROM (
110
+ SELECT COALESCE(JSON_AGG(row_to_json(enrich_table)), '[]'::json) AS enriched
111
+ FROM "${enrichOp.from}" AS enrich_table
112
+ WHERE enrich_table."${enrichOp.foreignField}" = (elem.value->>'${enrichField}')::integer
113
+ AND enrich_table."_deleted" IS NULL
114
+ ) AS enrich_data
115
+ CROSS JOIN LATERAL jsonb_array_elements(enrich_data.enriched::jsonb) AS enrich_elem
116
+ LEFT JOIN LATERAL (
117
+ SELECT row_to_json(nested_table) AS ${nestedJoin.as}
118
+ FROM "${nestedJoin.from}" AS nested_table
119
+ WHERE nested_table."${nestedJoin.foreignField}" = (enrich_elem.value->>'${nestedField}')::integer
120
+ AND nested_table."_deleted" IS NULL
121
+ LIMIT 1
122
+ ) AS nested_join_data ON true
123
+ ) AS ${enrichAlias} ON true`;
124
+ }
125
+ else {
126
+ enrichmentJoins += ` LEFT JOIN LATERAL (
127
+ SELECT COALESCE(JSON_AGG(row_to_json(enrich_table)), '[]'::json) AS enriched
128
+ FROM "${enrichOp.from}" AS enrich_table
129
+ WHERE enrich_table."${enrichOp.foreignField}" = (elem.value->>'${enrichField}')::integer
130
+ AND enrich_table."_deleted" IS NULL
131
+ ) AS ${enrichAlias} ON true`;
132
+ }
133
+ jsonBuildObjects += ` || jsonb_build_object('${enrichOp.as}', COALESCE(${enrichAlias}.enriched, '[]'::json))`;
134
+ }
135
+ joinClauses += ` LEFT JOIN LATERAL (
136
+ SELECT COALESCE(
137
+ JSON_AGG(
138
+ elem.value${jsonBuildObjects}
139
+ ),
140
+ '[]'::json
141
+ ) AS aggregated
142
+ FROM (
143
+ SELECT COALESCE(JSON_AGG(row_to_json(target_table)), '[]'::json) AS aggregated
144
+ FROM "${target.from}" AS target_table
145
+ WHERE target_table."${target.foreignField}" = ${targetLocalFieldRef}
146
+ AND target_table."_deleted" IS NULL
147
+ ) AS original_data
148
+ CROSS JOIN LATERAL jsonb_array_elements(original_data.aggregated::jsonb) AS elem${enrichmentJoins}
149
+ ) AS ${target.as} ON true`;
150
+ enrichedAliases.add(target.as);
151
+ processedAliases.add(target.as);
152
+ for (const enrich of enrichments) {
153
+ processedAliases.add(enrich.operation.as);
154
+ }
155
+ }
54
156
  }
55
157
  else {
56
- localFieldRef = mainTableName
57
- ? `"${mainTableName}"."${operation.localField}"`
58
- : `"${operation.localField}"`;
158
+ const localFieldRef = resolveLocalField(operation.localField, mainTableName, operations);
159
+ joinClauses += ` LEFT JOIN LATERAL (
160
+ SELECT COALESCE(JSON_AGG(row_to_json("${operation.from}")), '[]'::json) AS aggregated
161
+ FROM "${operation.from}"
162
+ WHERE "${operation.from}"."${operation.foreignField}" = ${localFieldRef}
163
+ AND "${operation.from}"."_deleted" IS NULL
164
+ ) AS ${operation.as} ON true`;
165
+ processedAliases.add(operation.as);
59
166
  }
60
- joinClauses += ` LEFT JOIN LATERAL (
61
- SELECT COALESCE(JSON_AGG(row_to_json("${operation.from}")), '[]'::json) AS aggregated
62
- FROM "${operation.from}"
63
- WHERE "${operation.from}"."${operation.foreignField}" = ${localFieldRef}
64
- AND "${operation.from}"."_deleted" IS NULL
65
- ) AS ${operation.as} ON true`;
66
167
  }
67
168
  else if (operation instanceof JoinThrough) {
68
- let localFieldRef;
69
- if (operation.localField.includes('.')) {
70
- const [tableAlias, columnName] = operation.localField.split('.');
71
- localFieldRef = `${tableAlias}."${columnName}"`;
72
- }
73
- else {
74
- localFieldRef = mainTableName
75
- ? `"${mainTableName}"."${operation.localField}"`
76
- : `"${operation.localField}"`;
77
- }
169
+ const localFieldRef = resolveLocalField(operation.localField, mainTableName, operations);
78
170
  joinClauses += ` LEFT JOIN LATERAL (
79
171
  SELECT row_to_json(${operation.as}) AS aggregated
80
172
  FROM "${operation.through}"
@@ -85,245 +177,166 @@ export function buildJoinClauses(operations, mainTableName) {
85
177
  AND ${operation.as}."_deleted" IS NULL
86
178
  LIMIT 1
87
179
  ) AS ${operation.as} ON true`;
180
+ processedAliases.add(operation.as);
88
181
  }
89
182
  else if (operation instanceof JoinThroughMany) {
90
- let localFieldRef;
91
- let shouldSkipOriginalJoin = false;
92
- if (operation.localField.includes('.')) {
93
- const [tableAlias, columnName] = operation.localField.split('.');
94
- const referencedJoinThroughMany = operations
95
- .filter(op => op instanceof JoinThroughMany)
96
- .find(jtm => jtm.as === tableAlias);
97
- const referencedJoinMany = operations
98
- .filter(op => op instanceof JoinMany)
99
- .find(jm => jm.as === tableAlias);
100
- if (referencedJoinThroughMany || referencedJoinMany) {
101
- const referencedJoin = referencedJoinThroughMany || referencedJoinMany;
102
- const referencedIndex = operations.indexOf(referencedJoin);
103
- const currentIndex = operations.indexOf(operation);
104
- if (referencedIndex < currentIndex) {
105
- shouldSkipOriginalJoin = true;
106
- const originalJoinWasSkipped = aliasesToSkip.has(tableAlias);
107
- const mainTableRef = mainTableName ? `"${mainTableName}"."_id"` : '"_id"';
108
- const isAgentsJoin = operation.from === 'agents';
109
- const isJoinMany = referencedJoinMany !== undefined;
110
- if (isAgentsJoin) {
111
- if (originalJoinWasSkipped) {
112
- if (isJoinMany) {
113
- joinClauses += ` LEFT JOIN LATERAL (
114
- SELECT COALESCE(
115
- JSON_AGG(
116
- policy_elem.value || jsonb_build_object(
117
- 'agents',
118
- COALESCE(
119
- (SELECT JSON_AGG(agent_elem.value || jsonb_build_object('agent_person', person_data.value))
120
- FROM jsonb_array_elements(COALESCE(agents_agg.agents, '[]'::json)::jsonb) AS agent_elem
121
- LEFT JOIN LATERAL (
122
- SELECT row_to_json(p) AS value
123
- FROM "persons" AS p
124
- WHERE p."_id" = (agent_elem.value->>'person_id')::integer
125
- AND p."_deleted" IS NULL
126
- LIMIT 1
127
- ) AS person_data ON true),
128
- '[]'::json
129
- )
130
- )
131
- ),
132
- '[]'::json
133
- ) AS aggregated
134
- FROM (
135
- SELECT COALESCE(JSON_AGG(row_to_json(${tableAlias})), '[]'::json) AS aggregated
136
- FROM "${referencedJoinMany.from}" AS ${tableAlias}
137
- WHERE ${tableAlias}."${referencedJoinMany.foreignField}" = ${mainTableRef}
138
- AND ${tableAlias}."_deleted" IS NULL
139
- ) AS policies_subquery
140
- CROSS JOIN LATERAL jsonb_array_elements(policies_subquery.aggregated::jsonb) AS policy_elem
141
- LEFT JOIN LATERAL (
142
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS agents
143
- FROM "${operation.through}"
144
- INNER JOIN "${operation.from}" AS ${operation.as}
145
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
146
- WHERE "${operation.through}"."${operation.throughLocalField}" = (policy_elem.value->>'${columnName}')::integer
147
- AND "${operation.through}"."_deleted" IS NULL
148
- AND ${operation.as}."_deleted" IS NULL
149
- ) AS agents_agg ON true
150
- ) AS ${operation.as} ON true`;
151
- }
152
- else {
153
- joinClauses += ` LEFT JOIN LATERAL (
154
- SELECT COALESCE(
155
- JSON_AGG(
156
- policy_elem.value || jsonb_build_object(
157
- 'agents',
158
- COALESCE(
159
- (SELECT JSON_AGG(agent_elem.value || jsonb_build_object('agent_person', person_data.value))
160
- FROM jsonb_array_elements(COALESCE(agents_agg.agents, '[]'::json)::jsonb) AS agent_elem
161
- LEFT JOIN LATERAL (
162
- SELECT row_to_json(p) AS value
163
- FROM "persons" AS p
164
- WHERE p."_id" = (agent_elem.value->>'person_id')::integer
165
- AND p."_deleted" IS NULL
166
- LIMIT 1
167
- ) AS person_data ON true),
168
- '[]'::json
169
- )
170
- )
171
- ),
172
- '[]'::json
173
- ) AS aggregated
174
- FROM (
175
- SELECT COALESCE(JSON_AGG(row_to_json(${tableAlias})), '[]'::json) AS aggregated
176
- FROM "${referencedJoinThroughMany.through}"
177
- INNER JOIN "${referencedJoinThroughMany.from}" AS ${tableAlias}
178
- ON ${tableAlias}."${referencedJoinThroughMany.foreignField}" = "${referencedJoinThroughMany.through}"."${referencedJoinThroughMany.throughForeignField}"
179
- WHERE "${referencedJoinThroughMany.through}"."${referencedJoinThroughMany.throughLocalField}" = ${mainTableRef}
180
- AND "${referencedJoinThroughMany.through}"."_deleted" IS NULL
181
- AND ${tableAlias}."_deleted" IS NULL
182
- ) AS policies_subquery
183
- CROSS JOIN LATERAL jsonb_array_elements(policies_subquery.aggregated::jsonb) AS policy_elem
184
- LEFT JOIN LATERAL (
185
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS agents
186
- FROM "${operation.through}"
187
- INNER JOIN "${operation.from}" AS ${operation.as}
188
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
189
- WHERE "${operation.through}"."${operation.throughLocalField}" = (policy_elem.value->>'${columnName}')::integer
190
- AND "${operation.through}"."_deleted" IS NULL
191
- AND ${operation.as}."_deleted" IS NULL
192
- ) AS agents_agg ON true
193
- ) AS ${operation.as} ON true`;
194
- }
195
- }
196
- else {
197
- joinClauses += ` LEFT JOIN LATERAL (
183
+ const enrichment = findEnrichmentTarget(operation, operations);
184
+ if (enrichment) {
185
+ const enrichments = enrichmentsByTarget.get(enrichment.target.as);
186
+ const isFirstEnrichment = enrichments[0].operation === operation;
187
+ if (isFirstEnrichment && !enrichedAliases.has(enrichment.target.as)) {
188
+ const { target, field } = enrichment;
189
+ const targetLocalFieldRef = target.localField.includes('.')
190
+ ? resolveLocalField(target.localField, mainTableName, operations)
191
+ : (mainTableName ? `"${mainTableName}"."${target.localField}"` : `"${target.localField}"`);
192
+ const isTargetJoinMany = target instanceof JoinMany;
193
+ let enrichmentJoins = '';
194
+ let jsonBuildObjects = '';
195
+ for (const enrich of enrichments) {
196
+ const enrichField = enrich.field;
197
+ const enrichOp = enrich.operation;
198
+ const enrichAlias = `enrich_${enrichOp.as}`;
199
+ if (enrichOp instanceof JoinMany) {
200
+ const nestedJoin = joinOperations.find(j => j.localField.includes('.') &&
201
+ j.localField.startsWith(`${enrichOp.as}.`));
202
+ if (nestedJoin) {
203
+ const nestedField = nestedJoin.localField.split('.')[1];
204
+ enrichmentJoins += ` LEFT JOIN LATERAL (
198
205
  SELECT COALESCE(
199
206
  JSON_AGG(
200
- policy_elem.value || jsonb_build_object(
201
- 'agents',
202
- COALESCE(
203
- (SELECT JSON_AGG(agent_elem.value || jsonb_build_object('agent_person', person_data.value))
204
- FROM jsonb_array_elements(COALESCE(agents_agg.agents, '[]'::json)::jsonb) AS agent_elem
205
- LEFT JOIN LATERAL (
206
- SELECT row_to_json(p) AS value
207
- FROM "persons" AS p
208
- WHERE p."_id" = (agent_elem.value->>'person_id')::integer
209
- AND p."_deleted" IS NULL
210
- LIMIT 1
211
- ) AS person_data ON true),
212
- '[]'::json
213
- )
214
- )
207
+ enrich_elem.value || jsonb_build_object('${nestedJoin.as}', nested_join_data.${nestedJoin.as})
215
208
  ),
216
209
  '[]'::json
217
- ) AS aggregated
218
- FROM jsonb_array_elements(COALESCE(${tableAlias}.aggregated, '[]'::json)::jsonb) AS policy_elem
210
+ ) AS enriched
211
+ FROM (
212
+ SELECT COALESCE(JSON_AGG(row_to_json(enrich_table)), '[]'::json) AS enriched
213
+ FROM "${enrichOp.from}" AS enrich_table
214
+ WHERE enrich_table."${enrichOp.foreignField}" = (elem.value->>'${enrichField}')::integer
215
+ AND enrich_table."_deleted" IS NULL
216
+ ) AS enrich_data
217
+ CROSS JOIN LATERAL jsonb_array_elements(enrich_data.enriched::jsonb) AS enrich_elem
219
218
  LEFT JOIN LATERAL (
220
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS agents
221
- FROM "${operation.through}"
222
- INNER JOIN "${operation.from}" AS ${operation.as}
223
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
224
- WHERE "${operation.through}"."${operation.throughLocalField}" = (policy_elem.value->>'${columnName}')::integer
225
- AND "${operation.through}"."_deleted" IS NULL
226
- AND ${operation.as}."_deleted" IS NULL
227
- ) AS agents_agg ON true
228
- ) AS ${operation.as} ON true`;
219
+ SELECT row_to_json(nested_table) AS ${nestedJoin.as}
220
+ FROM "${nestedJoin.from}" AS nested_table
221
+ WHERE nested_table."${nestedJoin.foreignField}" = (enrich_elem.value->>'${nestedField}')::integer
222
+ AND nested_table."_deleted" IS NULL
223
+ LIMIT 1
224
+ ) AS nested_join_data ON true
225
+ ) AS ${enrichAlias} ON true`;
226
+ }
227
+ else {
228
+ enrichmentJoins += ` LEFT JOIN LATERAL (
229
+ SELECT COALESCE(JSON_AGG(row_to_json(enrich_table)), '[]'::json) AS enriched
230
+ FROM "${enrichOp.from}" AS enrich_table
231
+ WHERE enrich_table."${enrichOp.foreignField}" = (elem.value->>'${enrichField}')::integer
232
+ AND enrich_table."_deleted" IS NULL
233
+ ) AS ${enrichAlias} ON true`;
229
234
  }
230
235
  }
231
236
  else {
232
- if (originalJoinWasSkipped) {
233
- if (isJoinMany) {
234
- joinClauses += ` LEFT JOIN LATERAL (
235
- SELECT COALESCE(
236
- JSON_AGG(
237
- policy_elem.value || jsonb_build_object('agents', COALESCE(agents_agg.agents, '[]'::json))
238
- ),
239
- '[]'::json
240
- ) AS aggregated
241
- FROM (
242
- SELECT COALESCE(JSON_AGG(row_to_json(${tableAlias})), '[]'::json) AS aggregated
243
- FROM "${referencedJoinMany.from}" AS ${tableAlias}
244
- WHERE ${tableAlias}."${referencedJoinMany.foreignField}" = ${mainTableRef}
245
- AND ${tableAlias}."_deleted" IS NULL
246
- ) AS policies_subquery
247
- CROSS JOIN LATERAL jsonb_array_elements(policies_subquery.aggregated::jsonb) AS policy_elem
248
- LEFT JOIN LATERAL (
249
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS agents
250
- FROM "${operation.through}"
251
- INNER JOIN "${operation.from}" AS ${operation.as}
252
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
253
- WHERE "${operation.through}"."${operation.throughLocalField}" = (policy_elem.value->>'${columnName}')::integer
254
- AND "${operation.through}"."_deleted" IS NULL
255
- AND ${operation.as}."_deleted" IS NULL
256
- ) AS agents_agg ON true
257
- ) AS ${operation.as} ON true`;
237
+ const nestedJoin = joinOperations.find((j) => {
238
+ if (!j.localField.includes('.'))
239
+ return false;
240
+ const [referencedAlias] = j.localField.split('.');
241
+ const referencedJoin = operations.find(op => (op instanceof Join || op instanceof JoinMany || op instanceof JoinThroughMany) &&
242
+ op.as === referencedAlias);
243
+ if (referencedJoin instanceof Join && referencedJoin.from === enrichOp.from) {
244
+ return true;
258
245
  }
259
- else {
260
- joinClauses += ` LEFT JOIN LATERAL (
261
- SELECT COALESCE(
262
- JSON_AGG(
263
- policy_elem.value || jsonb_build_object('agents', COALESCE(agents_agg.agents, '[]'::json))
264
- ),
265
- '[]'::json
266
- ) AS aggregated
267
- FROM (
268
- SELECT COALESCE(JSON_AGG(row_to_json(${tableAlias})), '[]'::json) AS aggregated
269
- FROM "${referencedJoinThroughMany.through}"
270
- INNER JOIN "${referencedJoinThroughMany.from}" AS ${tableAlias}
271
- ON ${tableAlias}."${referencedJoinThroughMany.foreignField}" = "${referencedJoinThroughMany.through}"."${referencedJoinThroughMany.throughForeignField}"
272
- WHERE "${referencedJoinThroughMany.through}"."${referencedJoinThroughMany.throughLocalField}" = ${mainTableRef}
273
- AND "${referencedJoinThroughMany.through}"."_deleted" IS NULL
274
- AND ${tableAlias}."_deleted" IS NULL
275
- ) AS policies_subquery
276
- CROSS JOIN LATERAL jsonb_array_elements(policies_subquery.aggregated::jsonb) AS policy_elem
277
- LEFT JOIN LATERAL (
278
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS agents
279
- FROM "${operation.through}"
280
- INNER JOIN "${operation.from}" AS ${operation.as}
281
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
282
- WHERE "${operation.through}"."${operation.throughLocalField}" = (policy_elem.value->>'${columnName}')::integer
283
- AND "${operation.through}"."_deleted" IS NULL
284
- AND ${operation.as}."_deleted" IS NULL
285
- ) AS agents_agg ON true
286
- ) AS ${operation.as} ON true`;
246
+ if (enrichOp instanceof JoinThroughMany && referencedJoin instanceof Join && referencedJoin.from === enrichOp.from) {
247
+ return true;
287
248
  }
288
- }
289
- else {
290
- joinClauses += ` LEFT JOIN LATERAL (
249
+ return false;
250
+ });
251
+ if (nestedJoin) {
252
+ const nestedField = nestedJoin.localField.split('.')[1];
253
+ enrichmentJoins += ` LEFT JOIN LATERAL (
291
254
  SELECT COALESCE(
292
255
  JSON_AGG(
293
- policy_elem.value || jsonb_build_object('agents', COALESCE(agents_agg.agents, '[]'::json))
256
+ enrich_elem.value || jsonb_build_object('${nestedJoin.as}', nested_join_data.${nestedJoin.as})
294
257
  ),
295
258
  '[]'::json
296
- ) AS aggregated
297
- FROM jsonb_array_elements(COALESCE(${tableAlias}.aggregated, '[]'::json)::jsonb) AS policy_elem
259
+ ) AS enriched
260
+ FROM (
261
+ SELECT COALESCE(JSON_AGG(row_to_json(${enrichAlias}_table)), '[]'::json) AS enriched
262
+ FROM "${enrichOp.through}"
263
+ INNER JOIN "${enrichOp.from}" AS ${enrichAlias}_table
264
+ ON ${enrichAlias}_table."${enrichOp.foreignField}" = "${enrichOp.through}"."${enrichOp.throughForeignField}"
265
+ WHERE "${enrichOp.through}"."${enrichOp.throughLocalField}" = (elem.value->>'${enrichField}')::integer
266
+ AND "${enrichOp.through}"."_deleted" IS NULL
267
+ AND ${enrichAlias}_table."_deleted" IS NULL
268
+ ) AS enrich_data
269
+ CROSS JOIN LATERAL jsonb_array_elements(enrich_data.enriched::jsonb) AS enrich_elem
298
270
  LEFT JOIN LATERAL (
299
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS agents
300
- FROM "${operation.through}"
301
- INNER JOIN "${operation.from}" AS ${operation.as}
302
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
303
- WHERE "${operation.through}"."${operation.throughLocalField}" = (policy_elem.value->>'${columnName}')::integer
304
- AND "${operation.through}"."_deleted" IS NULL
305
- AND ${operation.as}."_deleted" IS NULL
306
- ) AS agents_agg ON true
307
- ) AS ${operation.as} ON true`;
271
+ SELECT row_to_json(nested_table) AS ${nestedJoin.as}
272
+ FROM "${nestedJoin.from}" AS nested_table
273
+ WHERE nested_table."${nestedJoin.foreignField}" = (enrich_elem.value->>'${nestedField}')::integer
274
+ AND nested_table."_deleted" IS NULL
275
+ LIMIT 1
276
+ ) AS nested_join_data ON true
277
+ ) AS ${enrichAlias} ON true`;
278
+ }
279
+ else {
280
+ enrichmentJoins += ` LEFT JOIN LATERAL (
281
+ SELECT COALESCE(JSON_AGG(row_to_json(${enrichAlias}_table)), '[]'::json) AS enriched
282
+ FROM "${enrichOp.through}"
283
+ INNER JOIN "${enrichOp.from}" AS ${enrichAlias}_table
284
+ ON ${enrichAlias}_table."${enrichOp.foreignField}" = "${enrichOp.through}"."${enrichOp.throughForeignField}"
285
+ WHERE "${enrichOp.through}"."${enrichOp.throughLocalField}" = (elem.value->>'${enrichField}')::integer
286
+ AND "${enrichOp.through}"."_deleted" IS NULL
287
+ AND ${enrichAlias}_table."_deleted" IS NULL
288
+ ) AS ${enrichAlias} ON true`;
308
289
  }
309
290
  }
310
- processedAliases.add(operation.as);
311
- continue;
291
+ jsonBuildObjects += ` || jsonb_build_object('${enrichOp.as}', COALESCE(${enrichAlias}.enriched, '[]'::json))`;
292
+ }
293
+ if (isTargetJoinMany) {
294
+ joinClauses += ` LEFT JOIN LATERAL (
295
+ SELECT COALESCE(
296
+ JSON_AGG(
297
+ elem.value${jsonBuildObjects}
298
+ ),
299
+ '[]'::json
300
+ ) AS aggregated
301
+ FROM (
302
+ SELECT COALESCE(JSON_AGG(row_to_json(target_table)), '[]'::json) AS aggregated
303
+ FROM "${target.from}" AS target_table
304
+ WHERE target_table."${target.foreignField}" = ${targetLocalFieldRef}
305
+ AND target_table."_deleted" IS NULL
306
+ ) AS original_data
307
+ CROSS JOIN LATERAL jsonb_array_elements(original_data.aggregated::jsonb) AS elem${enrichmentJoins}
308
+ ) AS ${target.as} ON true`;
312
309
  }
313
310
  else {
314
- localFieldRef = `${tableAlias}."${columnName}"`;
311
+ const targetThrough = target;
312
+ joinClauses += ` LEFT JOIN LATERAL (
313
+ SELECT COALESCE(
314
+ JSON_AGG(
315
+ elem.value${jsonBuildObjects}
316
+ ),
317
+ '[]'::json
318
+ ) AS aggregated
319
+ FROM (
320
+ SELECT COALESCE(JSON_AGG(row_to_json(target_table)), '[]'::json) AS aggregated
321
+ FROM "${targetThrough.through}"
322
+ INNER JOIN "${targetThrough.from}" AS target_table
323
+ ON target_table."${targetThrough.foreignField}" = "${targetThrough.through}"."${targetThrough.throughForeignField}"
324
+ WHERE "${targetThrough.through}"."${targetThrough.throughLocalField}" = ${targetLocalFieldRef}
325
+ AND "${targetThrough.through}"."_deleted" IS NULL
326
+ AND target_table."_deleted" IS NULL
327
+ ) AS original_data
328
+ CROSS JOIN LATERAL jsonb_array_elements(original_data.aggregated::jsonb) AS elem${enrichmentJoins}
329
+ ) AS ${target.as} ON true`;
330
+ }
331
+ enrichedAliases.add(target.as);
332
+ processedAliases.add(target.as);
333
+ for (const enrich of enrichments) {
334
+ processedAliases.add(enrich.operation.as);
315
335
  }
316
- }
317
- else {
318
- localFieldRef = `${tableAlias}."${columnName}"`;
319
336
  }
320
337
  }
321
338
  else {
322
- localFieldRef = mainTableName
323
- ? `"${mainTableName}"."${operation.localField}"`
324
- : `"${operation.localField}"`;
325
- }
326
- if (!shouldSkipOriginalJoin && !aliasesToSkip.has(operation.as)) {
339
+ const localFieldRef = resolveLocalField(operation.localField, mainTableName, operations);
327
340
  joinClauses += ` LEFT JOIN LATERAL (
328
341
  SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS aggregated
329
342
  FROM "${operation.through}"
@@ -335,9 +348,6 @@ export function buildJoinClauses(operations, mainTableName) {
335
348
  ) AS ${operation.as} ON true`;
336
349
  processedAliases.add(operation.as);
337
350
  }
338
- else if (aliasesToSkip.has(operation.as)) {
339
- processedAliases.add(operation.as);
340
- }
341
351
  }
342
352
  }
343
353
  return joinClauses;