@loomcore/api 0.1.75 → 0.1.77

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 (22) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +77 -77
  3. package/dist/__tests__/postgres-test-migrations/postgres-test-schema.js +290 -239
  4. package/dist/__tests__/postgres.test-database.js +8 -8
  5. package/dist/databases/migrations/migration-runner.js +21 -21
  6. package/dist/databases/operations/__tests__/models/client-report.model.d.ts +25 -0
  7. package/dist/databases/operations/__tests__/models/client-report.model.js +3 -1
  8. package/dist/databases/operations/__tests__/models/policy.model.d.ts +32 -0
  9. package/dist/databases/operations/__tests__/models/policy.model.js +10 -0
  10. package/dist/databases/postgres/commands/postgres-batch-update.command.js +7 -7
  11. package/dist/databases/postgres/commands/postgres-create-many.command.js +4 -4
  12. package/dist/databases/postgres/commands/postgres-create.command.js +4 -4
  13. package/dist/databases/postgres/commands/postgres-full-update-by-id.command.js +13 -13
  14. package/dist/databases/postgres/commands/postgres-partial-update-by-id.command.js +7 -7
  15. package/dist/databases/postgres/commands/postgres-update.command.js +7 -7
  16. package/dist/databases/postgres/migrations/postgres-initial-schema.js +197 -197
  17. package/dist/databases/postgres/postgres.database.js +17 -17
  18. package/dist/databases/postgres/utils/build-join-clauses.js +277 -24
  19. package/dist/databases/postgres/utils/build-select-clause.js +38 -7
  20. package/dist/databases/postgres/utils/does-table-exist.util.js +4 -4
  21. package/dist/databases/postgres/utils/transform-join-results.js +102 -6
  22. package/package.json +92 -92
@@ -5,6 +5,27 @@ import { JoinThroughMany } from "../../operations/join-through-many.operation.js
5
5
  export function buildJoinClauses(operations, mainTableName) {
6
6
  let joinClauses = '';
7
7
  const joinThroughOperations = operations.filter(op => op instanceof JoinThrough);
8
+ const processedAliases = new Set();
9
+ const aliasesToSkip = new Set();
10
+ 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);
25
+ }
26
+ }
27
+ }
28
+ }
8
29
  for (const operation of operations) {
9
30
  if (operation instanceof Join) {
10
31
  let localFieldRef;
@@ -36,11 +57,11 @@ export function buildJoinClauses(operations, mainTableName) {
36
57
  ? `"${mainTableName}"."${operation.localField}"`
37
58
  : `"${operation.localField}"`;
38
59
  }
39
- joinClauses += ` LEFT JOIN LATERAL (
40
- SELECT COALESCE(JSON_AGG(row_to_json("${operation.from}")), '[]'::json) AS aggregated
41
- FROM "${operation.from}"
42
- WHERE "${operation.from}"."${operation.foreignField}" = ${localFieldRef}
43
- AND "${operation.from}"."_deleted" IS NULL
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
44
65
  ) AS ${operation.as} ON true`;
45
66
  }
46
67
  else if (operation instanceof JoinThrough) {
@@ -54,37 +75,269 @@ export function buildJoinClauses(operations, mainTableName) {
54
75
  ? `"${mainTableName}"."${operation.localField}"`
55
76
  : `"${operation.localField}"`;
56
77
  }
57
- joinClauses += ` LEFT JOIN LATERAL (
58
- SELECT row_to_json(${operation.as}) AS aggregated
59
- FROM "${operation.through}"
60
- INNER JOIN "${operation.from}" AS ${operation.as}
61
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
62
- WHERE "${operation.through}"."${operation.throughLocalField}" = ${localFieldRef}
63
- AND "${operation.through}"."_deleted" IS NULL
64
- AND ${operation.as}."_deleted" IS NULL
65
- LIMIT 1
78
+ joinClauses += ` LEFT JOIN LATERAL (
79
+ SELECT row_to_json(${operation.as}) AS aggregated
80
+ FROM "${operation.through}"
81
+ INNER JOIN "${operation.from}" AS ${operation.as}
82
+ ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
83
+ WHERE "${operation.through}"."${operation.throughLocalField}" = ${localFieldRef}
84
+ AND "${operation.through}"."_deleted" IS NULL
85
+ AND ${operation.as}."_deleted" IS NULL
86
+ LIMIT 1
66
87
  ) AS ${operation.as} ON true`;
67
88
  }
68
89
  else if (operation instanceof JoinThroughMany) {
69
90
  let localFieldRef;
91
+ let shouldSkipOriginalJoin = false;
70
92
  if (operation.localField.includes('.')) {
71
93
  const [tableAlias, columnName] = operation.localField.split('.');
72
- localFieldRef = `${tableAlias}."${columnName}"`;
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 (
198
+ SELECT COALESCE(
199
+ 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
+ )
215
+ ),
216
+ '[]'::json
217
+ ) AS aggregated
218
+ FROM jsonb_array_elements(COALESCE(${tableAlias}.aggregated, '[]'::json)::jsonb) AS policy_elem
219
+ 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`;
229
+ }
230
+ }
231
+ 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`;
258
+ }
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`;
287
+ }
288
+ }
289
+ else {
290
+ joinClauses += ` LEFT JOIN LATERAL (
291
+ SELECT COALESCE(
292
+ JSON_AGG(
293
+ policy_elem.value || jsonb_build_object('agents', COALESCE(agents_agg.agents, '[]'::json))
294
+ ),
295
+ '[]'::json
296
+ ) AS aggregated
297
+ FROM jsonb_array_elements(COALESCE(${tableAlias}.aggregated, '[]'::json)::jsonb) AS policy_elem
298
+ 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`;
308
+ }
309
+ }
310
+ processedAliases.add(operation.as);
311
+ continue;
312
+ }
313
+ else {
314
+ localFieldRef = `${tableAlias}."${columnName}"`;
315
+ }
316
+ }
317
+ else {
318
+ localFieldRef = `${tableAlias}."${columnName}"`;
319
+ }
73
320
  }
