@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.
- package/dist/__tests__/postgres-test-migrations/postgres-test-schema.js +25 -0
- package/dist/databases/operations/__tests__/models/agent.model.d.ts +2 -2
- package/dist/databases/operations/__tests__/models/client-report.model.d.ts +13 -8
- package/dist/databases/operations/__tests__/models/client-report.model.js +1 -1
- package/dist/databases/operations/__tests__/models/person.model.d.ts +4 -4
- package/dist/databases/operations/__tests__/models/person.model.js +2 -2
- package/dist/databases/operations/__tests__/models/policy.model.d.ts +9 -2
- package/dist/databases/operations/__tests__/models/policy.model.js +3 -1
- package/dist/databases/operations/__tests__/models/premium.model.d.ts +12 -0
- package/dist/databases/operations/__tests__/models/premium.model.js +8 -0
- package/dist/databases/operations/join-many.operation.js +3 -0
- package/dist/databases/operations/join-through-many.operation.js +3 -0
- package/dist/databases/operations/join-through.operation.js +3 -0
- package/dist/databases/operations/join.operation.js +3 -0
- package/dist/databases/postgres/utils/build-join-clauses.js +282 -272
- package/dist/databases/postgres/utils/build-select-clause.js +19 -34
- package/dist/databases/postgres/utils/transform-join-results.js +198 -194
- package/package.json +1 -1
|
@@ -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
|
|
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 (
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
if (
|
|
52
|
-
const
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const [
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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
|
|
218
|
-
FROM
|
|
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
|
|
221
|
-
FROM "${
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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
|
-
|
|
290
|
-
|
|
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
|
-
|
|
256
|
+
enrich_elem.value || jsonb_build_object('${nestedJoin.as}', nested_join_data.${nestedJoin.as})
|
|
294
257
|
),
|
|
295
258
|
'[]'::json
|
|
296
|
-
) AS
|
|
297
|
-
FROM
|
|
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
|
|
300
|
-
FROM "${
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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;
|