drizzle-cube 0.3.32 → 0.4.0
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/adapters/express/index.cjs +1 -1
- package/dist/adapters/express/index.js +1 -1
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/fastify/index.js +1 -1
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/hono/index.js +1 -1
- package/dist/adapters/{mcp-transport-B2rGcu1X.js → mcp-transport-Bbz3qrIy.js} +4324 -3230
- package/dist/adapters/mcp-transport-CXF4E5QJ.cjs +257 -0
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/utils.cjs +8 -8
- package/dist/adapters/utils.d.ts +2 -137
- package/dist/adapters/utils.js +1347 -1356
- package/dist/client/chunks/{DashboardEditModal-rLcmZpe_.js → DashboardEditModal-Bv7e3Q7O.js} +2 -2
- package/dist/client/chunks/{DashboardEditModal-rLcmZpe_.js.map → DashboardEditModal-Bv7e3Q7O.js.map} +1 -1
- package/dist/client/chunks/{analysis-builder-DCt5C58c.js → analysis-builder-BfH-w92z.js} +2962 -3019
- package/dist/client/chunks/analysis-builder-BfH-w92z.js.map +1 -0
- package/dist/client/chunks/{analysis-builder-shared-ysrRYGiU.js → analysis-builder-shared-DsbdRCzz.js} +361 -361
- package/dist/client/chunks/{analysis-builder-shared-ysrRYGiU.js.map → analysis-builder-shared-DsbdRCzz.js.map} +1 -1
- package/dist/client/chunks/useDirtyStateTracking-CTS_m9mg.js.map +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisResultsPanel.d.ts +2 -1
- package/dist/client/components/AnalysisBuilder/types.d.ts +18 -6
- package/dist/client/components.js +1 -1
- package/dist/client/hooks/queries/useDryRunQuery.d.ts +14 -48
- package/dist/client/hooks/useAnalysisBuilderHook.d.ts +6 -33
- package/dist/client/hooks/useAnalysisQueryExecution.d.ts +7 -7
- package/dist/client/hooks.js +194 -22
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/index.js +3 -3
- package/dist/client/shared/types.d.ts +27 -0
- package/dist/client/styles.css +1 -1
- package/dist/client-bundle-stats.html +1 -1
- package/dist/server/index.cjs +49 -49
- package/dist/server/index.d.ts +1727 -547
- package/dist/server/index.js +4836 -3715
- package/package.json +1 -1
- package/dist/adapters/mcp-transport-DeD7YevT.cjs +0 -257
- package/dist/client/chunks/analysis-builder-DCt5C58c.js.map +0 -1
- package/dist/client/chunks/hooks-CdyIO1-j.js +0 -236
- package/dist/client/chunks/hooks-CdyIO1-j.js.map +0 -1
package/dist/server/index.d.ts
CHANGED
|
@@ -277,6 +277,151 @@ export declare interface CacheProvider {
|
|
|
277
277
|
close?(): Promise<void>;
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
+
/**
|
|
281
|
+
* Calculated Measure Resolver
|
|
282
|
+
* Manages dependency resolution for calculated measures
|
|
283
|
+
*/
|
|
284
|
+
export declare class CalculatedMeasureResolver {
|
|
285
|
+
private dependencyGraph;
|
|
286
|
+
private cubes;
|
|
287
|
+
constructor(cubes: Map<string, Cube> | Cube);
|
|
288
|
+
/**
|
|
289
|
+
* Extract {member} references from calculatedSql template
|
|
290
|
+
* Supports both {measure} and {Cube.measure} syntax
|
|
291
|
+
*
|
|
292
|
+
* @param calculatedSql - Template string with {member} references
|
|
293
|
+
* @returns Array of dependency information
|
|
294
|
+
*/
|
|
295
|
+
extractDependencies(calculatedSql: string): MeasureDependency[];
|
|
296
|
+
/**
|
|
297
|
+
* Build dependency graph for all calculated measures in a cube
|
|
298
|
+
*
|
|
299
|
+
* @param cube - The cube containing measures
|
|
300
|
+
*/
|
|
301
|
+
buildGraph(cube: Cube): void;
|
|
302
|
+
/**
|
|
303
|
+
* Build dependency graph for multiple cubes
|
|
304
|
+
*
|
|
305
|
+
* @param cubes - Map of cubes to analyze
|
|
306
|
+
*/
|
|
307
|
+
buildGraphForMultipleCubes(cubes: Map<string, Cube>): void;
|
|
308
|
+
/**
|
|
309
|
+
* Calculate in-degree for each node (number of measures depending on it)
|
|
310
|
+
*/
|
|
311
|
+
private calculateInDegrees;
|
|
312
|
+
/**
|
|
313
|
+
* Perform topological sort using Kahn's algorithm
|
|
314
|
+
* Returns measures in dependency order (dependencies first)
|
|
315
|
+
*
|
|
316
|
+
* @param measureNames - List of measure names to sort
|
|
317
|
+
* @returns Sorted array of measure names
|
|
318
|
+
* @throws Error if circular dependency detected
|
|
319
|
+
*/
|
|
320
|
+
topologicalSort(measureNames: string[]): string[];
|
|
321
|
+
/**
|
|
322
|
+
* Detect circular dependencies using DFS
|
|
323
|
+
* Returns the cycle path if found, null otherwise
|
|
324
|
+
*
|
|
325
|
+
* @returns Array representing the cycle, or null
|
|
326
|
+
*/
|
|
327
|
+
detectCycle(): string[] | null;
|
|
328
|
+
/**
|
|
329
|
+
* DFS helper for cycle detection
|
|
330
|
+
*/
|
|
331
|
+
private dfs;
|
|
332
|
+
/**
|
|
333
|
+
* Get all dependencies for a specific measure (direct and transitive)
|
|
334
|
+
*
|
|
335
|
+
* @param measureName - Full measure name (e.g., "Cube.measure")
|
|
336
|
+
* @returns Set of all dependency measure names
|
|
337
|
+
*/
|
|
338
|
+
getAllDependencies(measureName: string): Set<string>;
|
|
339
|
+
/**
|
|
340
|
+
* Validate that all dependencies exist
|
|
341
|
+
*
|
|
342
|
+
* @param cube - The cube to validate
|
|
343
|
+
* @throws Error if dependencies are missing
|
|
344
|
+
*/
|
|
345
|
+
validateDependencies(cube: Cube): void;
|
|
346
|
+
/**
|
|
347
|
+
* Auto-populate dependencies array for calculated measures
|
|
348
|
+
* Updates the measure objects with detected dependencies
|
|
349
|
+
*
|
|
350
|
+
* @param cube - The cube to update
|
|
351
|
+
*/
|
|
352
|
+
populateDependencies(cube: Cube): void;
|
|
353
|
+
/**
|
|
354
|
+
* Check if a measure is a calculated measure
|
|
355
|
+
*
|
|
356
|
+
* @param measure - The measure to check
|
|
357
|
+
* @returns True if the measure is calculated
|
|
358
|
+
*/
|
|
359
|
+
static isCalculatedMeasure(measure: Measure): boolean;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Reference to a column for join keys */
|
|
363
|
+
declare interface ColumnRef {
|
|
364
|
+
column: AnyColumn;
|
|
365
|
+
alias?: string;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export declare class ComparisonQueryBuilder {
|
|
369
|
+
private dateTimeBuilder;
|
|
370
|
+
constructor(databaseAdapter: DatabaseAdapter);
|
|
371
|
+
/**
|
|
372
|
+
* Check if a query contains compareDateRange
|
|
373
|
+
*/
|
|
374
|
+
hasComparison(query: SemanticQuery): boolean;
|
|
375
|
+
/**
|
|
376
|
+
* Get the time dimension with compareDateRange
|
|
377
|
+
*/
|
|
378
|
+
getComparisonTimeDimension(query: SemanticQuery): TimeDimension | undefined;
|
|
379
|
+
/**
|
|
380
|
+
* Normalize compareDateRange entries to concrete date ranges
|
|
381
|
+
* Handles both relative strings ('last 30 days') and explicit arrays (['2024-01-01', '2024-01-31'])
|
|
382
|
+
*/
|
|
383
|
+
normalizePeriods(compareDateRange: (string | [string, string])[]): NormalizedPeriod[];
|
|
384
|
+
/**
|
|
385
|
+
* Create sub-query for a specific period
|
|
386
|
+
* Replaces compareDateRange with a concrete dateRange for that period
|
|
387
|
+
*/
|
|
388
|
+
createPeriodQuery(query: SemanticQuery, period: NormalizedPeriod): SemanticQuery;
|
|
389
|
+
/**
|
|
390
|
+
* Calculate the day-of-period index for a date
|
|
391
|
+
* Used for aligning data points across periods in overlay mode
|
|
392
|
+
*/
|
|
393
|
+
calculatePeriodDayIndex(date: Date | string, periodStart: Date, granularity: TimeGranularity): number;
|
|
394
|
+
/**
|
|
395
|
+
* Add period metadata to result rows
|
|
396
|
+
*/
|
|
397
|
+
addPeriodMetadata(data: Record<string, unknown>[], period: NormalizedPeriod, timeDimensionKey: string, granularity: TimeGranularity): ComparisonResultRow[];
|
|
398
|
+
/**
|
|
399
|
+
* Merge results from multiple period queries
|
|
400
|
+
* Adds period metadata and creates combined result with annotation
|
|
401
|
+
*/
|
|
402
|
+
mergeComparisonResults(periodResults: Array<{
|
|
403
|
+
result: QueryResult;
|
|
404
|
+
period: NormalizedPeriod;
|
|
405
|
+
}>, timeDimension: TimeDimension, granularity: TimeGranularity): QueryResult;
|
|
406
|
+
/**
|
|
407
|
+
* Sort merged results by period index and then by time dimension
|
|
408
|
+
* Ensures consistent ordering for client-side processing
|
|
409
|
+
*/
|
|
410
|
+
sortComparisonResults(data: ComparisonResultRow[], timeDimensionKey: string): ComparisonResultRow[];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Extended result row with period metadata for alignment
|
|
415
|
+
*/
|
|
416
|
+
declare interface ComparisonResultRow extends Record<string, unknown> {
|
|
417
|
+
/** Period label (e.g., "2024-01-01 - 2024-01-31") */
|
|
418
|
+
__period: string;
|
|
419
|
+
/** Period index (0 = current, 1 = prior, etc.) */
|
|
420
|
+
__periodIndex: number;
|
|
421
|
+
/** Day-of-period index for alignment in overlay mode */
|
|
422
|
+
__periodDayIndex: number;
|
|
423
|
+
}
|
|
424
|
+
|
|
280
425
|
/**
|
|
281
426
|
* Compiled cube with execution function
|
|
282
427
|
*/
|
|
@@ -328,6 +473,100 @@ export declare function createPostgresExecutor(db: DrizzleDatabase, schema?: any
|
|
|
328
473
|
*/
|
|
329
474
|
export declare function createSQLiteExecutor(db: DrizzleDatabase, schema?: any): SQLiteExecutor;
|
|
330
475
|
|
|
476
|
+
/**
|
|
477
|
+
* CTEBuilder handles the construction of Common Table Expressions
|
|
478
|
+
* for pre-aggregation in hasMany relationship queries.
|
|
479
|
+
*
|
|
480
|
+
* This enables efficient aggregation of "many" side data before joining,
|
|
481
|
+
* preventing the Cartesian product explosion that would occur with direct JOINs.
|
|
482
|
+
*/
|
|
483
|
+
export declare class CTEBuilder {
|
|
484
|
+
private queryBuilder;
|
|
485
|
+
constructor(queryBuilder: DrizzleSqlBuilder);
|
|
486
|
+
/**
|
|
487
|
+
* Build pre-aggregation CTE for hasMany relationships
|
|
488
|
+
*
|
|
489
|
+
* Creates a CTE that:
|
|
490
|
+
* 1. Selects join keys and aggregated measures
|
|
491
|
+
* 2. Applies security context filtering
|
|
492
|
+
* 3. Groups by join keys and requested dimensions
|
|
493
|
+
* 4. Handles propagating filters from related cubes
|
|
494
|
+
* 5. Handles multi-hop join paths by absorbing intermediate tables (fan-out prevention)
|
|
495
|
+
*/
|
|
496
|
+
buildPreAggregationCTE(cteInfo: CTEInfo, query: SemanticQuery, context: QueryContext, queryPlan: PhysicalQueryPlan, preBuiltFilterMap?: Map<string, SQL[]>): any;
|
|
497
|
+
/**
|
|
498
|
+
* Build join condition for CTE
|
|
499
|
+
*
|
|
500
|
+
* Creates the ON clause for joining a CTE to the main query.
|
|
501
|
+
* Uses stored column objects for type-safe joins.
|
|
502
|
+
*
|
|
503
|
+
* For multi-hop paths with intermediate joins:
|
|
504
|
+
* - The CTE includes columns from intermediate tables
|
|
505
|
+
* - The join condition uses the intermediate's primary-connected column
|
|
506
|
+
* - Example: departments.id = employeeteams_agg.department_id (not employee_id!)
|
|
507
|
+
*/
|
|
508
|
+
buildCTEJoinCondition(joinCube: PhysicalQueryPlan['joinCubes'][0], cteAlias: string, queryPlan: PhysicalQueryPlan): SQL;
|
|
509
|
+
/**
|
|
510
|
+
* Resolve source-side join expression for CTE joins.
|
|
511
|
+
*
|
|
512
|
+
* When two cubes are both materialized as CTEs in the same query, join keys can
|
|
513
|
+
* still point to the original table column object (e.g. departments.id). In that
|
|
514
|
+
* case the table is no longer present in FROM/JOIN, so rewrite to the upstream CTE
|
|
515
|
+
* alias column (e.g. departments_agg.id).
|
|
516
|
+
*/
|
|
517
|
+
private resolveCTEJoinSourceColumn;
|
|
518
|
+
/**
|
|
519
|
+
* Build a subquery filter for propagating filters from related cubes.
|
|
520
|
+
*
|
|
521
|
+
* This generates: cteCube.FK IN (SELECT sourceCube.PK FROM sourceCube WHERE filters...)
|
|
522
|
+
*
|
|
523
|
+
* Example: For Productivity CTE with Employees.createdAt filter:
|
|
524
|
+
* employee_id IN (SELECT id FROM employees WHERE organisation_id = $1 AND created_at >= $date)
|
|
525
|
+
*
|
|
526
|
+
* For composite keys, uses EXISTS instead of IN for better database compatibility:
|
|
527
|
+
* EXISTS (SELECT 1 FROM source WHERE source.pk1 = cte.fk1 AND source.pk2 = cte.fk2 AND <filters>)
|
|
528
|
+
*/
|
|
529
|
+
buildPropagatingFilterSubquery(propFilter: PropagatingFilter, context: QueryContext): SQL | null;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* CTE information type extracted from runtime physical plan context
|
|
534
|
+
*/
|
|
535
|
+
declare type CTEInfo = NonNullable<PhysicalQueryPlan['preAggregationCTEs']>[0];
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* CTE pre-aggregation node.
|
|
539
|
+
* Represents a WITH clause that pre-aggregates a hasMany cube
|
|
540
|
+
* to prevent fan-out in the outer query.
|
|
541
|
+
*/
|
|
542
|
+
export declare interface CTEPreAggregate extends LogicalNodeBase {
|
|
543
|
+
readonly type: 'ctePreAggregate';
|
|
544
|
+
/** The cube being pre-aggregated */
|
|
545
|
+
cube: CubeRef;
|
|
546
|
+
/** Table alias for the cube in the main query */
|
|
547
|
+
alias: string;
|
|
548
|
+
/** CTE alias (WITH clause name) */
|
|
549
|
+
cteAlias: string;
|
|
550
|
+
/** Keys connecting CTE back to the main query */
|
|
551
|
+
joinKeys: JoinKeyInfo[];
|
|
552
|
+
/** Measure names included in this CTE */
|
|
553
|
+
measures: string[];
|
|
554
|
+
/** Cross-cube filter propagation */
|
|
555
|
+
propagatingFilters?: PropagatingFilter[];
|
|
556
|
+
/** Downstream join keys for transitive joins through this CTE */
|
|
557
|
+
downstreamJoinKeys?: DownstreamJoinKeyInfo[];
|
|
558
|
+
/** Intermediate joins absorbed into this CTE for fan-out prevention */
|
|
559
|
+
intermediateJoins?: IntermediateJoinInfo[];
|
|
560
|
+
/** CTE type (currently only 'aggregate') */
|
|
561
|
+
cteType: 'aggregate';
|
|
562
|
+
/**
|
|
563
|
+
* Reason for creating this CTE:
|
|
564
|
+
* - 'hasMany': Direct hasMany target — outer query uses SUM
|
|
565
|
+
* - 'fanOutPrevention': Affected by hasMany elsewhere — outer query uses MAX
|
|
566
|
+
*/
|
|
567
|
+
cteReason: 'hasMany' | 'fanOutPrevention';
|
|
568
|
+
}
|
|
569
|
+
|
|
331
570
|
/**
|
|
332
571
|
* Pre-aggregation CTE analysis
|
|
333
572
|
*/
|
|
@@ -339,7 +578,7 @@ export declare type CTEReason = 'hasMany' | 'fanOutPrevention';
|
|
|
339
578
|
/**
|
|
340
579
|
* Cube definition focused on Drizzle query building
|
|
341
580
|
*/
|
|
342
|
-
declare interface Cube {
|
|
581
|
+
export declare interface Cube {
|
|
343
582
|
name: string;
|
|
344
583
|
title?: string;
|
|
345
584
|
description?: string;
|
|
@@ -375,8 +614,6 @@ declare interface Cube {
|
|
|
375
614
|
/** Additional metadata */
|
|
376
615
|
meta?: Record<string, any>;
|
|
377
616
|
}
|
|
378
|
-
export { Cube }
|
|
379
|
-
export { Cube as SemanticCube }
|
|
380
617
|
|
|
381
618
|
/**
|
|
382
619
|
* Helper type for creating type-safe cubes
|
|
@@ -432,7 +669,7 @@ export declare interface CubeDiscoveryResult {
|
|
|
432
669
|
/**
|
|
433
670
|
* Type-safe cube join definition with lazy loading support
|
|
434
671
|
*/
|
|
435
|
-
declare interface CubeJoin {
|
|
672
|
+
export declare interface CubeJoin {
|
|
436
673
|
/** Target cube reference - lazy loaded to avoid circular dependencies */
|
|
437
674
|
targetCube: Cube | (() => Cube);
|
|
438
675
|
/** Semantic relationship - determines join behavior */
|
|
@@ -479,8 +716,6 @@ declare interface CubeJoin {
|
|
|
479
716
|
securitySql?: (securityContext: SecurityContext) => SQL | SQL[];
|
|
480
717
|
};
|
|
481
718
|
}
|
|
482
|
-
export { CubeJoin }
|
|
483
|
-
export { CubeJoin as SemanticJoin }
|
|
484
719
|
|
|
485
720
|
/**
|
|
486
721
|
* Cube metadata for API responses
|
|
@@ -507,6 +742,14 @@ export declare interface CubeMetadata {
|
|
|
507
742
|
meta?: Record<string, any>;
|
|
508
743
|
}
|
|
509
744
|
|
|
745
|
+
/** Reference to a registered cube */
|
|
746
|
+
export declare interface CubeRef {
|
|
747
|
+
/** Cube name (e.g. 'Employees') */
|
|
748
|
+
name: string;
|
|
749
|
+
/** Resolved cube object */
|
|
750
|
+
cube: Cube;
|
|
751
|
+
}
|
|
752
|
+
|
|
510
753
|
/**
|
|
511
754
|
* Relationship types supported by cube joins
|
|
512
755
|
*/
|
|
@@ -749,7 +992,7 @@ export declare function defineCube(name: string, definition: Omit<Cube, 'name'>)
|
|
|
749
992
|
/**
|
|
750
993
|
* Dimension definition
|
|
751
994
|
*/
|
|
752
|
-
declare interface Dimension {
|
|
995
|
+
export declare interface Dimension {
|
|
753
996
|
name: string;
|
|
754
997
|
title?: string;
|
|
755
998
|
description?: string;
|
|
@@ -778,8 +1021,6 @@ declare interface Dimension {
|
|
|
778
1021
|
*/
|
|
779
1022
|
granularities?: TimeGranularity[];
|
|
780
1023
|
}
|
|
781
|
-
export { Dimension }
|
|
782
|
-
export { Dimension as SemanticDimension }
|
|
783
1024
|
|
|
784
1025
|
export declare interface DimensionAnnotation {
|
|
785
1026
|
title: string;
|
|
@@ -813,6 +1054,16 @@ export declare interface DimensionMetadata {
|
|
|
813
1054
|
granularities?: TimeGranularity[];
|
|
814
1055
|
}
|
|
815
1056
|
|
|
1057
|
+
/** Reference to a dimension on a specific cube */
|
|
1058
|
+
export declare interface DimensionRef {
|
|
1059
|
+
/** Fully qualified name: CubeName.dimensionName */
|
|
1060
|
+
name: string;
|
|
1061
|
+
/** Cube that owns this dimension */
|
|
1062
|
+
cube: CubeRef;
|
|
1063
|
+
/** Local dimension name (without cube prefix) */
|
|
1064
|
+
localName: string;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
816
1067
|
export declare type DimensionType = 'string' | 'number' | 'time' | 'boolean';
|
|
817
1068
|
|
|
818
1069
|
/**
|
|
@@ -880,6 +1131,156 @@ export declare interface DrizzleDatabase {
|
|
|
880
1131
|
schema?: unknown;
|
|
881
1132
|
}
|
|
882
1133
|
|
|
1134
|
+
/**
|
|
1135
|
+
* Converts optimised logical plans into runtime physical query plans,
|
|
1136
|
+
* then builds executable Drizzle queries.
|
|
1137
|
+
*/
|
|
1138
|
+
export declare class DrizzlePlanBuilder {
|
|
1139
|
+
private readonly queryBuilder;
|
|
1140
|
+
private readonly cteBuilder;
|
|
1141
|
+
private readonly databaseAdapter;
|
|
1142
|
+
constructor(queryBuilder: DrizzleSqlBuilder, cteBuilder: CTEBuilder, databaseAdapter: DatabaseAdapter);
|
|
1143
|
+
/**
|
|
1144
|
+
* Build runtime physical context from an optimised logical plan.
|
|
1145
|
+
* This is the physical-builder input for SQL generation.
|
|
1146
|
+
*/
|
|
1147
|
+
derivePhysicalPlanContext(plan: QueryNode): PhysicalQueryPlan;
|
|
1148
|
+
private derivePhysicalPlanContextFromMultiFact;
|
|
1149
|
+
private derivePhysicalPlanContextFromFullKeyAggregate;
|
|
1150
|
+
private toSemanticQuery;
|
|
1151
|
+
private resolvePhysicalSimpleSource;
|
|
1152
|
+
private resolvePhysicalSimpleSourceFromKeysDedup;
|
|
1153
|
+
private resolveKeysDeduplicationMeta;
|
|
1154
|
+
/**
|
|
1155
|
+
* Build unified query that works for both single and multi-cube queries.
|
|
1156
|
+
*/
|
|
1157
|
+
build(queryPlan: PhysicalQueryPlan, query: SemanticQuery, context: QueryContext): any;
|
|
1158
|
+
private tryBuildKeysDeduplicationQuery;
|
|
1159
|
+
private tryBuildMultiFactMergeQuery;
|
|
1160
|
+
private buildMultiFactUnionKeysFallbackQuery;
|
|
1161
|
+
private buildSharedKeySelection;
|
|
1162
|
+
private selectRuntimeMergeStrategy;
|
|
1163
|
+
private supportsFullOuterJoin;
|
|
1164
|
+
private coalesceQualifiedColumn;
|
|
1165
|
+
private canExecuteKeysDeduplication;
|
|
1166
|
+
private queryContainsMeasureFilter;
|
|
1167
|
+
private getPrimaryKeyDimensions;
|
|
1168
|
+
/**
|
|
1169
|
+
* Build type-specific outer aggregation for keys deduplication.
|
|
1170
|
+
* Each measure type needs different re-aggregation in the outer query:
|
|
1171
|
+
* - sum/count/number: SUM (re-combine additive values)
|
|
1172
|
+
* - min: MIN (preserve minimum across groups)
|
|
1173
|
+
* - max: MAX (preserve maximum across groups)
|
|
1174
|
+
* - avg: SUM(sums) / NULLIF(SUM(counts), 0) (weighted average from decomposed parts)
|
|
1175
|
+
*/
|
|
1176
|
+
private buildKeysOuterAggregation;
|
|
1177
|
+
private applyJoinByType;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
export declare class DrizzleSqlBuilder {
|
|
1181
|
+
private dateTimeBuilder;
|
|
1182
|
+
private filterBuilder;
|
|
1183
|
+
private groupByBuilder;
|
|
1184
|
+
private measureBuilder;
|
|
1185
|
+
constructor(databaseAdapter: DatabaseAdapter);
|
|
1186
|
+
/**
|
|
1187
|
+
* Build resolvedMeasures map for a set of measures
|
|
1188
|
+
* Delegates to MeasureBuilder
|
|
1189
|
+
*/
|
|
1190
|
+
buildResolvedMeasures(measureNames: string[], cubeMap: Map<string, Cube>, context: QueryContext, customMeasureBuilder?: (measureName: string, measure: any, cube: Cube) => SQL): ResolvedMeasures;
|
|
1191
|
+
/**
|
|
1192
|
+
* Build dynamic selections for measures, dimensions, and time dimensions
|
|
1193
|
+
* Works for both single and multi-cube queries
|
|
1194
|
+
* Handles calculated measures with dependency resolution
|
|
1195
|
+
*/
|
|
1196
|
+
buildSelections(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext): Record<string, SQL | AnyColumn>;
|
|
1197
|
+
/**
|
|
1198
|
+
* Build calculated measure expression by substituting {member} references
|
|
1199
|
+
* Delegates to MeasureBuilder
|
|
1200
|
+
*/
|
|
1201
|
+
buildCalculatedMeasure(measure: any, cube: Cube, allCubes: Map<string, Cube>, resolvedMeasures: ResolvedMeasures, context: QueryContext): SQL;
|
|
1202
|
+
/**
|
|
1203
|
+
* Build resolved measures map for a calculated measure from CTE columns
|
|
1204
|
+
* Delegates to MeasureBuilder
|
|
1205
|
+
*/
|
|
1206
|
+
buildCTECalculatedMeasure(measure: any, cube: Cube, cteInfo: {
|
|
1207
|
+
cteAlias: string;
|
|
1208
|
+
measures: string[];
|
|
1209
|
+
cube: Cube;
|
|
1210
|
+
}, allCubes: Map<string, Cube>, context: QueryContext): SQL;
|
|
1211
|
+
/**
|
|
1212
|
+
* Build measure expression for HAVING clause, handling CTE references correctly
|
|
1213
|
+
* Delegates to MeasureBuilder
|
|
1214
|
+
*/
|
|
1215
|
+
private buildHavingMeasureExpression;
|
|
1216
|
+
/**
|
|
1217
|
+
* Build measure expression with aggregation and filters
|
|
1218
|
+
* Delegates to MeasureBuilder
|
|
1219
|
+
*/
|
|
1220
|
+
buildMeasureExpression(measure: any, context: QueryContext, cube?: Cube): SQL;
|
|
1221
|
+
/**
|
|
1222
|
+
* Build time dimension expression with granularity using database adapter
|
|
1223
|
+
* Delegates to DateTimeBuilder
|
|
1224
|
+
*/
|
|
1225
|
+
buildTimeDimensionExpression(dimensionSql: any, granularity: string | undefined, context: QueryContext): SQL;
|
|
1226
|
+
/**
|
|
1227
|
+
* Build WHERE conditions from semantic query filters (dimensions only)
|
|
1228
|
+
* Works for both single and multi-cube queries
|
|
1229
|
+
* @param preBuiltFilters - Optional map of cube name to pre-built filter SQL for parameter deduplication
|
|
1230
|
+
*/
|
|
1231
|
+
buildWhereConditions(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: PhysicalQueryPlan, preBuiltFilters?: Map<string, SQL[]>): SQL[];
|
|
1232
|
+
/**
|
|
1233
|
+
* Build HAVING conditions from semantic query filters (measures only)
|
|
1234
|
+
* Works for both single and multi-cube queries
|
|
1235
|
+
*/
|
|
1236
|
+
buildHavingConditions(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: PhysicalQueryPlan): SQL[];
|
|
1237
|
+
/**
|
|
1238
|
+
* Process a single filter (basic or logical)
|
|
1239
|
+
* @param filterType - 'where' for dimension filters, 'having' for measure filters
|
|
1240
|
+
*/
|
|
1241
|
+
private processFilter;
|
|
1242
|
+
/**
|
|
1243
|
+
* Build filter condition using Drizzle operators
|
|
1244
|
+
* Delegates to FilterBuilder
|
|
1245
|
+
*/
|
|
1246
|
+
private buildFilterCondition;
|
|
1247
|
+
/**
|
|
1248
|
+
* Build date range condition for time dimensions
|
|
1249
|
+
* Delegates to DateTimeBuilder
|
|
1250
|
+
*/
|
|
1251
|
+
buildDateRangeCondition(fieldExpr: AnyColumn | SQL, dateRange: string | string[]): SQL | null;
|
|
1252
|
+
/**
|
|
1253
|
+
* Build GROUP BY fields from dimensions and time dimensions
|
|
1254
|
+
* Delegates to GroupByBuilder
|
|
1255
|
+
*/
|
|
1256
|
+
buildGroupByFields(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: any): (SQL | AnyColumn)[];
|
|
1257
|
+
/**
|
|
1258
|
+
* Build ORDER BY clause with automatic time dimension sorting
|
|
1259
|
+
*/
|
|
1260
|
+
buildOrderBy(query: SemanticQuery, selectedFields?: string[]): SQL[];
|
|
1261
|
+
/**
|
|
1262
|
+
* Collect numeric field names (measures + numeric dimensions) for type conversion
|
|
1263
|
+
* Works for both single and multi-cube queries
|
|
1264
|
+
*/
|
|
1265
|
+
collectNumericFields(cubes: Map<string, Cube> | Cube, query: SemanticQuery): string[];
|
|
1266
|
+
/**
|
|
1267
|
+
* Apply LIMIT and OFFSET to a query with validation
|
|
1268
|
+
* If offset is provided without limit, add a reasonable default limit
|
|
1269
|
+
*/
|
|
1270
|
+
applyLimitAndOffset<T>(query: T, semanticQuery: SemanticQuery): T;
|
|
1271
|
+
/**
|
|
1272
|
+
* Public wrapper for buildFilterCondition - used by executor for cache preloading
|
|
1273
|
+
* This allows pre-building filter SQL before query construction
|
|
1274
|
+
*/
|
|
1275
|
+
buildFilterConditionPublic(fieldExpr: AnyColumn | SQL, operator: FilterOperator, values: any[], field?: any, dateRange?: string | string[]): SQL | null;
|
|
1276
|
+
/**
|
|
1277
|
+
* Build a logical filter (AND/OR) - used by executor for cache preloading
|
|
1278
|
+
* This handles nested filter structures and builds combined SQL
|
|
1279
|
+
* Delegates to FilterBuilder
|
|
1280
|
+
*/
|
|
1281
|
+
buildLogicalFilter(filter: Filter, cubes: Map<string, Cube>, context: QueryContext): SQL | null;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
883
1284
|
export declare class DuckDBExecutor extends BaseDatabaseExecutor {
|
|
884
1285
|
execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
|
|
885
1286
|
/**
|
|
@@ -1137,55 +1538,162 @@ export declare function findBestFieldMatch(metadata: CubeMetadata[], fieldName:
|
|
|
1137
1538
|
type: 'measure' | 'dimension';
|
|
1138
1539
|
} | null;
|
|
1139
1540
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1541
|
+
export declare class FlowQueryBuilder {
|
|
1542
|
+
private filterBuilder;
|
|
1543
|
+
private dateTimeBuilder;
|
|
1544
|
+
private databaseAdapter;
|
|
1545
|
+
constructor(databaseAdapter: DatabaseAdapter);
|
|
1145
1546
|
/**
|
|
1146
|
-
*
|
|
1147
|
-
* Can be a single string like 'Events.userId' or array for multi-cube
|
|
1547
|
+
* Check if query contains flow configuration
|
|
1148
1548
|
*/
|
|
1149
|
-
|
|
1150
|
-
cube: string;
|
|
1151
|
-
dimension: string;
|
|
1152
|
-
}[];
|
|
1549
|
+
hasFlow(query: SemanticQuery): boolean;
|
|
1153
1550
|
/**
|
|
1154
|
-
*
|
|
1155
|
-
* Can be a single string like 'Events.timestamp' or array for multi-cube
|
|
1551
|
+
* Validate flow configuration
|
|
1156
1552
|
*/
|
|
1157
|
-
|
|
1158
|
-
cube: string;
|
|
1159
|
-
dimension: string;
|
|
1160
|
-
}[];
|
|
1553
|
+
validateConfig(config: FlowQueryConfig, cubes: Map<string, Cube>): FlowValidationResult;
|
|
1161
1554
|
/**
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1555
|
+
* Build complete flow query using Drizzle's query builder pattern
|
|
1556
|
+
*
|
|
1557
|
+
* Creates a series of CTEs to:
|
|
1558
|
+
* 1. Find entities at the starting step
|
|
1559
|
+
* 2. Walk backwards N steps to find preceding events
|
|
1560
|
+
* 3. Walk forwards N steps to find following events
|
|
1561
|
+
* 4. Aggregate into nodes and links for Sankey visualization
|
|
1164
1562
|
*/
|
|
1165
|
-
|
|
1166
|
-
/** Display name for the starting step */
|
|
1167
|
-
name: string;
|
|
1168
|
-
/** Filter(s) that identify events for this starting step */
|
|
1169
|
-
filter?: Filter | Filter[];
|
|
1170
|
-
};
|
|
1171
|
-
/** Number of steps to explore BEFORE the starting step (0-5) */
|
|
1172
|
-
stepsBefore: number;
|
|
1173
|
-
/** Number of steps to explore AFTER the starting step (0-5) */
|
|
1174
|
-
stepsAfter: number;
|
|
1563
|
+
buildFlowQuery(config: FlowQueryConfig, cubes: Map<string, Cube>, context: QueryContext): ReturnType<typeof context.db.select>;
|
|
1175
1564
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
1565
|
+
* Transform raw SQL result to FlowResultRow
|
|
1566
|
+
* The raw result contains rows with record_type = 'node' or 'link'
|
|
1178
1567
|
*/
|
|
1179
|
-
|
|
1568
|
+
transformResult(rawResult: Record<string, unknown>[]): FlowResultRow;
|
|
1180
1569
|
/**
|
|
1181
|
-
*
|
|
1182
|
-
* Useful for performance on large datasets
|
|
1570
|
+
* Resolve flow configuration to SQL expressions
|
|
1183
1571
|
*/
|
|
1184
|
-
|
|
1572
|
+
private resolveFlowConfig;
|
|
1185
1573
|
/**
|
|
1186
|
-
*
|
|
1187
|
-
|
|
1188
|
-
|
|
1574
|
+
* Resolve the cube for flow analysis
|
|
1575
|
+
*/
|
|
1576
|
+
private resolveCube;
|
|
1577
|
+
/**
|
|
1578
|
+
* Resolve binding key expression
|
|
1579
|
+
*/
|
|
1580
|
+
private resolveBindingKey;
|
|
1581
|
+
/**
|
|
1582
|
+
* Resolve time dimension expression
|
|
1583
|
+
*/
|
|
1584
|
+
private resolveTimeDimension;
|
|
1585
|
+
/**
|
|
1586
|
+
* Resolve event dimension expression
|
|
1587
|
+
*/
|
|
1588
|
+
private resolveEventDimension;
|
|
1589
|
+
/**
|
|
1590
|
+
* Build filter conditions for the starting step
|
|
1591
|
+
*/
|
|
1592
|
+
private buildStartingStepFilters;
|
|
1593
|
+
/**
|
|
1594
|
+
* Build a single filter condition
|
|
1595
|
+
*/
|
|
1596
|
+
private buildFilterCondition;
|
|
1597
|
+
/**
|
|
1598
|
+
* Build the starting entities CTE
|
|
1599
|
+
* Finds all entities matching the starting step filter with their start time and event type
|
|
1600
|
+
* For sunburst mode, also initializes event_path with the starting event type
|
|
1601
|
+
*/
|
|
1602
|
+
private buildStartingEntitiesCTE;
|
|
1603
|
+
/**
|
|
1604
|
+
* Build CTEs for steps BEFORE the starting point using LATERAL joins
|
|
1605
|
+
* Uses ORDER BY ... DESC LIMIT 1 to fetch immediate predecessor via index
|
|
1606
|
+
*/
|
|
1607
|
+
private buildBeforeCTEsLateral;
|
|
1608
|
+
/**
|
|
1609
|
+
* Build CTEs for steps AFTER the starting point using LATERAL joins
|
|
1610
|
+
* Uses ORDER BY ... ASC LIMIT 1 to fetch immediate successor via index
|
|
1611
|
+
*/
|
|
1612
|
+
private buildAfterCTEsLateral;
|
|
1613
|
+
/**
|
|
1614
|
+
* Build CTEs for steps BEFORE the starting point
|
|
1615
|
+
* Each CTE finds the immediate predecessor event for entities from the previous CTE
|
|
1616
|
+
*
|
|
1617
|
+
* Uses ROW_NUMBER() window function to get exactly the Nth previous event
|
|
1618
|
+
* For sunburst mode, accumulates event_path by prepending to previous path
|
|
1619
|
+
*/
|
|
1620
|
+
private buildBeforeCTEsWindow;
|
|
1621
|
+
/**
|
|
1622
|
+
* Build CTEs for steps AFTER the starting point
|
|
1623
|
+
* Each CTE finds the immediate successor event for entities from the previous CTE
|
|
1624
|
+
*
|
|
1625
|
+
* Uses ROW_NUMBER() window function to get exactly the Nth following event
|
|
1626
|
+
* For sunburst mode, accumulates event_path by concatenating with previous path
|
|
1627
|
+
*/
|
|
1628
|
+
private buildAfterCTEsWindow;
|
|
1629
|
+
/**
|
|
1630
|
+
* Build the nodes aggregation CTE
|
|
1631
|
+
* Aggregates counts per (layer, event_type) combination using UNION ALL
|
|
1632
|
+
* For sunburst mode, aggregates by event_path for unique tree branches
|
|
1633
|
+
*/
|
|
1634
|
+
private buildNodesAggregationCTE;
|
|
1635
|
+
/**
|
|
1636
|
+
* Build the links aggregation CTE
|
|
1637
|
+
* Counts transitions between adjacent layers
|
|
1638
|
+
* For sunburst mode, uses event_path for unique branch identification
|
|
1639
|
+
*/
|
|
1640
|
+
private buildLinksAggregationCTE;
|
|
1641
|
+
/**
|
|
1642
|
+
* Build the final result CTE
|
|
1643
|
+
* Combines nodes and links into a single result set with record_type discriminator
|
|
1644
|
+
*/
|
|
1645
|
+
private buildFinalResultCTE;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Flow query configuration for server-side execution
|
|
1650
|
+
* This is the configuration extracted from SemanticQuery.flow
|
|
1651
|
+
*/
|
|
1652
|
+
declare interface FlowQueryConfig {
|
|
1653
|
+
/**
|
|
1654
|
+
* Binding key that identifies individual entities (e.g., userId)
|
|
1655
|
+
* Can be a single string like 'Events.userId' or array for multi-cube
|
|
1656
|
+
*/
|
|
1657
|
+
bindingKey: string | {
|
|
1658
|
+
cube: string;
|
|
1659
|
+
dimension: string;
|
|
1660
|
+
}[];
|
|
1661
|
+
/**
|
|
1662
|
+
* Time dimension used for ordering events
|
|
1663
|
+
* Can be a single string like 'Events.timestamp' or array for multi-cube
|
|
1664
|
+
*/
|
|
1665
|
+
timeDimension: string | {
|
|
1666
|
+
cube: string;
|
|
1667
|
+
dimension: string;
|
|
1668
|
+
}[];
|
|
1669
|
+
/**
|
|
1670
|
+
* The starting step from which we explore paths
|
|
1671
|
+
* Defines the anchor point for bidirectional flow analysis
|
|
1672
|
+
*/
|
|
1673
|
+
startingStep: {
|
|
1674
|
+
/** Display name for the starting step */
|
|
1675
|
+
name: string;
|
|
1676
|
+
/** Filter(s) that identify events for this starting step */
|
|
1677
|
+
filter?: Filter | Filter[];
|
|
1678
|
+
};
|
|
1679
|
+
/** Number of steps to explore BEFORE the starting step (0-5) */
|
|
1680
|
+
stepsBefore: number;
|
|
1681
|
+
/** Number of steps to explore AFTER the starting step (0-5) */
|
|
1682
|
+
stepsAfter: number;
|
|
1683
|
+
/**
|
|
1684
|
+
* Event dimension that categorizes events (e.g., 'Events.eventType')
|
|
1685
|
+
* This dimension's values become the node labels in the Sankey diagram
|
|
1686
|
+
*/
|
|
1687
|
+
eventDimension: string;
|
|
1688
|
+
/**
|
|
1689
|
+
* Optional limit on the number of entities to process
|
|
1690
|
+
* Useful for performance on large datasets
|
|
1691
|
+
*/
|
|
1692
|
+
entityLimit?: number;
|
|
1693
|
+
/**
|
|
1694
|
+
* Output mode for flow data aggregation
|
|
1695
|
+
* - 'sankey': Aggregate by (layer, event_type) - standard flow visualization where paths can converge
|
|
1696
|
+
* - 'sunburst': Path-qualified nodes for hierarchical tree visualization where each path is unique
|
|
1189
1697
|
* @default 'sankey'
|
|
1190
1698
|
*/
|
|
1191
1699
|
outputMode?: 'sankey' | 'sunburst';
|
|
@@ -1198,6 +1706,24 @@ declare interface FlowQueryConfig {
|
|
|
1198
1706
|
joinStrategy?: 'auto' | 'lateral' | 'window';
|
|
1199
1707
|
}
|
|
1200
1708
|
|
|
1709
|
+
/**
|
|
1710
|
+
* Flow result row returned from query execution
|
|
1711
|
+
* Contains the complete Sankey diagram data
|
|
1712
|
+
*/
|
|
1713
|
+
declare interface FlowResultRow {
|
|
1714
|
+
nodes: SankeyNode[];
|
|
1715
|
+
links: SankeyLink[];
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* Flow validation result
|
|
1720
|
+
*/
|
|
1721
|
+
declare interface FlowValidationResult {
|
|
1722
|
+
isValid: boolean;
|
|
1723
|
+
errors: string[];
|
|
1724
|
+
warnings: string[];
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1201
1727
|
/**
|
|
1202
1728
|
* FNV-1a hash - fast, non-cryptographic hash function
|
|
1203
1729
|
* Returns hex string for cache key readability
|
|
@@ -1235,6 +1761,18 @@ export declare function formatExistingIndexes(indexes: Array<{
|
|
|
1235
1761
|
is_primary?: boolean;
|
|
1236
1762
|
}>): string;
|
|
1237
1763
|
|
|
1764
|
+
/**
|
|
1765
|
+
* Full key aggregate: merges results from multiple subqueries
|
|
1766
|
+
* that share the same dimension key set.
|
|
1767
|
+
*/
|
|
1768
|
+
declare interface FullKeyAggregate extends LogicalNodeBase {
|
|
1769
|
+
readonly type: 'fullKeyAggregate';
|
|
1770
|
+
/** Subqueries whose results will be merged */
|
|
1771
|
+
subqueries: LogicalNode[];
|
|
1772
|
+
/** Shared dimensions used as the merge key */
|
|
1773
|
+
dimensions: DimensionRef[];
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1238
1776
|
/**
|
|
1239
1777
|
* Binding key mapping for multi-cube funnels
|
|
1240
1778
|
* Maps the user/entity identifier across different cubes
|
|
@@ -1254,6 +1792,138 @@ export declare interface FunnelCapabilities {
|
|
|
1254
1792
|
supportsIntervalArithmetic: boolean;
|
|
1255
1793
|
}
|
|
1256
1794
|
|
|
1795
|
+
export declare class FunnelQueryBuilder {
|
|
1796
|
+
private databaseAdapter;
|
|
1797
|
+
private filterBuilder;
|
|
1798
|
+
private dateTimeBuilder;
|
|
1799
|
+
constructor(databaseAdapter: DatabaseAdapter);
|
|
1800
|
+
/**
|
|
1801
|
+
* Check if query contains funnel configuration
|
|
1802
|
+
*/
|
|
1803
|
+
hasFunnel(query: SemanticQuery): boolean;
|
|
1804
|
+
/**
|
|
1805
|
+
* Validate funnel configuration
|
|
1806
|
+
*/
|
|
1807
|
+
validateConfig(config: FunnelQueryConfig, cubes: Map<string, Cube>): {
|
|
1808
|
+
isValid: boolean;
|
|
1809
|
+
errors: string[];
|
|
1810
|
+
};
|
|
1811
|
+
/**
|
|
1812
|
+
* Build complete funnel query using Drizzle's query builder pattern
|
|
1813
|
+
*
|
|
1814
|
+
* Uses the industry-standard "sequential CTEs" pattern where each step
|
|
1815
|
+
* joins to the previous step CTE. This automatically enforces funnel
|
|
1816
|
+
* constraints (monotonically decreasing counts).
|
|
1817
|
+
*
|
|
1818
|
+
* Returns a Drizzle query builder that supports .toSQL() for dry-run
|
|
1819
|
+
* and can be executed directly for results.
|
|
1820
|
+
*/
|
|
1821
|
+
buildFunnelQuery(config: FunnelQueryConfig, cubes: Map<string, Cube>, context: QueryContext): ReturnType<typeof context.db.select>;
|
|
1822
|
+
/**
|
|
1823
|
+
* Transform raw SQL result to FunnelResultRow[]
|
|
1824
|
+
*/
|
|
1825
|
+
transformResult(rawResult: Record<string, unknown>[], config: FunnelQueryConfig): FunnelResultRow[];
|
|
1826
|
+
/**
|
|
1827
|
+
* Extract cube names referenced in step filters
|
|
1828
|
+
*/
|
|
1829
|
+
private extractFilterCubeNames;
|
|
1830
|
+
/**
|
|
1831
|
+
* Resolve steps with their cube, SQL expressions, and filter conditions
|
|
1832
|
+
*/
|
|
1833
|
+
private resolveSteps;
|
|
1834
|
+
/**
|
|
1835
|
+
* Resolve the cube for a step
|
|
1836
|
+
*/
|
|
1837
|
+
private resolveCubeForStep;
|
|
1838
|
+
/**
|
|
1839
|
+
* Resolve binding key expression for a cube
|
|
1840
|
+
*/
|
|
1841
|
+
private resolveBindingKey;
|
|
1842
|
+
/**
|
|
1843
|
+
* Resolve time dimension expression for a cube
|
|
1844
|
+
*/
|
|
1845
|
+
private resolveTimeDimension;
|
|
1846
|
+
/**
|
|
1847
|
+
* Build filter conditions for a step
|
|
1848
|
+
* @param step - The funnel step
|
|
1849
|
+
* @param baseCube - The step's primary cube
|
|
1850
|
+
* @param cubes - All cubes available for cross-cube filtering
|
|
1851
|
+
* @param context - Query context with security context
|
|
1852
|
+
*/
|
|
1853
|
+
private buildStepFilters;
|
|
1854
|
+
/**
|
|
1855
|
+
* Build a single filter condition
|
|
1856
|
+
* @param filter - The filter to build
|
|
1857
|
+
* @param baseCube - The step's primary cube
|
|
1858
|
+
* @param cubes - All cubes available for cross-cube filtering
|
|
1859
|
+
* @param context - Query context with security context
|
|
1860
|
+
*/
|
|
1861
|
+
private buildFilterCondition;
|
|
1862
|
+
/**
|
|
1863
|
+
* Build CTE for a single step using Drizzle's $with() pattern
|
|
1864
|
+
*
|
|
1865
|
+
* For step 0 (entry point): queries raw data directly
|
|
1866
|
+
* For subsequent steps: joins to the previous step CTE to enforce sequential progression
|
|
1867
|
+
*
|
|
1868
|
+
* This implements the industry-standard "sequential CTEs" pattern where each step
|
|
1869
|
+
* only includes binding_keys that successfully completed the previous step.
|
|
1870
|
+
*
|
|
1871
|
+
* @param step - The resolved step configuration
|
|
1872
|
+
* @param context - Query context with security context
|
|
1873
|
+
* @param previousStepCTE - Reference to the previous step's CTE (undefined for step 0)
|
|
1874
|
+
*/
|
|
1875
|
+
private buildStepCTE;
|
|
1876
|
+
/**
|
|
1877
|
+
* Build CTE for the first step (step 0) - entry point
|
|
1878
|
+
*
|
|
1879
|
+
* Queries raw data directly with security context and step filters.
|
|
1880
|
+
* Gets the first occurrence per binding key.
|
|
1881
|
+
*/
|
|
1882
|
+
private buildFirstStepCTE;
|
|
1883
|
+
/**
|
|
1884
|
+
* Build CTE for subsequent steps (step 1+) - joins to previous step
|
|
1885
|
+
*
|
|
1886
|
+
* This is the key to the sequential funnel pattern:
|
|
1887
|
+
* - INNER JOINs to the previous step CTE (only includes binding_keys that completed previous step)
|
|
1888
|
+
* - Applies temporal constraints (must occur after previous step)
|
|
1889
|
+
* - Applies step-specific filters and time-to-convert windows
|
|
1890
|
+
*
|
|
1891
|
+
* This automatically ensures monotonically decreasing counts.
|
|
1892
|
+
*/
|
|
1893
|
+
private buildSubsequentStepCTE;
|
|
1894
|
+
/**
|
|
1895
|
+
* Helper to add cross-cube JOINs to a step query
|
|
1896
|
+
* Extracted to avoid duplication between first and subsequent step methods
|
|
1897
|
+
*/
|
|
1898
|
+
private addCrossJoinsToQuery;
|
|
1899
|
+
/**
|
|
1900
|
+
* Build funnel results CTE that joins all step times for time metric calculation
|
|
1901
|
+
*
|
|
1902
|
+
* With the sequential CTE pattern, each step CTE already contains only the
|
|
1903
|
+
* binding_keys that successfully completed that step. This CTE simply joins
|
|
1904
|
+
* them together to enable time difference calculations.
|
|
1905
|
+
*
|
|
1906
|
+
* No CASE expressions needed - the temporal filtering is already done in each step CTE.
|
|
1907
|
+
*/
|
|
1908
|
+
private buildFunnelResultsCTE;
|
|
1909
|
+
/**
|
|
1910
|
+
* Build aggregation CTE with counts and optional time metrics
|
|
1911
|
+
*
|
|
1912
|
+
* OPTIMIZATION: Uses single-pass aggregation over funnel_joined CTE instead of
|
|
1913
|
+
* multiple scalar subqueries. This reduces table scans from 13+ to 1 for a typical
|
|
1914
|
+
* 3-step funnel with time metrics.
|
|
1915
|
+
*
|
|
1916
|
+
* - Step counts: COUNT(*) for step_0, COUNT(step_N_time) for subsequent steps
|
|
1917
|
+
* - Time metrics: Uses database-specific conditional aggregation (FILTER clause for
|
|
1918
|
+
* PostgreSQL, CASE WHEN for MySQL/SQLite)
|
|
1919
|
+
* - Percentiles: Still use subqueries since PERCENTILE_CONT with FILTER is non-standard
|
|
1920
|
+
*
|
|
1921
|
+
* Important: All SQL fields must have explicit aliases via .as() for Drizzle
|
|
1922
|
+
* to properly reference them when selecting from the CTE
|
|
1923
|
+
*/
|
|
1924
|
+
private buildAggregationCTE;
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1257
1927
|
/**
|
|
1258
1928
|
* Funnel query configuration
|
|
1259
1929
|
*/
|
|
@@ -1414,6 +2084,15 @@ export declare interface HierarchyMetadata {
|
|
|
1414
2084
|
levels: string[];
|
|
1415
2085
|
}
|
|
1416
2086
|
|
|
2087
|
+
/**
|
|
2088
|
+
* No-op optimiser — returns the plan unchanged.
|
|
2089
|
+
* Used as the default in Phase 1 and as a baseline for testing.
|
|
2090
|
+
*/
|
|
2091
|
+
export declare class IdentityOptimiser implements PlanOptimiser {
|
|
2092
|
+
readonly name = "identity";
|
|
2093
|
+
optimise(plan: LogicalNode): LogicalNode;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
1417
2096
|
/**
|
|
1418
2097
|
* Information about a database index
|
|
1419
2098
|
*/
|
|
@@ -1448,6 +2127,19 @@ export declare interface IntermediateJoinInfo {
|
|
|
1448
2127
|
cteJoinColumn: AnyColumn;
|
|
1449
2128
|
}
|
|
1450
2129
|
|
|
2130
|
+
/**
|
|
2131
|
+
* Internal representation of a join path step
|
|
2132
|
+
* Used during path finding - simpler than the public JoinPathStep analysis type
|
|
2133
|
+
*/
|
|
2134
|
+
declare interface InternalJoinPathStep {
|
|
2135
|
+
/** Source cube name */
|
|
2136
|
+
fromCube: string;
|
|
2137
|
+
/** Target cube name */
|
|
2138
|
+
toCube: string;
|
|
2139
|
+
/** The join definition from the source cube */
|
|
2140
|
+
joinDef: CubeJoin;
|
|
2141
|
+
}
|
|
2142
|
+
|
|
1451
2143
|
/**
|
|
1452
2144
|
* Type guard for multi-cube binding key
|
|
1453
2145
|
*/
|
|
@@ -1480,6 +2172,29 @@ export declare function isRetentionQuery(query: unknown): query is {
|
|
|
1480
2172
|
retention: RetentionQueryConfig;
|
|
1481
2173
|
};
|
|
1482
2174
|
|
|
2175
|
+
/**
|
|
2176
|
+
* Planned join entry used by runtime physical builders.
|
|
2177
|
+
*/
|
|
2178
|
+
export declare interface JoinCubePlanEntry {
|
|
2179
|
+
cube: Cube;
|
|
2180
|
+
alias: string;
|
|
2181
|
+
joinType: 'inner' | 'left' | 'right' | 'full';
|
|
2182
|
+
joinCondition: SQL;
|
|
2183
|
+
/** Relationship type from the join definition that produced this entry */
|
|
2184
|
+
relationship?: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany';
|
|
2185
|
+
/** Junction table information for belongsToMany relationships */
|
|
2186
|
+
junctionTable?: {
|
|
2187
|
+
table: Table;
|
|
2188
|
+
alias: string;
|
|
2189
|
+
joinType: 'inner' | 'left' | 'right' | 'full';
|
|
2190
|
+
joinCondition: SQL;
|
|
2191
|
+
/** Optional security SQL function to apply to junction table */
|
|
2192
|
+
securitySql?: (securityContext: SecurityContext) => SQL | SQL[];
|
|
2193
|
+
/** Source cube name for the belongsToMany relationship (needed for CTE rewriting) */
|
|
2194
|
+
sourceCubeName?: string;
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
|
|
1483
2198
|
/**
|
|
1484
2199
|
* Join key information for CTE joins
|
|
1485
2200
|
* Describes how a CTE should be joined to the main query
|
|
@@ -1521,6 +2236,114 @@ export declare interface JoinPathAnalysis {
|
|
|
1521
2236
|
error?: string;
|
|
1522
2237
|
/** Cubes that were visited during BFS search */
|
|
1523
2238
|
visitedCubes?: string[];
|
|
2239
|
+
/** Path selection decision and scoring details (when preferred routing is used) */
|
|
2240
|
+
selection?: {
|
|
2241
|
+
strategy: 'shortest' | 'preferred' | 'fallbackShortest';
|
|
2242
|
+
preferredCubes?: string[];
|
|
2243
|
+
selectedRank?: number;
|
|
2244
|
+
selectedScore?: number;
|
|
2245
|
+
candidates?: Array<{
|
|
2246
|
+
rank: number;
|
|
2247
|
+
score: number;
|
|
2248
|
+
usesPreferredJoin: boolean;
|
|
2249
|
+
preferredCubesInPath: number;
|
|
2250
|
+
usesProcessed: boolean;
|
|
2251
|
+
scoreBreakdown: {
|
|
2252
|
+
preferredJoinBonus: number;
|
|
2253
|
+
preferredCubeBonus: number;
|
|
2254
|
+
lengthPenalty: number;
|
|
2255
|
+
};
|
|
2256
|
+
path: JoinPathStep[];
|
|
2257
|
+
}>;
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
/**
|
|
2262
|
+
* Resolves join paths between cubes and manages connectivity caching
|
|
2263
|
+
*/
|
|
2264
|
+
export declare class JoinPathResolver {
|
|
2265
|
+
private cubes;
|
|
2266
|
+
private connectivityCache;
|
|
2267
|
+
/**
|
|
2268
|
+
* @param cubes Map of cube name to cube definition
|
|
2269
|
+
*/
|
|
2270
|
+
constructor(cubes: Map<string, Cube>);
|
|
2271
|
+
/**
|
|
2272
|
+
* Find the shortest join path from source cube to target cube
|
|
2273
|
+
* Uses BFS algorithm for optimal path discovery
|
|
2274
|
+
*
|
|
2275
|
+
* @param fromCube Source cube name
|
|
2276
|
+
* @param toCube Target cube name
|
|
2277
|
+
* @param alreadyProcessed Set of cubes to exclude from path finding
|
|
2278
|
+
* @returns Array of join steps or null if no path exists
|
|
2279
|
+
*/
|
|
2280
|
+
findPath(fromCube: string, toCube: string, alreadyProcessed?: Set<string>): InternalJoinPathStep[] | null;
|
|
2281
|
+
/**
|
|
2282
|
+
* Find path that prefers going through specified cubes when possible
|
|
2283
|
+
* Used when certain cubes have measures in the query - ensures joins go through
|
|
2284
|
+
* the semantically correct path (e.g., through junction tables when their measures are used)
|
|
2285
|
+
*
|
|
2286
|
+
* IMPORTANT: This method allows paths to go THROUGH already-processed cubes (as intermediate
|
|
2287
|
+
* steps) but won't return them as new cubes to add. This is crucial for preferring paths
|
|
2288
|
+
* through cubes that have measures (like junction tables).
|
|
2289
|
+
*
|
|
2290
|
+
* Path scoring priority (highest to lowest):
|
|
2291
|
+
* 1. Paths using joins with `preferredFor` that includes the target cube (score +10)
|
|
2292
|
+
* 2. Paths going through cubes with measures in the query (score +1 per cube)
|
|
2293
|
+
* 3. Paths reusing already-processed cubes
|
|
2294
|
+
* 4. Shorter paths
|
|
2295
|
+
*
|
|
2296
|
+
* @param fromCube Source cube name
|
|
2297
|
+
* @param toCube Target cube name
|
|
2298
|
+
* @param preferredCubes Set of cube names to prefer in the path (usually cubes with measures)
|
|
2299
|
+
* @param alreadyProcessed Set of cubes already in the join plan (can be used as intermediates)
|
|
2300
|
+
* @returns Array of join steps or null if no path exists
|
|
2301
|
+
*/
|
|
2302
|
+
findPathPreferring(fromCube: string, toCube: string, preferredCubes: Set<string>, alreadyProcessed?: Set<string>): InternalJoinPathStep[] | null;
|
|
2303
|
+
/**
|
|
2304
|
+
* Find preferred path with candidate scoring telemetry.
|
|
2305
|
+
* Used by analysis/debug panels to explain planner decisions.
|
|
2306
|
+
*/
|
|
2307
|
+
findPathPreferringDetailed(fromCube: string, toCube: string, preferredCubes: Set<string>, alreadyProcessed?: Set<string>): PreferredPathSelection;
|
|
2308
|
+
/**
|
|
2309
|
+
* Find all possible paths between two cubes (up to maxDepth)
|
|
2310
|
+
* Used by findPathPreferring to evaluate multiple paths
|
|
2311
|
+
*
|
|
2312
|
+
* @param fromCube Source cube name
|
|
2313
|
+
* @param toCube Target cube name
|
|
2314
|
+
* @param alreadyProcessed Set of cubes to exclude from path finding
|
|
2315
|
+
* @param maxDepth Maximum path length to search (default 4 to avoid explosion)
|
|
2316
|
+
* @returns Array of all valid paths
|
|
2317
|
+
*/
|
|
2318
|
+
private findAllPaths;
|
|
2319
|
+
/**
|
|
2320
|
+
* Check if a cube can reach all other cubes in the list via joins
|
|
2321
|
+
*
|
|
2322
|
+
* @param fromCube Starting cube name
|
|
2323
|
+
* @param allCubes List of all cubes that must be reachable
|
|
2324
|
+
* @returns true if all cubes are reachable
|
|
2325
|
+
*/
|
|
2326
|
+
canReachAll(fromCube: string, allCubes: string[]): boolean;
|
|
2327
|
+
/**
|
|
2328
|
+
* Build SQL join condition from join definition
|
|
2329
|
+
*
|
|
2330
|
+
* @param joinDef The cube join definition
|
|
2331
|
+
* @param sourceAlias Optional alias for source table (null uses actual column)
|
|
2332
|
+
* @param targetAlias Optional alias for target table (null uses actual column)
|
|
2333
|
+
* @returns SQL condition for the join
|
|
2334
|
+
*/
|
|
2335
|
+
buildJoinCondition(joinDef: CubeJoin, sourceAlias: string | null, targetAlias: string | null): SQL;
|
|
2336
|
+
/**
|
|
2337
|
+
* Get all reachable cubes from a starting cube
|
|
2338
|
+
* Useful for analyzing cube connectivity
|
|
2339
|
+
*
|
|
2340
|
+
* @param fromCube Starting cube name
|
|
2341
|
+
* @returns Set of all reachable cube names
|
|
2342
|
+
*/
|
|
2343
|
+
getReachableCubes(fromCube: string): Set<string>;
|
|
2344
|
+
private getCacheKey;
|
|
2345
|
+
private getFromCache;
|
|
2346
|
+
private setInCache;
|
|
1524
2347
|
}
|
|
1525
2348
|
|
|
1526
2349
|
/**
|
|
@@ -1548,41 +2371,382 @@ export declare interface JoinPathStep {
|
|
|
1548
2371
|
};
|
|
1549
2372
|
}
|
|
1550
2373
|
|
|
2374
|
+
declare interface JoinRef {
|
|
2375
|
+
/** Target cube being joined */
|
|
2376
|
+
target: CubeRef;
|
|
2377
|
+
/** Table alias in the generated SQL */
|
|
2378
|
+
alias: string;
|
|
2379
|
+
/** SQL join type */
|
|
2380
|
+
joinType: 'inner' | 'left' | 'right' | 'full';
|
|
2381
|
+
/** Drizzle SQL join condition (pre-built by the planner) */
|
|
2382
|
+
joinCondition: SQL;
|
|
2383
|
+
/** Relationship type from the join definition */
|
|
2384
|
+
relationship?: 'belongsTo' | 'hasOne' | 'hasMany' | 'belongsToMany';
|
|
2385
|
+
/** Junction table for belongsToMany relationships */
|
|
2386
|
+
junctionTable?: {
|
|
2387
|
+
table: Table;
|
|
2388
|
+
alias: string;
|
|
2389
|
+
joinType: 'inner' | 'left' | 'right' | 'full';
|
|
2390
|
+
joinCondition: SQL;
|
|
2391
|
+
securitySql?: (securityContext: SecurityContext) => SQL | SQL[];
|
|
2392
|
+
sourceCubeName?: string;
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
|
|
1551
2396
|
export declare type JoinType = 'left' | 'right' | 'inner' | 'full';
|
|
1552
2397
|
|
|
2398
|
+
/**
|
|
2399
|
+
* Keys-based deduplication for multiplied measures.
|
|
2400
|
+
*/
|
|
2401
|
+
declare interface KeysDeduplication extends LogicalNodeBase {
|
|
2402
|
+
readonly type: 'keysDeduplication';
|
|
2403
|
+
/** SELECT DISTINCT PKs + dims */
|
|
2404
|
+
keysSource: LogicalNode;
|
|
2405
|
+
/** Aggregated measures source */
|
|
2406
|
+
measureSource: LogicalNode;
|
|
2407
|
+
/** Primary key columns to join on */
|
|
2408
|
+
joinOn: ColumnRef[];
|
|
2409
|
+
/** Measure names NOT from the multiplied cube (pre-aggregated in keys CTE) */
|
|
2410
|
+
regularMeasures?: string[];
|
|
2411
|
+
}
|
|
2412
|
+
|
|
1553
2413
|
export declare interface LogicalFilter {
|
|
1554
2414
|
and?: Filter[];
|
|
1555
2415
|
or?: Filter[];
|
|
1556
2416
|
}
|
|
1557
2417
|
|
|
2418
|
+
export declare type LogicalNode = QueryNode | SimpleSource | FullKeyAggregate | CTEPreAggregate | KeysDeduplication | MultiFactMerge;
|
|
2419
|
+
|
|
2420
|
+
/** Base interface shared by all logical plan nodes */
|
|
2421
|
+
declare interface LogicalNodeBase {
|
|
2422
|
+
readonly type: LogicalNodeType;
|
|
2423
|
+
readonly schema: LogicalSchema;
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
/** Discriminated union tag for all node types */
|
|
2427
|
+
declare type LogicalNodeType = 'query' | 'simpleSource' | 'fullKeyAggregate' | 'ctePreAggregate' | 'keysDeduplication' | 'multiFactMerge';
|
|
2428
|
+
|
|
2429
|
+
export declare class LogicalPlanBuilder {
|
|
2430
|
+
private readonly queryPlanner;
|
|
2431
|
+
constructor(queryPlanner: LogicalPlanner);
|
|
2432
|
+
/**
|
|
2433
|
+
* Build a logical plan from a semantic query.
|
|
2434
|
+
*/
|
|
2435
|
+
plan(cubes: Map<string, Cube>, query: SemanticQuery, ctx: QueryContext): QueryNode;
|
|
2436
|
+
/**
|
|
2437
|
+
* Build a logical plan and an explicit decision trace for dry-run/explain.
|
|
2438
|
+
* The analysis is produced from the same phase outputs used to build the plan.
|
|
2439
|
+
*/
|
|
2440
|
+
planWithAnalysis(cubes: Map<string, Cube>, query: SemanticQuery, ctx: QueryContext): LogicalPlanWithAnalysis;
|
|
2441
|
+
private buildSourceFromPhases;
|
|
2442
|
+
private buildSimpleSourceFromPhases;
|
|
2443
|
+
/**
|
|
2444
|
+
* Detect and build multi-fact merge for star-schema style queries where:
|
|
2445
|
+
* - measures come from 2+ cubes
|
|
2446
|
+
* - all grouping dimensions/timeDimensions come from one shared dimension cube
|
|
2447
|
+
* - each measure cube directly belongsTo/hasOne that shared cube
|
|
2448
|
+
*/
|
|
2449
|
+
private tryBuildMultiFactMergeSource;
|
|
2450
|
+
private hasDirectJoinToSharedDimension;
|
|
2451
|
+
private buildGroupQueryNode;
|
|
2452
|
+
private projectFiltersToAllowedCubes;
|
|
2453
|
+
private projectFilterNodeToAllowedCubes;
|
|
2454
|
+
private classifyMeasuresForStrategy;
|
|
2455
|
+
private selectMeasureStrategy;
|
|
2456
|
+
/**
|
|
2457
|
+
* Initial execution scope for keys deduplication:
|
|
2458
|
+
* - exactly one multiplied cube
|
|
2459
|
+
* - all query measures belong to that cube
|
|
2460
|
+
* - only additive measure types currently handled by the physical keys path
|
|
2461
|
+
* - no measure filters (HAVING) in the query
|
|
2462
|
+
*/
|
|
2463
|
+
private isKeysDeduplicationExecutionSupported;
|
|
2464
|
+
private queryHasMeasureFilter;
|
|
2465
|
+
private buildKeysDeduplicationSource;
|
|
2466
|
+
private isDeduplicationSafeMeasure;
|
|
2467
|
+
private getPrimaryKeyColumns;
|
|
2468
|
+
private deduplicateColumnRefs;
|
|
2469
|
+
private buildQueryNode;
|
|
2470
|
+
private buildPreAggregationAnalysis;
|
|
2471
|
+
private buildMeasureRefs;
|
|
2472
|
+
private buildDimensionRefs;
|
|
2473
|
+
private buildTimeDimensionRefs;
|
|
2474
|
+
private buildOrderByRefs;
|
|
2475
|
+
private toCubeRef;
|
|
2476
|
+
private buildCTESchema;
|
|
2477
|
+
}
|
|
2478
|
+
|
|
1558
2479
|
/**
|
|
1559
|
-
*
|
|
2480
|
+
* Pre-aggregation plan for handling hasMany relationships
|
|
1560
2481
|
*/
|
|
1561
|
-
declare
|
|
1562
|
-
|
|
1563
|
-
title?: string;
|
|
1564
|
-
description?: string;
|
|
2482
|
+
export declare class LogicalPlanner {
|
|
2483
|
+
private resolverCache;
|
|
1565
2484
|
/**
|
|
1566
|
-
*
|
|
1567
|
-
* Used by AI agents for natural language matching
|
|
1568
|
-
* @example ['revenue', 'sales', 'income'] for a totalRevenue measure
|
|
2485
|
+
* Get or create a JoinPathResolver for the given cubes map
|
|
1569
2486
|
*/
|
|
1570
|
-
|
|
1571
|
-
type: MeasureType;
|
|
2487
|
+
private getResolver;
|
|
1572
2488
|
/**
|
|
1573
|
-
*
|
|
1574
|
-
* Optional for calculated measures (type: 'calculated') which use calculatedSql instead
|
|
2489
|
+
* Analyze a semantic query to determine which cubes are involved
|
|
1575
2490
|
*/
|
|
1576
|
-
|
|
1577
|
-
/**
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
/**
|
|
1584
|
-
|
|
1585
|
-
|
|
2491
|
+
analyzeCubeUsage(query: SemanticQuery): Set<string>;
|
|
2492
|
+
/**
|
|
2493
|
+
* Build query-level path hints (Cube-style) from all query members:
|
|
2494
|
+
* measures, dimensions, time dimensions, filters, and order-by.
|
|
2495
|
+
* These hints guide path selection toward the semantic query grain.
|
|
2496
|
+
*/
|
|
2497
|
+
private collectPathHintCubes;
|
|
2498
|
+
/**
|
|
2499
|
+
* Recursively extract cube names from filters (handles logical filters)
|
|
2500
|
+
*/
|
|
2501
|
+
private extractCubeNamesFromFilter;
|
|
2502
|
+
/**
|
|
2503
|
+
* Extract measures referenced in filters (for CTE inclusion)
|
|
2504
|
+
*/
|
|
2505
|
+
private extractMeasuresFromFilters;
|
|
2506
|
+
/**
|
|
2507
|
+
* Recursively extract measures from filters for a specific cube
|
|
2508
|
+
* Only includes filter members that are actually measures (not dimensions)
|
|
2509
|
+
*/
|
|
2510
|
+
private extractMeasuresFromFilter;
|
|
2511
|
+
/**
|
|
2512
|
+
* Choose the primary cube based on query analysis
|
|
2513
|
+
* Uses a consistent strategy to avoid measure order dependencies
|
|
2514
|
+
*
|
|
2515
|
+
* Delegates to analyzePrimaryCubeSelection() for the actual logic,
|
|
2516
|
+
* ensuring a single source of truth for primary cube selection.
|
|
2517
|
+
*/
|
|
2518
|
+
choosePrimaryCube(cubeNames: string[], query: SemanticQuery, cubes?: Map<string, Cube>): string;
|
|
2519
|
+
/**
|
|
2520
|
+
* Analyze primary cube selection with candidate details.
|
|
2521
|
+
* Exposed for LogicalPlanBuilder so dry-run/analyze can report
|
|
2522
|
+
* exactly which selection rule was used.
|
|
2523
|
+
*/
|
|
2524
|
+
analyzePrimaryCube(cubeNames: string[], query: SemanticQuery, cubes: Map<string, Cube>): PrimaryCubeAnalysis;
|
|
2525
|
+
/**
|
|
2526
|
+
* Analyze join path for a specific target cube.
|
|
2527
|
+
* Exposed for LogicalPlanBuilder to provide join decision trace.
|
|
2528
|
+
*/
|
|
2529
|
+
analyzeJoinPathForTarget(cubes: Map<string, Cube>, fromCube: string, toCube: string, query?: SemanticQuery): JoinPathAnalysis;
|
|
2530
|
+
/**
|
|
2531
|
+
* Build join plan for a known primary cube.
|
|
2532
|
+
* Exposed for LogicalPlanBuilder so logical planning can compose
|
|
2533
|
+
* planner phases directly.
|
|
2534
|
+
*/
|
|
2535
|
+
buildJoinPlanForPrimary(cubes: Map<string, Cube>, primaryCube: Cube, cubeNames: string[], ctx: QueryContext, query: SemanticQuery): PhysicalQueryPlan['joinCubes'];
|
|
2536
|
+
/**
|
|
2537
|
+
* Build pre-aggregation CTE plan from a primary cube and join plan.
|
|
2538
|
+
* Exposed for LogicalPlanBuilder phase composition.
|
|
2539
|
+
*/
|
|
2540
|
+
buildPreAggregationCTEs(cubes: Map<string, Cube>, primaryCube: Cube, joinCubes: PhysicalQueryPlan['joinCubes'], query: SemanticQuery, ctx: QueryContext): PhysicalQueryPlan['preAggregationCTEs'];
|
|
2541
|
+
/**
|
|
2542
|
+
* Generate query warnings from pre-aggregation analysis.
|
|
2543
|
+
* Exposed for LogicalPlanBuilder phase composition.
|
|
2544
|
+
*/
|
|
2545
|
+
buildWarnings(query: SemanticQuery, preAggregationCTEs?: PhysicalQueryPlan['preAggregationCTEs']): QueryWarning[];
|
|
2546
|
+
/**
|
|
2547
|
+
* Build join plan for multi-cube query
|
|
2548
|
+
* Supports both direct joins and transitive joins through intermediate cubes
|
|
2549
|
+
*
|
|
2550
|
+
* Uses query-aware path selection to prefer joining through cubes that have
|
|
2551
|
+
* measures in the query (e.g., joining Teams through EmployeeTeams when
|
|
2552
|
+
* EmployeeTeams.count is a measure)
|
|
2553
|
+
*/
|
|
2554
|
+
private buildJoinPlan;
|
|
2555
|
+
/**
|
|
2556
|
+
* Plan pre-aggregation CTEs for hasMany relationships to prevent fan-out
|
|
2557
|
+
* Note: belongsToMany relationships handle fan-out differently through their junction table structure
|
|
2558
|
+
* and don't require CTEs - the two-hop join with the junction table provides natural grouping
|
|
2559
|
+
*
|
|
2560
|
+
* CRITICAL FAN-OUT PREVENTION LOGIC:
|
|
2561
|
+
* When a query contains ANY hasMany relationship in the join graph, ALL cubes with measures
|
|
2562
|
+
* that could be affected by row multiplication need CTEs. This includes:
|
|
2563
|
+
*
|
|
2564
|
+
* 1. Cubes with direct hasMany FROM primary (existing logic)
|
|
2565
|
+
* 2. Cubes with measures that would be multiplied due to hasMany elsewhere in the query
|
|
2566
|
+
* - Example: Query has Departments.totalBudget + Productivity.recordCount
|
|
2567
|
+
* - Employees hasMany → Productivity causes row multiplication
|
|
2568
|
+
* - Departments.totalBudget would be inflated without CTE pre-aggregation
|
|
2569
|
+
*/
|
|
2570
|
+
private planPreAggregationCTEs;
|
|
2571
|
+
/**
|
|
2572
|
+
* Find join information TO a cube (reverse lookup)
|
|
2573
|
+
* Used when the primary cube needs a CTE and we need to find how other cubes join to it
|
|
2574
|
+
*/
|
|
2575
|
+
private findJoinInfoToCube;
|
|
2576
|
+
/**
|
|
2577
|
+
* Analyze the join path from primary cube to a target CTE cube.
|
|
2578
|
+
* Detects if there are intermediate hasMany relationships that would cause fan-out.
|
|
2579
|
+
*
|
|
2580
|
+
* Returns information about:
|
|
2581
|
+
* - The full join path
|
|
2582
|
+
* - Whether there are hasMany relationships ON the path (not just at the end)
|
|
2583
|
+
* - Which intermediate tables need to be absorbed into the CTE
|
|
2584
|
+
* - The correct join key to use (from primary cube's connection point)
|
|
2585
|
+
*
|
|
2586
|
+
* @param cubes Map of all registered cubes
|
|
2587
|
+
* @param primaryCube The primary cube (FROM clause)
|
|
2588
|
+
* @param targetCubeName The CTE cube we're analyzing the path to
|
|
2589
|
+
* @param ctx Query context for security filtering
|
|
2590
|
+
*/
|
|
2591
|
+
private analyzeJoinPathToPrimary;
|
|
2592
|
+
/**
|
|
2593
|
+
* Compute CTE reasons from the actual join plan entries.
|
|
2594
|
+
*
|
|
2595
|
+
* Instead of scanning all registered cubes (which causes false positives when
|
|
2596
|
+
* unrelated hasMany relationships exist), this walks only the planned joins
|
|
2597
|
+
* using the `relationship` field now stored on each JoinCubePlanEntry.
|
|
2598
|
+
*
|
|
2599
|
+
* Algorithm:
|
|
2600
|
+
* 1. Scan join plan entries for hasMany/belongsToMany relationships
|
|
2601
|
+
* 2. If none found → return empty map (no CTEs needed)
|
|
2602
|
+
* 3. hasMany/belongsToMany targets with measures → 'hasMany'
|
|
2603
|
+
* 4. Other join cubes with measures (not hasMany source) → 'fanOutPrevention'
|
|
2604
|
+
*/
|
|
2605
|
+
private computeCTEReasons;
|
|
2606
|
+
/**
|
|
2607
|
+
* Find join information for a cube from any cube in the query
|
|
2608
|
+
* This extends findHasManyJoinDef to work with any relationship type
|
|
2609
|
+
* and to search from any source cube, not just the primary
|
|
2610
|
+
*/
|
|
2611
|
+
private findJoinInfoForCube;
|
|
2612
|
+
/**
|
|
2613
|
+
* Find downstream cubes that need join keys included in the CTE.
|
|
2614
|
+
*
|
|
2615
|
+
* When a query has dimensions from a cube (e.g., Teams.name) and measures from
|
|
2616
|
+
* a junction cube (e.g., EmployeeTeams.count), the junction CTE needs to include
|
|
2617
|
+
* the join key to the dimension cube (team_id) so the dimension cube can be
|
|
2618
|
+
* joined through the CTE instead of via an alternative path.
|
|
2619
|
+
*
|
|
2620
|
+
* @param cteCube The cube being converted to a CTE (e.g., EmployeeTeams)
|
|
2621
|
+
* @param query The semantic query with dimensions and measures
|
|
2622
|
+
* @param allCubes Map of all registered cubes
|
|
2623
|
+
* @returns Array of downstream join key info for cubes needing join through this CTE
|
|
2624
|
+
*/
|
|
2625
|
+
private findDownstreamJoinKeys;
|
|
2626
|
+
/**
|
|
2627
|
+
* Expand calculated measures to include their dependencies
|
|
2628
|
+
*/
|
|
2629
|
+
private expandCalculatedMeasureDependencies;
|
|
2630
|
+
/**
|
|
2631
|
+
* Extract measure references from calculatedSql template
|
|
2632
|
+
*/
|
|
2633
|
+
private extractDependenciesFromTemplate;
|
|
2634
|
+
/**
|
|
2635
|
+
* Find hasMany join definition from primary cube to target cube
|
|
2636
|
+
*/
|
|
2637
|
+
private findHasManyJoinDef;
|
|
2638
|
+
/**
|
|
2639
|
+
* Find filters that need to propagate from related cubes to a CTE cube.
|
|
2640
|
+
* When cube A has filters and a hasMany relationship to cube B (the CTE cube),
|
|
2641
|
+
* A's filters should propagate into B's CTE via a subquery.
|
|
2642
|
+
*
|
|
2643
|
+
* Example: Employees.createdAt filter should propagate to Productivity CTE
|
|
2644
|
+
* via: employee_id IN (SELECT id FROM employees WHERE created_at >= $date)
|
|
2645
|
+
*/
|
|
2646
|
+
private findPropagatingFilters;
|
|
2647
|
+
/**
|
|
2648
|
+
* Extract cube names from filters into a Set (helper for findPropagatingFilters)
|
|
2649
|
+
*/
|
|
2650
|
+
private extractFilterCubeNamesToSet;
|
|
2651
|
+
/**
|
|
2652
|
+
* Extract filters for a specific cube from the filter array
|
|
2653
|
+
*
|
|
2654
|
+
* Logic for preserving filter semantics:
|
|
2655
|
+
* - AND: Safe to extract only matching branches (AND of fewer conditions is more permissive)
|
|
2656
|
+
* - OR: Must include ALL branches or skip entirely (partial OR changes semantics)
|
|
2657
|
+
* If any branch belongs to another cube, skip the entire OR to be safe
|
|
2658
|
+
* since we can't evaluate the other cube's conditions
|
|
2659
|
+
*/
|
|
2660
|
+
private extractFiltersForCube;
|
|
2661
|
+
/**
|
|
2662
|
+
* Check if all simple filters in a filter array belong to the specified cube
|
|
2663
|
+
* Recursively checks nested AND/OR filters
|
|
2664
|
+
*/
|
|
2665
|
+
private allFiltersFromCube;
|
|
2666
|
+
/**
|
|
2667
|
+
* Extract time dimension date range filters as regular filters for a specific cube
|
|
2668
|
+
*/
|
|
2669
|
+
private extractTimeDimensionFiltersForCube;
|
|
2670
|
+
/**
|
|
2671
|
+
* Analyze why a particular cube was chosen as primary
|
|
2672
|
+
*/
|
|
2673
|
+
private analyzePrimaryCubeSelection;
|
|
2674
|
+
/**
|
|
2675
|
+
* Analyze the join path between two cubes with detailed step information
|
|
2676
|
+
*
|
|
2677
|
+
* Uses JoinPathResolver.findPath() for the actual path finding,
|
|
2678
|
+
* then converts the result to human-readable analysis format.
|
|
2679
|
+
*/
|
|
2680
|
+
private analyzeJoinPath;
|
|
2681
|
+
private convertInternalPathToJoinPathSteps;
|
|
2682
|
+
private buildJoinPathSelectionAnalysis;
|
|
2683
|
+
private mapPreferredCandidate;
|
|
2684
|
+
/**
|
|
2685
|
+
* Generate warnings for query edge cases that users should be aware of.
|
|
2686
|
+
* Currently detects:
|
|
2687
|
+
* - FAN_OUT_NO_DIMENSIONS: Query has hasMany CTEs but no dimensions to group by
|
|
2688
|
+
*
|
|
2689
|
+
* Note: AVG measures in hasMany CTEs can produce mathematically imprecise results
|
|
2690
|
+
* (average of averages vs weighted average), but this warning was removed as it
|
|
2691
|
+
* fired too aggressively. The issue only occurs when the outer grouping is coarser
|
|
2692
|
+
* than the CTE grouping, which is rare in practice. The limitation is documented
|
|
2693
|
+
* in executor.ts comments.
|
|
2694
|
+
*/
|
|
2695
|
+
private generateWarnings;
|
|
2696
|
+
/**
|
|
2697
|
+
* Detect when a query has measures from multiple cubes with hasMany relationships
|
|
2698
|
+
* but no dimensions to provide grouping context.
|
|
2699
|
+
*
|
|
2700
|
+
* This is an edge case where:
|
|
2701
|
+
* - Query has measures from 2+ cubes
|
|
2702
|
+
* - At least one CTE exists (indicating hasMany relationship)
|
|
2703
|
+
* - Query has NO dimensions AND NO time dimensions with granularity
|
|
2704
|
+
*
|
|
2705
|
+
* The SQL is technically correct (CTEs with GROUP BY on join keys), but users
|
|
2706
|
+
* may be confused by the aggregated results without visible grouping.
|
|
2707
|
+
*/
|
|
2708
|
+
private checkFanOutNoDimensions;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
export declare interface LogicalPlanWithAnalysis {
|
|
2712
|
+
plan: QueryNode;
|
|
2713
|
+
analysis: QueryAnalysis;
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
declare interface LogicalSchema {
|
|
2717
|
+
measures: MeasureRef[];
|
|
2718
|
+
dimensions: DimensionRef[];
|
|
2719
|
+
timeDimensions: TimeDimensionRef[];
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
/**
|
|
2723
|
+
* Measure definition
|
|
2724
|
+
*/
|
|
2725
|
+
export declare interface Measure {
|
|
2726
|
+
name: string;
|
|
2727
|
+
title?: string;
|
|
2728
|
+
description?: string;
|
|
2729
|
+
/**
|
|
2730
|
+
* Alternative names for this measure
|
|
2731
|
+
* Used by AI agents for natural language matching
|
|
2732
|
+
* @example ['revenue', 'sales', 'income'] for a totalRevenue measure
|
|
2733
|
+
*/
|
|
2734
|
+
synonyms?: string[];
|
|
2735
|
+
type: MeasureType;
|
|
2736
|
+
/**
|
|
2737
|
+
* Column to aggregate or SQL expression
|
|
2738
|
+
* Optional for calculated measures (type: 'calculated') which use calculatedSql instead
|
|
2739
|
+
*/
|
|
2740
|
+
sql?: AnyColumn | SQL | ((ctx: QueryContext) => AnyColumn | SQL);
|
|
2741
|
+
/** Display format */
|
|
2742
|
+
format?: string;
|
|
2743
|
+
/** Whether to show in UI */
|
|
2744
|
+
shown?: boolean;
|
|
2745
|
+
/** Filters applied to this measure */
|
|
2746
|
+
filters?: Array<(ctx: QueryContext) => SQL>;
|
|
2747
|
+
/** Rolling window configuration */
|
|
2748
|
+
rollingWindow?: {
|
|
2749
|
+
trailing?: string;
|
|
1586
2750
|
leading?: string;
|
|
1587
2751
|
offset?: string;
|
|
1588
2752
|
};
|
|
@@ -1677,8 +2841,6 @@ declare interface Measure {
|
|
|
1677
2841
|
};
|
|
1678
2842
|
};
|
|
1679
2843
|
}
|
|
1680
|
-
export { Measure }
|
|
1681
|
-
export { Measure as SemanticMeasure }
|
|
1682
2844
|
|
|
1683
2845
|
/**
|
|
1684
2846
|
* Annotation interfaces for UI metadata
|
|
@@ -1690,6 +2852,18 @@ export declare interface MeasureAnnotation {
|
|
|
1690
2852
|
format?: MeasureFormat;
|
|
1691
2853
|
}
|
|
1692
2854
|
|
|
2855
|
+
/**
|
|
2856
|
+
* Dependency information for a calculated measure
|
|
2857
|
+
*/
|
|
2858
|
+
declare interface MeasureDependency {
|
|
2859
|
+
/** Full measure name (e.g., "Cube.measure" or "measure") */
|
|
2860
|
+
measureName: string;
|
|
2861
|
+
/** Referenced cube name (null if same cube) */
|
|
2862
|
+
cubeName: string | null;
|
|
2863
|
+
/** Referenced field name */
|
|
2864
|
+
fieldName: string;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
1693
2867
|
export declare type MeasureFormat = 'currency' | 'percent' | 'number' | 'integer';
|
|
1694
2868
|
|
|
1695
2869
|
/**
|
|
@@ -1715,6 +2889,16 @@ export declare interface MeasureMetadata {
|
|
|
1715
2889
|
drillMembers?: string[];
|
|
1716
2890
|
}
|
|
1717
2891
|
|
|
2892
|
+
/** Reference to a measure on a specific cube */
|
|
2893
|
+
export declare interface MeasureRef {
|
|
2894
|
+
/** Fully qualified name: CubeName.measureName */
|
|
2895
|
+
name: string;
|
|
2896
|
+
/** Cube that owns this measure */
|
|
2897
|
+
cube: CubeRef;
|
|
2898
|
+
/** Local measure name (without cube prefix) */
|
|
2899
|
+
localName: string;
|
|
2900
|
+
}
|
|
2901
|
+
|
|
1718
2902
|
/**
|
|
1719
2903
|
* Type enums and constants
|
|
1720
2904
|
*/
|
|
@@ -1836,6 +3020,20 @@ export declare interface MultiCubeQueryContext extends QueryContext {
|
|
|
1836
3020
|
currentCube: Cube;
|
|
1837
3021
|
}
|
|
1838
3022
|
|
|
3023
|
+
/**
|
|
3024
|
+
* Multi-fact merge: combines independent fact subqueries
|
|
3025
|
+
* that share dimensions.
|
|
3026
|
+
*/
|
|
3027
|
+
declare interface MultiFactMerge extends LogicalNodeBase {
|
|
3028
|
+
readonly type: 'multiFactMerge';
|
|
3029
|
+
/** Independent fact subqueries */
|
|
3030
|
+
groups: LogicalNode[];
|
|
3031
|
+
/** Shared dimensions for the merge */
|
|
3032
|
+
sharedDimensions: DimensionRef[];
|
|
3033
|
+
/** How to combine the groups */
|
|
3034
|
+
mergeStrategy: 'fullJoin' | 'leftJoin' | 'innerJoin';
|
|
3035
|
+
}
|
|
3036
|
+
|
|
1839
3037
|
export declare type MySQLDatabase = MySql2Database<any>;
|
|
1840
3038
|
|
|
1841
3039
|
export declare class MySQLExecutor extends BaseDatabaseExecutor {
|
|
@@ -1859,6 +3057,20 @@ export declare class MySQLExecutor extends BaseDatabaseExecutor {
|
|
|
1859
3057
|
getTableIndexes(tableNames: string[]): Promise<IndexInfo[]>;
|
|
1860
3058
|
}
|
|
1861
3059
|
|
|
3060
|
+
/**
|
|
3061
|
+
* Normalized period range with start/end dates and metadata
|
|
3062
|
+
*/
|
|
3063
|
+
declare interface NormalizedPeriod {
|
|
3064
|
+
/** Start date of the period */
|
|
3065
|
+
start: Date;
|
|
3066
|
+
/** End date of the period */
|
|
3067
|
+
end: Date;
|
|
3068
|
+
/** Human-readable label for the period */
|
|
3069
|
+
label: string;
|
|
3070
|
+
/** Index in the comparison (0 = first/current, 1 = second/prior, etc.) */
|
|
3071
|
+
index: number;
|
|
3072
|
+
}
|
|
3073
|
+
|
|
1862
3074
|
/**
|
|
1863
3075
|
* Normalize query for consistent hashing
|
|
1864
3076
|
* Sorts arrays and object keys to ensure same query = same hash
|
|
@@ -1868,6 +3080,31 @@ export declare class MySQLExecutor extends BaseDatabaseExecutor {
|
|
|
1868
3080
|
*/
|
|
1869
3081
|
export declare function normalizeQuery(query: SemanticQuery): SemanticQuery;
|
|
1870
3082
|
|
|
3083
|
+
/** Context available to optimiser passes */
|
|
3084
|
+
export declare interface OptimiserContext {
|
|
3085
|
+
/** Database engine type for engine-specific rewrites */
|
|
3086
|
+
engineType: 'postgres' | 'mysql' | 'sqlite' | 'duckdb';
|
|
3087
|
+
}
|
|
3088
|
+
|
|
3089
|
+
/**
|
|
3090
|
+
* Runs a sequence of optimiser passes in order.
|
|
3091
|
+
* Short-circuits if a pass returns a different reference (for future use).
|
|
3092
|
+
*/
|
|
3093
|
+
export declare class OptimiserPipeline implements PlanOptimiser {
|
|
3094
|
+
readonly name = "pipeline";
|
|
3095
|
+
private passes;
|
|
3096
|
+
constructor(passes?: PlanOptimiser[]);
|
|
3097
|
+
optimise(plan: LogicalNode, context: OptimiserContext): LogicalNode;
|
|
3098
|
+
addPass(pass: PlanOptimiser): void;
|
|
3099
|
+
}
|
|
3100
|
+
|
|
3101
|
+
/** Order-by specification */
|
|
3102
|
+
declare interface OrderByRef {
|
|
3103
|
+
/** Fully qualified field name */
|
|
3104
|
+
name: string;
|
|
3105
|
+
direction: 'asc' | 'desc';
|
|
3106
|
+
}
|
|
3107
|
+
|
|
1871
3108
|
/**
|
|
1872
3109
|
* Period comparison metadata for compareDateRange queries
|
|
1873
3110
|
* Provides information about the periods being compared
|
|
@@ -1884,20 +3121,86 @@ export declare interface PeriodComparisonMetadata {
|
|
|
1884
3121
|
}
|
|
1885
3122
|
|
|
1886
3123
|
/**
|
|
1887
|
-
*
|
|
3124
|
+
* Runtime physical-plan context used by query execution builders.
|
|
3125
|
+
* This is the only runtime plan context consumed by SQL builders.
|
|
1888
3126
|
*/
|
|
1889
|
-
export declare
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
3127
|
+
export declare interface PhysicalQueryPlan {
|
|
3128
|
+
/** Primary cube that drives the query */
|
|
3129
|
+
primaryCube: Cube;
|
|
3130
|
+
/** Additional cubes to join (empty for single-cube queries) */
|
|
3131
|
+
joinCubes: JoinCubePlanEntry[];
|
|
3132
|
+
/** Pre-aggregation CTEs for hasMany relationships to prevent fan-out */
|
|
3133
|
+
preAggregationCTEs?: PreAggregationCTEInfo[];
|
|
1893
3134
|
/**
|
|
1894
|
-
*
|
|
3135
|
+
* Optional keys-based deduplication metadata.
|
|
3136
|
+
* When present, physical builders may choose a keys+aggregate execution path.
|
|
1895
3137
|
*/
|
|
1896
|
-
|
|
3138
|
+
keysDeduplication?: {
|
|
3139
|
+
multipliedCubeName: string;
|
|
3140
|
+
primaryKeyDimensions: string[];
|
|
3141
|
+
/** Measure names NOT from the multiplied cube (pre-aggregated in keys CTE) */
|
|
3142
|
+
regularMeasures?: string[];
|
|
3143
|
+
};
|
|
1897
3144
|
/**
|
|
1898
|
-
*
|
|
3145
|
+
* Optional multi-fact merge metadata.
|
|
3146
|
+
* When present, SQL builders execute independent grouped subqueries and
|
|
3147
|
+
* merge them by shared dimension keys.
|
|
1899
3148
|
*/
|
|
1900
|
-
|
|
3149
|
+
multiFactMerge?: {
|
|
3150
|
+
mergeStrategy: 'fullJoin' | 'leftJoin' | 'innerJoin';
|
|
3151
|
+
sharedDimensions: string[];
|
|
3152
|
+
groups: Array<{
|
|
3153
|
+
alias: string;
|
|
3154
|
+
query: SemanticQuery;
|
|
3155
|
+
queryPlan: PhysicalQueryPlan;
|
|
3156
|
+
measures: string[];
|
|
3157
|
+
}>;
|
|
3158
|
+
};
|
|
3159
|
+
/** Warnings about potential query issues (e.g., fan-out without dimensions) */
|
|
3160
|
+
warnings?: QueryWarning[];
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
export declare interface PlanningTrace {
|
|
3164
|
+
/** Ordered decision trace */
|
|
3165
|
+
steps: PlanningTraceStep[];
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
/**
|
|
3169
|
+
* Structured planner trace for explain/dry-run UIs.
|
|
3170
|
+
* Captures key decisions made during logical planning.
|
|
3171
|
+
*/
|
|
3172
|
+
export declare interface PlanningTraceStep {
|
|
3173
|
+
/** Pipeline phase name */
|
|
3174
|
+
phase: 'cube_usage' | 'primary_cube_selection' | 'join_planning' | 'cte_planning' | 'measure_strategy' | 'warnings';
|
|
3175
|
+
/** Short summary of what was decided */
|
|
3176
|
+
decision: string;
|
|
3177
|
+
/** Optional machine-readable details */
|
|
3178
|
+
details?: Record<string, unknown>;
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
/** A single optimiser pass that rewrites a logical plan */
|
|
3182
|
+
export declare interface PlanOptimiser {
|
|
3183
|
+
/** Human-readable name for debugging / explain output */
|
|
3184
|
+
readonly name: string;
|
|
3185
|
+
/** Rewrite the plan tree. Must return a valid plan (may return the same reference). */
|
|
3186
|
+
optimise(plan: LogicalNode, context: OptimiserContext): LogicalNode;
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
/**
|
|
3190
|
+
* Type helpers for specific database types
|
|
3191
|
+
*/
|
|
3192
|
+
export declare type PostgresDatabase = PostgresJsDatabase<any>;
|
|
3193
|
+
|
|
3194
|
+
export declare class PostgresExecutor extends BaseDatabaseExecutor {
|
|
3195
|
+
execute<T = any[]>(query: SQL | any, numericFields?: string[]): Promise<T>;
|
|
3196
|
+
/**
|
|
3197
|
+
* Convert numeric string fields to numbers (only for measure fields)
|
|
3198
|
+
*/
|
|
3199
|
+
private convertNumericFields;
|
|
3200
|
+
/**
|
|
3201
|
+
* Coerce a value to a number if it represents a numeric type
|
|
3202
|
+
*/
|
|
3203
|
+
private coerceToNumber;
|
|
1901
3204
|
getEngineType(): 'postgres';
|
|
1902
3205
|
/**
|
|
1903
3206
|
* Execute EXPLAIN on a SQL query to get the execution plan
|
|
@@ -1996,6 +3299,38 @@ export declare interface PreAggregationCTEInfo {
|
|
|
1996
3299
|
cteReason?: 'hasMany' | 'fanOutPrevention';
|
|
1997
3300
|
}
|
|
1998
3301
|
|
|
3302
|
+
/**
|
|
3303
|
+
* A scored candidate path considered by preferred-path selection.
|
|
3304
|
+
*/
|
|
3305
|
+
declare interface PreferredPathCandidateScore {
|
|
3306
|
+
path: InternalJoinPathStep[];
|
|
3307
|
+
score: number;
|
|
3308
|
+
usesPreferredJoin: boolean;
|
|
3309
|
+
preferredCubesInPath: number;
|
|
3310
|
+
usesProcessed: boolean;
|
|
3311
|
+
scoreBreakdown: PreferredPathScoreBreakdown;
|
|
3312
|
+
}
|
|
3313
|
+
|
|
3314
|
+
/**
|
|
3315
|
+
* Score breakdown for a candidate preferred path.
|
|
3316
|
+
*/
|
|
3317
|
+
declare interface PreferredPathScoreBreakdown {
|
|
3318
|
+
preferredJoinBonus: number;
|
|
3319
|
+
preferredCubeBonus: number;
|
|
3320
|
+
lengthPenalty: number;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
/**
|
|
3324
|
+
* Detailed preferred-path selection output for analysis/debug UIs.
|
|
3325
|
+
*/
|
|
3326
|
+
declare interface PreferredPathSelection {
|
|
3327
|
+
strategy: 'preferred' | 'fallbackShortest';
|
|
3328
|
+
preferredCubes: string[];
|
|
3329
|
+
selectedIndex: number;
|
|
3330
|
+
candidates: PreferredPathCandidateScore[];
|
|
3331
|
+
selectedPath: InternalJoinPathStep[] | null;
|
|
3332
|
+
}
|
|
3333
|
+
|
|
1999
3334
|
/**
|
|
2000
3335
|
* Primary cube selection analysis
|
|
2001
3336
|
*/
|
|
@@ -2151,110 +3486,8 @@ export declare interface QueryAnalysis {
|
|
|
2151
3486
|
querySummary: QuerySummary;
|
|
2152
3487
|
/** Warnings or potential issues */
|
|
2153
3488
|
warnings?: string[];
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
export declare class QueryBuilder {
|
|
2157
|
-
private dateTimeBuilder;
|
|
2158
|
-
private filterBuilder;
|
|
2159
|
-
private groupByBuilder;
|
|
2160
|
-
private measureBuilder;
|
|
2161
|
-
constructor(databaseAdapter: DatabaseAdapter);
|
|
2162
|
-
/**
|
|
2163
|
-
* Build resolvedMeasures map for a set of measures
|
|
2164
|
-
* Delegates to MeasureBuilder
|
|
2165
|
-
*/
|
|
2166
|
-
buildResolvedMeasures(measureNames: string[], cubeMap: Map<string, Cube>, context: QueryContext, customMeasureBuilder?: (measureName: string, measure: any, cube: Cube) => SQL): ResolvedMeasures;
|
|
2167
|
-
/**
|
|
2168
|
-
* Build dynamic selections for measures, dimensions, and time dimensions
|
|
2169
|
-
* Works for both single and multi-cube queries
|
|
2170
|
-
* Handles calculated measures with dependency resolution
|
|
2171
|
-
*/
|
|
2172
|
-
buildSelections(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext): Record<string, SQL | AnyColumn>;
|
|
2173
|
-
/**
|
|
2174
|
-
* Build calculated measure expression by substituting {member} references
|
|
2175
|
-
* Delegates to MeasureBuilder
|
|
2176
|
-
*/
|
|
2177
|
-
buildCalculatedMeasure(measure: any, cube: Cube, allCubes: Map<string, Cube>, resolvedMeasures: ResolvedMeasures, context: QueryContext): SQL;
|
|
2178
|
-
/**
|
|
2179
|
-
* Build resolved measures map for a calculated measure from CTE columns
|
|
2180
|
-
* Delegates to MeasureBuilder
|
|
2181
|
-
*/
|
|
2182
|
-
buildCTECalculatedMeasure(measure: any, cube: Cube, cteInfo: {
|
|
2183
|
-
cteAlias: string;
|
|
2184
|
-
measures: string[];
|
|
2185
|
-
cube: Cube;
|
|
2186
|
-
}, allCubes: Map<string, Cube>, context: QueryContext): SQL;
|
|
2187
|
-
/**
|
|
2188
|
-
* Build measure expression for HAVING clause, handling CTE references correctly
|
|
2189
|
-
* Delegates to MeasureBuilder
|
|
2190
|
-
*/
|
|
2191
|
-
private buildHavingMeasureExpression;
|
|
2192
|
-
/**
|
|
2193
|
-
* Build measure expression with aggregation and filters
|
|
2194
|
-
* Delegates to MeasureBuilder
|
|
2195
|
-
*/
|
|
2196
|
-
buildMeasureExpression(measure: any, context: QueryContext, cube?: Cube): SQL;
|
|
2197
|
-
/**
|
|
2198
|
-
* Build time dimension expression with granularity using database adapter
|
|
2199
|
-
* Delegates to DateTimeBuilder
|
|
2200
|
-
*/
|
|
2201
|
-
buildTimeDimensionExpression(dimensionSql: any, granularity: string | undefined, context: QueryContext): SQL;
|
|
2202
|
-
/**
|
|
2203
|
-
* Build WHERE conditions from semantic query filters (dimensions only)
|
|
2204
|
-
* Works for both single and multi-cube queries
|
|
2205
|
-
* @param preBuiltFilters - Optional map of cube name to pre-built filter SQL for parameter deduplication
|
|
2206
|
-
*/
|
|
2207
|
-
buildWhereConditions(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: QueryPlan, preBuiltFilters?: Map<string, SQL[]>): SQL[];
|
|
2208
|
-
/**
|
|
2209
|
-
* Build HAVING conditions from semantic query filters (measures only)
|
|
2210
|
-
* Works for both single and multi-cube queries
|
|
2211
|
-
*/
|
|
2212
|
-
buildHavingConditions(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: QueryPlan): SQL[];
|
|
2213
|
-
/**
|
|
2214
|
-
* Process a single filter (basic or logical)
|
|
2215
|
-
* @param filterType - 'where' for dimension filters, 'having' for measure filters
|
|
2216
|
-
*/
|
|
2217
|
-
private processFilter;
|
|
2218
|
-
/**
|
|
2219
|
-
* Build filter condition using Drizzle operators
|
|
2220
|
-
* Delegates to FilterBuilder
|
|
2221
|
-
*/
|
|
2222
|
-
private buildFilterCondition;
|
|
2223
|
-
/**
|
|
2224
|
-
* Build date range condition for time dimensions
|
|
2225
|
-
* Delegates to DateTimeBuilder
|
|
2226
|
-
*/
|
|
2227
|
-
buildDateRangeCondition(fieldExpr: AnyColumn | SQL, dateRange: string | string[]): SQL | null;
|
|
2228
|
-
/**
|
|
2229
|
-
* Build GROUP BY fields from dimensions and time dimensions
|
|
2230
|
-
* Delegates to GroupByBuilder
|
|
2231
|
-
*/
|
|
2232
|
-
buildGroupByFields(cubes: Map<string, Cube> | Cube, query: SemanticQuery, context: QueryContext, queryPlan?: any): (SQL | AnyColumn)[];
|
|
2233
|
-
/**
|
|
2234
|
-
* Build ORDER BY clause with automatic time dimension sorting
|
|
2235
|
-
*/
|
|
2236
|
-
buildOrderBy(query: SemanticQuery, selectedFields?: string[]): SQL[];
|
|
2237
|
-
/**
|
|
2238
|
-
* Collect numeric field names (measures + numeric dimensions) for type conversion
|
|
2239
|
-
* Works for both single and multi-cube queries
|
|
2240
|
-
*/
|
|
2241
|
-
collectNumericFields(cubes: Map<string, Cube> | Cube, query: SemanticQuery): string[];
|
|
2242
|
-
/**
|
|
2243
|
-
* Apply LIMIT and OFFSET to a query with validation
|
|
2244
|
-
* If offset is provided without limit, add a reasonable default limit
|
|
2245
|
-
*/
|
|
2246
|
-
applyLimitAndOffset<T>(query: T, semanticQuery: SemanticQuery): T;
|
|
2247
|
-
/**
|
|
2248
|
-
* Public wrapper for buildFilterCondition - used by executor for cache preloading
|
|
2249
|
-
* This allows pre-building filter SQL before query construction
|
|
2250
|
-
*/
|
|
2251
|
-
buildFilterConditionPublic(fieldExpr: AnyColumn | SQL, operator: FilterOperator, values: any[], field?: any, dateRange?: string | string[]): SQL | null;
|
|
2252
|
-
/**
|
|
2253
|
-
* Build a logical filter (AND/OR) - used by executor for cache preloading
|
|
2254
|
-
* This handles nested filter structures and builds combined SQL
|
|
2255
|
-
* Delegates to FilterBuilder
|
|
2256
|
-
*/
|
|
2257
|
-
buildLogicalFilter(filter: Filter, cubes: Map<string, Cube>, context: QueryContext): SQL | null;
|
|
3489
|
+
/** Structured decision trace for debugging and dry-run UIs */
|
|
3490
|
+
planningTrace?: PlanningTrace;
|
|
2258
3491
|
}
|
|
2259
3492
|
|
|
2260
3493
|
/**
|
|
@@ -2275,7 +3508,7 @@ export declare interface QueryCacheMetadata {
|
|
|
2275
3508
|
* Query context passed to cube SQL functions
|
|
2276
3509
|
* Provides access to database, schema, and security context
|
|
2277
3510
|
*/
|
|
2278
|
-
declare interface QueryContext {
|
|
3511
|
+
export declare interface QueryContext {
|
|
2279
3512
|
/** Drizzle database instance */
|
|
2280
3513
|
db: DrizzleDatabase;
|
|
2281
3514
|
/** Database schema (tables, columns, etc.) */
|
|
@@ -2292,20 +3525,19 @@ declare interface QueryContext {
|
|
|
2292
3525
|
*/
|
|
2293
3526
|
filterCache?: FilterCacheManager;
|
|
2294
3527
|
}
|
|
2295
|
-
export { QueryContext }
|
|
2296
|
-
export { QueryContext as SemanticQueryContext }
|
|
2297
3528
|
|
|
2298
3529
|
export declare class QueryExecutor {
|
|
2299
3530
|
private dbExecutor;
|
|
2300
3531
|
private queryBuilder;
|
|
2301
|
-
private
|
|
2302
|
-
private cteBuilder;
|
|
3532
|
+
private drizzlePlanBuilder;
|
|
2303
3533
|
private databaseAdapter;
|
|
2304
3534
|
private comparisonQueryBuilder;
|
|
2305
3535
|
private funnelQueryBuilder;
|
|
2306
3536
|
private flowQueryBuilder;
|
|
2307
3537
|
private retentionQueryBuilder;
|
|
2308
3538
|
private cacheConfig?;
|
|
3539
|
+
private logicalPlanBuilder;
|
|
3540
|
+
private planOptimiser;
|
|
2309
3541
|
constructor(dbExecutor: DatabaseExecutor, cacheConfig?: CacheConfig);
|
|
2310
3542
|
/**
|
|
2311
3543
|
* Unified query execution method that handles both single and multi-cube queries
|
|
@@ -2313,411 +3545,180 @@ export declare class QueryExecutor {
|
|
|
2313
3545
|
*/
|
|
2314
3546
|
execute(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext, options?: ExecutionOptions): Promise<QueryResult>;
|
|
2315
3547
|
/**
|
|
2316
|
-
*
|
|
2317
|
-
|
|
2318
|
-
executeQuery(cube: Cube, query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
|
|
2319
|
-
/**
|
|
2320
|
-
* Execute a comparison query with caching support
|
|
2321
|
-
* Wraps executeComparisonQuery with cache set logic
|
|
2322
|
-
*/
|
|
2323
|
-
private executeComparisonQueryWithCache;
|
|
2324
|
-
/**
|
|
2325
|
-
* Execute a comparison query with multiple date periods
|
|
2326
|
-
* Expands compareDateRange into multiple sub-queries and merges results
|
|
2327
|
-
*/
|
|
2328
|
-
private executeComparisonQuery;
|
|
2329
|
-
/**
|
|
2330
|
-
* Execute a funnel query with caching support
|
|
2331
|
-
*/
|
|
2332
|
-
private executeFunnelQueryWithCache;
|
|
2333
|
-
/**
|
|
2334
|
-
* Execute a funnel analysis query
|
|
2335
|
-
*/
|
|
2336
|
-
private executeFunnelQuery;
|
|
2337
|
-
/**
|
|
2338
|
-
* Execute a flow query with caching support
|
|
2339
|
-
*/
|
|
2340
|
-
private executeFlowQueryWithCache;
|
|
2341
|
-
/**
|
|
2342
|
-
* Execute a flow analysis query
|
|
2343
|
-
* Produces Sankey diagram data (nodes and links)
|
|
2344
|
-
*/
|
|
2345
|
-
private executeFlowQuery;
|
|
2346
|
-
/**
|
|
2347
|
-
* Execute a retention query with caching support
|
|
2348
|
-
*/
|
|
2349
|
-
private executeRetentionQueryWithCache;
|
|
2350
|
-
/**
|
|
2351
|
-
* Execute a retention analysis query
|
|
2352
|
-
* Calculates cohort-based retention rates
|
|
2353
|
-
*/
|
|
2354
|
-
private executeRetentionQuery;
|
|
2355
|
-
/**
|
|
2356
|
-
* Standard query execution (non-comparison)
|
|
2357
|
-
* This is the core execution logic extracted for use by comparison queries
|
|
2358
|
-
*/
|
|
2359
|
-
private executeStandardQuery;
|
|
2360
|
-
/**
|
|
2361
|
-
* Validate that all cubes in the query plan have proper security filtering.
|
|
2362
|
-
* Emits a warning if a cube's sql() function doesn't return a WHERE clause.
|
|
2363
|
-
*
|
|
2364
|
-
* Security is critical in multi-tenant applications - this validation helps
|
|
2365
|
-
* detect cubes that may leak data across tenants.
|
|
2366
|
-
*/
|
|
2367
|
-
private validateSecurityContext;
|
|
2368
|
-
/**
|
|
2369
|
-
* Build unified query that works for both single and multi-cube queries
|
|
2370
|
-
*/
|
|
2371
|
-
private buildUnifiedQuery;
|
|
2372
|
-
/**
|
|
2373
|
-
* Convert query plan to cube map for QueryBuilder methods
|
|
2374
|
-
*/
|
|
2375
|
-
private getCubesFromPlan;
|
|
2376
|
-
/**
|
|
2377
|
-
* For hasMany CTEs, detect when the outer query grain already matches the CTE join key.
|
|
2378
|
-
* In that case, re-aggregation should use MAX (not SUM) to avoid multiplying values by
|
|
2379
|
-
* lower-grain primary cube rows.
|
|
2380
|
-
*/
|
|
2381
|
-
private shouldUseMaxForHasManyAtJoinKeyGrain;
|
|
2382
|
-
/**
|
|
2383
|
-
* Checks whether query grouping includes a dimension backed by the given column.
|
|
2384
|
-
*/
|
|
2385
|
-
private queryGroupsByColumn;
|
|
2386
|
-
/**
|
|
2387
|
-
* Generate raw SQL for debugging (without execution) - unified approach
|
|
2388
|
-
*/
|
|
2389
|
-
generateSQL(cube: Cube, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
2390
|
-
sql: string;
|
|
2391
|
-
params?: any[];
|
|
2392
|
-
}>;
|
|
2393
|
-
/**
|
|
2394
|
-
* Generate raw SQL for multi-cube queries without execution - unified approach
|
|
2395
|
-
*/
|
|
2396
|
-
generateMultiCubeSQL(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
2397
|
-
sql: string;
|
|
2398
|
-
params?: any[];
|
|
2399
|
-
}>;
|
|
2400
|
-
/**
|
|
2401
|
-
* Generate SQL for a funnel query without execution (dry-run)
|
|
2402
|
-
* Returns the actual CTE-based SQL that would be executed
|
|
2403
|
-
*/
|
|
2404
|
-
dryRunFunnel(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
2405
|
-
sql: string;
|
|
2406
|
-
params?: any[];
|
|
2407
|
-
}>;
|
|
2408
|
-
/**
|
|
2409
|
-
* Generate SQL for a flow query without execution (dry-run)
|
|
2410
|
-
* Returns the actual CTE-based SQL that would be executed
|
|
2411
|
-
*/
|
|
2412
|
-
dryRunFlow(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
2413
|
-
sql: string;
|
|
2414
|
-
params?: any[];
|
|
2415
|
-
}>;
|
|
2416
|
-
/**
|
|
2417
|
-
* Generate SQL for a retention query without execution (dry-run)
|
|
2418
|
-
* Returns the actual CTE-based SQL that would be executed
|
|
2419
|
-
*/
|
|
2420
|
-
dryRunRetention(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
2421
|
-
sql: string;
|
|
2422
|
-
params?: any[];
|
|
2423
|
-
}>;
|
|
2424
|
-
/**
|
|
2425
|
-
* Execute EXPLAIN on a query to get the execution plan
|
|
2426
|
-
* Generates the SQL using the same secure path as execute/generateSQL,
|
|
2427
|
-
* then runs EXPLAIN on the database.
|
|
2428
|
-
*/
|
|
2429
|
-
explainQuery(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext, options?: ExplainOptions): Promise<ExplainResult>;
|
|
2430
|
-
/**
|
|
2431
|
-
* Generate SQL using unified approach (works for both single and multi-cube)
|
|
2432
|
-
*/
|
|
2433
|
-
private generateUnifiedSQL;
|
|
2434
|
-
/**
|
|
2435
|
-
* Generate annotations for UI metadata - unified approach
|
|
2436
|
-
*/
|
|
2437
|
-
private generateAnnotations;
|
|
2438
|
-
/**
|
|
2439
|
-
* Pre-build filter SQL and store in cache for reuse across CTEs and main query
|
|
2440
|
-
* This enables parameter deduplication - the same filter values are shared
|
|
2441
|
-
* rather than appearing as separate parameters in different parts of the query
|
|
3548
|
+
* Build a logical plan for a query without executing it.
|
|
3549
|
+
* Useful for testing, debugging, and plan inspection.
|
|
2442
3550
|
*/
|
|
2443
|
-
|
|
3551
|
+
buildLogicalPlan(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): QueryNode;
|
|
2444
3552
|
/**
|
|
2445
|
-
*
|
|
2446
|
-
*
|
|
2447
|
-
* Post-aggregation windows operate on already-aggregated data:
|
|
2448
|
-
* 1. The base measure is aggregated (e.g., SUM(revenue))
|
|
2449
|
-
* 2. The window function is applied (e.g., LAG(...) OVER ORDER BY date)
|
|
2450
|
-
* 3. An optional operation is applied (e.g., current - previous)
|
|
2451
|
-
*
|
|
2452
|
-
* @param measure - The window function measure definition
|
|
2453
|
-
* @param baseMeasureExpr - The aggregated base measure expression
|
|
2454
|
-
* @param query - The semantic query (for dimension context)
|
|
2455
|
-
* @param context - Query context
|
|
2456
|
-
* @param cube - The cube containing this measure
|
|
2457
|
-
* @returns SQL expression for the window function
|
|
3553
|
+
* Analyze planning decisions for a regular query using the same logical
|
|
3554
|
+
* planning path as execution and dry-run SQL generation.
|
|
2458
3555
|
*/
|
|
2459
|
-
|
|
2460
|
-
}
|
|
2461
|
-
|
|
2462
|
-
/**
|
|
2463
|
-
* Unified Query Plan for both single and multi-cube queries
|
|
2464
|
-
* - For single-cube queries: joinCubes array is empty
|
|
2465
|
-
* - For multi-cube queries: joinCubes contains the additional cubes to join
|
|
2466
|
-
* - selections, whereConditions, and groupByFields are populated by QueryBuilder
|
|
2467
|
-
*/
|
|
2468
|
-
export declare interface QueryPlan {
|
|
2469
|
-
/** Primary cube that drives the query */
|
|
2470
|
-
primaryCube: Cube;
|
|
2471
|
-
/** Additional cubes to join (empty for single-cube queries) */
|
|
2472
|
-
joinCubes: Array<{
|
|
2473
|
-
cube: Cube;
|
|
2474
|
-
alias: string;
|
|
2475
|
-
joinType: 'inner' | 'left' | 'right' | 'full';
|
|
2476
|
-
joinCondition: SQL;
|
|
2477
|
-
/** Junction table information for belongsToMany relationships */
|
|
2478
|
-
junctionTable?: {
|
|
2479
|
-
table: Table;
|
|
2480
|
-
alias: string;
|
|
2481
|
-
joinType: 'inner' | 'left' | 'right' | 'full';
|
|
2482
|
-
joinCondition: SQL;
|
|
2483
|
-
/** Optional security SQL function to apply to junction table */
|
|
2484
|
-
securitySql?: (securityContext: SecurityContext) => SQL | SQL[];
|
|
2485
|
-
/** Source cube name for the belongsToMany relationship (needed for CTE rewriting) */
|
|
2486
|
-
sourceCubeName?: string;
|
|
2487
|
-
};
|
|
2488
|
-
}>;
|
|
2489
|
-
/** Combined field selections across all cubes (built by QueryBuilder) */
|
|
2490
|
-
selections: Record<string, SQL | AnyColumn>;
|
|
2491
|
-
/** WHERE conditions for the entire query (built by QueryBuilder) */
|
|
2492
|
-
whereConditions: SQL[];
|
|
2493
|
-
/** GROUP BY fields if aggregations are present (built by QueryBuilder) */
|
|
2494
|
-
groupByFields: (SQL | AnyColumn)[];
|
|
2495
|
-
/** Pre-aggregation CTEs for hasMany relationships to prevent fan-out */
|
|
2496
|
-
preAggregationCTEs?: PreAggregationCTEInfo[];
|
|
2497
|
-
/** Warnings about potential query issues (e.g., fan-out without dimensions) */
|
|
2498
|
-
warnings?: QueryWarning[];
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
/**
|
|
2502
|
-
* Pre-aggregation plan for handling hasMany relationships
|
|
2503
|
-
*/
|
|
2504
|
-
export declare class QueryPlanner {
|
|
2505
|
-
private resolverCache;
|
|
3556
|
+
analyzeQuery(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): QueryAnalysis;
|
|
2506
3557
|
/**
|
|
2507
|
-
*
|
|
2508
|
-
*/
|
|
2509
|
-
private getResolver;
|
|
2510
|
-
/**
|
|
2511
|
-
* Analyze a semantic query to determine which cubes are involved
|
|
2512
|
-
*/
|
|
2513
|
-
analyzeCubeUsage(query: SemanticQuery): Set<string>;
|
|
2514
|
-
/**
|
|
2515
|
-
* Recursively extract cube names from filters (handles logical filters)
|
|
2516
|
-
*/
|
|
2517
|
-
private extractCubeNamesFromFilter;
|
|
2518
|
-
/**
|
|
2519
|
-
* Extract measures referenced in filters (for CTE inclusion)
|
|
2520
|
-
*/
|
|
2521
|
-
private extractMeasuresFromFilters;
|
|
2522
|
-
/**
|
|
2523
|
-
* Recursively extract measures from filters for a specific cube
|
|
2524
|
-
* Only includes filter members that are actually measures (not dimensions)
|
|
2525
|
-
*/
|
|
2526
|
-
private extractMeasuresFromFilter;
|
|
2527
|
-
/**
|
|
2528
|
-
* Create a unified query plan that works for both single and multi-cube queries
|
|
2529
|
-
*/
|
|
2530
|
-
createQueryPlan(cubes: Map<string, Cube>, query: SemanticQuery, ctx: QueryContext): QueryPlan;
|
|
2531
|
-
/**
|
|
2532
|
-
* Choose the primary cube based on query analysis
|
|
2533
|
-
* Uses a consistent strategy to avoid measure order dependencies
|
|
2534
|
-
*
|
|
2535
|
-
* Delegates to analyzePrimaryCubeSelection() for the actual logic,
|
|
2536
|
-
* ensuring a single source of truth for primary cube selection.
|
|
2537
|
-
*/
|
|
2538
|
-
choosePrimaryCube(cubeNames: string[], query: SemanticQuery, cubes?: Map<string, Cube>): string;
|
|
2539
|
-
/**
|
|
2540
|
-
* Build join plan for multi-cube query
|
|
2541
|
-
* Supports both direct joins and transitive joins through intermediate cubes
|
|
2542
|
-
*
|
|
2543
|
-
* Uses query-aware path selection to prefer joining through cubes that have
|
|
2544
|
-
* measures in the query (e.g., joining Teams through EmployeeTeams when
|
|
2545
|
-
* EmployeeTeams.count is a measure)
|
|
3558
|
+
* Legacy interface for single cube queries
|
|
2546
3559
|
*/
|
|
2547
|
-
|
|
2548
|
-
/**
|
|
2549
|
-
*
|
|
2550
|
-
*
|
|
2551
|
-
* and don't require CTEs - the two-hop join with the junction table provides natural grouping
|
|
2552
|
-
*
|
|
2553
|
-
* CRITICAL FAN-OUT PREVENTION LOGIC:
|
|
2554
|
-
* When a query contains ANY hasMany relationship in the join graph, ALL cubes with measures
|
|
2555
|
-
* that could be affected by row multiplication need CTEs. This includes:
|
|
2556
|
-
*
|
|
2557
|
-
* 1. Cubes with direct hasMany FROM primary (existing logic)
|
|
2558
|
-
* 2. Cubes with measures that would be multiplied due to hasMany elsewhere in the query
|
|
2559
|
-
* - Example: Query has Departments.totalBudget + Productivity.recordCount
|
|
2560
|
-
* - Employees hasMany → Productivity causes row multiplication
|
|
2561
|
-
* - Departments.totalBudget would be inflated without CTE pre-aggregation
|
|
3560
|
+
executeQuery(cube: Cube, query: SemanticQuery, securityContext: SecurityContext): Promise<QueryResult>;
|
|
3561
|
+
/**
|
|
3562
|
+
* Execute a comparison query with caching support
|
|
3563
|
+
* Wraps executeComparisonQuery with cache set logic
|
|
2562
3564
|
*/
|
|
2563
|
-
private
|
|
3565
|
+
private executeComparisonQueryWithCache;
|
|
2564
3566
|
/**
|
|
2565
|
-
*
|
|
2566
|
-
*
|
|
3567
|
+
* Execute a comparison query with multiple date periods
|
|
3568
|
+
* Expands compareDateRange into multiple sub-queries and merges results
|
|
2567
3569
|
*/
|
|
2568
|
-
private
|
|
3570
|
+
private executeComparisonQuery;
|
|
3571
|
+
private buildComparisonExecutionPlan;
|
|
2569
3572
|
/**
|
|
2570
|
-
*
|
|
2571
|
-
* Detects if there are intermediate hasMany relationships that would cause fan-out.
|
|
2572
|
-
*
|
|
2573
|
-
* Returns information about:
|
|
2574
|
-
* - The full join path
|
|
2575
|
-
* - Whether there are hasMany relationships ON the path (not just at the end)
|
|
2576
|
-
* - Which intermediate tables need to be absorbed into the CTE
|
|
2577
|
-
* - The correct join key to use (from primary cube's connection point)
|
|
2578
|
-
*
|
|
2579
|
-
* @param cubes Map of all registered cubes
|
|
2580
|
-
* @param primaryCube The primary cube (FROM clause)
|
|
2581
|
-
* @param targetCubeName The CTE cube we're analyzing the path to
|
|
2582
|
-
* @param ctx Query context for security filtering
|
|
3573
|
+
* Execute a funnel query with caching support
|
|
2583
3574
|
*/
|
|
2584
|
-
private
|
|
3575
|
+
private executeFunnelQueryWithCache;
|
|
2585
3576
|
/**
|
|
2586
|
-
*
|
|
2587
|
-
*
|
|
2588
|
-
* This searches ALL cubes involved in the query (including intermediary cubes)
|
|
2589
|
-
* to find any hasMany relationships that could cause row multiplication.
|
|
2590
|
-
*
|
|
2591
|
-
* Key insight: A hasMany relationship in ANY cube that's part of the join path
|
|
2592
|
-
* will cause fan-out. We need to detect hasMany from:
|
|
2593
|
-
* 1. The primary cube
|
|
2594
|
-
* 2. All join cubes
|
|
2595
|
-
* 3. Any intermediate cubes that form the join path
|
|
3577
|
+
* Execute a funnel analysis query
|
|
2596
3578
|
*/
|
|
2597
|
-
private
|
|
3579
|
+
private executeFunnelQuery;
|
|
2598
3580
|
/**
|
|
2599
|
-
*
|
|
2600
|
-
*
|
|
2601
|
-
* Returns:
|
|
2602
|
-
* - 'hasMany': Cube is the TARGET of a hasMany relationship - needs SUM in outer query
|
|
2603
|
-
* - 'fanOutPrevention': Cube has measures affected by hasMany elsewhere - needs MAX in outer query
|
|
2604
|
-
* - null: No CTE needed
|
|
2605
|
-
*
|
|
2606
|
-
* Key insight: When ANY hasMany exists in a query, ALL cubes with additive measures (sum, count)
|
|
2607
|
-
* that are joined to the hasMany source cube are at risk of inflation.
|
|
3581
|
+
* Execute a flow query with caching support
|
|
2608
3582
|
*/
|
|
2609
|
-
private
|
|
3583
|
+
private executeFlowQueryWithCache;
|
|
2610
3584
|
/**
|
|
2611
|
-
*
|
|
2612
|
-
*
|
|
2613
|
-
* and to search from any source cube, not just the primary
|
|
3585
|
+
* Execute a flow analysis query
|
|
3586
|
+
* Produces Sankey diagram data (nodes and links)
|
|
2614
3587
|
*/
|
|
2615
|
-
private
|
|
3588
|
+
private executeFlowQuery;
|
|
2616
3589
|
/**
|
|
2617
|
-
*
|
|
2618
|
-
*
|
|
2619
|
-
* When a query has dimensions from a cube (e.g., Teams.name) and measures from
|
|
2620
|
-
* a junction cube (e.g., EmployeeTeams.count), the junction CTE needs to include
|
|
2621
|
-
* the join key to the dimension cube (team_id) so the dimension cube can be
|
|
2622
|
-
* joined through the CTE instead of via an alternative path.
|
|
2623
|
-
*
|
|
2624
|
-
* @param cteCube The cube being converted to a CTE (e.g., EmployeeTeams)
|
|
2625
|
-
* @param query The semantic query with dimensions and measures
|
|
2626
|
-
* @param allCubes Map of all registered cubes
|
|
2627
|
-
* @returns Array of downstream join key info for cubes needing join through this CTE
|
|
3590
|
+
* Execute a retention query with caching support
|
|
2628
3591
|
*/
|
|
2629
|
-
private
|
|
3592
|
+
private executeRetentionQueryWithCache;
|
|
2630
3593
|
/**
|
|
2631
|
-
*
|
|
3594
|
+
* Execute a retention analysis query
|
|
3595
|
+
* Calculates cohort-based retention rates
|
|
2632
3596
|
*/
|
|
2633
|
-
private
|
|
3597
|
+
private executeRetentionQuery;
|
|
2634
3598
|
/**
|
|
2635
|
-
*
|
|
3599
|
+
* Standard query execution (non-comparison)
|
|
3600
|
+
* This is the core execution logic extracted for use by comparison queries
|
|
2636
3601
|
*/
|
|
2637
|
-
private
|
|
3602
|
+
private executeStandardQuery;
|
|
2638
3603
|
/**
|
|
2639
|
-
*
|
|
3604
|
+
* Create a query context with optional filter cache.
|
|
2640
3605
|
*/
|
|
2641
|
-
private
|
|
3606
|
+
private createQueryContext;
|
|
2642
3607
|
/**
|
|
2643
|
-
*
|
|
2644
|
-
*
|
|
2645
|
-
* A's filters should propagate into B's CTE via a subquery.
|
|
2646
|
-
*
|
|
2647
|
-
* Example: Employees.createdAt filter should propagate to Productivity CTE
|
|
2648
|
-
* via: employee_id IN (SELECT id FROM employees WHERE created_at >= $date)
|
|
3608
|
+
* Normalize engine type for optimiser passes.
|
|
3609
|
+
* SingleStore follows MySQL SQL semantics for planner choices.
|
|
2649
3610
|
*/
|
|
2650
|
-
private
|
|
3611
|
+
private getOptimiserEngineType;
|
|
2651
3612
|
/**
|
|
2652
|
-
*
|
|
3613
|
+
* Shared regular-query planning pipeline used by execute, dry-run SQL,
|
|
3614
|
+
* and analysis. This is the single source of planning truth.
|
|
2653
3615
|
*/
|
|
2654
|
-
private
|
|
3616
|
+
private buildRegularQueryArtifacts;
|
|
2655
3617
|
/**
|
|
2656
|
-
*
|
|
3618
|
+
* Validate that all cubes in the query plan have proper security filtering.
|
|
3619
|
+
* Emits a warning if a cube's sql() function doesn't return a WHERE clause.
|
|
2657
3620
|
*
|
|
2658
|
-
*
|
|
2659
|
-
*
|
|
2660
|
-
* - OR: Must include ALL branches or skip entirely (partial OR changes semantics)
|
|
2661
|
-
* If any branch belongs to another cube, skip the entire OR to be safe
|
|
2662
|
-
* since we can't evaluate the other cube's conditions
|
|
3621
|
+
* Security is critical in multi-tenant applications - this validation helps
|
|
3622
|
+
* detect cubes that may leak data across tenants.
|
|
2663
3623
|
*/
|
|
2664
|
-
private
|
|
3624
|
+
private validateSecurityContext;
|
|
2665
3625
|
/**
|
|
2666
|
-
*
|
|
2667
|
-
* Recursively checks nested AND/OR filters
|
|
3626
|
+
* Generate raw SQL for debugging (without execution) - unified approach
|
|
2668
3627
|
*/
|
|
2669
|
-
|
|
3628
|
+
generateSQL(cube: Cube, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
3629
|
+
sql: string;
|
|
3630
|
+
params?: any[];
|
|
3631
|
+
}>;
|
|
2670
3632
|
/**
|
|
2671
|
-
*
|
|
3633
|
+
* Generate raw SQL for multi-cube queries without execution - unified approach
|
|
2672
3634
|
*/
|
|
2673
|
-
|
|
3635
|
+
generateMultiCubeSQL(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
3636
|
+
sql: string;
|
|
3637
|
+
params?: any[];
|
|
3638
|
+
}>;
|
|
2674
3639
|
/**
|
|
2675
|
-
*
|
|
2676
|
-
* Returns
|
|
2677
|
-
* Used for debugging and transparency in the playground UI
|
|
3640
|
+
* Generate SQL for a funnel query without execution (dry-run)
|
|
3641
|
+
* Returns the actual CTE-based SQL that would be executed
|
|
2678
3642
|
*/
|
|
2679
|
-
|
|
3643
|
+
dryRunFunnel(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
3644
|
+
sql: string;
|
|
3645
|
+
params?: any[];
|
|
3646
|
+
}>;
|
|
2680
3647
|
/**
|
|
2681
|
-
*
|
|
3648
|
+
* Generate SQL for a flow query without execution (dry-run)
|
|
3649
|
+
* Returns the actual CTE-based SQL that would be executed
|
|
2682
3650
|
*/
|
|
2683
|
-
|
|
3651
|
+
dryRunFlow(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
3652
|
+
sql: string;
|
|
3653
|
+
params?: any[];
|
|
3654
|
+
}>;
|
|
2684
3655
|
/**
|
|
2685
|
-
*
|
|
2686
|
-
*
|
|
2687
|
-
* Uses JoinPathResolver.findPath() for the actual path finding,
|
|
2688
|
-
* then converts the result to human-readable analysis format.
|
|
3656
|
+
* Generate SQL for a retention query without execution (dry-run)
|
|
3657
|
+
* Returns the actual CTE-based SQL that would be executed
|
|
2689
3658
|
*/
|
|
2690
|
-
|
|
3659
|
+
dryRunRetention(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
3660
|
+
sql: string;
|
|
3661
|
+
params?: any[];
|
|
3662
|
+
}>;
|
|
2691
3663
|
/**
|
|
2692
|
-
*
|
|
2693
|
-
*
|
|
3664
|
+
* Execute EXPLAIN on a query to get the execution plan
|
|
3665
|
+
* Generates the SQL using the same secure path as execute/generateSQL,
|
|
3666
|
+
* then runs EXPLAIN on the database.
|
|
2694
3667
|
*/
|
|
2695
|
-
|
|
3668
|
+
explainQuery(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext, options?: ExplainOptions): Promise<ExplainResult>;
|
|
2696
3669
|
/**
|
|
2697
|
-
* Generate
|
|
2698
|
-
*
|
|
2699
|
-
* - FAN_OUT_NO_DIMENSIONS: Query has hasMany CTEs but no dimensions to group by
|
|
2700
|
-
*
|
|
2701
|
-
* Note: AVG measures in hasMany CTEs can produce mathematically imprecise results
|
|
2702
|
-
* (average of averages vs weighted average), but this warning was removed as it
|
|
2703
|
-
* fired too aggressively. The issue only occurs when the outer grouping is coarser
|
|
2704
|
-
* than the CTE grouping, which is rare in practice. The limitation is documented
|
|
2705
|
-
* in executor.ts comments.
|
|
3670
|
+
* Generate SQL for any query mode without execution.
|
|
3671
|
+
* This is the canonical dry-run SQL entrypoint used by explain/adapters.
|
|
2706
3672
|
*/
|
|
2707
|
-
|
|
3673
|
+
dryRunSQL(cubes: Map<string, Cube>, query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
3674
|
+
sql: string;
|
|
3675
|
+
params?: any[];
|
|
3676
|
+
}>;
|
|
2708
3677
|
/**
|
|
2709
|
-
*
|
|
2710
|
-
* but no dimensions to provide grouping context.
|
|
2711
|
-
*
|
|
2712
|
-
* This is an edge case where:
|
|
2713
|
-
* - Query has measures from 2+ cubes
|
|
2714
|
-
* - At least one CTE exists (indicating hasMany relationship)
|
|
2715
|
-
* - Query has NO dimensions AND NO time dimensions with granularity
|
|
2716
|
-
*
|
|
2717
|
-
* The SQL is technically correct (CTEs with GROUP BY on join keys), but users
|
|
2718
|
-
* may be confused by the aggregated results without visible grouping.
|
|
3678
|
+
* Generate SQL using unified approach (works for both single and multi-cube)
|
|
2719
3679
|
*/
|
|
2720
|
-
private
|
|
3680
|
+
private generateUnifiedSQL;
|
|
3681
|
+
private resolveQueryMode;
|
|
3682
|
+
private validateQueryForMode;
|
|
3683
|
+
private executeQueryByModeWithCache;
|
|
3684
|
+
private executeRegularQueryWithCache;
|
|
3685
|
+
private cacheResult;
|
|
3686
|
+
private generateSqlForMode;
|
|
3687
|
+
private generateComparisonSQL;
|
|
3688
|
+
/**
|
|
3689
|
+
* Generate annotations for UI metadata - unified approach
|
|
3690
|
+
*/
|
|
3691
|
+
private generateAnnotations;
|
|
3692
|
+
/**
|
|
3693
|
+
* Pre-build filter SQL and store in cache for reuse across CTEs and main query
|
|
3694
|
+
* This enables parameter deduplication - the same filter values are shared
|
|
3695
|
+
* rather than appearing as separate parameters in different parts of the query
|
|
3696
|
+
*/
|
|
3697
|
+
private preloadFilterCache;
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
/**
|
|
3701
|
+
* Root query node — represents the outermost SELECT.
|
|
3702
|
+
* Wraps a source node with dimensions, measures, filters, ordering, etc.
|
|
3703
|
+
*/
|
|
3704
|
+
export declare interface QueryNode extends LogicalNodeBase {
|
|
3705
|
+
readonly type: 'query';
|
|
3706
|
+
/** Source of rows (SimpleSource, FullKeyAggregate, etc.) */
|
|
3707
|
+
source: LogicalNode;
|
|
3708
|
+
/** Dimensions to group by */
|
|
3709
|
+
dimensions: DimensionRef[];
|
|
3710
|
+
/** Measures to aggregate */
|
|
3711
|
+
measures: MeasureRef[];
|
|
3712
|
+
/** User-supplied filters */
|
|
3713
|
+
filters: Filter[];
|
|
3714
|
+
/** Time dimensions with granularity / date range */
|
|
3715
|
+
timeDimensions: TimeDimensionRef[];
|
|
3716
|
+
/** ORDER BY clauses */
|
|
3717
|
+
orderBy: OrderByRef[];
|
|
3718
|
+
limit?: number;
|
|
3719
|
+
offset?: number;
|
|
3720
|
+
/** Planning warnings surfaced to the caller */
|
|
3721
|
+
warnings: QueryWarning[];
|
|
2721
3722
|
}
|
|
2722
3723
|
|
|
2723
3724
|
/**
|
|
@@ -2768,6 +3769,8 @@ export declare interface QuerySuggestion {
|
|
|
2768
3769
|
export declare interface QuerySummary {
|
|
2769
3770
|
/** Query type: 'single_cube', 'multi_cube_join', or 'multi_cube_cte' */
|
|
2770
3771
|
queryType: 'single_cube' | 'multi_cube_join' | 'multi_cube_cte';
|
|
3772
|
+
/** Strategy selected for multiplied-measure handling / multi-fact merge */
|
|
3773
|
+
measureStrategy?: 'simple' | 'keysDeduplication' | 'ctePreAggregateFallback' | 'multiFactMerge';
|
|
2771
3774
|
/** Total number of joins */
|
|
2772
3775
|
joinCount: number;
|
|
2773
3776
|
/** Total number of CTEs */
|
|
@@ -2861,6 +3864,104 @@ export declare interface RetentionDateRange {
|
|
|
2861
3864
|
end: string;
|
|
2862
3865
|
}
|
|
2863
3866
|
|
|
3867
|
+
export declare class RetentionQueryBuilder {
|
|
3868
|
+
private databaseAdapter;
|
|
3869
|
+
private filterBuilder;
|
|
3870
|
+
private dateTimeBuilder;
|
|
3871
|
+
constructor(databaseAdapter: DatabaseAdapter);
|
|
3872
|
+
/**
|
|
3873
|
+
* Check if query contains retention configuration
|
|
3874
|
+
*/
|
|
3875
|
+
hasRetention(query: SemanticQuery): boolean;
|
|
3876
|
+
/**
|
|
3877
|
+
* Validate retention configuration against registered cubes
|
|
3878
|
+
*/
|
|
3879
|
+
validateConfig(config: RetentionQueryConfig, cubes: Map<string, Cube>): {
|
|
3880
|
+
isValid: boolean;
|
|
3881
|
+
errors: string[];
|
|
3882
|
+
};
|
|
3883
|
+
/**
|
|
3884
|
+
* Build the retention SQL query using CTEs
|
|
3885
|
+
*
|
|
3886
|
+
* CTE Structure (Simplified Mixpanel-style):
|
|
3887
|
+
* 1. cohort_base - Users entering the cohort (first event in date range)
|
|
3888
|
+
* - When breakdown is specified, includes breakdown_value
|
|
3889
|
+
* 2. activity_periods - All activity with period_number relative to cohort entry
|
|
3890
|
+
* 3. cohort_sizes - Aggregate cohort sizes (per breakdown value if applicable)
|
|
3891
|
+
* 4. retention_counts - Retained users per period (and breakdown value)
|
|
3892
|
+
* 5. Final SELECT - Join with retention rate calculation
|
|
3893
|
+
*/
|
|
3894
|
+
buildRetentionQuery(config: RetentionQueryConfig, cubes: Map<string, Cube>, context: QueryContext): any;
|
|
3895
|
+
/**
|
|
3896
|
+
* Transform raw SQL results to RetentionResultRow[]
|
|
3897
|
+
*/
|
|
3898
|
+
transformResult(rawResult: unknown[], config: RetentionQueryConfig): RetentionResultRow[];
|
|
3899
|
+
/**
|
|
3900
|
+
* Resolve retention configuration with SQL expressions
|
|
3901
|
+
* Same cube/dimension used for both cohort entry and activity detection
|
|
3902
|
+
*/
|
|
3903
|
+
private resolveConfig;
|
|
3904
|
+
/**
|
|
3905
|
+
* Resolve binding key expression for a cube
|
|
3906
|
+
*/
|
|
3907
|
+
private resolveBindingKey;
|
|
3908
|
+
/**
|
|
3909
|
+
* Build filter conditions from config filters
|
|
3910
|
+
*/
|
|
3911
|
+
private buildFilterConditions;
|
|
3912
|
+
/**
|
|
3913
|
+
* Build a single filter condition
|
|
3914
|
+
*/
|
|
3915
|
+
private buildSingleFilterCondition;
|
|
3916
|
+
/**
|
|
3917
|
+
* Build cohort_base CTE
|
|
3918
|
+
* Groups users by their first activity (cohort entry) within the date range.
|
|
3919
|
+
* When breakdowns are specified, includes breakdown values for each dimension.
|
|
3920
|
+
*/
|
|
3921
|
+
private buildCohortBaseCTE;
|
|
3922
|
+
/**
|
|
3923
|
+
* Build date range condition for WHERE clause
|
|
3924
|
+
* Filters records to those within the specified date range
|
|
3925
|
+
*/
|
|
3926
|
+
private buildDateRangeCondition;
|
|
3927
|
+
/**
|
|
3928
|
+
* Build date range condition for HAVING clause
|
|
3929
|
+
* Used to filter aggregated cohort_period values
|
|
3930
|
+
*/
|
|
3931
|
+
private buildDateRangeHavingCondition;
|
|
3932
|
+
/**
|
|
3933
|
+
* Build activity_periods CTE
|
|
3934
|
+
* Joins activity events to cohort_base and calculates period_number.
|
|
3935
|
+
* Includes breakdown values from cohort_base when breakdowns are specified.
|
|
3936
|
+
*/
|
|
3937
|
+
private buildActivityPeriodsCTE;
|
|
3938
|
+
/**
|
|
3939
|
+
* Build cohort_sizes CTE
|
|
3940
|
+
* Aggregates the size of the cohort (or per breakdown combination if specified).
|
|
3941
|
+
*/
|
|
3942
|
+
private buildCohortSizesCTE;
|
|
3943
|
+
/**
|
|
3944
|
+
* Build retention_counts CTE
|
|
3945
|
+
* Aggregates retained users per period (and breakdown combination if specified).
|
|
3946
|
+
*/
|
|
3947
|
+
private buildRetentionCountsCTE;
|
|
3948
|
+
/**
|
|
3949
|
+
* Build rolling retention counts query
|
|
3950
|
+
* For rolling retention, a user is retained in period N if they were active
|
|
3951
|
+
* in period N or any later period
|
|
3952
|
+
*/
|
|
3953
|
+
private buildRollingRetentionCountsQuery;
|
|
3954
|
+
/**
|
|
3955
|
+
* Build period number expression using database-specific DATE_DIFF
|
|
3956
|
+
*/
|
|
3957
|
+
private buildPeriodNumberExpression;
|
|
3958
|
+
/**
|
|
3959
|
+
* Extract dimension name from a dimension reference
|
|
3960
|
+
* Handles both 'CubeName.dimName' and just 'dimName' formats
|
|
3961
|
+
*/
|
|
3962
|
+
private extractDimensionName;
|
|
3963
|
+
}
|
|
3964
|
+
|
|
2864
3965
|
/**
|
|
2865
3966
|
* Retention query configuration (Simplified Mixpanel-style format)
|
|
2866
3967
|
*
|
|
@@ -2954,6 +4055,40 @@ export declare interface RetentionTimeDimensionMapping {
|
|
|
2954
4055
|
dimension: string;
|
|
2955
4056
|
}
|
|
2956
4057
|
|
|
4058
|
+
/**
|
|
4059
|
+
* A link (edge) in the Sankey diagram
|
|
4060
|
+
* Represents a transition between two nodes
|
|
4061
|
+
*/
|
|
4062
|
+
declare interface SankeyLink {
|
|
4063
|
+
/** Source node ID */
|
|
4064
|
+
source: string;
|
|
4065
|
+
/** Target node ID */
|
|
4066
|
+
target: string;
|
|
4067
|
+
/** Count of entities that follow this path */
|
|
4068
|
+
value: number;
|
|
4069
|
+
}
|
|
4070
|
+
|
|
4071
|
+
/**
|
|
4072
|
+
* A node in the Sankey diagram
|
|
4073
|
+
* Represents an event type at a specific layer (distance from starting step)
|
|
4074
|
+
*/
|
|
4075
|
+
declare interface SankeyNode {
|
|
4076
|
+
/**
|
|
4077
|
+
* Unique identifier for this node
|
|
4078
|
+
* Format: "before_{depth}_{eventType}" or "after_{depth}_{eventType}" or "start_{eventType}"
|
|
4079
|
+
*/
|
|
4080
|
+
id: string;
|
|
4081
|
+
/** Display name (typically the event type value) */
|
|
4082
|
+
name: string;
|
|
4083
|
+
/**
|
|
4084
|
+
* Layer position in the Sankey diagram
|
|
4085
|
+
* Negative for steps before starting step, 0 for starting step, positive for after
|
|
4086
|
+
*/
|
|
4087
|
+
layer: number;
|
|
4088
|
+
/** Total count of entities passing through this node */
|
|
4089
|
+
value?: number;
|
|
4090
|
+
}
|
|
4091
|
+
|
|
2957
4092
|
/**
|
|
2958
4093
|
* Security context passed to cube SQL functions
|
|
2959
4094
|
* Contains user/tenant-specific data for filtering
|
|
@@ -2991,6 +4126,18 @@ export declare class SemanticLayerCompiler {
|
|
|
2991
4126
|
* Check if database executor is configured
|
|
2992
4127
|
*/
|
|
2993
4128
|
hasExecutor(): boolean;
|
|
4129
|
+
/**
|
|
4130
|
+
* Get configured executor or throw.
|
|
4131
|
+
*/
|
|
4132
|
+
private requireExecutor;
|
|
4133
|
+
/**
|
|
4134
|
+
* Create a query executor with optional cache integration.
|
|
4135
|
+
*/
|
|
4136
|
+
private createQueryExecutor;
|
|
4137
|
+
/**
|
|
4138
|
+
* Format SQL result using current engine dialect.
|
|
4139
|
+
*/
|
|
4140
|
+
private formatSqlResult;
|
|
2994
4141
|
/**
|
|
2995
4142
|
* Register a simplified cube with dynamic query building
|
|
2996
4143
|
* Validates calculated measures during registration
|
|
@@ -3063,6 +4210,13 @@ export declare class SemanticLayerCompiler {
|
|
|
3063
4210
|
sql: string;
|
|
3064
4211
|
params?: any[];
|
|
3065
4212
|
}>;
|
|
4213
|
+
/**
|
|
4214
|
+
* Canonical dry-run SQL generation entrypoint for all query modes.
|
|
4215
|
+
*/
|
|
4216
|
+
dryRun(query: SemanticQuery, securityContext: SecurityContext): Promise<{
|
|
4217
|
+
sql: string;
|
|
4218
|
+
params?: any[];
|
|
4219
|
+
}>;
|
|
3066
4220
|
/**
|
|
3067
4221
|
* Get SQL for a funnel query without executing it (debugging)
|
|
3068
4222
|
* Returns the actual CTE-based SQL that would be executed for funnel queries
|
|
@@ -3237,6 +4391,20 @@ export declare interface SemanticQuery {
|
|
|
3237
4391
|
retention?: RetentionQueryConfig;
|
|
3238
4392
|
}
|
|
3239
4393
|
|
|
4394
|
+
/**
|
|
4395
|
+
* Simple source: one primary cube optionally joined to other cubes.
|
|
4396
|
+
* This is the runtime physical-source shape used by SQL generation today.
|
|
4397
|
+
*/
|
|
4398
|
+
export declare interface SimpleSource extends LogicalNodeBase {
|
|
4399
|
+
readonly type: 'simpleSource';
|
|
4400
|
+
/** Primary cube (FROM clause) */
|
|
4401
|
+
primaryCube: CubeRef;
|
|
4402
|
+
/** Cubes joined to the primary */
|
|
4403
|
+
joins: JoinRef[];
|
|
4404
|
+
/** Pre-aggregation CTEs attached to this source */
|
|
4405
|
+
ctes: CTEPreAggregate[];
|
|
4406
|
+
}
|
|
4407
|
+
|
|
3240
4408
|
export { SQL }
|
|
3241
4409
|
|
|
3242
4410
|
/**
|
|
@@ -3394,6 +4562,18 @@ export declare interface TimeDimensionAnnotation {
|
|
|
3394
4562
|
granularity?: TimeGranularity;
|
|
3395
4563
|
}
|
|
3396
4564
|
|
|
4565
|
+
/** Reference to a time dimension with granularity/dateRange */
|
|
4566
|
+
declare interface TimeDimensionRef {
|
|
4567
|
+
/** Fully qualified dimension name */
|
|
4568
|
+
name: string;
|
|
4569
|
+
cube: CubeRef;
|
|
4570
|
+
localName: string;
|
|
4571
|
+
granularity?: TimeGranularity;
|
|
4572
|
+
dateRange?: string | string[];
|
|
4573
|
+
fillMissingDates?: boolean;
|
|
4574
|
+
compareDateRange?: (string | [string, string])[];
|
|
4575
|
+
}
|
|
4576
|
+
|
|
3397
4577
|
export declare type TimeGranularity = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
|
|
3398
4578
|
|
|
3399
4579
|
/**
|