@finos/legend-data-cube 0.1.32 → 0.2.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.
Files changed (78) hide show
  1. package/lib/components/DataCubeStatusBar.d.ts.map +1 -1
  2. package/lib/components/DataCubeStatusBar.js +4 -2
  3. package/lib/components/DataCubeStatusBar.js.map +1 -1
  4. package/lib/components/view/editor/DataCubeEditor.d.ts.map +1 -1
  5. package/lib/components/view/editor/DataCubeEditor.js +5 -1
  6. package/lib/components/view/editor/DataCubeEditor.js.map +1 -1
  7. package/lib/components/view/grid/DataCubeMultidimensionalGrid.d.ts.map +1 -1
  8. package/lib/components/view/grid/DataCubeMultidimensionalGrid.js +40 -2
  9. package/lib/components/view/grid/DataCubeMultidimensionalGrid.js.map +1 -1
  10. package/lib/index.css +1 -1
  11. package/lib/package.json +1 -1
  12. package/lib/stores/DataCubeAPI.d.ts +6 -0
  13. package/lib/stores/DataCubeAPI.d.ts.map +1 -1
  14. package/lib/stores/DataCubeAPI.js +4 -0
  15. package/lib/stores/DataCubeAPI.js.map +1 -1
  16. package/lib/stores/core/DataCubeQueryBuilder.d.ts +12 -0
  17. package/lib/stores/core/DataCubeQueryBuilder.d.ts.map +1 -1
  18. package/lib/stores/core/DataCubeQueryBuilder.js +141 -4
  19. package/lib/stores/core/DataCubeQueryBuilder.js.map +1 -1
  20. package/lib/stores/core/DataCubeQueryBuilderUtils.d.ts +2 -0
  21. package/lib/stores/core/DataCubeQueryBuilderUtils.d.ts.map +1 -1
  22. package/lib/stores/core/DataCubeQueryBuilderUtils.js +18 -1
  23. package/lib/stores/core/DataCubeQueryBuilderUtils.js.map +1 -1
  24. package/lib/stores/core/DataCubeQueryEngine.d.ts +5 -0
  25. package/lib/stores/core/DataCubeQueryEngine.d.ts.map +1 -1
  26. package/lib/stores/core/DataCubeQueryEngine.js +8 -0
  27. package/lib/stores/core/DataCubeQueryEngine.js.map +1 -1
  28. package/lib/stores/core/DataCubeSnapshot.d.ts +2 -0
  29. package/lib/stores/core/DataCubeSnapshot.d.ts.map +1 -1
  30. package/lib/stores/core/DataCubeSnapshot.js.map +1 -1
  31. package/lib/stores/view/editor/DataCubeEditorDimensionsPanelState.d.ts.map +1 -1
  32. package/lib/stores/view/editor/DataCubeEditorDimensionsPanelState.js +7 -4
  33. package/lib/stores/view/editor/DataCubeEditorDimensionsPanelState.js.map +1 -1
  34. package/lib/stores/view/grid/DataCubeGridClientEngine.d.ts +163 -1
  35. package/lib/stores/view/grid/DataCubeGridClientEngine.d.ts.map +1 -1
  36. package/lib/stores/view/grid/DataCubeGridClientEngine.js +134 -2
  37. package/lib/stores/view/grid/DataCubeGridClientEngine.js.map +1 -1
  38. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.d.ts +155 -1
  39. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.d.ts.map +1 -1
  40. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.js +170 -2
  41. package/lib/stores/view/grid/DataCubeGridConfigurationBuilder.js.map +1 -1
  42. package/lib/stores/view/grid/DataCubeGridControllerState.d.ts.map +1 -1
  43. package/lib/stores/view/grid/DataCubeGridControllerState.js +5 -3
  44. package/lib/stores/view/grid/DataCubeGridControllerState.js.map +1 -1
  45. package/lib/stores/view/grid/DataCubeGridDimensionalTree.d.ts +48 -0
  46. package/lib/stores/view/grid/DataCubeGridDimensionalTree.d.ts.map +1 -0
  47. package/lib/stores/view/grid/DataCubeGridDimensionalTree.js +168 -0
  48. package/lib/stores/view/grid/DataCubeGridDimensionalTree.js.map +1 -0
  49. package/lib/stores/view/grid/DataCubeGridMenuBuilder.d.ts +1 -0
  50. package/lib/stores/view/grid/DataCubeGridMenuBuilder.d.ts.map +1 -1
  51. package/lib/stores/view/grid/DataCubeGridMenuBuilder.js +197 -0
  52. package/lib/stores/view/grid/DataCubeGridMenuBuilder.js.map +1 -1
  53. package/lib/stores/view/grid/DataCubeGridQueryBuilder.d.ts +2 -0
  54. package/lib/stores/view/grid/DataCubeGridQueryBuilder.d.ts.map +1 -1
  55. package/lib/stores/view/grid/DataCubeGridQueryBuilder.js +7 -1
  56. package/lib/stores/view/grid/DataCubeGridQueryBuilder.js.map +1 -1
  57. package/lib/stores/view/grid/DataCubeGridState.d.ts +4 -1
  58. package/lib/stores/view/grid/DataCubeGridState.d.ts.map +1 -1
  59. package/lib/stores/view/grid/DataCubeGridState.js +24 -2
  60. package/lib/stores/view/grid/DataCubeGridState.js.map +1 -1
  61. package/package.json +2 -2
  62. package/src/components/DataCubeStatusBar.tsx +9 -2
  63. package/src/components/view/editor/DataCubeEditor.tsx +8 -1
  64. package/src/components/view/grid/DataCubeMultidimensionalGrid.tsx +63 -1
  65. package/src/stores/DataCubeAPI.ts +10 -0
  66. package/src/stores/core/DataCubeQueryBuilder.ts +256 -2
  67. package/src/stores/core/DataCubeQueryBuilderUtils.ts +22 -0
  68. package/src/stores/core/DataCubeQueryEngine.ts +9 -0
  69. package/src/stores/core/DataCubeSnapshot.ts +2 -0
  70. package/src/stores/view/editor/DataCubeEditorDimensionsPanelState.ts +8 -7
  71. package/src/stores/view/grid/DataCubeGridClientEngine.ts +252 -5
  72. package/src/stores/view/grid/DataCubeGridConfigurationBuilder.tsx +244 -3
  73. package/src/stores/view/grid/DataCubeGridControllerState.ts +10 -2
  74. package/src/stores/view/grid/DataCubeGridDimensionalTree.ts +268 -0
  75. package/src/stores/view/grid/DataCubeGridMenuBuilder.tsx +240 -0
  76. package/src/stores/view/grid/DataCubeGridQueryBuilder.ts +22 -1
  77. package/src/stores/view/grid/DataCubeGridState.ts +52 -2
  78. package/tsconfig.json +1 -0