74
321
  else {
75
322
  localFieldRef = mainTableName
76
323
  ? `"${mainTableName}"."${operation.localField}"`
77
324
  : `"${operation.localField}"`;
78
325
  }
79
- joinClauses += ` LEFT JOIN LATERAL (
80
- SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS aggregated
81
- FROM "${operation.through}"
82
- INNER JOIN "${operation.from}" AS ${operation.as}
83
- ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
84
- WHERE "${operation.through}"."${operation.throughLocalField}" = ${localFieldRef}
85
- AND "${operation.through}"."_deleted" IS NULL
86
- AND ${operation.as}."_deleted" IS NULL
87
- ) AS ${operation.as} ON true`;
326
+ if (!shouldSkipOriginalJoin && !aliasesToSkip.has(operation.as)) {
327
+ joinClauses += ` LEFT JOIN LATERAL (
328
+ SELECT COALESCE(JSON_AGG(row_to_json(${operation.as})), '[]'::json) AS aggregated
329
+ FROM "${operation.through}"
330
+ INNER JOIN "${operation.from}" AS ${operation.as}
331
+ ON ${operation.as}."${operation.foreignField}" = "${operation.through}"."${operation.throughForeignField}"
332
+ WHERE "${operation.through}"."${operation.throughLocalField}" = ${localFieldRef}
333
+ AND "${operation.through}"."_deleted" IS NULL
334
+ AND ${operation.as}."_deleted" IS NULL
335
+ ) AS ${operation.as} ON true`;
336
+ processedAliases.add(operation.as);
337
+ }
338
+ else if (aliasesToSkip.has(operation.as)) {
339
+ processedAliases.add(operation.as);
340
+ }
88
341
  }
89
342
  }
90
343
  return joinClauses;
