rawsql-ts 0.9.0-beta → 0.10.0-beta
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/README.md +54 -7
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/models/SqlPrintToken.js +2 -0
- package/dist/esm/models/SqlPrintToken.js.map +1 -1
- package/dist/esm/models/ValueComponent.js +13 -12
- package/dist/esm/models/ValueComponent.js.map +1 -1
- package/dist/esm/parsers/SqlPrintTokenParser.js +22 -6
- package/dist/esm/parsers/SqlPrintTokenParser.js.map +1 -1
- package/dist/esm/transformers/PostgreJsonQueryBuilder.js +215 -0
- package/dist/esm/transformers/PostgreJsonQueryBuilder.js.map +1 -0
- package/dist/esm/transformers/PostgresArrayEntityCteBuilder.js +231 -0
- package/dist/esm/transformers/PostgresArrayEntityCteBuilder.js.map +1 -0
- package/dist/esm/transformers/PostgresObjectEntityCteBuilder.js +283 -0
- package/dist/esm/transformers/PostgresObjectEntityCteBuilder.js.map +1 -0
- package/dist/esm/transformers/SqlPrinter.js +9 -3
- package/dist/esm/transformers/SqlPrinter.js.map +1 -1
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/models/SqlPrintToken.d.ts +2 -0
- package/dist/esm/types/models/ValueComponent.d.ts +2 -2
- package/dist/esm/types/transformers/PostgreJsonQueryBuilder.d.ts +78 -0
- package/dist/esm/types/transformers/PostgresArrayEntityCteBuilder.d.ts +97 -0
- package/dist/esm/types/transformers/PostgresObjectEntityCteBuilder.d.ts +140 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/models/SqlPrintToken.d.ts +2 -0
- package/dist/models/SqlPrintToken.js +2 -0
- package/dist/models/SqlPrintToken.js.map +1 -1
- package/dist/models/ValueComponent.d.ts +2 -2
- package/dist/models/ValueComponent.js +13 -12
- package/dist/models/ValueComponent.js.map +1 -1
- package/dist/parsers/SelectQueryParser.js +3 -14
- package/dist/parsers/SelectQueryParser.js.map +1 -1
- package/dist/parsers/SqlPrintTokenParser.js +23 -7
- package/dist/parsers/SqlPrintTokenParser.js.map +1 -1
- package/dist/transformers/PostgreJsonQueryBuilder.d.ts +78 -0
- package/dist/transformers/PostgreJsonQueryBuilder.js +219 -0
- package/dist/transformers/PostgreJsonQueryBuilder.js.map +1 -0
- package/dist/transformers/PostgresArrayEntityCteBuilder.d.ts +97 -0
- package/dist/transformers/PostgresArrayEntityCteBuilder.js +235 -0
- package/dist/transformers/PostgresArrayEntityCteBuilder.js.map +1 -0
- package/dist/transformers/PostgresObjectEntityCteBuilder.d.ts +140 -0
- package/dist/transformers/PostgresObjectEntityCteBuilder.js +287 -0
- package/dist/transformers/PostgresObjectEntityCteBuilder.js.map +1 -0
- package/dist/transformers/SqlFormatter.js +6 -1
- package/dist/transformers/SqlFormatter.js.map +1 -1
- package/dist/transformers/SqlPrinter.js +9 -3
- package/dist/transformers/SqlPrinter.js.map +1 -1
- package/package.json +1 -1
@@ -0,0 +1,215 @@
|
|
1
|
+
import { CommonTable, SourceAliasExpression, SelectItem, SelectClause, FromClause, SourceExpression, TableSource, WithClause, LimitClause } from '../models/Clause';
|
2
|
+
import { SimpleSelectQuery } from '../models/SimpleSelectQuery';
|
3
|
+
import { IdentifierString, ColumnReference, FunctionCall, ValueList, LiteralValue, RawString } from '../models/ValueComponent';
|
4
|
+
import { SelectValueCollector } from "./SelectValueCollector";
|
5
|
+
import { PostgresObjectEntityCteBuilder } from './PostgresObjectEntityCteBuilder';
|
6
|
+
import { PostgresArrayEntityCteBuilder } from './PostgresArrayEntityCteBuilder';
|
7
|
+
/**
|
8
|
+
* PostgreSQL JSON query builder that transforms SimpleSelectQuery into queries
|
9
|
+
* that return JSON arrays or single JSON objects using PostgreSQL JSON functions.
|
10
|
+
*/
|
11
|
+
export class PostgreJsonQueryBuilder {
|
12
|
+
constructor() {
|
13
|
+
this.selectValueCollector = new SelectValueCollector(null);
|
14
|
+
this.objectEntityCteBuilder = new PostgresObjectEntityCteBuilder();
|
15
|
+
this.arrayEntityCteBuilder = new PostgresArrayEntityCteBuilder();
|
16
|
+
}
|
17
|
+
/**
|
18
|
+
* Validates the JSON mapping and the original query.
|
19
|
+
* @param query Original query to transform
|
20
|
+
* @param mapping JSON mapping configuration
|
21
|
+
*/
|
22
|
+
validateMapping(query, mapping) {
|
23
|
+
var _a, _b;
|
24
|
+
const collector = new SelectValueCollector();
|
25
|
+
const selectedValues = collector.collect(query);
|
26
|
+
// sv.name is the alias or derived name
|
27
|
+
const availableColumns = new Set(selectedValues.map(sv => sv.name));
|
28
|
+
// Check root entity columns
|
29
|
+
for (const jsonKey in mapping.rootEntity.columns) {
|
30
|
+
const sourceColumn = mapping.rootEntity.columns[jsonKey];
|
31
|
+
if (!availableColumns.has(sourceColumn)) {
|
32
|
+
throw new Error(`Validation Error: Column "${sourceColumn}" for JSON key "${jsonKey}" in root entity "${mapping.rootEntity.name}" not found in the query's select list.`);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
// Check nested entity columns and parent-child relationships
|
36
|
+
const entityIds = new Set([mapping.rootEntity.id]);
|
37
|
+
const parentToChildrenMap = new Map();
|
38
|
+
mapping.nestedEntities.forEach(ne => {
|
39
|
+
entityIds.add(ne.id);
|
40
|
+
if (!parentToChildrenMap.has(ne.parentId)) {
|
41
|
+
parentToChildrenMap.set(ne.parentId, []);
|
42
|
+
}
|
43
|
+
parentToChildrenMap.get(ne.parentId).push(ne.id);
|
44
|
+
});
|
45
|
+
for (const entity of mapping.nestedEntities) {
|
46
|
+
if (!entityIds.has(entity.parentId)) {
|
47
|
+
throw new Error(`Validation Error: Parent entity with ID "${entity.parentId}" for nested entity "${entity.name}" (ID: ${entity.id}) not found.`);
|
48
|
+
}
|
49
|
+
for (const jsonKey in entity.columns) {
|
50
|
+
const sourceColumn = entity.columns[jsonKey];
|
51
|
+
if (!availableColumns.has(sourceColumn)) {
|
52
|
+
throw new Error(`Validation Error: Column "${sourceColumn}" for JSON key "${jsonKey}" in nested entity "${entity.name}" (ID: ${entity.id}) not found in the query's select list.`);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
// Validate: An entity should not have multiple direct array children.
|
57
|
+
// Validate: Child propertyNames under a single parent must be unique.
|
58
|
+
const allParentIds = new Set([mapping.rootEntity.id, ...mapping.nestedEntities.map(ne => ne.parentId)]);
|
59
|
+
for (const parentId of allParentIds) {
|
60
|
+
const directChildren = mapping.nestedEntities.filter(ne => ne.parentId === parentId);
|
61
|
+
const directArrayChildrenCount = directChildren.filter(c => c.relationshipType === 'array').length;
|
62
|
+
if (directArrayChildrenCount > 1) {
|
63
|
+
const parentName = parentId === mapping.rootEntity.id ? mapping.rootEntity.name : (_a = mapping.nestedEntities.find(ne => ne.id === parentId)) === null || _a === void 0 ? void 0 : _a.name;
|
64
|
+
throw new Error(`Validation Error: Parent entity "${parentName}" (ID: ${parentId}) has multiple direct array children. This is not supported.`);
|
65
|
+
}
|
66
|
+
const propertyNames = new Set();
|
67
|
+
for (const child of directChildren) {
|
68
|
+
if (propertyNames.has(child.propertyName)) {
|
69
|
+
const parentName = parentId === mapping.rootEntity.id ? mapping.rootEntity.name : (_b = mapping.nestedEntities.find(ne => ne.id === parentId)) === null || _b === void 0 ? void 0 : _b.name;
|
70
|
+
throw new Error(`Validation Error: Parent entity "${parentName}" (ID: ${parentId}) has duplicate property name "${child.propertyName}" for its children.`);
|
71
|
+
}
|
72
|
+
propertyNames.add(child.propertyName);
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
/**
|
77
|
+
* Build JSON query from original query and mapping configuration.
|
78
|
+
* @param originalQuery Original query to transform
|
79
|
+
* @param mapping JSON mapping configuration
|
80
|
+
* @returns Transformed query with JSON aggregation
|
81
|
+
*/
|
82
|
+
buildJson(originalQuery, mapping) {
|
83
|
+
return this.buildJsonWithCteStrategy(originalQuery, mapping);
|
84
|
+
}
|
85
|
+
/**
|
86
|
+
* Builds the JSON structure using a unified CTE-based strategy.
|
87
|
+
* @param originalQuery Original query
|
88
|
+
* @param mapping JSON mapping configuration
|
89
|
+
* @returns Query with CTE-based JSON aggregation
|
90
|
+
*/
|
91
|
+
buildJsonWithCteStrategy(originalQuery, mapping) {
|
92
|
+
this.validateMapping(originalQuery, mapping);
|
93
|
+
// Step 1: Create the initial CTE from the original query
|
94
|
+
const { initialCte, initialCteAlias } = this.createInitialCte(originalQuery);
|
95
|
+
let ctesForProcessing = [initialCte];
|
96
|
+
let currentAliasToBuildUpon = initialCteAlias;
|
97
|
+
// Step 2: Prepare entity information
|
98
|
+
const allEntities = new Map();
|
99
|
+
allEntities.set(mapping.rootEntity.id, Object.assign(Object.assign({}, mapping.rootEntity), { isRoot: true, propertyName: mapping.rootName }));
|
100
|
+
mapping.nestedEntities.forEach(ne => allEntities.set(ne.id, Object.assign(Object.assign({}, ne), { isRoot: false, propertyName: ne.propertyName }))); // Step 2.5: Build CTEs for object entities using dedicated builder
|
101
|
+
const objectEntityResult = this.objectEntityCteBuilder.buildObjectEntityCtes(initialCte, allEntities, mapping);
|
102
|
+
// Important: Replace the entire CTE list with the result from object entity builder
|
103
|
+
// The object entity builder returns all CTEs including the initial one
|
104
|
+
ctesForProcessing = objectEntityResult.ctes;
|
105
|
+
currentAliasToBuildUpon = objectEntityResult.lastCteAlias;
|
106
|
+
// Step 3: Build CTEs for array entities using dedicated builder
|
107
|
+
const arrayCteBuildResult = this.arrayEntityCteBuilder.buildArrayEntityCtes(ctesForProcessing, currentAliasToBuildUpon, allEntities, mapping);
|
108
|
+
ctesForProcessing = arrayCteBuildResult.updatedCtes;
|
109
|
+
currentAliasToBuildUpon = arrayCteBuildResult.lastCteAlias;
|
110
|
+
// Step 4: Build the final SELECT query using all generated CTEs
|
111
|
+
return this.buildFinalSelectQuery(ctesForProcessing, currentAliasToBuildUpon, allEntities, mapping);
|
112
|
+
}
|
113
|
+
/**
|
114
|
+
* Creates the initial Common Table Expression (CTE) from the original query.
|
115
|
+
* @param originalQuery The base SimpleSelectQuery.
|
116
|
+
* @returns An object containing the initial CTE and its alias.
|
117
|
+
*/
|
118
|
+
createInitialCte(originalQuery) {
|
119
|
+
const originCteAlias = "origin_query";
|
120
|
+
const originCte = new CommonTable(originalQuery, new SourceAliasExpression(originCteAlias, null), null);
|
121
|
+
return { initialCte: originCte, initialCteAlias: originCteAlias };
|
122
|
+
}
|
123
|
+
/**
|
124
|
+
* Builds the final SELECT query that constructs the root JSON object (or array of objects).
|
125
|
+
* This query uses all previously generated CTEs.
|
126
|
+
* @param finalCtesList The complete list of all CTEs (initial and array CTEs).
|
127
|
+
* @param lastCteAliasForFromClause Alias of the final CTE from which the root object will be built.
|
128
|
+
* @param allEntities Map of all processable entities.
|
129
|
+
* @param mapping JSON mapping configuration.
|
130
|
+
* @returns The final SimpleSelectQuery.
|
131
|
+
*/
|
132
|
+
buildFinalSelectQuery(finalCtesList, lastCteAliasForFromClause, allEntities, mapping) {
|
133
|
+
const currentCtes = [...finalCtesList];
|
134
|
+
// Define rootObjectCteAlias outside of if block
|
135
|
+
const rootObjectCteAlias = `cte_root_${mapping.rootName.toLowerCase().replace(/[^a-z0-9_]/g, '_')}`;
|
136
|
+
const rootEntity = allEntities.get(mapping.rootEntity.id);
|
137
|
+
if (!rootEntity) {
|
138
|
+
throw new Error(`Root entity ${mapping.rootEntity.id} not found`);
|
139
|
+
}
|
140
|
+
if (mapping.resultFormat === "array" || !mapping.resultFormat) {
|
141
|
+
// Step 4.1a: Create a CTE that wraps the final result as the root object
|
142
|
+
// No alias needed for single table SELECT
|
143
|
+
const rootObjectBuilderExpression = this.buildEntityJsonObject(rootEntity, null, // No source alias for single table
|
144
|
+
mapping.nestedEntities, allEntities, mapping.useJsonb);
|
145
|
+
const rootObjectSelectItem = new SelectItem(rootObjectBuilderExpression, mapping.rootName);
|
146
|
+
const rootObjectCte = new CommonTable(new SimpleSelectQuery({
|
147
|
+
selectClause: new SelectClause([rootObjectSelectItem]),
|
148
|
+
fromClause: new FromClause(new SourceExpression(new TableSource(null, new IdentifierString(lastCteAliasForFromClause)), null // No alias
|
149
|
+
), null),
|
150
|
+
}), new SourceAliasExpression(rootObjectCteAlias, null), null);
|
151
|
+
currentCtes.push(rootObjectCte);
|
152
|
+
// Step 4.1b: Aggregate all the root objects
|
153
|
+
const aggregationFunc = mapping.useJsonb ? "jsonb_agg" : "json_agg";
|
154
|
+
const aggregateExpression = new FunctionCall(null, new RawString(aggregationFunc), new ValueList([new ColumnReference(null, new IdentifierString(mapping.rootName))]), null);
|
155
|
+
return new SimpleSelectQuery({
|
156
|
+
withClause: new WithClause(false, currentCtes),
|
157
|
+
selectClause: new SelectClause([
|
158
|
+
new SelectItem(aggregateExpression, `${mapping.rootName}_array`)
|
159
|
+
]),
|
160
|
+
fromClause: new FromClause(new SourceExpression(new TableSource(null, new IdentifierString(rootObjectCteAlias)), null), null),
|
161
|
+
});
|
162
|
+
}
|
163
|
+
else {
|
164
|
+
// For a single object result, create root object CTE without alias
|
165
|
+
const rootObjectBuilderExpression = this.buildEntityJsonObject(rootEntity, null, // No source alias for single table
|
166
|
+
mapping.nestedEntities, allEntities, mapping.useJsonb);
|
167
|
+
const rootObjectSelectItem = new SelectItem(rootObjectBuilderExpression, mapping.rootName);
|
168
|
+
const rootObjectCte = new CommonTable(new SimpleSelectQuery({
|
169
|
+
selectClause: new SelectClause([rootObjectSelectItem]),
|
170
|
+
fromClause: new FromClause(new SourceExpression(new TableSource(null, new IdentifierString(lastCteAliasForFromClause)), null // No alias
|
171
|
+
), null),
|
172
|
+
}), new SourceAliasExpression(rootObjectCteAlias, null), null);
|
173
|
+
currentCtes.push(rootObjectCte);
|
174
|
+
// Select directly from the root_object_cte with LIMIT 1
|
175
|
+
return new SimpleSelectQuery({
|
176
|
+
withClause: new WithClause(false, currentCtes),
|
177
|
+
selectClause: new SelectClause([
|
178
|
+
new SelectItem(new ColumnReference(null, new IdentifierString(mapping.rootName)), mapping.rootName)
|
179
|
+
]),
|
180
|
+
fromClause: new FromClause(new SourceExpression(new TableSource(null, new IdentifierString(rootObjectCteAlias)), null), null),
|
181
|
+
limitClause: new LimitClause(new LiteralValue(1)) // Correctly use LimitClause
|
182
|
+
});
|
183
|
+
}
|
184
|
+
}
|
185
|
+
/**
|
186
|
+
* Build JSON object for entity, using parent JSON columns when available
|
187
|
+
*/
|
188
|
+
buildEntityJsonObject(entity, sourceAlias, nestedEntities, allEntities, useJsonb = false) {
|
189
|
+
const jsonBuildFunction = useJsonb ? "jsonb_build_object" : "json_build_object";
|
190
|
+
const args = []; // Add the entity's own columns
|
191
|
+
Object.entries(entity.columns).forEach(([jsonKey, sqlColumn]) => {
|
192
|
+
args.push(new LiteralValue(jsonKey));
|
193
|
+
args.push(new ColumnReference(null, new IdentifierString(sqlColumn)));
|
194
|
+
});
|
195
|
+
// Find and process child entities (both object and array types)
|
196
|
+
const childEntities = nestedEntities.filter((ne) => ne.parentId === entity.id);
|
197
|
+
childEntities.forEach((childEntity) => {
|
198
|
+
const child = allEntities.get(childEntity.id);
|
199
|
+
if (!child)
|
200
|
+
return;
|
201
|
+
args.push(new LiteralValue(childEntity.propertyName));
|
202
|
+
if (childEntity.relationshipType === "object") {
|
203
|
+
// For object relationships, use pre-computed JSON column
|
204
|
+
const jsonColumnName = `${child.name.toLowerCase()}_json`;
|
205
|
+
args.push(new ColumnReference(null, new IdentifierString(jsonColumnName)));
|
206
|
+
}
|
207
|
+
else if (childEntity.relationshipType === "array") {
|
208
|
+
// For array relationships, use the column directly
|
209
|
+
args.push(new ColumnReference(null, new IdentifierString(childEntity.propertyName)));
|
210
|
+
}
|
211
|
+
});
|
212
|
+
return new FunctionCall(null, new RawString(jsonBuildFunction), new ValueList(args), null);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
//# sourceMappingURL=PostgreJsonQueryBuilder.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"PostgreJsonQueryBuilder.js","sourceRoot":"","sources":["../../../src/transformers/PostgreJsonQueryBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAiB,UAAU,EAAkB,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACnM,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAkB,eAAe,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAA0E,SAAS,EAAmB,MAAM,0BAA0B,CAAC;AACxO,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,8BAA8B,EAAqB,MAAM,kCAAkC,CAAC;AACrG,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AA0BhF;;;GAGG;AACH,MAAM,OAAO,uBAAuB;IAG8B;QAC1D,IAAI,CAAC,oBAAoB,GAAG,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,sBAAsB,GAAG,IAAI,8BAA8B,EAAE,CAAC;QACnE,IAAI,CAAC,qBAAqB,GAAG,IAAI,6BAA6B,EAAE,CAAC;IACrE,CAAC;IAED;;;;OAIG;IACK,eAAe,CAAC,KAAwB,EAAE,OAAoB;;QAClE,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAC7C,MAAM,cAAc,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEhD,uCAAuC;QACvC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAEpE,4BAA4B;QAC5B,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACzD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CAAC,6BAA6B,YAAY,mBAAmB,OAAO,qBAAqB,OAAO,CAAC,UAAU,CAAC,IAAI,yCAAyC,CAAC,CAAC;YAC9K,CAAC;QACL,CAAC;QAED,6DAA6D;QAC7D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAS,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3D,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAoB,CAAC;QAExD,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE;YAChC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACrB,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACxC,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC7C,CAAC;YACD,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;YAC1C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClC,MAAM,IAAI,KAAK,CAAC,4CAA4C,MAAM,CAAC,QAAQ,wBAAwB,MAAM,CAAC,IAAI,UAAU,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC;YACrJ,CAAC;YACD,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAC7C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC;oBACtC,MAAM,IAAI,KAAK,CAAC,6BAA6B,YAAY,mBAAmB,OAAO,uBAAuB,MAAM,CAAC,IAAI,UAAU,MAAM,CAAC,EAAE,yCAAyC,CAAC,CAAC;gBACvL,CAAC;YACL,CAAC;QACL,CAAC;QAED,sEAAsE;QACtE,sEAAsE;QACtE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACxG,KAAK,MAAM,QAAQ,IAAI,YAAY,EAAE,CAAC;YAClC,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;YACrF,MAAM,wBAAwB,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;YACnG,IAAI,wBAAwB,GAAG,CAAC,EAAE,CAAC;gBAC/B,MAAM,UAAU,GAAG,QAAQ,KAAK,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAA,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,QAAQ,CAAC,0CAAE,IAAI,CAAC;gBAC9I,MAAM,IAAI,KAAK,CAAC,oCAAoC,UAAU,UAAU,QAAQ,8DAA8D,CAAC,CAAC;YACpJ,CAAC;YAED,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;YACxC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;gBACjC,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;oBACxC,MAAM,UAAU,GAAG,QAAQ,KAAK,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAA,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,QAAQ,CAAC,0CAAE,IAAI,CAAC;oBAC9I,MAAM,IAAI,KAAK,CAAC,oCAAoC,UAAU,UAAU,QAAQ,kCAAkC,KAAK,CAAC,YAAY,qBAAqB,CAAC,CAAC;gBAC/J,CAAC;gBACD,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC1C,CAAC;QACL,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,SAAS,CAAC,aAAgC,EAAE,OAAoB;QACnE,OAAO,IAAI,CAAC,wBAAwB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAED;;;;;OAKG;IACK,wBAAwB,CAC5B,aAAgC,EAChC,OAAoB;QAEpB,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAE7C,yDAAyD;QACzD,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QAE7E,IAAI,iBAAiB,GAAkB,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,uBAAuB,GAAG,eAAe,CAAC;QAE9C,qCAAqC;QACrC,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6B,CAAC;QACzD,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,kCAAO,OAAO,CAAC,UAAU,KAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,QAAQ,IAAG,CAAC;QAChH,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,kCAAO,EAAE,KAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,CAAC,YAAY,IAAG,CAAC,CAAC,CAAQ,mEAAmE;QACjM,MAAM,kBAAkB,GAAG,IAAI,CAAC,sBAAsB,CAAC,qBAAqB,CACxE,UAAU,EACV,WAAW,EACX,OAAO,CACV,CAAC;QACF,oFAAoF;QACpF,uEAAuE;QACvE,iBAAiB,GAAG,kBAAkB,CAAC,IAAI,CAAC;QAC5C,uBAAuB,GAAG,kBAAkB,CAAC,YAAY,CAAC;QAE1D,gEAAgE;QAChE,MAAM,mBAAmB,GAAG,IAAI,CAAC,qBAAqB,CAAC,oBAAoB,CACvE,iBAAiB,EACjB,uBAAuB,EACvB,WAAW,EACX,OAAO,CACV,CAAC;QACF,iBAAiB,GAAG,mBAAmB,CAAC,WAAW,CAAC;QACpD,uBAAuB,GAAG,mBAAmB,CAAC,YAAY,CAAC;QAE3D,gEAAgE;QAChE,OAAO,IAAI,CAAC,qBAAqB,CAC7B,iBAAiB,EACjB,uBAAuB,EACvB,WAAW,EACX,OAAO,CACV,CAAC;IACN,CAAC;IAED;;;;OAIG;IACK,gBAAgB,CAAC,aAAgC;QACrD,MAAM,cAAc,GAAG,cAAc,CAAC;QACtC,MAAM,SAAS,GAAG,IAAI,WAAW,CAC7B,aAAa,EACb,IAAI,qBAAqB,CAAC,cAAc,EAAE,IAAI,CAAC,EAC/C,IAAI,CACP,CAAC;QACF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC;IACtE,CAAC;IAED;;;;;;;;OAQG;IACK,qBAAqB,CACzB,aAA4B,EAC5B,yBAAiC,EACjC,WAA2C,EAC3C,OAAoB;QAEpB,MAAM,WAAW,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC;QAEvC,gDAAgD;QAChD,MAAM,kBAAkB,GAAG,YAAY,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE,CAAC;QACpG,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,OAAO,CAAC,YAAY,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC5D,yEAAyE;YACzE,0CAA0C;YAC1C,MAAM,2BAA2B,GAAG,IAAI,CAAC,qBAAqB,CAC1D,UAAU,EACV,IAAI,EAAG,mCAAmC;YAC1C,OAAO,CAAC,cAAc,EACtB,WAAW,EACX,OAAO,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,oBAAoB,GAAG,IAAI,UAAU,CAAC,2BAA2B,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3F,MAAM,aAAa,GAAG,IAAI,WAAW,CACjC,IAAI,iBAAiB,CAAC;gBAClB,YAAY,EAAE,IAAI,YAAY,CAAC,CAAC,oBAAoB,CAAC,CAAC;gBACtD,UAAU,EAAE,IAAI,UAAU,CACtB,IAAI,gBAAgB,CAChB,IAAI,WAAW,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,yBAAyB,CAAC,CAAC,EACtE,IAAI,CAAE,WAAW;iBACpB,EACD,IAAI,CACP;aACJ,CAAC,EACF,IAAI,qBAAqB,CAAC,kBAAkB,EAAE,IAAI,CAAC,EACnD,IAAI,CACP,CAAC;YACF,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAEhC,4CAA4C;YAC5C,MAAM,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;YACpE,MAAM,mBAAmB,GAAG,IAAI,YAAY,CACxC,IAAI,EACJ,IAAI,SAAS,CAAC,eAAe,CAAC,EAC9B,IAAI,SAAS,CAAC,CAAC,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAClF,IAAI,CACP,CAAC;YAEF,OAAO,IAAI,iBAAiB,CAAC;gBACzB,UAAU,EAAE,IAAI,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC;gBAC9C,YAAY,EAAE,IAAI,YAAY,CAAC;oBAC3B,IAAI,UAAU,CAAC,mBAAmB,EAAE,GAAG,OAAO,CAAC,QAAQ,QAAQ,CAAC;iBACnE,CAAC;gBACF,UAAU,EAAE,IAAI,UAAU,CACtB,IAAI,gBAAgB,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,EAC3F,IAAI,CACP;aACJ,CAAC,CAAC;QACP,CAAC;aAAM,CAAC;YACJ,mEAAmE;YACnE,MAAM,2BAA2B,GAAG,IAAI,CAAC,qBAAqB,CAC1D,UAAU,EACV,IAAI,EAAG,mCAAmC;YAC1C,OAAO,CAAC,cAAc,EACtB,WAAW,EACX,OAAO,CAAC,QAAQ,CACnB,CAAC;YAEF,MAAM,oBAAoB,GAAG,IAAI,UAAU,CAAC,2BAA2B,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3F,MAAM,aAAa,GAAG,IAAI,WAAW,CACjC,IAAI,iBAAiB,CAAC;gBAClB,YAAY,EAAE,IAAI,YAAY,CAAC,CAAC,oBAAoB,CAAC,CAAC;gBACtD,UAAU,EAAE,IAAI,UAAU,CACtB,IAAI,gBAAgB,CAChB,IAAI,WAAW,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,yBAAyB,CAAC,CAAC,EACtE,IAAI,CAAE,WAAW;iBACpB,EACD,IAAI,CACP;aACJ,CAAC,EACF,IAAI,qBAAqB,CAAC,kBAAkB,EAAE,IAAI,CAAC,EACnD,IAAI,CACP,CAAC;YACF,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAEhC,wDAAwD;YACxD,OAAO,IAAI,iBAAiB,CAAC;gBACzB,UAAU,EAAE,IAAI,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC;gBAC9C,YAAY,EAAE,IAAI,YAAY,CAAC;oBAC3B,IAAI,UAAU,CAAC,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;iBACtG,CAAC;gBACF,UAAU,EAAE,IAAI,UAAU,CACtB,IAAI,gBAAgB,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,EAC3F,IAAI,CACP;gBACD,WAAW,EAAE,IAAI,WAAW,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,4BAA4B;aACjF,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED;;OAEG;IACK,qBAAqB,CACzB,MAAyB,EACzB,WAA0B,EAC1B,cAA6C,EAC7C,WAA2C,EAC3C,WAAoB,KAAK;QAEzB,MAAM,iBAAiB,GAAG,QAAQ,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,mBAAmB,CAAC;QAChF,MAAM,IAAI,GAAqB,EAAE,CAAC,CAAQ,+BAA+B;QACzE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE;YAC5D,IAAI,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,gEAAgE;QAChE,MAAM,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC;QAE/E,aAAa,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAC9C,IAAI,CAAC,KAAK;gBAAE,OAAO;YAEnB,IAAI,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC;YAAC,IAAI,WAAW,CAAC,gBAAgB,KAAK,QAAQ,EAAE,CAAC;gBACnG,yDAAyD;gBACzD,MAAM,cAAc,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;YAC/E,CAAC;iBAAM,IAAI,WAAW,CAAC,gBAAgB,KAAK,OAAO,EAAE,CAAC;gBAClD,mDAAmD;gBACnD,IAAI,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,IAAI,EAAE,IAAI,gBAAgB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YACzF,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC,iBAAiB,CAAC,EAAE,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/F,CAAC;CACJ"}
|
@@ -0,0 +1,231 @@
|
|
1
|
+
import { CommonTable, SourceAliasExpression, SelectItem, SelectClause, FromClause, SourceExpression, TableSource, GroupByClause } from '../models/Clause';
|
2
|
+
import { SimpleSelectQuery } from '../models/SimpleSelectQuery';
|
3
|
+
import { IdentifierString, ColumnReference, FunctionCall, ValueList, LiteralValue, RawString } from '../models/ValueComponent';
|
4
|
+
import { SelectValueCollector } from './SelectValueCollector';
|
5
|
+
/**
|
6
|
+
* PostgreSQL-specific builder for creating CTEs for array entities (array relationships).
|
7
|
+
* This class handles the creation of CTEs that build JSON/JSONB arrays for child entities,
|
8
|
+
* processing them from the deepest level up to ensure proper dependency ordering.
|
9
|
+
*
|
10
|
+
* Features:
|
11
|
+
* - Depth-based CTE naming (cte_array_depth_N)
|
12
|
+
* - Row compression using GROUP BY operations
|
13
|
+
* - JSONB/JSON array aggregation
|
14
|
+
* - Hierarchical processing of nested arrays
|
15
|
+
* - Column exclusion to avoid duplication
|
16
|
+
*
|
17
|
+
* Why depth calculation is critical:
|
18
|
+
* 1. Array entities can be nested at multiple levels. We must process the deepest
|
19
|
+
* (most distant) arrays first to ensure their JSON representations are available
|
20
|
+
* when building their parent arrays.
|
21
|
+
* 2. Array entity processing is essentially a row compression operation using GROUP BY.
|
22
|
+
* Unlike parent entities which use column compression, arrays require grouping
|
23
|
+
* to aggregate multiple rows into JSON arrays.
|
24
|
+
*
|
25
|
+
* Example hierarchy:
|
26
|
+
* Order (root, depth 0)
|
27
|
+
* └─ Items (array, depth 1)
|
28
|
+
* └─ Details (array, depth 2)
|
29
|
+
*
|
30
|
+
* Processing order: depth 2 → depth 1 → depth 0
|
31
|
+
*/
|
32
|
+
export class PostgresArrayEntityCteBuilder {
|
33
|
+
/**
|
34
|
+
* Build CTEs for all array entities in the correct dependency order
|
35
|
+
* @param ctesSoFar Array of CTEs built so far (starts with the initial CTE)
|
36
|
+
* @param aliasOfCteToBuildUpon Alias of the CTE from which the current array CTE will select
|
37
|
+
* @param allEntities Map of all entities in the mapping
|
38
|
+
* @param mapping The JSON mapping configuration
|
39
|
+
* @returns Object containing the updated list of all CTEs and the alias of the last CTE created
|
40
|
+
*/
|
41
|
+
buildArrayEntityCtes(ctesSoFar, aliasOfCteToBuildUpon, allEntities, mapping) {
|
42
|
+
let currentCtes = [...ctesSoFar];
|
43
|
+
let currentCteAlias = aliasOfCteToBuildUpon;
|
44
|
+
// Collect and sort array entities by depth
|
45
|
+
const sortedArrayInfos = this.collectAndSortArrayEntities(mapping, allEntities);
|
46
|
+
if (sortedArrayInfos.length === 0) {
|
47
|
+
return { updatedCtes: currentCtes, lastCteAlias: currentCteAlias };
|
48
|
+
}
|
49
|
+
// Group array entities by depth level for batch processing
|
50
|
+
const entitiesByDepth = this.groupEntitiesByDepth(sortedArrayInfos);
|
51
|
+
// Process from deepest to shallowest (depth-first)
|
52
|
+
const depths = Array.from(entitiesByDepth.keys()).sort((a, b) => b - a);
|
53
|
+
for (const depth of depths) {
|
54
|
+
const infos = entitiesByDepth.get(depth);
|
55
|
+
// Build CTE for all entities at this depth
|
56
|
+
const { cte, newCteAlias } = this.buildDepthCte(infos, currentCteAlias, currentCtes, depth, mapping);
|
57
|
+
currentCtes.push(cte);
|
58
|
+
currentCteAlias = newCteAlias;
|
59
|
+
}
|
60
|
+
return { updatedCtes: currentCtes, lastCteAlias: currentCteAlias };
|
61
|
+
}
|
62
|
+
/**
|
63
|
+
* Collect all array entities and calculate their depth from root.
|
64
|
+
*
|
65
|
+
* Depth calculation ensures proper processing order where deeper nested
|
66
|
+
* arrays are processed first, making their aggregated data available
|
67
|
+
* for parent array processing.
|
68
|
+
*
|
69
|
+
* @param mapping The JSON mapping configuration
|
70
|
+
* @param allEntities Map of all entities in the mapping
|
71
|
+
* @returns Array of array entity information with calculated depths, sorted deepest first
|
72
|
+
*/
|
73
|
+
collectAndSortArrayEntities(mapping, allEntities) {
|
74
|
+
const arrayEntityInfos = [];
|
75
|
+
// Helper function to calculate depth for an entity
|
76
|
+
const getDepth = (entityId) => {
|
77
|
+
const entity = allEntities.get(entityId);
|
78
|
+
if (!entity || entity.isRoot)
|
79
|
+
return 0;
|
80
|
+
if (!entity.parentId)
|
81
|
+
return 1;
|
82
|
+
return 1 + getDepth(entity.parentId);
|
83
|
+
};
|
84
|
+
// Collect all array-type nested entities
|
85
|
+
mapping.nestedEntities.forEach(ne => {
|
86
|
+
if (ne.relationshipType === "array") {
|
87
|
+
const currentArrayEntity = allEntities.get(ne.id);
|
88
|
+
const parentEntity = allEntities.get(ne.parentId);
|
89
|
+
if (!currentArrayEntity || !parentEntity) {
|
90
|
+
throw new Error(`Configuration error: Array entity '${ne.id}' or its parent '${ne.parentId}' not found.`);
|
91
|
+
}
|
92
|
+
// Determine the linking column from parent entity
|
93
|
+
// This assumes the first column of the parent is a suitable key for linking.
|
94
|
+
// More robust linking might require explicit configuration in the mapping.
|
95
|
+
const parentSqlColumns = Object.values(parentEntity.columns);
|
96
|
+
if (parentSqlColumns.length === 0) {
|
97
|
+
throw new Error(`Configuration error: Parent entity '${parentEntity.name}' (ID: ${parentEntity.id}) must have at least one column defined to serve as a linking key for child array '${ne.name}'.`);
|
98
|
+
}
|
99
|
+
const parentIdColumnSqlName = parentSqlColumns[0];
|
100
|
+
arrayEntityInfos.push({
|
101
|
+
entity: currentArrayEntity,
|
102
|
+
parentEntity: parentEntity,
|
103
|
+
parentIdColumnSqlName: parentIdColumnSqlName,
|
104
|
+
depth: getDepth(ne.id)
|
105
|
+
});
|
106
|
+
}
|
107
|
+
});
|
108
|
+
// Sort by depth, deepest arrays (higher depth number) processed first (bottom-up for arrays)
|
109
|
+
arrayEntityInfos.sort((a, b) => b.depth - a.depth);
|
110
|
+
return arrayEntityInfos;
|
111
|
+
}
|
112
|
+
/**
|
113
|
+
* Group array entities by their depth level.
|
114
|
+
*
|
115
|
+
* Grouping by depth allows us to:
|
116
|
+
* - Process all entities at the same level in a single CTE
|
117
|
+
* - Optimize query performance by reducing the number of CTEs
|
118
|
+
* - Maintain clear dependency ordering
|
119
|
+
*
|
120
|
+
* @param arrayInfos Array of array entity information with depths
|
121
|
+
* @returns Map of depth level to entities at that depth
|
122
|
+
*/
|
123
|
+
groupEntitiesByDepth(arrayInfos) {
|
124
|
+
const entitiesByDepth = new Map();
|
125
|
+
arrayInfos.forEach(info => {
|
126
|
+
const depth = info.depth;
|
127
|
+
if (!entitiesByDepth.has(depth)) {
|
128
|
+
entitiesByDepth.set(depth, []);
|
129
|
+
}
|
130
|
+
entitiesByDepth.get(depth).push(info);
|
131
|
+
});
|
132
|
+
return entitiesByDepth;
|
133
|
+
}
|
134
|
+
/**
|
135
|
+
* Build a CTE that processes all array entities at a specific depth level.
|
136
|
+
*
|
137
|
+
* This method creates a single CTE that aggregates multiple array entities
|
138
|
+
* at the same depth, using GROUP BY to compress rows into JSON arrays.
|
139
|
+
*
|
140
|
+
* @param infos Array entities at this depth level
|
141
|
+
* @param currentCteAlias Alias of the CTE to build upon
|
142
|
+
* @param currentCtes All CTEs built so far
|
143
|
+
* @param depth Current depth level being processed
|
144
|
+
* @param mapping JSON mapping configuration
|
145
|
+
* @returns The new CTE and its alias
|
146
|
+
*/
|
147
|
+
buildDepthCte(infos, currentCteAlias, currentCtes, depth, mapping) {
|
148
|
+
var _a;
|
149
|
+
// Collect columns that will be compressed into arrays
|
150
|
+
const arrayColumns = new Set();
|
151
|
+
infos.forEach(info => {
|
152
|
+
Object.values(info.entity.columns).forEach(col => arrayColumns.add(col));
|
153
|
+
});
|
154
|
+
// Get columns from previous CTE
|
155
|
+
const prevCte = (_a = currentCtes.find(c => c.aliasExpression.table.name === currentCteAlias)) === null || _a === void 0 ? void 0 : _a.query;
|
156
|
+
if (!prevCte) {
|
157
|
+
throw new Error(`CTE not found: ${currentCteAlias}`);
|
158
|
+
}
|
159
|
+
const prevSelects = new SelectValueCollector(null, currentCtes).collect(prevCte);
|
160
|
+
// Build SELECT items: columns that are NOT being compressed (for GROUP BY)
|
161
|
+
const groupByItems = [];
|
162
|
+
const selectItems = [];
|
163
|
+
prevSelects.forEach(sv => {
|
164
|
+
if (!arrayColumns.has(sv.name)) {
|
165
|
+
selectItems.push(new SelectItem(new ColumnReference(null, new IdentifierString(sv.name)), sv.name));
|
166
|
+
groupByItems.push(new ColumnReference(null, new IdentifierString(sv.name)));
|
167
|
+
}
|
168
|
+
});
|
169
|
+
// Add JSON aggregation columns for each array entity at this depth
|
170
|
+
for (const info of infos) {
|
171
|
+
const agg = this.buildAggregationDetailsForArrayEntity(info.entity, mapping.nestedEntities, new Map(), // allEntities - not needed for array aggregation
|
172
|
+
mapping.useJsonb);
|
173
|
+
selectItems.push(new SelectItem(agg.jsonAgg, info.entity.propertyName));
|
174
|
+
}
|
175
|
+
// Create the new CTE
|
176
|
+
const cteAlias = `${PostgresArrayEntityCteBuilder.CTE_ARRAY_PREFIX}${depth}`;
|
177
|
+
const cteSelect = new SimpleSelectQuery({
|
178
|
+
selectClause: new SelectClause(selectItems),
|
179
|
+
fromClause: new FromClause(new SourceExpression(new TableSource(null, new IdentifierString(currentCteAlias)), null), null),
|
180
|
+
groupByClause: groupByItems.length > 0 ? new GroupByClause(groupByItems) : null,
|
181
|
+
});
|
182
|
+
const cte = new CommonTable(cteSelect, new SourceAliasExpression(cteAlias, null), null);
|
183
|
+
return { cte, newCteAlias: cteAlias };
|
184
|
+
}
|
185
|
+
/**
|
186
|
+
* Build JSON aggregation function for an array entity.
|
187
|
+
*
|
188
|
+
* This method creates a jsonb_agg or json_agg function call that aggregates
|
189
|
+
* the entity's columns into a JSON array. It also handles nested relationships
|
190
|
+
* by including child entity properties in the JSON object.
|
191
|
+
*
|
192
|
+
* @param entity The array entity being processed
|
193
|
+
* @param nestedEntities All nested entities from the mapping
|
194
|
+
* @param allEntities Map of all entities (not used in current implementation)
|
195
|
+
* @param useJsonb Whether to use JSONB functions
|
196
|
+
* @returns Object containing the JSON aggregation function
|
197
|
+
*/
|
198
|
+
buildAggregationDetailsForArrayEntity(entity, nestedEntities, allEntities, useJsonb = false) {
|
199
|
+
// Build JSON object for array elements
|
200
|
+
const jsonBuildFunction = useJsonb ? "jsonb_build_object" : "json_build_object";
|
201
|
+
const args = [];
|
202
|
+
// Add the entity's own columns
|
203
|
+
Object.entries(entity.columns).forEach(([jsonKey, sqlColumn]) => {
|
204
|
+
args.push(new LiteralValue(jsonKey));
|
205
|
+
args.push(new ColumnReference(null, new IdentifierString(sqlColumn)));
|
206
|
+
});
|
207
|
+
// Find and process child entities (both object and array types)
|
208
|
+
const childEntities = nestedEntities.filter((ne) => ne.parentId === entity.id);
|
209
|
+
childEntities.forEach((childEntity) => {
|
210
|
+
args.push(new LiteralValue(childEntity.propertyName));
|
211
|
+
if (childEntity.relationshipType === "object") {
|
212
|
+
// For object relationships, use pre-computed JSON column
|
213
|
+
const jsonColumnName = `${childEntity.name.toLowerCase()}_json`;
|
214
|
+
args.push(new ColumnReference(null, new IdentifierString(jsonColumnName)));
|
215
|
+
}
|
216
|
+
else if (childEntity.relationshipType === "array") {
|
217
|
+
// For array relationships, use the column directly
|
218
|
+
args.push(new ColumnReference(null, new IdentifierString(childEntity.propertyName)));
|
219
|
+
}
|
220
|
+
});
|
221
|
+
// Create JSON object
|
222
|
+
const jsonObject = new FunctionCall(null, new RawString(jsonBuildFunction), new ValueList(args), null);
|
223
|
+
// Create JSON aggregation
|
224
|
+
const jsonAggFunction = useJsonb ? "jsonb_agg" : "json_agg";
|
225
|
+
const jsonAgg = new FunctionCall(null, new RawString(jsonAggFunction), new ValueList([jsonObject]), null);
|
226
|
+
return { jsonAgg };
|
227
|
+
}
|
228
|
+
}
|
229
|
+
// Constants for consistent naming conventions
|
230
|
+
PostgresArrayEntityCteBuilder.CTE_ARRAY_PREFIX = 'cte_array_depth_';
|
231
|
+
//# sourceMappingURL=PostgresArrayEntityCteBuilder.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"PostgresArrayEntityCteBuilder.js","sourceRoot":"","sources":["../../../src/transformers/PostgresArrayEntityCteBuilder.ts"],"names":[],"mappings":"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"}
|