@@ -23,13 +23,21 @@
23
23
 
24
24
  import {
25
25
  PRIMITIVE_TYPE,
26
+ V1_AppliedProperty,
27
+ V1_CString,
26
28
  V1_Lambda,
29
+ V1_Variable,
27
30
  type V1_AppliedFunction,
28
31
  } from '@finos/legend-graph';
29
- import { type DataCubeSnapshot } from './DataCubeSnapshot.js';
32
+ import {
33
+ type DataCubeSnapshot,
34
+ type DataCubeSnapshotExtendedColumn,
35
+ type DataCubeSnapshotGroupBy,
36
+ } from './DataCubeSnapshot.js';
30
37
  import { at, guaranteeType } from '@finos/legend-shared';
31
38
  import {
32
39
  DataCubeFunction,
40
+ DataCubeQueryFilterGroupOperator,
33
41
  DataCubeQuerySortDirection,
34
42
  type DataCubeQueryFunctionMap,
35
43
  } from './DataCubeQueryEngine.js';
@@ -49,10 +57,15 @@ import {
49
57
  _var,
50
58
  _functionCompositionProcessor,
51
59
  _extendRootAggregation,
60
+ _flattenFilterSnapshot,
52
61
  } from './DataCubeQueryBuilderUtils.js';
53
62
  import type { DataCubeSource } from './model/DataCubeSource.js';
54
- import { _findCol } from './model/DataCubeColumn.js';
63
+ import { _findCol, _toCol } from './model/DataCubeColumn.js';
55
64
  import type { DataCubeEngine } from './DataCubeEngine.js';
65
+ import {
66
+ DIMENSIONAL_L0_COLUMN,
67
+ type DataCubeDimensionalNode,
68
+ } from '../view/grid/DataCubeGridDimensionalTree.js';
56
69
 