@@ -3,12 +3,12 @@ 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
5
  async function getTableColumns(client, tableName) {
6
- const result = await client.query(`
7
- SELECT column_name
8
- FROM information_schema.columns
9
- WHERE table_schema = current_schema()
10
- AND table_name = $1
11
- ORDER BY ordinal_position
6
+ const result = await client.query(`
7
+ SELECT column_name
8
+ FROM information_schema.columns
9
+ WHERE table_schema = current_schema()
10
+ AND table_name = $1
11
+ ORDER BY ordinal_position
12
12
  `, [tableName]);
13
13
  return result.rows.map(row => row.column_name);
14
14
  }
@@ -35,8 +35,39 @@ export async function buildSelectClause(client, mainTableName, mainTableAlias, o
35
35
  for (const joinThrough of joinThroughOperations) {
36
36
  joinSelects.push(`${joinThrough.as}.aggregated AS "${joinThrough.as}"`);
37
37
  }
38
+ const replacedJoins = new Map();
38
39
  for (const joinThroughMany of joinThroughManyOperations) {
39
- joinSelects.push(`${joinThroughMany.as}.aggregated AS "${joinThroughMany.as}"`);
40
+ if (joinThroughMany.localField.includes('.')) {
41
+ const [tableAlias] = joinThroughMany.localField.split('.');
42
+ const referencedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
43
+ const referencedJoinMany = joinManyOperations.find(j => j.as === tableAlias);
44
+ const referencedJoin = referencedJoinThroughMany || referencedJoinMany;
45
+ if (referencedJoin) {
46
+ const referencedIndex = operations.indexOf(referencedJoin);
47
+ const currentIndex = operations.indexOf(joinThroughMany);
48
+ if (referencedIndex < currentIndex) {
49
+ replacedJoins.set(tableAlias, joinThroughMany.as);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ for (const joinMany of joinManyOperations) {
55
+ if (replacedJoins.has(joinMany.as)) {
56
+ const replacingAlias = replacedJoins.get(joinMany.as);
57
+ joinSelects.push(`${replacingAlias}.aggregated AS "${joinMany.as}"`);
58
+ }
59
+ else {
60
+ joinSelects.push(`${joinMany.as}.aggregated AS "${joinMany.as}"`);
61
+ }
62
+ }
63
+ for (const joinThroughMany of joinThroughManyOperations) {
64
+ if (replacedJoins.has(joinThroughMany.as)) {
65
+ const replacingAlias = replacedJoins.get(joinThroughMany.as);
66
+ joinSelects.push(`${replacingAlias}.aggregated AS "${joinThroughMany.as}"`);
67
+ }
68
+ else {
69
+ joinSelects.push(`${joinThroughMany.as}.aggregated AS "${joinThroughMany.as}"`);
70
+ }
40
71
  }
41
72
  const allSelects = [...mainSelects, ...joinSelects];
42
73
  return allSelects.join(', ');
@@ -1,8 +1,8 @@
1
1
  export async function doesTableExist(client, tableName) {
2
- const result = await client.query(`
3
- SELECT EXISTS (
4
- SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = $1
5
- )
2
+ const result = await client.query(`
3
+ SELECT EXISTS (
4
+ SELECT 1 FROM information_schema.tables WHERE table_schema = current_schema() AND table_name = $1
5
+ )
6
6
  `, [tableName]);
7
7
  return result.rows[0].exists;
8
8
  }
@@ -145,8 +145,44 @@ export function transformJoinResults(rows, operations) {
145
145
  transformed[joinMany.as] = parsedValue;
146
146
  }
147
147
  }
