rawsql-ts 0.11.1-beta → 0.11.2-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/dist/esm/index.js +7 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +45 -11
- package/dist/esm/index.min.js.map +4 -4
- package/dist/esm/src/index.js +7 -2
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/parsers/ValueParser.js +86 -4
- package/dist/esm/src/parsers/ValueParser.js.map +1 -1
- package/dist/esm/src/transformers/DynamicQueryBuilder.js.map +1 -1
- package/dist/esm/src/transformers/EnhancedJsonMapping.js +217 -0
- package/dist/esm/src/transformers/EnhancedJsonMapping.js.map +1 -0
- package/dist/esm/src/transformers/JsonMappingConverter.js +388 -0
- package/dist/esm/src/transformers/JsonMappingConverter.js.map +1 -0
- package/dist/esm/src/transformers/JsonMappingUnifier.js +36 -46
- package/dist/esm/src/transformers/JsonMappingUnifier.js.map +1 -1
- package/dist/esm/src/transformers/ModelDrivenJsonMapping.js +14 -2
- package/dist/esm/src/transformers/ModelDrivenJsonMapping.js.map +1 -1
- package/dist/esm/src/transformers/PostgresArrayEntityCteBuilder.js +132 -97
- package/dist/esm/src/transformers/PostgresArrayEntityCteBuilder.js.map +1 -1
- package/dist/esm/src/transformers/PostgresJsonQueryBuilder.js +21 -11
- package/dist/esm/src/transformers/PostgresJsonQueryBuilder.js.map +1 -1
- package/dist/esm/src/transformers/PostgresObjectEntityCteBuilder.js +71 -14
- package/dist/esm/src/transformers/PostgresObjectEntityCteBuilder.js.map +1 -1
- package/dist/esm/src/transformers/SqlParamInjector.js +189 -108
- package/dist/esm/src/transformers/SqlParamInjector.js.map +1 -1
- package/dist/esm/src/transformers/UpstreamSelectQueryFinder.js +43 -2
- package/dist/esm/src/transformers/UpstreamSelectQueryFinder.js.map +1 -1
- package/dist/esm/tsconfig.browser.tsbuildinfo +1 -1
- package/dist/esm/types/src/index.d.ts +7 -2
- package/dist/esm/types/src/parsers/ValueParser.d.ts +8 -0
- package/dist/esm/types/src/transformers/DynamicQueryBuilder.d.ts +6 -0
- package/dist/esm/types/src/transformers/EnhancedJsonMapping.d.ts +194 -0
- package/dist/esm/types/src/transformers/JsonMappingConverter.d.ts +200 -0
- package/dist/esm/types/src/transformers/JsonMappingUnifier.d.ts +5 -0
- package/dist/esm/types/src/transformers/PostgresArrayEntityCteBuilder.d.ts +39 -77
- package/dist/esm/types/src/transformers/PostgresJsonQueryBuilder.d.ts +3 -2
- package/dist/esm/types/src/transformers/PostgresObjectEntityCteBuilder.d.ts +31 -6
- package/dist/esm/types/src/transformers/SqlParamInjector.d.ts +48 -0
- package/dist/esm/types/src/transformers/UpstreamSelectQueryFinder.d.ts +8 -0
- package/dist/index.min.js +45 -11
- package/dist/index.min.js.map +4 -4
- package/dist/src/index.d.ts +7 -2
- package/dist/src/index.js +7 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/parsers/ValueParser.d.ts +8 -0
- package/dist/src/parsers/ValueParser.js +86 -4
- package/dist/src/parsers/ValueParser.js.map +1 -1
- package/dist/src/transformers/DynamicQueryBuilder.d.ts +6 -0
- package/dist/src/transformers/DynamicQueryBuilder.js.map +1 -1
- package/dist/src/transformers/EnhancedJsonMapping.d.ts +194 -0
- package/dist/src/transformers/EnhancedJsonMapping.js +223 -0
- package/dist/src/transformers/EnhancedJsonMapping.js.map +1 -0
- package/dist/src/transformers/JsonMappingConverter.d.ts +200 -0
- package/dist/src/transformers/JsonMappingConverter.js +392 -0
- package/dist/src/transformers/JsonMappingConverter.js.map +1 -0
- package/dist/src/transformers/JsonMappingUnifier.d.ts +5 -0
- package/dist/src/transformers/JsonMappingUnifier.js +36 -46
- package/dist/src/transformers/JsonMappingUnifier.js.map +1 -1
- package/dist/src/transformers/ModelDrivenJsonMapping.js +14 -2
- package/dist/src/transformers/ModelDrivenJsonMapping.js.map +1 -1
- package/dist/src/transformers/PostgresArrayEntityCteBuilder.d.ts +39 -77
- package/dist/src/transformers/PostgresArrayEntityCteBuilder.js +132 -97
- package/dist/src/transformers/PostgresArrayEntityCteBuilder.js.map +1 -1
- package/dist/src/transformers/PostgresJsonQueryBuilder.d.ts +3 -2
- package/dist/src/transformers/PostgresJsonQueryBuilder.js +21 -11
- package/dist/src/transformers/PostgresJsonQueryBuilder.js.map +1 -1
- package/dist/src/transformers/PostgresObjectEntityCteBuilder.d.ts +31 -6
- package/dist/src/transformers/PostgresObjectEntityCteBuilder.js +71 -14
- package/dist/src/transformers/PostgresObjectEntityCteBuilder.js.map +1 -1
- package/dist/src/transformers/SqlParamInjector.d.ts +48 -0
- package/dist/src/transformers/SqlParamInjector.js +189 -108
- package/dist/src/transformers/SqlParamInjector.js.map +1 -1
- package/dist/src/transformers/UpstreamSelectQueryFinder.d.ts +8 -0
- package/dist/src/transformers/UpstreamSelectQueryFinder.js +43 -2
- package/dist/src/transformers/UpstreamSelectQueryFinder.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -1,104 +1,77 @@
|
|
|
1
1
|
import { CommonTable } from '../models/Clause';
|
|
2
2
|
import { JsonMapping } from './PostgresJsonQueryBuilder';
|
|
3
|
-
import { ProcessableEntity } from './PostgresObjectEntityCteBuilder';
|
|
3
|
+
import { ProcessableEntity, JsonColumnMapping } from './PostgresObjectEntityCteBuilder';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* This class handles the creation of CTEs that build JSON/JSONB arrays for child entities,
|
|
7
|
-
* processing them from the deepest level up to ensure proper dependency ordering.
|
|
5
|
+
* Builds CTEs for array entities using depth-first processing and row compression.
|
|
8
6
|
*
|
|
9
|
-
*
|
|
10
|
-
* -
|
|
11
|
-
* - Row
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* - Column exclusion to avoid duplication
|
|
15
|
-
*
|
|
16
|
-
* Why depth calculation is critical:
|
|
17
|
-
* 1. Array entities can be nested at multiple levels. We must process the deepest
|
|
18
|
-
* (most distant) arrays first to ensure their JSON representations are available
|
|
19
|
-
* when building their parent arrays.
|
|
20
|
-
* 2. Array entity processing is essentially a row compression operation using GROUP BY.
|
|
21
|
-
* Unlike parent entities which use column compression, arrays require grouping
|
|
22
|
-
* to aggregate multiple rows into JSON arrays.
|
|
23
|
-
*
|
|
24
|
-
* Example hierarchy:
|
|
25
|
-
* Order (root, depth 0)
|
|
26
|
-
* └─ Items (array, depth 1)
|
|
27
|
-
* └─ Details (array, depth 2)
|
|
28
|
-
*
|
|
29
|
-
* Processing order: depth 2 → depth 1 → depth 0
|
|
7
|
+
* Core concepts:
|
|
8
|
+
* - Column Compression: OBJECT relationships (user_id, user_name → user_json)
|
|
9
|
+
* - Row Compression: ARRAY relationships (multiple rows → JSON array via GROUP BY)
|
|
10
|
+
* - Depth-First: Process deepest arrays first for dependency ordering
|
|
11
|
+
* - GROUP BY Exclusion: Exclude array-internal columns to prevent over-grouping
|
|
30
12
|
*/
|
|
31
13
|
export declare class PostgresArrayEntityCteBuilder {
|
|
32
14
|
private static readonly CTE_ARRAY_PREFIX;
|
|
15
|
+
private static readonly JSON_FUNCTIONS;
|
|
33
16
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
17
|
+
* Builds CTEs for all array entities using depth-first processing.
|
|
18
|
+
* Collects arrays by depth, processes deepest first, chains CTEs.
|
|
19
|
+
*
|
|
20
|
+
* @param ctesSoFar Array of CTEs built so far
|
|
21
|
+
* @param aliasOfCteToBuildUpon Alias of the CTE to build upon
|
|
37
22
|
* @param allEntities Map of all entities in the mapping
|
|
38
23
|
* @param mapping The JSON mapping configuration
|
|
39
|
-
* @
|
|
24
|
+
* @param columnMappings Optional mappings from object entity IDs to generated JSON column names
|
|
25
|
+
* @returns Object containing updated CTEs and last CTE alias
|
|
40
26
|
*/
|
|
41
|
-
buildArrayEntityCtes(ctesSoFar: CommonTable[], aliasOfCteToBuildUpon: string, allEntities: Map<string, ProcessableEntity>, mapping: JsonMapping): {
|
|
27
|
+
buildArrayEntityCtes(ctesSoFar: CommonTable[], aliasOfCteToBuildUpon: string, allEntities: Map<string, ProcessableEntity>, mapping: JsonMapping, columnMappings?: JsonColumnMapping[]): {
|
|
42
28
|
updatedCtes: CommonTable[];
|
|
43
29
|
lastCteAlias: string;
|
|
44
30
|
};
|
|
45
31
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* Depth calculation ensures proper processing order where deeper nested
|
|
49
|
-
* arrays are processed first, making their aggregated data available
|
|
50
|
-
* for parent array processing.
|
|
32
|
+
* Collects array entities and calculates depth for dependency ordering.
|
|
33
|
+
* Depth = distance from root. Deeper arrays processed first.
|
|
51
34
|
*
|
|
52
35
|
* @param mapping The JSON mapping configuration
|
|
53
36
|
* @param allEntities Map of all entities in the mapping
|
|
54
|
-
* @returns Array of array entity information with
|
|
37
|
+
* @returns Array of array entity information with depths, sorted deepest first
|
|
55
38
|
*/
|
|
56
39
|
private collectAndSortArrayEntities;
|
|
57
40
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* Grouping by depth allows us to:
|
|
61
|
-
* - Process all entities at the same level in a single CTE
|
|
62
|
-
* - Optimize query performance by reducing the number of CTEs
|
|
63
|
-
* - Maintain clear dependency ordering
|
|
41
|
+
* Groups array entities by depth level for batch processing.
|
|
64
42
|
*
|
|
65
43
|
* @param arrayInfos Array of array entity information with depths
|
|
66
44
|
* @returns Map of depth level to entities at that depth
|
|
67
45
|
*/
|
|
68
46
|
private groupEntitiesByDepth;
|
|
69
47
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* at the same depth, using GROUP BY to compress rows into JSON arrays.
|
|
48
|
+
* Builds CTE for specific depth level using row compression.
|
|
49
|
+
* Uses GROUP BY to aggregate multiple rows into JSON arrays.
|
|
50
|
+
* Excludes array-internal columns from GROUP BY to prevent over-grouping.
|
|
74
51
|
*
|
|
75
52
|
* @param infos Array entities at this depth level
|
|
76
53
|
* @param currentCteAlias Alias of the CTE to build upon
|
|
77
54
|
* @param currentCtes All CTEs built so far
|
|
78
55
|
* @param depth Current depth level being processed
|
|
79
56
|
* @param mapping JSON mapping configuration
|
|
57
|
+
* @param columnMappings Optional mappings from object entity IDs to generated JSON column names
|
|
80
58
|
* @returns The new CTE and its alias
|
|
81
59
|
*/
|
|
82
60
|
private buildDepthCte;
|
|
83
61
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* the entity's columns into a JSON array. It also handles nested relationships
|
|
88
|
-
* by including child entity properties in the JSON object.
|
|
62
|
+
* Creates jsonb_agg function for array entity.
|
|
63
|
+
* Handles entity columns and nested child relationships.
|
|
64
|
+
* Uses originalPropertyName to avoid sequential numbering.
|
|
89
65
|
*
|
|
90
66
|
* @param entity The array entity being processed
|
|
91
67
|
* @param nestedEntities All nested entities from the mapping
|
|
92
68
|
* @param allEntities Map of all entities (not used in current implementation)
|
|
69
|
+
* @param columnMappings Mappings from object entity IDs to generated JSON column names
|
|
93
70
|
* @returns Object containing the JSON aggregation function
|
|
94
71
|
*/
|
|
95
72
|
private buildAggregationDetailsForArrayEntity;
|
|
96
73
|
/**
|
|
97
|
-
* Collects array entity columns
|
|
98
|
-
*
|
|
99
|
-
* This method creates a mapping from depth levels to sets of column names that belong to
|
|
100
|
-
* array entities at each depth. This is used to determine which columns should be excluded
|
|
101
|
-
* from GROUP BY clauses when performing array aggregation at specific depths.
|
|
74
|
+
* Collects array entity columns by depth for GROUP BY exclusion strategy.
|
|
102
75
|
*
|
|
103
76
|
* @param mapping The JSON mapping configuration containing all entities
|
|
104
77
|
* @param currentDepth The current aggregation depth being processed
|
|
@@ -106,7 +79,7 @@ export declare class PostgresArrayEntityCteBuilder {
|
|
|
106
79
|
*/
|
|
107
80
|
private collectArrayEntityColumnsByDepth;
|
|
108
81
|
/**
|
|
109
|
-
* Calculates
|
|
82
|
+
* Calculates entity depth by traversing up to root.
|
|
110
83
|
*
|
|
111
84
|
* @param entity The entity to calculate depth for
|
|
112
85
|
* @param mapping The JSON mapping containing all entities
|
|
@@ -114,7 +87,7 @@ export declare class PostgresArrayEntityCteBuilder {
|
|
|
114
87
|
*/
|
|
115
88
|
private calculateEntityDepth;
|
|
116
89
|
/**
|
|
117
|
-
* Adds
|
|
90
|
+
* Adds entity columns to depth set.
|
|
118
91
|
*
|
|
119
92
|
* @param entity The entity whose columns should be added
|
|
120
93
|
* @param depth The depth level to add columns to
|
|
@@ -122,10 +95,7 @@ export declare class PostgresArrayEntityCteBuilder {
|
|
|
122
95
|
*/
|
|
123
96
|
private addEntityColumnsToDepthSet;
|
|
124
97
|
/**
|
|
125
|
-
* Recursively collects columns from
|
|
126
|
-
*
|
|
127
|
-
* This method ensures that all nested entities (at any depth) under an array entity
|
|
128
|
-
* have their columns properly categorized by the array entity's depth level.
|
|
98
|
+
* Recursively collects columns from descendant entities.
|
|
129
99
|
*
|
|
130
100
|
* @param parentEntityId The ID of the parent entity
|
|
131
101
|
* @param targetDepth The depth level to assign collected columns to
|
|
@@ -134,11 +104,8 @@ export declare class PostgresArrayEntityCteBuilder {
|
|
|
134
104
|
*/
|
|
135
105
|
private collectDescendantColumns;
|
|
136
106
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* This method implements the core logic for deciding which columns from previous CTEs
|
|
140
|
-
* should be included in the GROUP BY clause when performing array aggregation. It handles
|
|
141
|
-
* special cases for JSON columns and applies depth-based filtering to prevent over-grouping.
|
|
107
|
+
* Implements GROUP BY exclusion strategy for array aggregation.
|
|
108
|
+
* Excludes current array columns and array-internal object JSON columns.
|
|
142
109
|
*
|
|
143
110
|
* @param prevSelects SELECT variables from the previous CTE
|
|
144
111
|
* @param arrayColumns Columns that are being aggregated (should be excluded from GROUP BY)
|
|
@@ -146,14 +113,12 @@ export declare class PostgresArrayEntityCteBuilder {
|
|
|
146
113
|
* @param currentDepth The current aggregation depth being processed
|
|
147
114
|
* @param selectItems Output array for SELECT items
|
|
148
115
|
* @param groupByItems Output array for GROUP BY items
|
|
116
|
+
* @param arrayInternalObjectColumns JSON columns from objects within arrays being processed
|
|
149
117
|
*/
|
|
150
118
|
private processSelectVariablesForGroupBy;
|
|
151
119
|
/**
|
|
152
|
-
* Determines
|
|
153
|
-
*
|
|
154
|
-
* This method applies depth-based filtering and special handling for JSON columns
|
|
155
|
-
* to prevent over-grouping during array aggregation. It implements heuristics for
|
|
156
|
-
* excluding columns that belong to nested entities within array contexts.
|
|
120
|
+
* Determines if column should be included in GROUP BY clause.
|
|
121
|
+
* Applies depth-based filtering and special handling for JSON columns.
|
|
157
122
|
*
|
|
158
123
|
* @param columnName The name of the column to evaluate
|
|
159
124
|
* @param arrayEntitiesByDepth Map of depth levels to their column sets
|
|
@@ -162,11 +127,8 @@ export declare class PostgresArrayEntityCteBuilder {
|
|
|
162
127
|
*/
|
|
163
128
|
private shouldIncludeColumnInGroupBy;
|
|
164
129
|
/**
|
|
165
|
-
* Applies heuristics
|
|
166
|
-
*
|
|
167
|
-
* This method uses entity numbering patterns to identify deeply nested entities
|
|
168
|
-
* that should be excluded from GROUP BY when processing array aggregations.
|
|
169
|
-
* This is a simplified heuristic approach that works for current use cases.
|
|
130
|
+
* Applies heuristics for entity JSON column inclusion in GROUP BY.
|
|
131
|
+
* Uses entity numbering patterns to identify deeply nested entities.
|
|
170
132
|
*
|
|
171
133
|
* @param columnName The JSON column name (expected format: entity_N_json)
|
|
172
134
|
* @param currentDepth The current aggregation depth
|
|
@@ -6,42 +6,27 @@ const SimpleSelectQuery_1 = require("../models/SimpleSelectQuery");
|
|
|
6
6
|
const ValueComponent_1 = require("../models/ValueComponent");
|
|
7
7
|
const SelectValueCollector_1 = require("./SelectValueCollector");
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* This class handles the creation of CTEs that build JSON/JSONB arrays for child entities,
|
|
11
|
-
* processing them from the deepest level up to ensure proper dependency ordering.
|
|
9
|
+
* Builds CTEs for array entities using depth-first processing and row compression.
|
|
12
10
|
*
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
* - Row
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
* - Column exclusion to avoid duplication
|
|
19
|
-
*
|
|
20
|
-
* Why depth calculation is critical:
|
|
21
|
-
* 1. Array entities can be nested at multiple levels. We must process the deepest
|
|
22
|
-
* (most distant) arrays first to ensure their JSON representations are available
|
|
23
|
-
* when building their parent arrays.
|
|
24
|
-
* 2. Array entity processing is essentially a row compression operation using GROUP BY.
|
|
25
|
-
* Unlike parent entities which use column compression, arrays require grouping
|
|
26
|
-
* to aggregate multiple rows into JSON arrays.
|
|
27
|
-
*
|
|
28
|
-
* Example hierarchy:
|
|
29
|
-
* Order (root, depth 0)
|
|
30
|
-
* └─ Items (array, depth 1)
|
|
31
|
-
* └─ Details (array, depth 2)
|
|
32
|
-
*
|
|
33
|
-
* Processing order: depth 2 → depth 1 → depth 0
|
|
11
|
+
* Core concepts:
|
|
12
|
+
* - Column Compression: OBJECT relationships (user_id, user_name → user_json)
|
|
13
|
+
* - Row Compression: ARRAY relationships (multiple rows → JSON array via GROUP BY)
|
|
14
|
+
* - Depth-First: Process deepest arrays first for dependency ordering
|
|
15
|
+
* - GROUP BY Exclusion: Exclude array-internal columns to prevent over-grouping
|
|
34
16
|
*/
|
|
35
17
|
class PostgresArrayEntityCteBuilder {
|
|
36
18
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
19
|
+
* Builds CTEs for all array entities using depth-first processing.
|
|
20
|
+
* Collects arrays by depth, processes deepest first, chains CTEs.
|
|
21
|
+
*
|
|
22
|
+
* @param ctesSoFar Array of CTEs built so far
|
|
23
|
+
* @param aliasOfCteToBuildUpon Alias of the CTE to build upon
|
|
40
24
|
* @param allEntities Map of all entities in the mapping
|
|
41
25
|
* @param mapping The JSON mapping configuration
|
|
42
|
-
* @
|
|
26
|
+
* @param columnMappings Optional mappings from object entity IDs to generated JSON column names
|
|
27
|
+
* @returns Object containing updated CTEs and last CTE alias
|
|
43
28
|
*/
|
|
44
|
-
buildArrayEntityCtes(ctesSoFar, aliasOfCteToBuildUpon, allEntities, mapping) {
|
|
29
|
+
buildArrayEntityCtes(ctesSoFar, aliasOfCteToBuildUpon, allEntities, mapping, columnMappings) {
|
|
45
30
|
let currentCtes = [...ctesSoFar];
|
|
46
31
|
let currentCteAlias = aliasOfCteToBuildUpon;
|
|
47
32
|
// Collect and sort array entities by depth
|
|
@@ -56,22 +41,19 @@ class PostgresArrayEntityCteBuilder {
|
|
|
56
41
|
for (const depth of depths) {
|
|
57
42
|
const infos = entitiesByDepth.get(depth);
|
|
58
43
|
// Build CTE for all entities at this depth
|
|
59
|
-
const { cte, newCteAlias } = this.buildDepthCte(infos, currentCteAlias, currentCtes, depth, mapping);
|
|
44
|
+
const { cte, newCteAlias } = this.buildDepthCte(infos, currentCteAlias, currentCtes, depth, mapping, columnMappings);
|
|
60
45
|
currentCtes.push(cte);
|
|
61
46
|
currentCteAlias = newCteAlias;
|
|
62
47
|
}
|
|
63
48
|
return { updatedCtes: currentCtes, lastCteAlias: currentCteAlias };
|
|
64
49
|
}
|
|
65
50
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* Depth calculation ensures proper processing order where deeper nested
|
|
69
|
-
* arrays are processed first, making their aggregated data available
|
|
70
|
-
* for parent array processing.
|
|
51
|
+
* Collects array entities and calculates depth for dependency ordering.
|
|
52
|
+
* Depth = distance from root. Deeper arrays processed first.
|
|
71
53
|
*
|
|
72
54
|
* @param mapping The JSON mapping configuration
|
|
73
55
|
* @param allEntities Map of all entities in the mapping
|
|
74
|
-
* @returns Array of array entity information with
|
|
56
|
+
* @returns Array of array entity information with depths, sorted deepest first
|
|
75
57
|
*/
|
|
76
58
|
collectAndSortArrayEntities(mapping, allEntities) {
|
|
77
59
|
const arrayEntityInfos = [];
|
|
@@ -113,12 +95,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
113
95
|
return arrayEntityInfos;
|
|
114
96
|
}
|
|
115
97
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* Grouping by depth allows us to:
|
|
119
|
-
* - Process all entities at the same level in a single CTE
|
|
120
|
-
* - Optimize query performance by reducing the number of CTEs
|
|
121
|
-
* - Maintain clear dependency ordering
|
|
98
|
+
* Groups array entities by depth level for batch processing.
|
|
122
99
|
*
|
|
123
100
|
* @param arrayInfos Array of array entity information with depths
|
|
124
101
|
* @returns Map of depth level to entities at that depth
|
|
@@ -135,19 +112,19 @@ class PostgresArrayEntityCteBuilder {
|
|
|
135
112
|
return entitiesByDepth;
|
|
136
113
|
}
|
|
137
114
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* at the same depth, using GROUP BY to compress rows into JSON arrays.
|
|
115
|
+
* Builds CTE for specific depth level using row compression.
|
|
116
|
+
* Uses GROUP BY to aggregate multiple rows into JSON arrays.
|
|
117
|
+
* Excludes array-internal columns from GROUP BY to prevent over-grouping.
|
|
142
118
|
*
|
|
143
119
|
* @param infos Array entities at this depth level
|
|
144
120
|
* @param currentCteAlias Alias of the CTE to build upon
|
|
145
121
|
* @param currentCtes All CTEs built so far
|
|
146
122
|
* @param depth Current depth level being processed
|
|
147
123
|
* @param mapping JSON mapping configuration
|
|
124
|
+
* @param columnMappings Optional mappings from object entity IDs to generated JSON column names
|
|
148
125
|
* @returns The new CTE and its alias
|
|
149
126
|
*/
|
|
150
|
-
buildDepthCte(infos, currentCteAlias, currentCtes, depth, mapping) {
|
|
127
|
+
buildDepthCte(infos, currentCteAlias, currentCtes, depth, mapping, columnMappings) {
|
|
151
128
|
var _a;
|
|
152
129
|
// Collect columns that will be compressed into arrays
|
|
153
130
|
// This includes both direct columns and columns from nested entities within the array
|
|
@@ -186,12 +163,28 @@ class PostgresArrayEntityCteBuilder {
|
|
|
186
163
|
});
|
|
187
164
|
// Collect array entity columns organized by depth for GROUP BY exclusion strategy
|
|
188
165
|
const arrayEntityColumns = this.collectArrayEntityColumnsByDepth(mapping, depth);
|
|
166
|
+
// Identify JSON columns from objects within the arrays being processed at this depth
|
|
167
|
+
const arrayInternalObjectColumns = new Set();
|
|
168
|
+
if (columnMappings) {
|
|
169
|
+
infos.forEach(info => {
|
|
170
|
+
// Find all object-type nested entities within this array entity
|
|
171
|
+
mapping.nestedEntities
|
|
172
|
+
.filter(ne => ne.parentId === info.entity.id && ne.relationshipType === "object")
|
|
173
|
+
.forEach(objectEntity => {
|
|
174
|
+
// Find the corresponding JSON column mapping for this object entity
|
|
175
|
+
const columnMapping = columnMappings.find(cm => cm.entityId === objectEntity.id);
|
|
176
|
+
if (columnMapping) {
|
|
177
|
+
arrayInternalObjectColumns.add(columnMapping.generatedColumnName);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
189
182
|
// Process existing SELECT variables to determine which should be included in GROUP BY
|
|
190
|
-
this.processSelectVariablesForGroupBy(prevSelects, arrayColumns, arrayEntityColumns, depth, selectItems, groupByItems);
|
|
183
|
+
this.processSelectVariablesForGroupBy(prevSelects, arrayColumns, arrayEntityColumns, depth, selectItems, groupByItems, arrayInternalObjectColumns);
|
|
191
184
|
// Add JSON aggregation columns for each array entity at this depth
|
|
192
185
|
for (const info of infos) {
|
|
193
|
-
const agg = this.buildAggregationDetailsForArrayEntity(info.entity, mapping.nestedEntities, new Map() // allEntities - not needed for array aggregation
|
|
194
|
-
);
|
|
186
|
+
const agg = this.buildAggregationDetailsForArrayEntity(info.entity, mapping.nestedEntities, new Map(), // allEntities - not needed for array aggregation
|
|
187
|
+
columnMappings);
|
|
195
188
|
selectItems.push(new Clause_1.SelectItem(agg.jsonAgg, info.entity.propertyName));
|
|
196
189
|
}
|
|
197
190
|
// Create the new CTE
|
|
@@ -205,20 +198,19 @@ class PostgresArrayEntityCteBuilder {
|
|
|
205
198
|
return { cte, newCteAlias: cteAlias };
|
|
206
199
|
}
|
|
207
200
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* the entity's columns into a JSON array. It also handles nested relationships
|
|
212
|
-
* by including child entity properties in the JSON object.
|
|
201
|
+
* Creates jsonb_agg function for array entity.
|
|
202
|
+
* Handles entity columns and nested child relationships.
|
|
203
|
+
* Uses originalPropertyName to avoid sequential numbering.
|
|
213
204
|
*
|
|
214
205
|
* @param entity The array entity being processed
|
|
215
206
|
* @param nestedEntities All nested entities from the mapping
|
|
216
207
|
* @param allEntities Map of all entities (not used in current implementation)
|
|
208
|
+
* @param columnMappings Mappings from object entity IDs to generated JSON column names
|
|
217
209
|
* @returns Object containing the JSON aggregation function
|
|
218
210
|
*/
|
|
219
|
-
buildAggregationDetailsForArrayEntity(entity, nestedEntities, allEntities) {
|
|
211
|
+
buildAggregationDetailsForArrayEntity(entity, nestedEntities, allEntities, columnMappings) {
|
|
220
212
|
// Build JSON object for array elements using JSONB functions
|
|
221
|
-
const jsonBuildFunction =
|
|
213
|
+
const jsonBuildFunction = PostgresArrayEntityCteBuilder.JSON_FUNCTIONS.BUILD_OBJECT;
|
|
222
214
|
const args = [];
|
|
223
215
|
// Add the entity's own columns
|
|
224
216
|
Object.entries(entity.columns).forEach(([jsonKey, sqlColumn]) => {
|
|
@@ -228,12 +220,54 @@ class PostgresArrayEntityCteBuilder {
|
|
|
228
220
|
// Find and process child entities (both object and array types)
|
|
229
221
|
const childEntities = nestedEntities.filter((ne) => ne.parentId === entity.id);
|
|
230
222
|
childEntities.forEach((childEntity) => {
|
|
231
|
-
|
|
223
|
+
// Use originalPropertyName if available to avoid sequential numbering in final JSON
|
|
224
|
+
const propertyNameForJson = childEntity.originalPropertyName || childEntity.propertyName;
|
|
225
|
+
args.push(new ValueComponent_1.LiteralValue(propertyNameForJson));
|
|
232
226
|
if (childEntity.relationshipType === "object") {
|
|
233
|
-
// For object relationships, use pre-computed JSON column
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
227
|
+
// For object relationships, use pre-computed JSON column from column mappings
|
|
228
|
+
if (!columnMappings) {
|
|
229
|
+
throw new Error(`❌ PostgresArrayEntityCteBuilder Error: Column mappings not provided\n` +
|
|
230
|
+
`\n` +
|
|
231
|
+
`🔍 Details:\n` +
|
|
232
|
+
` - Entity ID: ${childEntity.id}\n` +
|
|
233
|
+
` - Entity Name: ${childEntity.name || 'unknown'}\n` +
|
|
234
|
+
` - Property Name: ${childEntity.propertyName}\n` +
|
|
235
|
+
` - Relationship Type: ${childEntity.relationshipType}\n` +
|
|
236
|
+
`\n` +
|
|
237
|
+
`💡 Solution:\n` +
|
|
238
|
+
` Column mappings are required for hybrid JSON column naming.\n` +
|
|
239
|
+
` This error indicates that PostgresObjectEntityCteBuilder did not\n` +
|
|
240
|
+
` pass column mappings to PostgresArrayEntityCteBuilder.\n` +
|
|
241
|
+
`\n` +
|
|
242
|
+
`🔧 Check:\n` +
|
|
243
|
+
` 1. Ensure PostgresJsonQueryBuilder.buildJsonWithCteStrategy() passes columnMappings\n` +
|
|
244
|
+
` 2. Verify PostgresObjectEntityCteBuilder.buildObjectEntityCtes() returns columnMappings\n` +
|
|
245
|
+
` 3. Check that Model-driven mapping conversion generates unique entity IDs`);
|
|
246
|
+
}
|
|
247
|
+
const mapping = columnMappings.find(m => m.entityId === childEntity.id);
|
|
248
|
+
if (!mapping) {
|
|
249
|
+
const availableMappings = columnMappings.map(m => `${m.entityId} → ${m.generatedColumnName}`).join(', ');
|
|
250
|
+
throw new Error(`❌ PostgresArrayEntityCteBuilder Error: Column mapping not found\n` +
|
|
251
|
+
`\n` +
|
|
252
|
+
`🔍 Details:\n` +
|
|
253
|
+
` - Looking for Entity ID: ${childEntity.id}\n` +
|
|
254
|
+
` - Entity Name: ${childEntity.name || 'unknown'}\n` +
|
|
255
|
+
` - Property Name: ${childEntity.propertyName}\n` +
|
|
256
|
+
` - Relationship Type: ${childEntity.relationshipType}\n` +
|
|
257
|
+
`\n` +
|
|
258
|
+
`📋 Available Mappings:\n` +
|
|
259
|
+
` ${availableMappings || 'None'}\n` +
|
|
260
|
+
`\n` +
|
|
261
|
+
`💡 Solution:\n` +
|
|
262
|
+
` Entity IDs must match between mapping generation and usage.\n` +
|
|
263
|
+
` This suggests a mismatch in entity ID generation or processing.\n` +
|
|
264
|
+
`\n` +
|
|
265
|
+
`🔧 Check:\n` +
|
|
266
|
+
` 1. Model-driven mapping conversion generates consistent entity IDs\n` +
|
|
267
|
+
` 2. PostgresObjectEntityCteBuilder processes all entities correctly\n` +
|
|
268
|
+
` 3. Entity hierarchy and parentId relationships are correct`);
|
|
269
|
+
}
|
|
270
|
+
args.push(new ValueComponent_1.ColumnReference(null, new ValueComponent_1.IdentifierString(mapping.generatedColumnName)));
|
|
237
271
|
}
|
|
238
272
|
else if (childEntity.relationshipType === "array") {
|
|
239
273
|
// For array relationships, use the column directly
|
|
@@ -244,7 +278,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
244
278
|
const jsonObject = new ValueComponent_1.FunctionCall(null, new ValueComponent_1.RawString(jsonBuildFunction), new ValueComponent_1.ValueList(args), null);
|
|
245
279
|
// Create JSON aggregation using JSONB with NULL filtering
|
|
246
280
|
// Use FILTER clause to exclude rows where primary key is NULL (no actual data)
|
|
247
|
-
const jsonAggFunction =
|
|
281
|
+
const jsonAggFunction = PostgresArrayEntityCteBuilder.JSON_FUNCTIONS.AGGREGATE;
|
|
248
282
|
// Find the primary column (typically the first column) to use for NULL filtering
|
|
249
283
|
const primaryColumn = Object.values(entity.columns)[0];
|
|
250
284
|
// For now, create standard jsonb_agg and handle NULL filtering in post-processing
|
|
@@ -253,11 +287,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
253
287
|
return { jsonAgg };
|
|
254
288
|
}
|
|
255
289
|
/**
|
|
256
|
-
* Collects array entity columns
|
|
257
|
-
*
|
|
258
|
-
* This method creates a mapping from depth levels to sets of column names that belong to
|
|
259
|
-
* array entities at each depth. This is used to determine which columns should be excluded
|
|
260
|
-
* from GROUP BY clauses when performing array aggregation at specific depths.
|
|
290
|
+
* Collects array entity columns by depth for GROUP BY exclusion strategy.
|
|
261
291
|
*
|
|
262
292
|
* @param mapping The JSON mapping configuration containing all entities
|
|
263
293
|
* @param currentDepth The current aggregation depth being processed
|
|
@@ -266,7 +296,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
266
296
|
collectArrayEntityColumnsByDepth(mapping, currentDepth) {
|
|
267
297
|
const arrayEntitiesByDepth = new Map(); // Initialize depth maps for current and deeper levels
|
|
268
298
|
// Use a reasonable maximum depth limit to avoid infinite loops
|
|
269
|
-
const maxDepth = Math.max(currentDepth + 3, 5);
|
|
299
|
+
const maxDepth = Math.max(currentDepth + 3, 5);
|
|
270
300
|
for (let d = currentDepth; d <= maxDepth; d++) {
|
|
271
301
|
arrayEntitiesByDepth.set(d, new Set());
|
|
272
302
|
}
|
|
@@ -287,7 +317,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
287
317
|
return arrayEntitiesByDepth;
|
|
288
318
|
}
|
|
289
319
|
/**
|
|
290
|
-
* Calculates
|
|
320
|
+
* Calculates entity depth by traversing up to root.
|
|
291
321
|
*
|
|
292
322
|
* @param entity The entity to calculate depth for
|
|
293
323
|
* @param mapping The JSON mapping containing all entities
|
|
@@ -303,7 +333,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
303
333
|
return entityDepth;
|
|
304
334
|
}
|
|
305
335
|
/**
|
|
306
|
-
* Adds
|
|
336
|
+
* Adds entity columns to depth set.
|
|
307
337
|
*
|
|
308
338
|
* @param entity The entity whose columns should be added
|
|
309
339
|
* @param depth The depth level to add columns to
|
|
@@ -316,10 +346,7 @@ class PostgresArrayEntityCteBuilder {
|
|
|
316
346
|
});
|
|
317
347
|
}
|
|
318
348
|
/**
|
|
319
|
-
* Recursively collects columns from
|
|
320
|
-
*
|
|
321
|
-
* This method ensures that all nested entities (at any depth) under an array entity
|
|
322
|
-
* have their columns properly categorized by the array entity's depth level.
|
|
349
|
+
* Recursively collects columns from descendant entities.
|
|
323
350
|
*
|
|
324
351
|
* @param parentEntityId The ID of the parent entity
|
|
325
352
|
* @param targetDepth The depth level to assign collected columns to
|
|
@@ -337,11 +364,8 @@ class PostgresArrayEntityCteBuilder {
|
|
|
337
364
|
});
|
|
338
365
|
}
|
|
339
366
|
/**
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* This method implements the core logic for deciding which columns from previous CTEs
|
|
343
|
-
* should be included in the GROUP BY clause when performing array aggregation. It handles
|
|
344
|
-
* special cases for JSON columns and applies depth-based filtering to prevent over-grouping.
|
|
367
|
+
* Implements GROUP BY exclusion strategy for array aggregation.
|
|
368
|
+
* Excludes current array columns and array-internal object JSON columns.
|
|
345
369
|
*
|
|
346
370
|
* @param prevSelects SELECT variables from the previous CTE
|
|
347
371
|
* @param arrayColumns Columns that are being aggregated (should be excluded from GROUP BY)
|
|
@@ -349,24 +373,30 @@ class PostgresArrayEntityCteBuilder {
|
|
|
349
373
|
* @param currentDepth The current aggregation depth being processed
|
|
350
374
|
* @param selectItems Output array for SELECT items
|
|
351
375
|
* @param groupByItems Output array for GROUP BY items
|
|
376
|
+
* @param arrayInternalObjectColumns JSON columns from objects within arrays being processed
|
|
352
377
|
*/
|
|
353
|
-
processSelectVariablesForGroupBy(prevSelects, arrayColumns, arrayEntitiesByDepth, currentDepth, selectItems, groupByItems) {
|
|
378
|
+
processSelectVariablesForGroupBy(prevSelects, arrayColumns, arrayEntitiesByDepth, currentDepth, selectItems, groupByItems, arrayInternalObjectColumns) {
|
|
354
379
|
prevSelects.forEach(sv => {
|
|
355
380
|
if (!arrayColumns.has(sv.name)) {
|
|
381
|
+
// Exclude JSON columns from objects within arrays being processed
|
|
382
|
+
if (arrayInternalObjectColumns && arrayInternalObjectColumns.has(sv.name)) {
|
|
383
|
+
// Skip this column - it's an object within the array being aggregated
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
356
386
|
const shouldInclude = this.shouldIncludeColumnInGroupBy(sv.name, arrayEntitiesByDepth, currentDepth);
|
|
357
387
|
if (shouldInclude) {
|
|
358
388
|
selectItems.push(new Clause_1.SelectItem(new ValueComponent_1.ColumnReference(null, new ValueComponent_1.IdentifierString(sv.name)), sv.name));
|
|
359
|
-
|
|
389
|
+
// Exclude JSON columns from GROUP BY as PostgreSQL doesn't support equality operators for JSON type
|
|
390
|
+
if (!sv.name.endsWith('_json')) {
|
|
391
|
+
groupByItems.push(new ValueComponent_1.ColumnReference(null, new ValueComponent_1.IdentifierString(sv.name)));
|
|
392
|
+
}
|
|
360
393
|
}
|
|
361
394
|
}
|
|
362
395
|
});
|
|
363
396
|
}
|
|
364
397
|
/**
|
|
365
|
-
* Determines
|
|
366
|
-
*
|
|
367
|
-
* This method applies depth-based filtering and special handling for JSON columns
|
|
368
|
-
* to prevent over-grouping during array aggregation. It implements heuristics for
|
|
369
|
-
* excluding columns that belong to nested entities within array contexts.
|
|
398
|
+
* Determines if column should be included in GROUP BY clause.
|
|
399
|
+
* Applies depth-based filtering and special handling for JSON columns.
|
|
370
400
|
*
|
|
371
401
|
* @param columnName The name of the column to evaluate
|
|
372
402
|
* @param arrayEntitiesByDepth Map of depth levels to their column sets
|
|
@@ -384,19 +414,19 @@ class PostgresArrayEntityCteBuilder {
|
|
|
384
414
|
break;
|
|
385
415
|
}
|
|
386
416
|
}
|
|
387
|
-
//
|
|
388
|
-
|
|
389
|
-
|
|
417
|
+
// Critical: JSON columns from objects within arrays being processed
|
|
418
|
+
// must be excluded from GROUP BY as they are aggregated within the array
|
|
419
|
+
if (isJsonColumn) {
|
|
420
|
+
// Legacy handling for entity_ prefixed JSON columns
|
|
421
|
+
if (columnName.startsWith('entity_')) {
|
|
422
|
+
shouldInclude = this.shouldIncludeJsonColumn(columnName, currentDepth);
|
|
423
|
+
}
|
|
390
424
|
}
|
|
391
|
-
|
|
392
|
-
return shouldInclude || (isJsonColumn && !columnName.startsWith('entity_'));
|
|
425
|
+
return shouldInclude;
|
|
393
426
|
}
|
|
394
427
|
/**
|
|
395
|
-
* Applies heuristics
|
|
396
|
-
*
|
|
397
|
-
* This method uses entity numbering patterns to identify deeply nested entities
|
|
398
|
-
* that should be excluded from GROUP BY when processing array aggregations.
|
|
399
|
-
* This is a simplified heuristic approach that works for current use cases.
|
|
428
|
+
* Applies heuristics for entity JSON column inclusion in GROUP BY.
|
|
429
|
+
* Uses entity numbering patterns to identify deeply nested entities.
|
|
400
430
|
*
|
|
401
431
|
* @param columnName The JSON column name (expected format: entity_N_json)
|
|
402
432
|
* @param currentDepth The current aggregation depth
|
|
@@ -420,4 +450,9 @@ class PostgresArrayEntityCteBuilder {
|
|
|
420
450
|
exports.PostgresArrayEntityCteBuilder = PostgresArrayEntityCteBuilder;
|
|
421
451
|
// Constants for consistent naming conventions
|
|
422
452
|
PostgresArrayEntityCteBuilder.CTE_ARRAY_PREFIX = 'cte_array_depth_';
|
|
453
|
+
// JSON function names for PostgreSQL aggregation
|
|
454
|
+
PostgresArrayEntityCteBuilder.JSON_FUNCTIONS = {
|
|
455
|
+
BUILD_OBJECT: 'jsonb_build_object',
|
|
456
|
+
AGGREGATE: 'jsonb_agg'
|
|
457
|
+
};
|
|
423
458
|
//# sourceMappingURL=PostgresArrayEntityCteBuilder.js.map
|