57
70
  export function buildExecutableQuery(
58
71
  snapshot: DataCubeSnapshot,
@@ -312,3 +325,244 @@ export function buildExecutableQuery(
312
325
  }
313
326
  return at(sequence, sequence.length - 1);
314
327
  }
328
+
329
+ export function buildDimensionalExecutableQuery(
330
+ snapshot: DataCubeSnapshot,
331
+ source: DataCubeSource,
332
+ engine: DataCubeEngine,
333
+ nodes: DataCubeDimensionalNode[],
334
+ options?: {
335
+ postProcessor?: (
336
+ snapshot: DataCubeSnapshot,
337
+ sequence: V1_AppliedFunction[],
338
+ funcMap: DataCubeQueryFunctionMap,
339
+ configuration: DataCubeConfiguration,
340
+ engine: DataCubeEngine,
341
+ ) => void;
342
+ rootAggregation?:
343
+ | {
344
+ columnName: string;
345
+ }
346
+ | undefined;
347
+ pagination?:
348
+ | {
349
+ start: number;
350
+ end: number;
351
+ }
352
+ | undefined;
353
+ skipExecutionContext?: boolean;
354
+ },
355
+ ) {
356
+ const data = snapshot.data;
357
+ const configuration = DataCubeConfiguration.serialization.fromJson(
358
+ data.configuration,
359
+ );
360
+ const sequence: V1_AppliedFunction[] = [];
361
+ const funcMap: DataCubeQueryFunctionMap = {};
362
+ const _process = _functionCompositionProcessor(sequence, funcMap);
363
+
364
+ // ------------------------------ DIMENSIONS -------------------------------------
365
+
366
+ if (data.tree && nodes.length > 0) {
367
+ const dimensionColNames = configuration.dimensions.dimensions.flatMap(
368
+ (col) => col.columns,
369
+ );
370
+
371
+ // removing columns which have been added to represent diimensions
372
+ configuration.columns = configuration.columns.map((col) => {
373
+ if (dimensionColNames.includes(col.name)) {
374
+ col.isSelected = false;
375
+ }
376
+ return col;
377
+ });
378
+
379
+ // Adding dimensions to the extended column
380
+ nodes.forEach((node) => {
381
+ const lambda = new V1_Lambda();
382
+ const parameter = new V1_Variable();
383
+ parameter.name = 'temp';
384
+ if (node.column === DIMENSIONAL_L0_COLUMN) {
385
+ const defaultValue = new V1_CString();
386
+ defaultValue.value = node.column;
387
+ lambda.body = [defaultValue];
388
+ } else {
389
+ const defaultValue = new V1_AppliedProperty();
390
+ defaultValue.parameters = [parameter];
391
+ defaultValue.property = node.column;
392
+ lambda.body = [defaultValue];
393
+ }
394
+ lambda.parameters = [parameter];
395
+ data.leafExtendedColumns.unshift({
396
+ name: node.dimension,
397
+ type: PRIMITIVE_TYPE.STRING,
398
+ mapFn: engine.serializeValueSpecification(lambda),
399
+ } satisfies DataCubeSnapshotExtendedColumn);
400
+ });
401
+
402
+ // Adding dimensions to selected list
403
+ data.selectColumns = data.selectColumns.filter(
404
+ (col) => !dimensionColNames.includes(col.name),
405
+ );
406
+
407
+ const dimensionCols = nodes.map((col) =>
408
+ _toCol({ name: col.dimension, type: PRIMITIVE_TYPE.STRING }),
409
+ );
410
+ data.selectColumns.unshift(...dimensionCols);
411
+
412
+ // Adding filter based on group by nodes
413
+ const groupByNodes = nodes.flatMap((x) => x.groupByNodes);
414
+ if (groupByNodes.length > 0) {
415
+ const filter = [
416
+ _flattenFilterSnapshot(nodes.flatMap((x) => x.groupByNodes)),
417
+ ];
418
+ if (data.filter) {
419
+ filter.push(data.filter);
420
+ data.filter = {
421
+ groupOperator: DataCubeQueryFilterGroupOperator.AND,
422
+ conditions: filter,
423
+ };
424
+ } else {
425
+ data.filter = filter.at(0);
426
+ }
427
+ }
428
+
429
+ // Adding dimensions to groupby
430
+ if (data.groupBy) {
431
+ data.groupBy.columns.unshift(...dimensionCols);
432
+ } else {
433
+ data.groupBy = {
434
+ columns: dimensionCols,
435
+ } satisfies DataCubeSnapshotGroupBy;
436
+ }
437
+ }
438
+
439
+ // --------------------------------- LEAF-LEVEL EXTEND ---------------------------------
440
+
441
+ if (data.leafExtendedColumns.length) {
442
+ _process(
443
+ 'leafExtend',
444
+ data.leafExtendedColumns.map((col) =>
445
+ _function(DataCubeFunction.EXTEND, [
446
+ _cols([
447
+ _colSpec(
448
+ col.name,
449
+ guaranteeType(
450
+ engine.deserializeValueSpecification(col.mapFn),
451
+ V1_Lambda,
452
+ ),
453
+ col.reduceFn
454
+ ? guaranteeType(
455
+ engine.deserializeValueSpecification(col.reduceFn),
456
+ V1_Lambda,
457
+ )
458
+ : undefined,
459
+ ),
460
+ ]),
461
+ ]),
462
+ ),
463
+ );
464
+ }
465
+
466
+ // --------------------------------- FILTER ---------------------------------
467
+
468
+ if (data.filter) {
469
+ _process(
470
+ 'filter',
471
+ _function(DataCubeFunction.FILTER, [
472
+ _lambda([_var()], [_filter(data.filter, engine.filterOperations)]),
473
+ ]),
474
+ );
475
+ }
476
+
477
+ // --------------------------------- SELECT ---------------------------------
478
+
479
+ if (data.selectColumns.length) {
480
+ _process(
481
+ 'select',
482
+ _function(DataCubeFunction.SELECT, [
483
+ _cols(data.selectColumns.map((col) => _colSpec(col.name))),
484
+ ]),
485
+ );
486
+ }
487
+
488
+ // --------------------------------- PIVOT ---------------------------------
489
+
490
+ // TODO: rethink pivots for dimensional grid mode
491
+
492
+ // --------------------------------- GROUP BY ---------------------------------
493
+
494
+ if (data.groupBy) {
495
+ const groupBy = data.groupBy;
496
+ if (configuration.showRootAggregation && options?.rootAggregation) {
497
+ sequence.push(_extendRootAggregation(options.rootAggregation.columnName));
498
+ }
499
+ _process(
500
+ 'groupBy',
501
+ _function(DataCubeFunction.GROUP_BY, [
502
+ _cols(groupBy.columns.map((col) => _colSpec(col.name))),
503
+ _cols(
504
+ _groupByAggCols(
505
+ groupBy.columns,
506
+ snapshot,
507
+ configuration,
508
+ engine.aggregateOperations,
509
+ ),
510
+ ),
511
+ ]),
512
+ );
513
+ _process(
514
+ 'groupBySort',
515
+ _function(DataCubeFunction.SORT, [
516
+ _collection(
517
+ groupBy.columns.map((col) =>
518
+ _function(
519
+ configuration.treeColumnSortDirection ===
520
+ DataCubeQuerySortDirection.ASCENDING
521
+ ? DataCubeFunction.ASCENDING
522
+ : DataCubeFunction.DESCENDING,
523
+ [_col(col.name)],
524
+ ),
525
+ ),
526
+ ),
527
+ ]),
528
+ );
529
+ }
530
+
531
+ // --------------------------------- GROUP-LEVEL EXTEND ---------------------------------
532
+
533
+ // TODO: implement group level extends for dimensional grid mode
534
+
535
+ // --------------------------------- SORT ---------------------------------
536
+
537
+ // TODO: implement sort for dimensional grid mode
538
+
539
+ // --------------------------------- LIMIT ---------------------------------
540
+
541
+ // TODO: rethink limits for dimensional grid mode
542
+
543
+ // --------------------------------- SLICE ---------------------------------
544
+
545
+ // TODO: rethink slice for dimensional grid mode
546
+ // slice can lead to incorrect view of data for dimensionality
547
+
548
+ // --------------------------------- FINALIZE ---------------------------------
549
+
550
+ if (!options?.skipExecutionContext) {
551
+ const executionContext = engine.buildExecutionContext(source);
552
+ if (executionContext) {
553
+ sequence.push(executionContext);
554
+ }
555
+ }
556
+
557
+ options?.postProcessor?.(snapshot, sequence, funcMap, configuration, engine);
558
+
559
+ if (sequence.length === 0) {
560
+ return source.query;
561
+ }
562
+ for (let i = 0; i < sequence.length; i++) {
563
+ at(sequence, i).parameters.unshift(
564
+ i === 0 ? source.query : at(sequence, i - 1),
565
+ );
566
+ }
567
+ return at(sequence, sequence.length - 1);
568
+ }
@@ -81,6 +81,7 @@ import {
81
81
  isPivotResultColumnName,
82
82
  getPivotResultColumnBaseColumnName,
83
83
  DEFAULT_ROOT_AGGREGATION_COLUMN_VALUE,
84
+ DataCubeQueryFilterOperator,
84
85
  } from './DataCubeQueryEngine.js';
