rawsql-ts
Version:
[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
177 lines (176 loc) • 8.48 kB
TypeScript
import { CommonTable } from '../models/Clause';
import { JsonMapping } from './PostgresJsonQueryBuilder';
import { ProcessableEntity } from './PostgresObjectEntityCteBuilder';
/**
* PostgreSQL-specific builder for creating CTEs for array entities (array relationships).
* This class handles the creation of CTEs that build JSON/JSONB arrays for child entities,
* processing them from the deepest level up to ensure proper dependency ordering.
*
* Features:
* - Depth-based CTE naming (cte_array_depth_N)
* - Row compression using GROUP BY operations
* - JSONB/JSON array aggregation
* - Hierarchical processing of nested arrays
* - Column exclusion to avoid duplication
*
* Why depth calculation is critical:
* 1. Array entities can be nested at multiple levels. We must process the deepest
* (most distant) arrays first to ensure their JSON representations are available
* when building their parent arrays.
* 2. Array entity processing is essentially a row compression operation using GROUP BY.
* Unlike parent entities which use column compression, arrays require grouping
* to aggregate multiple rows into JSON arrays.
*
* Example hierarchy:
* Order (root, depth 0)
* └─ Items (array, depth 1)
* └─ Details (array, depth 2)
*
* Processing order: depth 2 → depth 1 → depth 0
*/
export declare class PostgresArrayEntityCteBuilder {
private static readonly CTE_ARRAY_PREFIX;
/**
* Build CTEs for all array entities in the correct dependency order
* @param ctesSoFar Array of CTEs built so far (starts with the initial CTE)
* @param aliasOfCteToBuildUpon Alias of the CTE from which the current array CTE will select
* @param allEntities Map of all entities in the mapping
* @param mapping The JSON mapping configuration
* @returns Object containing the updated list of all CTEs and the alias of the last CTE created
*/
buildArrayEntityCtes(ctesSoFar: CommonTable[], aliasOfCteToBuildUpon: string, allEntities: Map<string, ProcessableEntity>, mapping: JsonMapping): {
updatedCtes: CommonTable[];
lastCteAlias: string;
};
/**
* Collect all array entities and calculate their depth from root.
*
* Depth calculation ensures proper processing order where deeper nested
* arrays are processed first, making their aggregated data available
* for parent array processing.
*
* @param mapping The JSON mapping configuration
* @param allEntities Map of all entities in the mapping
* @returns Array of array entity information with calculated depths, sorted deepest first
*/
private collectAndSortArrayEntities;
/**
* Group array entities by their depth level.
*
* Grouping by depth allows us to:
* - Process all entities at the same level in a single CTE
* - Optimize query performance by reducing the number of CTEs
* - Maintain clear dependency ordering
*
* @param arrayInfos Array of array entity information with depths
* @returns Map of depth level to entities at that depth
*/
private groupEntitiesByDepth;
/**
* Build a CTE that processes all array entities at a specific depth level.
*
* This method creates a single CTE that aggregates multiple array entities
* at the same depth, using GROUP BY to compress rows into JSON arrays.
*
* @param infos Array entities at this depth level
* @param currentCteAlias Alias of the CTE to build upon
* @param currentCtes All CTEs built so far
* @param depth Current depth level being processed
* @param mapping JSON mapping configuration
* @returns The new CTE and its alias
*/
private buildDepthCte;
/**
* Build JSON aggregation function for an array entity.
*
* This method creates a jsonb_agg or json_agg function call that aggregates
* the entity's columns into a JSON array. It also handles nested relationships
* by including child entity properties in the JSON object.
*
* @param entity The array entity being processed
* @param nestedEntities All nested entities from the mapping
* @param allEntities Map of all entities (not used in current implementation)
* @returns Object containing the JSON aggregation function
*/
private buildAggregationDetailsForArrayEntity;
/**
* Collects array entity columns organized by depth for the GROUP BY exclusion strategy.
*
* This method creates a mapping from depth levels to sets of column names that belong to
* array entities at each depth. This is used to determine which columns should be excluded
* from GROUP BY clauses when performing array aggregation at specific depths.
*
* @param mapping The JSON mapping configuration containing all entities
* @param currentDepth The current aggregation depth being processed
* @returns A map where keys are depth levels and values are sets of column names
*/
private collectArrayEntityColumnsByDepth;
/**
* Calculates the depth of an entity in the hierarchy by traversing up to the root.
*
* @param entity The entity to calculate depth for
* @param mapping The JSON mapping containing all entities
* @returns The depth level (0 for root level, 1 for first level, etc.)
*/
private calculateEntityDepth;
/**
* Adds all columns from an entity to the specified depth set.
*
* @param entity The entity whose columns should be added
* @param depth The depth level to add columns to
* @param arrayEntitiesByDepth The map to update
*/
private addEntityColumnsToDepthSet;
/**
* Recursively collects columns from all descendant entities under a parent entity.
*
* This method ensures that all nested entities (at any depth) under an array entity
* have their columns properly categorized by the array entity's depth level.
*
* @param parentEntityId The ID of the parent entity
* @param targetDepth The depth level to assign collected columns to
* @param mapping The JSON mapping containing all entities
* @param arrayEntitiesByDepth The map to update with collected columns
*/
private collectDescendantColumns;
/**
* Processes SELECT variables to determine which should be included in GROUP BY clauses.
*
* This method implements the core logic for deciding which columns from previous CTEs
* should be included in the GROUP BY clause when performing array aggregation. It handles
* special cases for JSON columns and applies depth-based filtering to prevent over-grouping.
*
* @param prevSelects SELECT variables from the previous CTE
* @param arrayColumns Columns that are being aggregated (should be excluded from GROUP BY)
* @param arrayEntitiesByDepth Map of depth levels to their column sets
* @param currentDepth The current aggregation depth being processed
* @param selectItems Output array for SELECT items
* @param groupByItems Output array for GROUP BY items
*/
private processSelectVariablesForGroupBy;
/**
* Determines whether a column should be included in the GROUP BY clause.
*
* This method applies depth-based filtering and special handling for JSON columns
* to prevent over-grouping during array aggregation. It implements heuristics for
* excluding columns that belong to nested entities within array contexts.
*
* @param columnName The name of the column to evaluate
* @param arrayEntitiesByDepth Map of depth levels to their column sets
* @param currentDepth The current aggregation depth
* @returns True if the column should be included in GROUP BY, false otherwise
*/
private shouldIncludeColumnInGroupBy;
/**
* Applies heuristics to determine if an entity JSON column should be included in GROUP BY.
*
* This method uses entity numbering patterns to identify deeply nested entities
* that should be excluded from GROUP BY when processing array aggregations.
* This is a simplified heuristic approach that works for current use cases.
*
* @param columnName The JSON column name (expected format: entity_N_json)
* @param currentDepth The current aggregation depth
* @returns True if the JSON column should be included, false otherwise
*/
private shouldIncludeJsonColumn;
}