@finos/legend-query-builder 4.10.1 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. package/lib/__lib__/QueryBuilderDocumentation.d.ts +2 -1
  2. package/lib/__lib__/QueryBuilderDocumentation.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderDocumentation.js +1 -0
  4. package/lib/__lib__/QueryBuilderDocumentation.js.map +1 -1
  5. package/lib/__lib__/QueryBuilderEvent.d.ts +2 -1
  6. package/lib/__lib__/QueryBuilderEvent.d.ts.map +1 -1
  7. package/lib/__lib__/QueryBuilderEvent.js +1 -0
  8. package/lib/__lib__/QueryBuilderEvent.js.map +1 -1
  9. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  10. package/lib/components/QueryBuilderResultPanel.js +165 -57
  11. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  12. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
  13. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +2 -1
  14. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
  15. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js +1 -1
  16. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js.map +1 -1
  17. package/lib/graph/QueryBuilderMetaModelConst.d.ts +22 -1
  18. package/lib/graph/QueryBuilderMetaModelConst.d.ts.map +1 -1
  19. package/lib/graph/QueryBuilderMetaModelConst.js +24 -0
  20. package/lib/graph/QueryBuilderMetaModelConst.js.map +1 -1
  21. package/lib/index.css +2 -2
  22. package/lib/index.css.map +1 -1
  23. package/lib/index.d.ts +2 -1
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +2 -1
  26. package/lib/index.js.map +1 -1
  27. package/lib/package.json +1 -1
  28. package/lib/stores/QueryBuilderPreviewDataHelper.d.ts.map +1 -1
  29. package/lib/stores/QueryBuilderPreviewDataHelper.js +2 -1
  30. package/lib/stores/QueryBuilderPreviewDataHelper.js.map +1 -1
  31. package/lib/stores/QueryBuilderResultState.d.ts +8 -3
  32. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  33. package/lib/stores/QueryBuilderResultState.js +11 -30
  34. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  35. package/lib/stores/QueryBuilderState.d.ts +2 -0
  36. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  37. package/lib/stores/QueryBuilderState.js +8 -0
  38. package/lib/stores/QueryBuilderState.js.map +1 -1
  39. package/lib/stores/QueryBuilderTypeaheadHelper.d.ts.map +1 -1
  40. package/lib/stores/QueryBuilderTypeaheadHelper.js +1 -1
  41. package/lib/stores/QueryBuilderTypeaheadHelper.js.map +1 -1
  42. package/lib/stores/fetch-structure/tds/QueryBuilderTDSHelper.d.ts +4 -1
  43. package/lib/stores/fetch-structure/tds/QueryBuilderTDSHelper.d.ts.map +1 -1
  44. package/lib/stores/fetch-structure/tds/QueryBuilderTDSHelper.js +37 -3
  45. package/lib/stores/fetch-structure/tds/QueryBuilderTDSHelper.js.map +1 -1
  46. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts +1 -4
  47. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.d.ts.map +1 -1
  48. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js +1 -5
  49. package/lib/stores/fetch-structure/tds/QueryResultSetModifierState.js.map +1 -1
  50. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.d.ts +1 -1
  51. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.d.ts.map +1 -1
  52. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js +2 -2
  53. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js.map +1 -1
  54. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.d.ts +3 -2
  55. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.d.ts.map +1 -1
  56. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js +9 -0
  57. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js.map +1 -1
  58. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_DistinctCount.d.ts.map +1 -1
  59. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_DistinctCount.js +2 -4
  60. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_DistinctCount.js.map +1 -1
  61. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.d.ts +2 -1
  62. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.d.ts.map +1 -1
  63. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts +1 -15
  64. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts.map +1 -1
  65. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js +3 -44
  66. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
  67. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.d.ts.map +1 -1
  68. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.js +13 -3
  69. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.js.map +1 -1
  70. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.d.ts.map +1 -1
  71. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.js +3 -2
  72. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.js.map +1 -1
  73. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.d.ts +2 -1
  74. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.d.ts.map +1 -1
  75. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js +2 -1
  76. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js.map +1 -1
  77. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.d.ts.map +1 -1
  78. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.js +2 -2
  79. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.js.map +1 -1
  80. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.d.ts.map +1 -1
  81. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.js +3 -10
  82. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.js.map +1 -1
  83. package/lib/stores/fetch-structure/tds/window/QueryBuilderWindowState.d.ts +1 -1
  84. package/lib/stores/fetch-structure/tds/window/QueryBuilderWindowState.d.ts.map +1 -1
  85. package/package.json +5 -5
  86. package/src/__lib__/QueryBuilderDocumentation.ts +1 -0
  87. package/src/__lib__/QueryBuilderEvent.ts +1 -0
  88. package/src/components/QueryBuilderResultPanel.tsx +269 -90
  89. package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +2 -4
  90. package/src/components/fetch-structure/QueryBuilderTDSWindowPanel.tsx +1 -1
  91. package/src/graph/QueryBuilderMetaModelConst.ts +25 -0
  92. package/src/index.ts +9 -2
  93. package/src/stores/QueryBuilderPreviewDataHelper.ts +2 -4
  94. package/src/stores/QueryBuilderResultState.ts +17 -34
  95. package/src/stores/QueryBuilderState.ts +12 -0
  96. package/src/stores/QueryBuilderTypeaheadHelper.ts +2 -4
  97. package/src/stores/fetch-structure/tds/QueryBuilderTDSHelper.ts +58 -2
  98. package/src/stores/fetch-structure/tds/QueryResultSetModifierState.ts +1 -5
  99. package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.ts +5 -9
  100. package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.ts +16 -2
  101. package/src/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_DistinctCount.ts +2 -4
  102. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterOperator.ts +1 -1
  103. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +1 -50
  104. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterStateBuilder.ts +22 -4
  105. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperatorValueSpecificationBuilder.ts +4 -4
  106. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.ts +1 -1
  107. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts +5 -5
  108. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.ts +5 -15
  109. package/src/stores/fetch-structure/tds/window/QueryBuilderWindowState.ts +1 -1