85
86
  import type { DataCubeQueryFilterOperation } from './filter/DataCubeQueryFilterOperation.js';
86
87
  import type { DataCubeQueryAggregateOperation } from './aggregation/DataCubeQueryAggregateOperation.js';
@@ -88,6 +89,7 @@ import {
88
89
  DataCubeColumnConfiguration,
89
90
  type DataCubeConfiguration,
90
91
  } from './model/DataCubeConfiguration.js';
92
+ import type { DataCubeDimensionalGroupByNode } from '../view/grid/DataCubeGridDimensionalTree.js';
91
93
 
92
94
  // --------------------------------- UTILITIES ---------------------------------
93
95
 
@@ -645,3 +647,23 @@ export function _filter(
645
647
  return filterCondition.not ? _not(condition) : condition;
646
648
  }
647
649
  }
650
+
651
+ export function _flattenFilterSnapshot(
652
+ groupByNodes: DataCubeDimensionalGroupByNode[],
653
+ ): DataCubeSnapshotFilter {
654
+ const filterConditions = groupByNodes.map((node) => {
655
+ return {
656
+ name: node.column,
657
+ type: 'String', // Assuming all filters are strings — update if needed
658
+ operator: DataCubeQueryFilterOperator.EQUAL,
659
+ value: {
660
+ type: 'String',
661
+ value: node.filter,
662
+ } satisfies DataCubeOperationValue,
663
+ } satisfies DataCubeSnapshotFilterCondition;
664
+ });
665
+ return {
666
+ groupOperator: DataCubeQueryFilterGroupOperator.AND,
667
+ conditions: filterConditions,
668
+ } satisfies DataCubeSnapshotFilter;
669
+ }
@@ -273,6 +273,11 @@ export enum DataCubeGridMode {
273
273
  MULTIDIMENSIONAL = 'Multidimensional',
274
274
  }