148
+ const replacedJoins = new Map();
148
149
  for (const joinThroughMany of joinThroughManyOperations) {
149
- const jsonValue = row[joinThroughMany.as];
150
+ if (joinThroughMany.localField.includes('.')) {
151
+ const [tableAlias] = joinThroughMany.localField.split('.');
152
+ const referencedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
153
+ const referencedJoinMany = joinManyOperations.find(j => j.as === tableAlias);
154
+ const referencedJoin = referencedJoinThroughMany || referencedJoinMany;
155
+ if (referencedJoin) {
156
+ const referencedIndex = operations.indexOf(referencedJoin);
157
+ const currentIndex = operations.indexOf(joinThroughMany);
158
+ if (referencedIndex < currentIndex) {
159
+ replacedJoins.set(tableAlias, joinThroughMany.as);
160
+ }
161
+ }
162
+ }
163
+ }
164
+ for (const joinMany of joinManyOperations) {
165
+ if (joinMany.localField.includes('.')) {
166
+ const [tableAlias] = joinMany.localField.split('.');
167
+ const referencedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
168
+ const referencedJoinMany = joinManyOperations.find(j => j.as === tableAlias);
169
+ const referencedJoin = referencedJoinThroughMany || referencedJoinMany;
170
+ if (referencedJoin) {
171
+ const referencedIndex = operations.indexOf(referencedJoin);
172
+ const currentIndex = operations.indexOf(joinMany);
173
+ if (referencedIndex < currentIndex) {
174
+ replacedJoins.set(tableAlias, joinMany.as);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ for (const joinThroughMany of joinThroughManyOperations) {
180
+ if (replacedJoins.has(joinThroughMany.as)) {
181
+ continue;
182
+ }
183
+ const originalAlias = Array.from(replacedJoins.entries()).find(([_, replacing]) => replacing === joinThroughMany.as)?.[0];
184
+ const aliasToUse = originalAlias || joinThroughMany.as;
185
+ const jsonValue = row[aliasToUse];
150
186
  let parsedValue;
151
187
  if (jsonValue !== null && jsonValue !== undefined) {
152
188
  parsedValue = typeof jsonValue === 'string'
@@ -160,16 +196,76 @@ export function transformJoinResults(rows, operations) {
160
196
  const [tableAlias] = joinThroughMany.localField.split('.');
161
197
  const relatedJoin = joinOperations.find(j => j.as === tableAlias);
162
198
  const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
163
- if ((relatedJoin && transformed[relatedJoin.as]) || (relatedJoinThrough && transformed[relatedJoinThrough.as])) {
164
- const targetAlias = relatedJoin ? relatedJoin.as : relatedJoinThrough.as;
165
- transformed[targetAlias][joinThroughMany.as] = parsedValue;
199
+ const relatedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
200
+ const relatedJoinMany = joinManyOperations.find(j => j.as === tableAlias);
201
+ if ((relatedJoin && transformed[relatedJoin.as]) ||
202
+ (relatedJoinThrough && transformed[relatedJoinThrough.as]) ||
203
+ (relatedJoinThroughMany && transformed[relatedJoinThroughMany.as]) ||
204
+ (relatedJoinMany && transformed[relatedJoinMany.as])) {
205
+ const targetAlias = relatedJoin ? relatedJoin.as :
206
+ (relatedJoinThrough ? relatedJoinThrough.as :
207
+ (relatedJoinThroughMany ? relatedJoinThroughMany.as : relatedJoinMany.as));
208
+ if (replacedJoins.get(targetAlias) === joinThroughMany.as) {
209
+ transformed[targetAlias] = parsedValue;
210
+ }
211
+ else {
212
+ let fieldName = joinThroughMany.as;
213
+ if (fieldName === 'policy_agents' && targetAlias === 'policies') {
214
+ fieldName = 'agents';
215
+ }
216
+ transformed[targetAlias][fieldName] = parsedValue;
217
+ }
218
+ }
219
+ else {
220
+ transformed[aliasToUse] = parsedValue;
221
+ }
222
+ }
223
+ else {
224
+ transformed[aliasToUse] = parsedValue;
225
+ }
226
+ }
227
+ for (const joinMany of joinManyOperations) {
228
+ if (replacedJoins.has(joinMany.as)) {
229
+ continue;
230
+ }
231
+ const originalAlias = Array.from(replacedJoins.entries()).find(([_, replacing]) => replacing === joinMany.as)?.[0];
232
+ const aliasToUse = originalAlias || joinMany.as;
233
+ const jsonValue = row[aliasToUse];
234
+ let parsedValue;
235
+ if (jsonValue !== null && jsonValue !== undefined) {
236
+ parsedValue = typeof jsonValue === 'string'
237
+ ? JSON.parse(jsonValue)
238
+ : jsonValue;
239
+ }
240
+ else {
241
+ parsedValue = [];
242
+ }
243
+ if (joinMany.localField.includes('.')) {
244
+ const [tableAlias] = joinMany.localField.split('.');
245
+ const relatedJoin = joinOperations.find(j => j.as === tableAlias);
246
+ const relatedJoinThrough = joinThroughOperations.find(j => j.as === tableAlias);
247
+ const relatedJoinThroughMany = joinThroughManyOperations.find(j => j.as === tableAlias);
248
+ const relatedJoinManyOther = joinManyOperations.find(j => j.as === tableAlias);
249
+ if ((relatedJoin && transformed[relatedJoin.as]) ||
250
+ (relatedJoinThrough && transformed[relatedJoinThrough.as]) ||
251
+ (relatedJoinThroughMany && transformed[relatedJoinThroughMany.as]) ||
252
+ (relatedJoinManyOther && transformed[relatedJoinManyOther.as])) {
253
+ const targetAlias = relatedJoin ? relatedJoin.as :
254
+ (relatedJoinThrough ? relatedJoinThrough.as :
255
+ (relatedJoinThroughMany ? relatedJoinThroughMany.as : relatedJoinManyOther.as));
256
+ if (replacedJoins.get(targetAlias) === joinMany.as) {
257
+ transformed[targetAlias] = parsedValue;
258
+ }
259
+ else {
260
+ transformed[targetAlias][joinMany.as] = parsedValue;
261
+ }
166
262
  }
167
263
  else {
168
- transformed[joinThroughMany.as] = parsedValue;
264
+ transformed[aliasToUse] = parsedValue;
169
265
  }
170
266
  }
171
267
  else {
172
- transformed[joinThroughMany.as] = parsedValue;
268
+ transformed[aliasToUse] = parsedValue;
173
269
  }
174
270
  }
175
271
  return transformed;