@@ -37,6 +37,8 @@ import {
37
37
  ModalFooterButton,
38
38
  ModalHeader,
39
39
  PanelDivider,
40
+ SquareIcon,
41
+ CheckSquareIcon,
40
42
  } from '@finos/legend-art';
41
43
  import { format as formatSQL } from 'sql-formatter';
42
44
  import { observer } from 'mobx-react-lite';
@@ -52,6 +54,8 @@ import {
52
54
  EnumValueInstanceValue,
53
55
  EnumValueExplicitReference,
54
56
  RelationalExecutionActivities,
57
+ getTDSRowRankByColumnInAsc,
58
+ PRIMITIVE_TYPE,
55
59
  } from '@finos/legend-graph';
56
60
  import {
57
61
  ActionAlertActionType,
@@ -62,6 +66,8 @@ import {
62
66
  import {
63
67
  assertErrorThrown,
64
68
  guaranteeNonNullable,
69
+ isBoolean,
70
+ type PlainObject,
65
71
  prettyDuration,
66
72
  filterByType,
67
73
  isValidURL,
@@ -97,6 +103,7 @@ import { PARAMETER_SUBMIT_ACTION } from '../stores/shared/LambdaParameterState.j
97
103
  import { QUERY_BUILDER_TEST_ID } from '../__lib__/QueryBuilderTesting.js';
98
104
  import {
99
105
  DataGrid,
106
+ type DataGridColumnApi,
100
107
  type DataGridCellRendererParams,
101
108
  type DataGridColumnDefinition,
102
109
  } from '@finos/legend-lego/data-grid';
@@ -116,6 +123,8 @@ import {
116
123
  } from '../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js';
117
124
  import { QueryUsageViewer } from './QueryUsageViewer.js';
118
125
  import { DEFAULT_LOCALE } from '../graph-manager/QueryBuilderConst.js';
126
+ import { DocumentationLink } from '@finos/legend-lego/application';
127
+ import { QUERY_BUILDER_DOCUMENTATION_KEY } from '../__lib__/QueryBuilderDocumentation.js';
119
128
 
120
129
  export const tryToFormatSql = (sql: string): string => {
121
130
  try {
@@ -417,7 +426,9 @@ const QueryBuilderGridResultContextMenu = observer(
417
426
  ),
418
427
  );
419
428
 
420
- const findSelectedCellRowData = (): string => {
429
+ const findRowFromRowIndex = (
430
+ rowIndex: number,
431
+ ): (string | number | boolean | null)[] => {
421
432
  if (
422
433
  !tdsState.queryBuilderState.resultState.executionResult ||
423
434
  !(
@@ -425,31 +436,21 @@ const QueryBuilderGridResultContextMenu = observer(
425
436
  TDSExecutionResult
426
437
  )
427
438
  ) {
428
- return '';
439
+ return [''];
429
440
  }
430
- const rowData = tdsState.queryBuilderState.resultState.rowData.find(
431
- (rData) =>
432
- rData.rowNumber ===
433
- tdsState.queryBuilderState.resultState.selectedCells[0]?.coordinates
434
- .rowIndex,
441
+ return (
442
+ tdsState.queryBuilderState.resultState.executionResult.result.rows[
443
+ rowIndex
444
+ ]?.values ?? ['']
435
445
  );
436
- // try to get the entire row value separated by comma
437
- // rowData is in format of {columnName: value, columnName1: value, ...., rowNumber:value}
438
- const valueArr: (string | number | boolean | null | undefined)[] = [];
439
- if (rowData) {
440
- Object.entries(rowData).forEach((entry) => {
441
- if (entry[0] !== 'rowNumber') {
442
- valueArr.push(entry[1]);
443
- }
444
- });
445
- return valueArr.join(',');
446
- }
447
- return '';
448
446
  };
449
447
 
450
448
  const handleCopyRowValue = applicationStore.guardUnhandledError(() =>
451
449
  applicationStore.clipboardService.copyTextToClipboard(
452
- findSelectedCellRowData(),
450
+ findRowFromRowIndex(
451
+ tdsState.queryBuilderState.resultState.selectedCells[0]?.coordinates
452
+ .rowIndex ?? 0,
453
+ ).toString(),
453
454
  ),
454
455
  );
455
456
 
@@ -507,18 +508,18 @@ const QueryResultCellRenderer = observer(
507
508
  isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
508
509
  const columnName = params.column?.getColId() ?? '';
509
510
  const findCoordinatesFromResultValue = (
510
- colName: string,
511
+ colId: string,
511
512
  rowNumber: number,
512
513
  ): QueryBuilderTDSResultCellCoordinate => {
513
514
  const colIndex = tdsExecutionResult.result.columns.findIndex(
514
- (col) => col === colName,
515
+ (col) => col === colId,
515
516
  );
516
517
  return { rowIndex: rowNumber, colIndex: colIndex };
517
518
  };
518
519
 
519
520
  const currentCellCoordinates = findCoordinatesFromResultValue(
520
521
  columnName,
521
- params.data.rowNumber,
522
+ params.rowIndex,
522
523
  );
523
524
  const cellInFilteredResults = resultState.selectedCells.some(
524
525
  (result) =>
@@ -526,9 +527,9 @@ const QueryResultCellRenderer = observer(
526
527
  result.coordinates.rowIndex === currentCellCoordinates.rowIndex,
527
528
  );
528
529
 
529
- const findColumnNameFromColumnIndex = (
530
+ const findColumnFromCoordinates = (
530
531
  colIndex: number,
531
- ): string | undefined => {
532
+ ): string | number | boolean | null | undefined => {
532
533
  if (
533
534
  !resultState.executionResult ||
534
535
  !(resultState.executionResult instanceof TDSExecutionResult)
@@ -539,12 +540,30 @@ const QueryResultCellRenderer = observer(
539
540
  };
540
541
 
541
542
  const findResultValueFromCoordinates = (
542
- rowIndex: number,
543
- colName: string,
544
- ): string | number | boolean | null | undefined =>
545
- resultState.rowData.find((data) => data.rowNumber === rowIndex)![
546
- colName
547
- ] as string | number | boolean | null | undefined;
543
+ resultCoordinate: [number, number],
544
+ ): string | number | boolean | null | undefined => {
545
+ const rowIndex = resultCoordinate[0];
546
+ const colIndex = resultCoordinate[1];
547
+
548
+ if (
549
+ !resultState.executionResult ||
550
+ !(resultState.executionResult instanceof TDSExecutionResult)
551
+ ) {
552
+ return undefined;
553
+ }
554
+ if (params.columnApi.getColumnState()[colIndex]?.sort === 'asc') {
555
+ resultState.executionResult.result.rows.sort((a, b) =>
556
+ getTDSRowRankByColumnInAsc(a, b, colIndex),
557
+ );
558
+ } else if (params.columnApi.getColumnState()[colIndex]?.sort === 'desc') {
559
+ resultState.executionResult.result.rows.sort((a, b) =>
560
+ getTDSRowRankByColumnInAsc(b, a, colIndex),
561
+ );
562
+ }
563
+ return resultState.executionResult.result.rows[rowIndex]?.values[
564
+ colIndex
565
+ ];
566
+ };
548
567
 
549
568
  const isCoordinatesSelected = (
550
569
  resultCoordinate: QueryBuilderTDSResultCellCoordinate,
@@ -561,12 +580,12 @@ const QueryResultCellRenderer = observer(
561
580
  if (event.shiftKey) {
562
581
  const coordinates = findCoordinatesFromResultValue(
563
582
  columnName,
564
- params.data.rowNumber,
583
+ params.rowIndex,
565
584
  );
566
- const actualValue = findResultValueFromCoordinates(
585
+ const actualValue = findResultValueFromCoordinates([
567
586
  coordinates.rowIndex,
568
- columnName,
569
- );
587
+ coordinates.colIndex,
588
+ ]);
570
589
  resultState.addSelectedCell({
571
590
  value: actualValue,
572
591
  columnName: columnName,
@@ -580,12 +599,12 @@ const QueryResultCellRenderer = observer(
580
599
  resultState.setSelectedCells([]);
581
600
  const coordinates = findCoordinatesFromResultValue(
582
601
  columnName,
583
- params.data.rowNumber,
602
+ params.rowIndex,
584
603
  );
585
- const actualValue = findResultValueFromCoordinates(
604
+ const actualValue = findResultValueFromCoordinates([
586
605
  coordinates.rowIndex,
587
- columnName,
588
- );
606
+ coordinates.colIndex,
607
+ ]);
589
608
  resultState.setSelectedCells([
590
609
  {
591
610
  value: actualValue,
@@ -599,14 +618,14 @@ const QueryResultCellRenderer = observer(
599
618
  if (event.button === 2) {
600
619
  const coordinates = findCoordinatesFromResultValue(
601
620
  columnName,
602
- params.data.rowNumber,
621
+ params.rowIndex,
603
622
  );
604
623
  const isInSelected = isCoordinatesSelected(coordinates);
605
624
  if (!isInSelected) {
606
- const actualValue = findResultValueFromCoordinates(
625
+ const actualValue = findResultValueFromCoordinates([
607
626
  coordinates.rowIndex,
608
- columnName,
609
- );
627
+ coordinates.colIndex,
628
+ ]);
610
629
  resultState.setSelectedCells([
611
630
  {
612
631
  value: actualValue,
@@ -636,7 +655,7 @@ const QueryResultCellRenderer = observer(
636
655
  const firstCorner = results.coordinates;
637
656
  const secondCorner = findCoordinatesFromResultValue(
638
657
  columnName,
639
- params.data.rowNumber,
658
+ params.rowIndex,
640
659
  );
641
660
 
642
661
  resultState.setSelectedCells([results]);
@@ -648,13 +667,11 @@ const QueryResultCellRenderer = observer(
648
667
 
649
668
  for (let x = minRow; x <= maxRow; x++) {
650
669
  for (let y = minCol; y <= maxCol; y++) {
651
- const actualValue = findResultValueFromCoordinates(
652
- x,
653
- findColumnNameFromColumnIndex(y) as string,
654
- );
670
+ const actualValue = findResultValueFromCoordinates([x, y]);
671
+
655
672
  const valueAndColumnId = {
656
673
  value: actualValue,
657
- columnName: findColumnNameFromColumnIndex(y),
674
+ columnName: findColumnFromCoordinates(y),
658
675
  coordinates: {
659
676
  rowIndex: x,
660
677
  colIndex: y,
@@ -673,6 +690,7 @@ const QueryResultCellRenderer = observer(
673
690
  }
674
691
  }
675
692
  }
693
+
676
694
  resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
677
695
  };
678
696
 
@@ -720,6 +738,40 @@ const QueryResultCellRenderer = observer(
720
738
  },
721
739
  );
722
740
 
741
+ const getColumnCustomizations = (
742
+ result: TDSExecutionResult,
743
+ columnName: string,
744
+ ): object | undefined => {
745
+ const columnType = result.builder.columns.find(
746
+ (col) => col.name === columnName,
747
+ )?.type;
748
+ switch (columnType) {
749
+ case PRIMITIVE_TYPE.STRING:
750
+ return {
751
+ filter: 'agTextColumnFilter',
752
+ allowedAggFuncs: ['count'],
753
+ };
754
+ case PRIMITIVE_TYPE.DATE:
755
+ case PRIMITIVE_TYPE.DATETIME:
756
+ case PRIMITIVE_TYPE.STRICTDATE:
757
+ return {
758
+ filter: 'agDateColumnFilter',
759
+ allowedAggFuncs: ['count'],
760
+ };
761
+ case PRIMITIVE_TYPE.DECIMAL:
762
+ case PRIMITIVE_TYPE.INTEGER:
763
+ case PRIMITIVE_TYPE.FLOAT:
764
+ return {
765
+ filter: 'agNumberColumnFilter',
766
+ allowedAggFuncs: ['count', 'sum', 'max', 'min', 'avg'],
767
+ };
768
+ default:
769
+ return {
770
+ allowedAggFuncs: ['count'],
771
+ };
772
+ }
773
+ };
774
+
723
775
  const QueryBuilderGridResult = observer(
724
776
  (props: {
725
777
  executionResult: TDSExecutionResult;
@@ -727,7 +779,79 @@ const QueryBuilderGridResult = observer(
727
779
  }) => {
728
780
  const { executionResult, queryBuilderState } = props;
729
781
 
782
+ const [columnAPi, setColumnApi] = useState<DataGridColumnApi | undefined>(
783
+ undefined,
784
+ );
730
785
  const resultState = queryBuilderState.resultState;
786
+ const isAdvancedModeEnabled = queryBuilderState.isAdvancedModeEnabled;
787
+ const colDefs = isAdvancedModeEnabled
788
+ ? executionResult.result.columns.map((colName) => {
789
+ const col = {
790
+ minWidth: 50,
791
+ sortable: true,
792
+ resizable: true,
793
+ field: colName,
794
+ flex: 1,
795
+ enablePivot: true,
796
+ enableRowGroup: true,
797
+ enableValue: true,
798
+ ...getColumnCustomizations(executionResult, colName),
799
+ } as DataGridColumnDefinition;
800
+ const persistedColumn = resultState.gridConfig.columns.find(
801
+ (c) => c.colId === colName,
802
+ );
803
+ if (persistedColumn) {
804
+ if (persistedColumn.width) {
805
+ col.width = persistedColumn.width;
806
+ }
807
+ col.pinned = persistedColumn.pinned ?? null;
808
+ col.rowGroup = persistedColumn.rowGroup ?? false;
809
+ col.rowGroupIndex = persistedColumn.rowGroupIndex ?? null;
810
+ col.aggFunc = persistedColumn.aggFunc ?? null;
811
+ col.pivot = persistedColumn.pivot ?? false;
812
+ col.hide = persistedColumn.hide ?? false;
813
+ }
814
+ return col;
815
+ })
816
+ : executionResult.result.columns.map(
817
+ (colName) =>
818
+ ({
819
+ minWidth: 50,
820
+ sortable: true,
821
+ resizable: true,
822
+ field: colName,
823
+ flex: 1,
824
+ cellRenderer: QueryResultCellRenderer,
825
+ cellRendererParams: {
826
+ resultState: resultState,
827
+ tdsExecutionResult: executionResult,
828
+ },
829
+ }) as DataGridColumnDefinition,
830
+ );
831
+ const sideBar = isAdvancedModeEnabled ? ['columns', 'filters'] : null;
832
+
833
+ const rowData = executionResult.result.rows.map((_row, rowIdx) => {
834
+ const row: PlainObject = {};
835
+ const cols = executionResult.result.columns;
836
+ _row.values.forEach((value, colIdx) => {
837
+ // `ag-grid` shows `false` value as empty string so we have
838
+ // call `.toString()` to avoid this behavior.
839
+ // See https://github.com/finos/legend-studio/issues/1008
840
+ row[cols[colIdx] as string] = isBoolean(value) ? String(value) : value;
841
+ });
842
+
843
+ row.rowNumber = rowIdx;
844
+ return row;
845
+ });
846
+ const onSaveGridColumnState = (): void => {
847
+ if (!columnAPi) {
848
+ return;
849
+ }
850
+ resultState.setGridConfig({
851
+ columns: columnAPi.getColumnState(),
852
+ isPivotModeEnabled: columnAPi.isPivotMode(),
853
+ });
854
+ };
731
855
 
732
856
  return (
733
857
  <div className="query-builder__result__values__table">
@@ -736,47 +860,57 @@ const QueryBuilderGridResult = observer(
736
860
  'ag-theme-balham-dark query-builder__result__tds-grid',
737
861
  )}
738
862
  >
739
- <DataGrid
740
- rowData={queryBuilderState.resultState.getRowData()}
741
- onSortChanged={(params) => {
742
- const sortedData: Record<
743
- string,
744
- string | number | boolean | null
745
- >[] = [];
746
- params.api.forEachNodeAfterFilterAndSort((node, index) => {
747
- node.rowIndex = index;
748
- // rowNumber has to be manually updated after sorting the column
749
- node.data.rowNumber = index;
750
- sortedData.push(node.data);
751
- });
752
- queryBuilderState.resultState.setRowData(sortedData);
753
- }}
754
- gridOptions={{
755
- suppressScrollOnNewData: true,
756
- getRowId: (data) => data.data.rowNumber,
757
- }}
758
- // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
759
- // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
760
- onRowDataUpdated={(params) => {
761
- params.api.refreshCells({ force: true });
762
- }}
763
- suppressFieldDotNotation={true}
764
- columnDefs={executionResult.result.columns.map(
765
- (colName) =>
766
- ({
767
- minWidth: 50,
768
- sortable: true,
769
- resizable: true,
770
- field: colName,
771
- flex: 1,
772
- cellRenderer: QueryResultCellRenderer,
773
- cellRendererParams: {
774
- resultState: resultState,
775
- tdsExecutionResult: executionResult,
776
- },
777
- }) as DataGridColumnDefinition,
778
- )}
779
- />
863
+ {isAdvancedModeEnabled ? (
864
+ <DataGrid
865
+ rowData={rowData}
866
+ onGridReady={(params): void => {
867
+ setColumnApi(params.columnApi);
868
+ params.columnApi.setPivotMode(
869
+ resultState.gridConfig.isPivotModeEnabled,
870
+ );
871
+ }}
872
+ gridOptions={{
873
+ suppressScrollOnNewData: true,
874
+ getRowId: (data) => data.data.rowNumber,
875
+ rowSelection: 'multiple',
876
+ pivotPanelShow: 'always',
877
+ rowGroupPanelShow: 'always',
878
+ }}
879
+ // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
880
+ // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
881
+ onRowDataUpdated={(params) => {
882
+ params.api.refreshCells({ force: true });
883
+ }}
884
+ suppressFieldDotNotation={true}
885
+ suppressContextMenu={!isAdvancedModeEnabled}
886
+ columnDefs={colDefs}
887
+ sideBar={sideBar}
888
+ onColumnVisible={onSaveGridColumnState}
889
+ onColumnPinned={onSaveGridColumnState}
890
+ onColumnResized={onSaveGridColumnState}
891
+ onColumnRowGroupChanged={onSaveGridColumnState}
892
+ onColumnValueChanged={onSaveGridColumnState}
893
+ onColumnPivotChanged={onSaveGridColumnState}
894
+ onColumnPivotModeChanged={onSaveGridColumnState}
895
+ />
896
+ ) : (
897
+ <DataGrid
898
+ rowData={rowData}
899
+ gridOptions={{
900
+ suppressScrollOnNewData: true,
901
+ getRowId: (data) => data.data.rowNumber,
902
+ rowSelection: 'multiple',
903
+ }}
904
+ // NOTE: when column definition changed, we need to force refresh the cell to make sure the cell renderer is updated
905
+ // See https://stackoverflow.com/questions/56341073/how-to-refresh-an-ag-grid-when-a-change-occurs-inside-a-custom-cell-renderer-com
906
+ onRowDataUpdated={(params) => {
907
+ params.api.refreshCells({ force: true });
908
+ }}
909
+ suppressFieldDotNotation={true}
910
+ suppressContextMenu={!isAdvancedModeEnabled}
911
+ columnDefs={colDefs}
912
+ />
913
+ )}
780
914
  </div>
781
915
  </div>
782
916
  );
@@ -893,6 +1027,11 @@ export const QueryBuilderResultPanel = observer(
893
1027
  const isQueryValid =
894
1028
  !queryBuilderState.isQuerySupported || isSupportedQueryValid;
895
1029
 
1030
+ const isQueryValidForAdvancedMode =
1031
+ isQueryValid &&
1032
+ queryBuilderState.fetchStructureState.implementation instanceof
1033
+ QueryBuilderTDSState;
1034
+
896
1035
  const runQuery = (): void => {
897
1036
  resultState.setSelectedCells([]);
898
1037
  resultState.pressedRunQuery.inProgress();
@@ -923,6 +1062,7 @@ export const QueryBuilderResultPanel = observer(
923
1062
  );
924
1063
 
925
1064
  const allowSettingPreviewLimit = queryBuilderState.isQuerySupported;
1065
+ const allowSettingAdvancedMode = queryBuilderState.isQuerySupported;
926
1066
 
927
1067
  const copyExpression = (value: string): void => {
928
1068
  applicationStore.clipboardService
@@ -995,6 +1135,13 @@ export const QueryBuilderResultPanel = observer(
995
1135
  }
996
1136
  };
997
1137
 
1138
+ const toggleIsAdvancedModeEnabled = (): void => {
1139
+ resultState.setExecutionResult(undefined);
1140
+ queryBuilderState.setIsAdvancedModeEnabled(
1141
+ !queryBuilderState.isAdvancedModeEnabled,
1142
+ );
1143
+ };
1144
+
998
1145
  return (
999
1146
  <div
1000
1147
  data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_PANEL}
@@ -1071,6 +1218,38 @@ export const QueryBuilderResultPanel = observer(
1071
1218
  )}
1072
1219
  </div>
1073
1220
  <div className="panel__header__actions query-builder__result__header__actions">
1221
+ {allowSettingAdvancedMode && (
1222
+ <div className="query-builder__result__advanced__mode">
1223
+ <div className="query-builder__result__advanced__mode__label">
1224
+ Advanced Mode
1225
+ <DocumentationLink
1226
+ title="The grid in advanced mode performs all operations like grouping, sorting, filtering, etc after initial query execution locally withought reaching out to server. This limits the number of rows to smaller number so they can fit in memory"
1227
+ documentationKey={
1228
+ QUERY_BUILDER_DOCUMENTATION_KEY.QUESTION_HOW_TO_USE_ADVANCED_GRID_MODE
1229
+ }
1230
+ />
1231
+ </div>
1232
+ <button
1233
+ className={clsx(
1234
+ 'query-builder__result__advanced__mode__toggler__btn',
1235
+ {
1236
+ 'query-builder__result__advanced__mode__toggler__btn--toggled':
1237
+ queryBuilderState.isAdvancedModeEnabled,
1238
+ },
1239
+ )}
1240
+ disabled={!isQueryValidForAdvancedMode}
1241
+ onClick={toggleIsAdvancedModeEnabled}
1242
+ tabIndex={-1}
1243
+ >
1244
+ {queryBuilderState.isAdvancedModeEnabled ? (
1245
+ <CheckSquareIcon />
1246
+ ) : (
1247
+ <SquareIcon />
1248
+ )}
1249
+ </button>
1250
+ </div>
1251
+ )}
1252
+
1074
1253
  {allowSettingPreviewLimit && (
1075
1254
  <div className="query-builder__result__limit">
1076
1255
  <div className="query-builder__result__limit__label">
@@ -32,14 +32,12 @@ import {
32
32
  MenuContentItem,
33
33
  ModalFooterButton,
34
34
  } from '@finos/legend-art';
35
- import {
36
- COLUMN_SORT_TYPE,
37
- SortColumnState,
38
- } from '../../stores/fetch-structure/tds/QueryResultSetModifierState.js';
35
+ import { SortColumnState } from '../../stores/fetch-structure/tds/QueryResultSetModifierState.js';
39
36
  import { guaranteeNonNullable } from '@finos/legend-shared';
40
37
  import { useApplicationStore } from '@finos/legend-application';
41
38
  import type { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
42
39
  import type { QueryBuilderTDSColumnState } from '../../stores/fetch-structure/tds/QueryBuilderTDSColumnState.js';
40
+ import { COLUMN_SORT_TYPE } from '../../graph/QueryBuilderMetaModelConst.js';
43
41
 
44
42
  const ColumnSortEditor = observer(
45
43
  (props: { tdsState: QueryBuilderTDSState; sortState: SortColumnState }) => {
@@ -66,9 +66,9 @@ import {
66
66
  import { QUERY_BUILDER_PROJECTION_COLUMN_DND_TYPE } from '../../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
67
67
  import type { QueryBuilderTDSColumnState } from '../../stores/fetch-structure/tds/QueryBuilderTDSColumnState.js';
68
68
  import type { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
69
- import { COLUMN_SORT_TYPE } from '../../stores/fetch-structure/tds/QueryResultSetModifierState.js';
70
69
  import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
71
70
  import { QueryBuilderPanelIssueCountBadge } from '../shared/QueryBuilderPanelIssueCountBadge.js';
71
+ import { COLUMN_SORT_TYPE } from '../../graph/QueryBuilderMetaModelConst.js';
72
72
 
73
73
  // helpers
74
74
  const createWindowColumnState = (
@@ -105,6 +105,7 @@ export enum QUERY_BUILDER_SUPPORTED_FUNCTIONS {
105
105
  TDS_GROUP_BY = 'meta::pure::tds::groupBy',
106
106
  TDS_SORT = 'meta::pure::tds::sort',
107
107
  TDS_TAKE = 'meta::pure::tds::take',
108
+ TDS_RESTRICT = 'meta::pure::tds::restrict',
108
109
  TDS_FUNC = 'meta::pure::tds::func',
109
110
 
110
111
  // filter
@@ -149,4 +150,28 @@ export enum QUERY_BUILDER_SUPPORTED_FUNCTIONS {
149
150
  FROM = 'meta::pure::mapping::from',
150
151
  CHECKED = 'meta::pure::dataQuality::checked',
151
152
  MERGERUNTIMES = 'meta::pure::runtime::mergeRuntimes',
153
+
154
+ // TOTDS
155
+ TABLE_TO_TDS = 'meta::pure::tds::tableToTDS',
156
+ TABLE_REFERENCE = 'meta::relational::functions::database::tableReference',
157
+ }
158
+
159
+ export enum TDS_COLUMN_GETTER {
160
+ GET_STRING = 'getString',
161
+ GET_NUMBER = 'getNumber',
162
+ GET_INTEGER = 'getInteger',
163
+ GET_FLOAT = 'getFloat',
164
+ GET_DECIMAL = 'getDecimal',
165
+ GET_DATE = 'getDate',
166
+ GET_DATETIME = 'getDateTime',
167
+ GET_STRICTDATE = 'getStrictDate',
168
+ GET_BOOLEAN = 'getBoolean',
169
+ GET_ENUM = 'getEnum',
170
+ IS_NULL = 'isNull',
171
+ IS_NOT_NULL = 'isNotNull',
172
+ }
173
+
174
+ export enum COLUMN_SORT_TYPE {
175
+ ASC = 'ASC',
176
+ DESC = 'DESC',
152
177
  }
package/src/index.ts CHANGED
@@ -29,7 +29,10 @@ export { QueryBuilderNavigationBlocker } from './components/QueryBuilderNavigati
29
29
  export { QueryBuilder } from './components/QueryBuilder.js';
30
30
  export { QUERY_BUILDER_COMPONENT_ELEMENT_ID } from './components/QueryBuilderComponentElement.js';
31
31
  export { QueryBuilderState } from './stores/QueryBuilderState.js';
32
-
32
+ export {
33
+ getTDSColumnDerivedProperyFromType,
34
+ buildTDSSortTypeExpression,
35
+ } from './stores/fetch-structure/tds/QueryBuilderTDSHelper.js';
33
36
  export { QueryBuilderPropertySearchState } from './stores/explorer/QueryBuilderPropertySearchState.js';
34
37
  export {
35
38
  QueryBuilderClassSelector,
@@ -43,7 +46,11 @@ export {
43
46
  type ServiceExecutionContext,
44
47
  ServiceQueryBuilderState,
45
48
  } from './stores/workflows/ServiceQueryBuilderState.js';
46
- export { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from './graph/QueryBuilderMetaModelConst.js';
49
+ export {
50
+ TDS_COLUMN_GETTER,
51
+ COLUMN_SORT_TYPE,
52
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS,
53
+ } from './graph/QueryBuilderMetaModelConst.js';
47
54
  export { getQueryBuilderGraphManagerExtension } from './graph-manager/protocol/pure/QueryBuilder_PureGraphManagerExtension.js';
48
55
  export type { ServiceExecutionAnalysisResult } from './graph-manager/action/analytics/ServiceExecutionAnalysis.js';
49
56
  export type { MappingRuntimeCompatibilityAnalysisResult } from './graph-manager/action/analytics/MappingRuntimeCompatibilityAnalysis.js';
@@ -29,12 +29,10 @@ import { QueryBuilderAggregateOperator_StdDev_Sample } from './fetch-structure/t
29
29
  import { QueryBuilderAggregateOperator_Sum } from './fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Sum.js';
30
30
  import { QueryBuilderSimpleProjectionColumnState } from './fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
31
31
  import type { QueryBuilderState } from './QueryBuilderState.js';
32
- import {
33
- COLUMN_SORT_TYPE,
34
- SortColumnState,
35
- } from './fetch-structure/tds/QueryResultSetModifierState.js';
32
+ import { SortColumnState } from './fetch-structure/tds/QueryResultSetModifierState.js';
36
33
  import type { QueryBuilderAggregateOperator } from './fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js';
37
34
  import { QueryBuilderTDSState } from './fetch-structure/tds/QueryBuilderTDSState.js';
35
+ import { COLUMN_SORT_TYPE } from '../graph/QueryBuilderMetaModelConst.js';
38
36
 
39
37
  const PREVIEW_DATA_TAKE_LIMIT = 10;
40
38
  const PREVIEW_DATA_NON_NUMERIC_VALUE_COLUMN_NAME = 'Value';