275
275
 
276
+ export enum DataCubeClientModelOption {
277
+ ROW_DATA = 'rowData',
278
+ COLUMN_DEFS = 'columnDefs',
279
+ }
280
+
276
281
  export enum DataCubeTitleBarMenuItems {
277
282
  VIEW_SOURCE = 'View Source',
278
283
  RESET_TO_LATEST_SAVE = 'Reset to Latest Save',
@@ -409,6 +414,10 @@ export function ofDataType(
409
414
  return dataTypes.includes(getDataType(type));
410
415
  }
411
416
 
417
+ export function isDimensionalGridMode(gridMode: DataCubeGridMode) {
418
+ return gridMode === DataCubeGridMode.MULTIDIMENSIONAL;
419
+ }
420
+
412
421
  export function isPrimitiveType(type: string) {
413
422
  return (
414
423
  [
@@ -29,6 +29,7 @@ import type {
29
29
  DataCubeQuerySortDirection,
30
30
  } from './DataCubeQueryEngine.js';
31
31
  import type { DataCubeColumn } from './model/DataCubeColumn.js';
32
+ import type { DataCubeDimensionalTree } from '../view/grid/DataCubeGridDimensionalTree.js';
32
33
 
33
34
  export type DataCubeSnapshotFilterCondition = DataCubeColumn & {
34
35
  value: DataCubeOperationValue;
@@ -85,6 +86,7 @@ export type DataCubeSnapshotData = {
85
86
  groupExtendedColumns: DataCubeSnapshotExtendedColumn[];
86
87
  sortColumns: DataCubeSnapshotSortColumn[];
87
88
  limit: number | undefined;
89
+ tree?: DataCubeDimensionalTree | undefined;
88
90
  };
89
91
 
90
92
  export class DataCubeSnapshot {
@@ -226,13 +226,14 @@ export class DataCubeEditorDimensionsPanelState
226
226
  name: dimension.name,
227
227
  columns: dimension.columns.map((column) => column.name),
228
228
  }));
229
+ newSnapshot.data.configuration = {
230
+ ...newSnapshot.data.configuration,
231
+ dimensions: DataCubeDimensionsConfiguration.serialization.toJson(
232
+ dimensionsConfiguration,
233
+ ),
234
+ };
235
+ } else {
236
+ newSnapshot = baseSnapshot;
229
237
  }
230
-
231
- newSnapshot.data.configuration = {
232
- ...newSnapshot.data.configuration,
233
- dimensions: DataCubeDimensionsConfiguration.serialization.toJson(
234
- dimensionsConfiguration,
235
- ),
236
- };
237
238
  }
238
239
  }
@@ -25,6 +25,7 @@ import {
25
25
  IllegalStateError,
26
26
  assertErrorThrown,
27
27
  guaranteeNonNullable,
28
+ guaranteeType,
28
29
  hashObject,
29
30
  isBoolean,
30
31
  isNonNullable,
@@ -35,11 +36,13 @@ import {
35
36
  PRIMITIVE_TYPE,
36
37
  PureClientVersion,
37
38
  type TabularDataSet,
39
+ type TDSRow,
38
40
  } from '@finos/legend-graph';
39
41
  import { makeObservable, observable, runInAction } from 'mobx';
40
- import type {
42
+ import {
41
43
  DataCubeConfiguration,
42
- DataCubeConfigurationColorKey,
44
+ type DataCubeConfigurationColorKey,
45
+ type DataCubeDimensionConfiguration,
43
46
  } from '../../core/model/DataCubeConfiguration.js';
44
47
  import { type DataCubeSnapshot } from '../../core/DataCubeSnapshot.js';
45
48
  import { _findCol, _sortByColName } from '../../core/model/DataCubeColumn.js';
@@ -47,7 +50,10 @@ import { isPivotResultColumnName } from '../../core/DataCubeQueryEngine.js';
47
50
  import { buildSnapshotFromGridState } from './DataCubeGridSnapshotBuilder.js';
48
51
  import { AlertType } from '../../services/DataCubeAlertService.js';
49
52
  import type { DataCubeViewState } from '../DataCubeViewState.js';
50
- import { buildGridDataFetchExecutableQuery } from './DataCubeGridQueryBuilder.js';
53
+ import {
54
+ buildDimensionalGridDataFetchExecutableQuery,
55
+ buildGridDataFetchExecutableQuery,
56
+ } from './DataCubeGridQueryBuilder.js';
51
57
  import { DataCubeSettingKey } from '../../../__lib__/DataCubeSetting.js';
52
58
  import { DEFAULT_ALERT_WINDOW_CONFIG } from '../../services/DataCubeLayoutService.js';
53
59
  import {
@@ -57,8 +63,22 @@ import {
57
63
  import { _lambda } from '../../core/DataCubeQueryBuilderUtils.js';
58
64
  import { sum } from 'mathjs';
59
65
  import { DataCubeEvent } from '../../../__lib__/DataCubeEvent.js';
60
-
61
- type DataCubeGridClientCellValue = string | number | boolean | null | undefined;
66
+ import { generateColumnDefsForDimensions } from './DataCubeGridConfigurationBuilder.js';
67
+ import {
68
+ DataCubeDimensionalMetadata,
69
+ generateDimensionalPaths,
70
+ type DataCubeDimensionalNode,
71
+ type DataCubeDimensionalGroupByNode,
72
+ DIMENSIONAL_L0_COLUMN,
73
+ } from './DataCubeGridDimensionalTree.js';
74
+
75
+ type DataCubeGridClientCellValue =
76
+ | string
77
+ | number
78
+ | boolean
79
+ | Map<string, DataCubeDimensionalMetadata>
80
+ | null
81
+ | undefined;
62
82
  type DataCubeGridClientRowData = {
63
83
  [key: string]: DataCubeGridClientCellValue;
64
84
  };
@@ -259,6 +279,136 @@ function buildRowData(
259
279
  });
260
280
  }
261
281
 
282
+ function buildDimensionalRowData(
283
+ result: TabularDataSet,
284
+ snapshot: DataCubeSnapshot,
285
+ dimensionalNodes: DataCubeDimensionalNode[],
286
+ rowData: DataCubeGridClientRowData[],
287
+ dimensions: DataCubeDimensionConfiguration[],
288
+ ): DataCubeGridClientRowData[] {
289
+ const newRows = result.rows.map((rawRow) =>
290
+ _createClientRow(rawRow, result.columns, dimensionalNodes, dimensions),
291
+ );
292
+
293
+ const updated = [...rowData];
294
+ for (const newRow of newRows) {
295
+ const insertIndex = _findInsertIndex(newRow, updated, dimensionalNodes);
296
+ updated.splice(insertIndex, 0, newRow);
297
+ }
298
+
299
+ return updated;
300
+ }
301
+
302
+ function _createClientRow(
303
+ rawRow: TDSRow,
304
+ columns: string[],
305
+ nodes: DataCubeDimensionalNode[],
306
+ configs: DataCubeDimensionConfiguration[],
307
+ ): DataCubeGridClientRowData {
308
+ const row: DataCubeGridClientRowData = {};
309
+ rawRow.values.forEach((value, i) => {
310
+ row[guaranteeNonNullable(columns[i])] = isBoolean(value)
311
+ ? String(value)
312
+ : isNonNullable(value)
313
+ ? value
314
+ : INTERNAL__GRID_CLIENT_MISSING_VALUE;
315
+ });
316
+
317
+ const metadata = new Map<string, DataCubeDimensionalMetadata>();
318
+ for (const node of nodes) {
319
+ const config = guaranteeNonNullable(
320
+ configs.find((d) => d.name === node.dimension),
321
+ );
322
+ metadata.set(
323
+ node.dimension,
324
+ new DataCubeDimensionalMetadata(
325
+ node.column,
326
+ config.columns.indexOf(node.column),
327
+ node.groupByNodes,
328
+ ),
329
+ );
330
+ }
331
+ row.metadata = metadata;
332
+ return row;
333
+ }
334
+
335
+ function _findInsertIndex(
336
+ newRow: DataCubeGridClientRowData,
337
+ existing: DataCubeGridClientRowData[],
338
+ dimensionalNodes: DataCubeDimensionalNode[],
339
+ ): number {
340
+ const metadata = guaranteeType(
341
+ newRow.metadata,
342
+ Map<string, DataCubeDimensionalMetadata>,
343
+ );
344
+ const groupByNodes = [...metadata.values()].flatMap((m) => m.groupByNodes);
345
+ const dimensions = dimensionalNodes.map((d) => d.dimension);
346
+ const latest = _latestGroupByFilters(groupByNodes);
347
+
348
+ for (let i = existing.length - 1; i >= 0; i--) {
349
+ const existingRow = guaranteeNonNullable(existing[i]);
350
+ if (
351
+ _isParentRow(existingRow, groupByNodes.length, newRow, latest, dimensions)
352
+ ) {
353
+ return i + 1;
354
+ }
355
+ }
356
+
357
+ return existing.length;
358
+ }
359
+
360
+ function _isParentRow(
361
+ existing: DataCubeGridClientRowData,
362
+ groupByCount: number,
363
+ newRow: DataCubeGridClientRowData,
364
+ filters: DataCubeDimensionalGroupByNode[],
365
+ dimensions: string[],
366
+ ): boolean {
367
+ if (groupByCount === 0) {
368
+ let allCount = 0;
369
+ const match = dimensions.every((d) => {
370
+ const e = existing[d],
371
+ n = newRow[d];
372
+ if (e === null || n === null) {
373
+ return false;
374
+ }
375
+ if (String(e) === DIMENSIONAL_L0_COLUMN) {
376
+ allCount++;
377
+ return true;
378
+ }
379
+ return String(e) === String(n);
380
+ });
381
+ return match && allCount >= 1 && allCount < dimensions.length;
382
+ }
383
+
384
+ return dimensions.every((d) => {
385
+ const filter = filters.find((f) => f.dimension === d);
386
+ const e = existing[d],
387
+ n = newRow[d];
388
+ if (e === null || n === null) {
389
+ return false;
390
+ }
391
+ if (filter) {
392
+ return (
393
+ String(e) === String(filter.filter) ||
394
+ String(e) === String(n) ||
395
+ String(e) === DIMENSIONAL_L0_COLUMN
396
+ );
397
+ }
398
+ return String(e) === String(n);
399
+ });
400
+ }
401
+
402
+ function _latestGroupByFilters(
403
+ nodes: DataCubeDimensionalGroupByNode[],
404
+ ): DataCubeDimensionalGroupByNode[] {
405
+ const map = new Map<string, DataCubeDimensionalGroupByNode>();
406
+ for (const node of nodes) {
407
+ map.set(node.dimension, node);
408
+ }
409
+ return [...map.values()];
410
+ }
411
+
262
412
  async function getCastColumns(
263
413
  currentSnapshot: DataCubeSnapshot,
264
414
  view: DataCubeViewState,
@@ -302,6 +452,103 @@ async function getCastColumns(
302
452
  });
303
453
  }
304
454
 
455
+ export async function fetchDimensionalQueryRows(
456
+ snapshot: DataCubeSnapshot,
457
+ view: DataCubeViewState,
458
+ ) {
459
+ const task = view.taskService.newTask('Fetching data...');
460
+
461
+ let rowData: DataCubeGridClientRowData[] = [];
462
+
463
+ const configuration = DataCubeConfiguration.serialization.fromJson(
464
+ snapshot.data.configuration,
465
+ );
466
+
467
+ // upgrade here by not shooting extra sqls
468
+ const paths = generateDimensionalPaths(
469
+ guaranteeNonNullable(snapshot.data.tree),
470
+ );
471
+
472
+ for (const path of paths) {
473
+ const executableQuery = buildDimensionalGridDataFetchExecutableQuery(
474
+ snapshot,
475
+ view.source,
476
+ view.engine,
477
+ path,
478
+ );
479
+ let result: DataCubeExecutionResult;
480
+
481
+ try {
482
+ result = await view.engine.executeQuery(
483
+ _lambda([], [executableQuery]),
484
+ view.source,
485
+ {
486
+ debug: view.settingService.getBooleanValue(
487
+ DataCubeSettingKey.DEBUGGER__ENABLE_DEBUG_MODE,
488
+ ),
489
+ clientVersion: view.settingService.getBooleanValue(
490
+ DataCubeSettingKey.DEBUGGER__USE_DEV_CLIENT_PROTOCOL_VERSION,
491
+ )
492
+ ? PureClientVersion.VX_X_X
493
+ : undefined,
494
+ },
495
+ );
496
+
497
+ rowData = buildDimensionalRowData(
498
+ result.result.result,
499
+ snapshot,
500
+ path,
501
+ rowData,
502
+ configuration.dimensions.dimensions,
503
+ );
504
+
505
+ view.taskService.endTask(task);
506
+ } catch (error) {
507
+ assertErrorThrown(error);
508
+ if (error instanceof DataCubeExecutionError) {
509
+ view.alertService.alertExecutionError(error, {
510
+ message: `Data Fetch Failure: Can't execute query.`,
511
+ text: `Error: ${error.message}`,
512
+ });
513
+ } else {
514
+ view.alertService.alertError(error, {
515
+ message: `Data Fetch Failure: ${error.message}`,
516
+ });
517
+ }
518
+ view.taskService.endTask(task);
519
+ return {
520
+ rowData: [],
521
+ columnDefs: [],
522
+ };
523
+ }
524
+
525
+ if (
526
+ view.settingService.getBooleanValue(
527
+ DataCubeSettingKey.DEBUGGER__ENABLE_DEBUG_MODE,
528
+ )
529
+ ) {
530
+ view.engine.debugProcess(
531
+ `Execution`,
532
+ ['Query', result.executedQuery],
533
+ [
534
+ 'Stats',
535
+ `${rowData.length} rows, ${result.result.result.columns.length} columns`,
536
+ ],
537
+ [
538
+ 'SQL',
539
+ result.executedSQL ?? `-- Error: failed to extract executed SQL`,
540
+ ],
541
+ ['SQL Execution Time', result.executionTime],
542
+ );
543
+ }
544
+ }
545
+
546
+ return {
547
+ rowData: rowData,
548
+ columnDefs: generateColumnDefsForDimensions(snapshot, configuration),
549
+ };
550
+ }
551
+
305
552
  export type DataCubeGridClientDataFetchRequest = {
306
553
  startRow: number | undefined;
307
554
  endRow: number | undefined;