@finos/legend-query-builder 4.14.44 → 4.14.46

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 (71) hide show
  1. package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts +3 -3
  2. package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderTesting.d.ts +1 -0
  4. package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
  5. package/lib/__lib__/QueryBuilderTesting.js +1 -0
  6. package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
  7. package/lib/components/QueryBuilder.d.ts.map +1 -1
  8. package/lib/components/QueryBuilder.js +5 -2
  9. package/lib/components/QueryBuilder.js.map +1 -1
  10. package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.d.ts.map +1 -1
  11. package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.js.map +1 -1
  12. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  13. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +93 -2
  14. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  15. package/lib/graph/QueryBuilderMetaModelConst.d.ts +1 -0
  16. package/lib/graph/QueryBuilderMetaModelConst.d.ts.map +1 -1
  17. package/lib/graph/QueryBuilderMetaModelConst.js +1 -0
  18. package/lib/graph/QueryBuilderMetaModelConst.js.map +1 -1
  19. package/lib/index.css +2 -2
  20. package/lib/index.css.map +1 -1
  21. package/lib/index.d.ts +1 -1
  22. package/lib/index.d.ts.map +1 -1
  23. package/lib/package.json +5 -4
  24. package/lib/stores/QueryBuilderState.d.ts +5 -5
  25. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  26. package/lib/stores/QueryBuilderState.js.map +1 -1
  27. package/lib/stores/QueryBuilderStateHashUtils.d.ts +1 -0
  28. package/lib/stores/QueryBuilderStateHashUtils.d.ts.map +1 -1
  29. package/lib/stores/QueryBuilderStateHashUtils.js +1 -0
  30. package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
  31. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.d.ts.map +1 -1
  32. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js +2 -0
  33. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js.map +1 -1
  34. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.d.ts +2 -0
  35. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.d.ts.map +1 -1
  36. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js +8 -0
  37. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js.map +1 -1
  38. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.d.ts.map +1 -1
  39. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.js +2 -0
  40. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.js.map +1 -1
  41. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.d.ts +1 -0
  42. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.d.ts.map +1 -1
  43. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js +7 -3
  44. package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js.map +1 -1
  45. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperatorValueSpecificationBuilder.d.ts.map +1 -1
  46. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperatorValueSpecificationBuilder.js.map +1 -1
  47. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.d.ts +37 -0
  48. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.d.ts.map +1 -0
  49. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.js +123 -0
  50. package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.js.map +1 -0
  51. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts.map +1 -1
  52. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js +3 -7
  53. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
  54. package/package.json +13 -12
  55. package/src/__lib__/QueryBuilderTelemetryHelper.ts +3 -3
  56. package/src/__lib__/QueryBuilderTesting.ts +1 -0
  57. package/src/components/QueryBuilder.tsx +5 -2
  58. package/src/components/fetch-structure/QueryBuilderFetchStructurePanel.tsx +0 -1
  59. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +208 -4
  60. package/src/graph/QueryBuilderMetaModelConst.ts +1 -0
  61. package/src/index.ts +1 -1
  62. package/src/stores/QueryBuilderState.ts +5 -5
  63. package/src/stores/QueryBuilderStateHashUtils.ts +1 -0
  64. package/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts +5 -0
  65. package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.ts +10 -0
  66. package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.ts +2 -0
  67. package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.ts +9 -3
  68. package/src/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperatorValueSpecificationBuilder.ts +0 -1
  69. package/src/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.ts +241 -0
  70. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +3 -7
  71. package/tsconfig.json +1 -0
@@ -41,6 +41,8 @@ import {
41
41
  FunctionIcon,
42
42
  CogIcon,
43
43
  InfoCircleIcon,
44
+ BasePopover,
45
+ InputWithInlineValidation,
44
46
  } from '@finos/legend-art';
45
47
  import {
46
48
  type QueryBuilderExplorerTreeDragSource,
@@ -116,6 +118,7 @@ import {
116
118
  QueryBuilderPropertyInfoTooltip,
117
119
  } from '../shared/QueryBuilderPropertyInfoTooltip.js';
118
120
  import { getNameOfValueSpecification } from '../shared/QueryBuilderVariableSelector.js';
121
+ import { QueryBuilderAggregateOperator_Percentile } from '../../stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.js';
119
122
 
120
123
  const QueryBuilderProjectionColumnContextMenu = observer(
121
124
  forwardRef<
@@ -689,6 +692,52 @@ const QueryBuilderProjectionColumnEditor = observer(
689
692
  }),
690
693
  [handleDrop],
691
694
  );
695
+ const percentileButtonRef = useRef<HTMLButtonElement>(null);
696
+ const percentileInputRef = useRef<HTMLInputElement>(null);
697
+ const [isPercentileOpen, setIsPercentileOpen] = useState(false);
698
+ const [percentileValue, setPercentileValue] = useState(
699
+ aggregateColumnState &&
700
+ aggregateColumnState.operator instanceof
701
+ QueryBuilderAggregateOperator_Percentile &&
702
+ aggregateColumnState.operator.percentile !== undefined
703
+ ? (aggregateColumnState.operator.percentile * 100).toString()
704
+ : '',
705
+ );
706
+ const [acending, setAcending] = useState(
707
+ aggregateColumnState &&
708
+ aggregateColumnState.operator instanceof
709
+ QueryBuilderAggregateOperator_Percentile
710
+ ? aggregateColumnState.operator.acending
711
+ : undefined,
712
+ );
713
+ const [continuous, setContinuous] = useState(
714
+ aggregateColumnState &&
715
+ aggregateColumnState.operator instanceof
716
+ QueryBuilderAggregateOperator_Percentile
717
+ ? aggregateColumnState.operator.continuous
718
+ : undefined,
719
+ );
720
+ const percentileOptions = ['true', 'false'].map((op) => ({
721
+ label: op,
722
+ value: op,
723
+ }));
724
+ const getPercentileDisplayValue = (): string => {
725
+ if (percentileValue === '') {
726
+ return '...';
727
+ }
728
+ if (acending === undefined || continuous === undefined) {
729
+ return `${Number(percentileValue)}`;
730
+ }
731
+ return `${Number(percentileValue)}, ${acending}, ${continuous}`;
732
+ };
733
+ const setPercentileArguments = (): void => {
734
+ setIsPercentileOpen(!isPercentileOpen);
735
+ };
736
+ const onPercentileValueChange: React.ChangeEventHandler<
737
+ HTMLInputElement
738
+ > = (event) => {
739
+ setPercentileValue(event.target.value);
740
+ };
692
741
 
693
742
  return (
694
743
  <PanelDnDEntry
@@ -817,12 +866,167 @@ const QueryBuilderProjectionColumnEditor = observer(
817
866
  <div className="query-builder__projection__column__aggregate">
818
867
  <div className="query-builder__projection__column__aggregate__operator">
819
868
  {aggregateColumnState && (
820
- <div className="query-builder__projection__column__aggregate__operator__label">
821
- {aggregateColumnState.operator.getLabel(
822
- projectionColumnState,
823
- )}
869
+ <div className="query-builder__projection__column__aggregate__operator">
870
+ <div className="query-builder__projection__column__aggregate__operator__label">
871
+ {aggregateColumnState.operator.getLabel(
872
+ projectionColumnState,
873
+ )}
874
+ {aggregateColumnState.operator instanceof
875
+ QueryBuilderAggregateOperator_Percentile && (
876
+ <button
877
+ className="query-builder__projection__column__aggregate__operator__percentile__badge"
878
+ ref={percentileButtonRef}
879
+ onClick={setPercentileArguments}
880
+ title="Set Percentile Argument(s)..."
881
+ >
882
+ {getPercentileDisplayValue()}
883
+ </button>
884
+ )}
885
+ </div>
824
886
  </div>
825
887
  )}
888
+ {aggregateColumnState && isPercentileOpen && (
889
+ <BasePopover
890
+ open={isPercentileOpen}
891
+ PaperProps={{
892
+ classes: {
893
+ root: 'query-builder__projection__column__aggregate__operator__percentile__container__root',
894
+ },
895
+ }}
896
+ className={clsx(
897
+ 'query-builder__projection__column__aggregate__operator__percentile__container',
898
+ )}
899
+ anchorEl={percentileButtonRef.current}
900
+ onClose={() => {
901
+ const percentileOperator =
902
+ aggregateColumnState.operator as QueryBuilderAggregateOperator_Percentile;
903
+ if (percentileValue === '') {
904
+ percentileOperator.setPercentile(undefined);
905
+ } else {
906
+ percentileOperator.setPercentile(
907
+ Number(percentileValue) / 100,
908
+ );
909
+ }
910
+ if (acending !== undefined && continuous !== undefined) {
911
+ percentileOperator.setAcending(acending);
912
+ percentileOperator.setContinuous(continuous);
913
+ }
914
+ setIsPercentileOpen(false);
915
+ }}
916
+ anchorOrigin={{
917
+ vertical: 'bottom',
918
+ horizontal: 'left',
919
+ }}
920
+ transformOrigin={{
921
+ vertical: 'top',
922
+ horizontal: 'center',
923
+ }}
924
+ >
925
+ <div
926
+ data-testid={
927
+ QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PERCENTILE_PANEL
928
+ }
929
+ className=""
930
+ >
931
+ <div className="query-builder__projection__column__aggregate__operator__percentile__argument-combo">
932
+ <div className="query-builder__projection__column__aggregate__operator__percentile__argument-combo__label">
933
+ Percentile
934
+ <br />
935
+ (0-100)
936
+ </div>
937
+ <div
938
+ className={clsx(
939
+ 'query-builder__projection__column__aggregate__operator__percentile__argument-combo__value',
940
+ )}
941
+ >
942
+ <InputWithInlineValidation
943
+ ref={percentileInputRef}
944
+ className={clsx(
945
+ 'query-builder__projection__column__aggregate__operator__percentile__input input--dark',
946
+ )}
947
+ type="text"
948
+ inputMode="numeric"
949
+ onChange={onPercentileValueChange}
950
+ value={percentileValue}
951
+ error={
952
+ percentileValue && Number(percentileValue) > 100
953
+ ? `Invalid aggregation agruement for ${aggregateColumnState.columnName}`
954
+ : undefined
955
+ }
956
+ />
957
+ </div>
958
+ </div>
959
+ <div className="query-builder__projection__column__aggregate__operator__percentile__argument-combo">
960
+ <div className="query-builder__projection__column__aggregate__operator__percentile__argument-combo__label">
961
+ Acending
962
+ </div>
963
+ <CustomSelectorInput
964
+ className="query-loader__results__sort-by__selector query-builder__projection__column__aggregate__operator__percentile__argument-combo__value"
965
+ options={percentileOptions}
966
+ onChange={(option: {
967
+ label: string;
968
+ value: string;
969
+ }) => {
970
+ setAcending(option.value === 'true' ? true : false);
971
+ }}
972
+ value={{
973
+ label:
974
+ acending === undefined
975
+ ? ''
976
+ : acending
977
+ ? 'true'
978
+ : 'false',
979
+ value:
980
+ acending === undefined
981
+ ? ''
982
+ : acending
983
+ ? 'true'
984
+ : 'false',
985
+ }}
986
+ darkMode={
987
+ !applicationStore.layoutService
988
+ .TEMPORARY__isLightColorThemeEnabled
989
+ }
990
+ />
991
+ </div>
992
+ <div className="query-builder__projection__column__aggregate__operator__percentile__argument-combo">
993
+ <div className="query-builder__projection__column__aggregate__operator__percentile__argument-combo__label">
994
+ Continous
995
+ </div>
996
+ <CustomSelectorInput
997
+ className="query-loader__results__sort-by__selector query-builder__projection__column__aggregate__operator__percentile__argument-combo__value"
998
+ options={percentileOptions}
999
+ onChange={(option: {
1000
+ label: string;
1001
+ value: string;
1002
+ }) =>
1003
+ setContinuous(
1004
+ option.value === 'true' ? true : false,
1005
+ )
1006
+ }
1007
+ value={{
1008
+ label:
1009
+ continuous === undefined
1010
+ ? ''
1011
+ : continuous
1012
+ ? 'true'
1013
+ : 'false',
1014
+ value:
1015
+ continuous === undefined
1016
+ ? ''
1017
+ : continuous
1018
+ ? 'true'
1019
+ : 'false',
1020
+ }}
1021
+ darkMode={
1022
+ !applicationStore.layoutService
1023
+ .TEMPORARY__isLightColorThemeEnabled
1024
+ }
1025
+ />
1026
+ </div>
1027
+ </div>
1028
+ </BasePopover>
1029
+ )}
826
1030
  {isCalendarEnabled &&
827
1031
  aggregateColumnState &&
828
1032
  aggregateCalendarFunctions.length > 0 && (
@@ -163,6 +163,7 @@ export enum QUERY_BUILDER_SUPPORTED_FUNCTIONS {
163
163
  FROM = 'meta::pure::mapping::from',
164
164
  CHECKED = 'meta::pure::dataQuality::checked',
165
165
  MERGERUNTIMES = 'meta::pure::runtime::mergeRuntimes',
166
+ PERCENTILE = 'meta::pure::functions::math::percentile',
166
167
 
167
168
  // TOTDS
168
169
  TABLE_TO_TDS = 'meta::pure::tds::tableToTDS',
package/src/index.ts CHANGED
@@ -30,7 +30,7 @@ export { QueryBuilderNavigationBlocker } from './components/QueryBuilderNavigati
30
30
  export { QueryBuilder } from './components/QueryBuilder.js';
31
31
  export { QUERY_BUILDER_COMPONENT_ELEMENT_ID } from './components/QueryBuilderComponentElement.js';
32
32
  export {
33
- type QuerySDLC,
33
+ type QueryableSourceInfo as QuerySDLC,
34
34
  QueryBuilderState,
35
35
  } from './stores/QueryBuilderState.js';
36
36
  export {
@@ -111,9 +111,9 @@ import { QUERY_BUILDER_EVENT } from '../__lib__/QueryBuilderEvent.js';
111
111
  import { QueryBuilderChangeHistoryState } from './QueryBuilderChangeHistoryState.js';
112
112
  import { type QueryBuilderWorkflowState } from './query-workflow/QueryBuilderWorkFlowState.js';
113
113
 
114
- export interface QuerySDLC {}
114
+ export interface QueryableSourceInfo {}
115
115
 
116
- export type QueryStateInfo = QuerySDLC & {
116
+ export type QueryableClassMappingRuntimeInfo = QueryableSourceInfo & {
117
117
  class: string;
118
118
  mapping: string;
119
119
  runtime: string;
@@ -169,7 +169,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
169
169
  // NOTE: This property contains information about workflow used
170
170
  // to create this state. This should only be used to add additional
171
171
  // information to query builder analytics.
172
- sourceInfo?: QuerySDLC | undefined;
172
+ sourceInfo?: QueryableSourceInfo | undefined;
173
173
 
174
174
  // NOTE: this makes it so that we need to import components in stores code,
175
175
  // we probably want to refactor to an extension mechanism
@@ -180,7 +180,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
180
180
  graphManagerState: GraphManagerState,
181
181
  workflowState: QueryBuilderWorkflowState,
182
182
  config: QueryBuilderConfig | undefined,
183
- sourceInfo?: QuerySDLC | undefined,
183
+ sourceInfo?: QueryableSourceInfo | undefined,
184
184
  ) {
185
185
  makeObservable(this, {
186
186
  explorerState: observable,
@@ -341,7 +341,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
341
341
  * Gets information about the current queryBuilderState.
342
342
  * This information can be used as a part of analytics
343
343
  */
344
- getStateInfo(): QueryStateInfo | undefined {
344
+ getStateInfo(): QueryableClassMappingRuntimeInfo | undefined {
345
345
  if (this.sourceInfo) {
346
346
  const classPath = this.class?.path;
347
347
  const mappingPath = this.executionContextState.mapping?.path;
@@ -32,6 +32,7 @@ export enum QUERY_BUILDER_STATE_HASH_STRUCTURE {
32
32
  AGGREGATE_OPERATOR_STD_DEV_SAMPLE = 'AGGREGATE_OPERATOR_STD_DEV_SAMPLE',
33
33
  AGGREGATE_OPERATOR_SUM = 'AGGREGATE_OPERATOR_SUM',
34
34
  AGGREGATE_COLUMN_STATE = 'AGGREGATE_COLUMN_STATE',
35
+ AGGREGATE_OPERATOR_PERCENTILE = 'AGGREGATE_OPERATOR_PERCENTILE',
35
36
  AGGREGATION_STATE = 'AGGREGATION_STATE',
36
37
  SIMPLE_PROJECTION_COLUMN_STATE = 'SIMPLE_PROJECTION_COLUMN_STATE',
37
38
  PROPERTY_EXPRESSION_STATE = 'PROPERTY_EXPRESSION_STATE',
@@ -139,6 +139,7 @@ export class QueryBuilderTDSState
139
139
  super(queryBuilderState, fetchStructureState);
140
140
 
141
141
  makeObservable(this, {
142
+ aggregationState: observable,
142
143
  projectionColumns: observable,
143
144
  isConvertDerivationProjectionObjects: observable,
144
145
  showPostFilterPanel: observable,
@@ -343,6 +344,10 @@ export class QueryBuilderTDSState
343
344
  }
344
345
  });
345
346
 
347
+ this.aggregationState.allValidationIssues.forEach((issue) =>
348
+ validationIssues.push(issue),
349
+ );
350
+
346
351
  return validationIssues;
347
352
  }
348
353
 
@@ -35,6 +35,8 @@ export abstract class QueryBuilderAggregateOperator implements Hashable {
35
35
 
36
36
  constructor() {
37
37
  makeObservable(this, {
38
+ getOperator: computed,
39
+ allValidationIssues: computed,
38
40
  hashCode: computed,
39
41
  });
40
42
  }
@@ -84,5 +86,13 @@ export abstract class QueryBuilderAggregateOperator implements Hashable {
84
86
  return aggregateColumnState.projectionColumnState.getColumnType();
85
87
  }
86
88
 
89
+ get getOperator(): QueryBuilderAggregateOperator {
90
+ return this;
91
+ }
92
+
93
+ get allValidationIssues(): string[] {
94
+ return [];
95
+ }
96
+
87
97
  abstract get hashCode(): string;
88
98
  }
@@ -25,6 +25,7 @@ import { QueryBuilderAggregateOperator_Min } from './operators/QueryBuilderAggre
25
25
  import { QueryBuilderAggregateOperator_Max } from './operators/QueryBuilderAggregateOperator_Max.js';
26
26
  import { QueryBuilderAggregateOperator_JoinString } from './operators/QueryBuilderAggregateOperator_JoinString.js';
27
27
  import type { QueryBuilderAggregateOperator } from './QueryBuilderAggregateOperator.js';
28
+ import { QueryBuilderAggregateOperator_Percentile } from './operators/QueryBuilderAggregateOperator_Percentile.js';
28
29
 
29
30
  export const getQueryBuilderCoreAggregrationOperators =
30
31
  (): QueryBuilderAggregateOperator[] => [
@@ -35,6 +36,7 @@ export const getQueryBuilderCoreAggregrationOperators =
35
36
  new QueryBuilderAggregateOperator_Average(),
36
37
  new QueryBuilderAggregateOperator_Min(),
37
38
  new QueryBuilderAggregateOperator_Max(),
39
+ new QueryBuilderAggregateOperator_Percentile(),
38
40
  new QueryBuilderAggregateOperator_StdDev_Population(),
39
41
  new QueryBuilderAggregateOperator_StdDev_Sample(),
40
42
  new QueryBuilderAggregateOperator_JoinString(),
@@ -136,6 +136,8 @@ export class QueryBuilderAggregationState implements Hashable {
136
136
  addColumn: action,
137
137
  changeColumnAggregateOperator: action,
138
138
  disableCalendar: action,
139
+
140
+ allValidationIssues: computed,
139
141
  hashCode: computed,
140
142
  });
141
143
 
@@ -179,7 +181,7 @@ export class QueryBuilderAggregationState implements Hashable {
179
181
  `${colName} (${val.getLabel(projectionColumnState)})`,
180
182
  );
181
183
  }
182
- aggregateColumnState.setOperator(val);
184
+ aggregateColumnState.setOperator(val.getOperator);
183
185
  } else {
184
186
  if (!hideOperatorInColumnName) {
185
187
  projectionColumnState.setColumnName(
@@ -189,9 +191,9 @@ export class QueryBuilderAggregationState implements Hashable {
189
191
  const newAggregateColumnState = new QueryBuilderAggregateColumnState(
190
192
  this,
191
193
  projectionColumnState,
192
- val,
194
+ val.getOperator,
193
195
  );
194
- newAggregateColumnState.setOperator(val);
196
+ newAggregateColumnState.setOperator(val.getOperator);
195
197
  this.addColumn(newAggregateColumnState);
196
198
 
197
199
  // automatically move the column to the end
@@ -233,6 +235,10 @@ export class QueryBuilderAggregationState implements Hashable {
233
235
  });
234
236
  }
235
237
 
238
+ get allValidationIssues(): string[] {
239
+ return this.columns.map((col) => col.operator.allValidationIssues).flat();
240
+ }
241
+
236
242
  get hashCode(): string {
237
243
  return hashArray([
238
244
  QUERY_BUILDER_STATE_HASH_STRUCTURE.AGGREGATION_STATE,
@@ -97,7 +97,6 @@ export const buildAggregateColumnState = (
97
97
  )}() expression: property is not compatible with operator`,
98
98
  );
99
99
  aggregateColumnState.setOperator(operator);
100
-
101
100
  return aggregateColumnState;
102
101
  }
103
102
 
@@ -0,0 +1,241 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import {
18
+ matchFunctionName,
19
+ PRIMITIVE_TYPE,
20
+ type ValueSpecification,
21
+ SimpleFunctionExpression,
22
+ VariableExpression,
23
+ type PureModel,
24
+ type AbstractPropertyExpression,
25
+ GenericType,
26
+ GenericTypeExplicitReference,
27
+ Multiplicity,
28
+ PrimitiveInstanceValue,
29
+ PrimitiveType,
30
+ extractElementNameFromPath,
31
+ } from '@finos/legend-graph';
32
+ import { QueryBuilderAggregateColumnState } from '../QueryBuilderAggregationState.js';
33
+ import { QueryBuilderAggregateOperator } from '../QueryBuilderAggregateOperator.js';
34
+ import {
35
+ type QueryBuilderProjectionColumnState,
36
+ QueryBuilderSimpleProjectionColumnState,
37
+ } from '../../projection/QueryBuilderProjectionColumnState.js';
38
+ import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../../../../graph/QueryBuilderMetaModelConst.js';
39
+ import {
40
+ type Hashable,
41
+ hashArray,
42
+ assertTrue,
43
+ guaranteeType,
44
+ } from '@finos/legend-shared';
45
+ import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from '../../../../QueryBuilderStateHashUtils.js';
46
+ import { action, makeObservable, observable } from 'mobx';
47
+
48
+ export class QueryBuilderAggregateOperator_Percentile
49
+ extends QueryBuilderAggregateOperator
50
+ implements Hashable
51
+ {
52
+ percentile: number | undefined;
53
+ acending: boolean | undefined;
54
+ continuous: boolean | undefined;
55
+
56
+ constructor() {
57
+ super();
58
+ makeObservable(this, {
59
+ percentile: observable,
60
+ acending: observable,
61
+ continuous: observable,
62
+ setPercentile: action,
63
+ setAcending: action,
64
+ setContinuous: action,
65
+ });
66
+ }
67
+
68
+ setPercentile(val: number | undefined) {
69
+ this.percentile = val;
70
+ }
71
+
72
+ setAcending(val: boolean | undefined) {
73
+ this.acending = val;
74
+ }
75
+
76
+ setContinuous(val: boolean | undefined) {
77
+ this.continuous = val;
78
+ }
79
+
80
+ getLabel(projectionColumnState: QueryBuilderProjectionColumnState): string {
81
+ return 'percentile';
82
+ }
83
+
84
+ isCompatibleWithColumn(
85
+ projectionColumnState: QueryBuilderProjectionColumnState,
86
+ ): boolean {
87
+ if (
88
+ projectionColumnState instanceof QueryBuilderSimpleProjectionColumnState
89
+ ) {
90
+ const propertyType =
91
+ projectionColumnState.propertyExpressionState.propertyExpression.func
92
+ .value.genericType.value.rawType;
93
+ return (
94
+ [
95
+ PRIMITIVE_TYPE.NUMBER,
96
+ PRIMITIVE_TYPE.INTEGER,
97
+ PRIMITIVE_TYPE.DECIMAL,
98
+ PRIMITIVE_TYPE.FLOAT,
99
+ ] as string[]
100
+ ).includes(propertyType.path);
101
+ }
102
+ return true;
103
+ }
104
+
105
+ buildAggregateExpression(
106
+ propertyExpression: AbstractPropertyExpression | undefined,
107
+ variableName: string,
108
+ graph: PureModel,
109
+ ): ValueSpecification {
110
+ const percentileValue = this.percentile ?? 0;
111
+ const expression = new SimpleFunctionExpression(
112
+ extractElementNameFromPath(QUERY_BUILDER_SUPPORTED_FUNCTIONS.PERCENTILE),
113
+ );
114
+ const percentile = new PrimitiveInstanceValue(
115
+ GenericTypeExplicitReference.create(
116
+ new GenericType(PrimitiveType.NUMBER),
117
+ ),
118
+ );
119
+ percentile.values = [percentileValue];
120
+ if (
121
+ this.acending === undefined ||
122
+ this.continuous === undefined ||
123
+ (this.acending && this.continuous)
124
+ ) {
125
+ expression.parametersValues.push(
126
+ new VariableExpression(variableName, Multiplicity.ONE),
127
+ percentile,
128
+ );
129
+ } else {
130
+ const acending = new PrimitiveInstanceValue(
131
+ GenericTypeExplicitReference.create(
132
+ new GenericType(PrimitiveType.BOOLEAN),
133
+ ),
134
+ );
135
+ acending.values = [this.acending];
136
+ const continuous = new PrimitiveInstanceValue(
137
+ GenericTypeExplicitReference.create(
138
+ new GenericType(PrimitiveType.BOOLEAN),
139
+ ),
140
+ );
141
+ continuous.values = [this.continuous];
142
+ expression.parametersValues.push(
143
+ new VariableExpression(variableName, Multiplicity.ONE),
144
+ percentile,
145
+ acending,
146
+ continuous,
147
+ );
148
+ }
149
+ return expression;
150
+ }
151
+
152
+ buildAggregateColumnState(
153
+ expression: SimpleFunctionExpression,
154
+ lambdaParam: VariableExpression,
155
+ projectionColumnState: QueryBuilderProjectionColumnState,
156
+ ): QueryBuilderAggregateColumnState | undefined {
157
+ if (
158
+ matchFunctionName(
159
+ expression.functionName,
160
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.PERCENTILE,
161
+ )
162
+ ) {
163
+ const aggregateColumnState = new QueryBuilderAggregateColumnState(
164
+ projectionColumnState.tdsState.aggregationState,
165
+ projectionColumnState,
166
+ new QueryBuilderAggregateOperator_Percentile(),
167
+ );
168
+
169
+ const currentOperator = guaranteeType(
170
+ aggregateColumnState.operator,
171
+ QueryBuilderAggregateOperator_Percentile,
172
+ );
173
+
174
+ aggregateColumnState.setLambdaParameterName(lambdaParam.name);
175
+
176
+ assertTrue(
177
+ [2, 4].includes(expression.parametersValues.length),
178
+ `Can't process percentile() expression: percentile() expects 2 or 4 argument`,
179
+ );
180
+
181
+ // variable
182
+ const variableExpression = guaranteeType(
183
+ expression.parametersValues[0],
184
+ VariableExpression,
185
+ `Can't process percentile() expression: only support percentile() immediately following a variable expression`,
186
+ );
187
+ assertTrue(
188
+ aggregateColumnState.lambdaParameterName === variableExpression.name,
189
+ `Can't process percentile() expression: expects variable used in lambda body '${variableExpression.name}' to match lambda parameter '${aggregateColumnState.lambdaParameterName}'`,
190
+ );
191
+ const percentile = guaranteeType(
192
+ expression.parametersValues[1],
193
+ PrimitiveInstanceValue,
194
+ `Can't process percentile() expression: percentile() expects arugment #2 to be a primitive instance value`,
195
+ );
196
+ currentOperator.percentile = percentile.values[0] as number;
197
+
198
+ if (expression.parametersValues.length === 4) {
199
+ const acending = guaranteeType(
200
+ expression.parametersValues[2],
201
+ PrimitiveInstanceValue,
202
+ `Can't process percentile() expression: percentile() expects arugment #3 to be a primitive instance value`,
203
+ );
204
+ currentOperator.acending = acending.values[0] as boolean;
205
+ const continuous = guaranteeType(
206
+ expression.parametersValues[3],
207
+ PrimitiveInstanceValue,
208
+ `Can't process percentile() expression: percentile() expects arugment #4 to be a primitive instance value`,
209
+ );
210
+ currentOperator.continuous = continuous.values[0] as boolean;
211
+ }
212
+ // operator
213
+ assertTrue(
214
+ this.isCompatibleWithColumn(aggregateColumnState.projectionColumnState),
215
+ `Can't process percentile() expression: property is not compatible with operator`,
216
+ );
217
+ aggregateColumnState.setOperator(currentOperator);
218
+ return aggregateColumnState;
219
+ }
220
+
221
+ return undefined;
222
+ }
223
+
224
+ override get getOperator(): QueryBuilderAggregateOperator {
225
+ return new QueryBuilderAggregateOperator_Percentile();
226
+ }
227
+
228
+ override get allValidationIssues(): string[] {
229
+ const validationIssues: string[] = [];
230
+ if (this.percentile === undefined || this.percentile > 100) {
231
+ validationIssues.push('Invalid Aggregation Argument for Percentile');
232
+ }
233
+ return validationIssues;
234
+ }
235
+
236
+ get hashCode(): string {
237
+ return hashArray([
238
+ QUERY_BUILDER_STATE_HASH_STRUCTURE.AGGREGATE_OPERATOR_PERCENTILE,
239
+ ]);
240
+ }
241
+ }
@@ -1000,12 +1000,10 @@ export class QueryBuilderPostFilterState
1000
1000
  node.condition.rightConditionValue instanceof
1001
1001
  PostFilterValueSpecConditionValueState &&
1002
1002
  node.condition.rightConditionValue.value instanceof InstanceValue &&
1003
- !isValidInstanceValue(node.condition.rightConditionValue.value) &&
1004
- node.condition.leftConditionValue instanceof
1005
- QueryBuilderSimpleProjectionColumnState
1003
+ !isValidInstanceValue(node.condition.rightConditionValue.value)
1006
1004
  ) {
1007
1005
  validationIssues.push(
1008
- `Filter value for ${node.condition.leftConditionValue.propertyExpressionState.title} is missing or invalid`,
1006
+ `Filter value for ${node.condition.leftConditionValue.columnName} is missing or invalid`,
1009
1007
  );
1010
1008
  }
1011
1009
  if (
@@ -1029,9 +1027,7 @@ export class QueryBuilderPostFilterState
1029
1027
  node.condition.rightConditionValue instanceof
1030
1028
  PostFilterValueSpecConditionValueState &&
1031
1029
  node.condition.rightConditionValue.value instanceof InstanceValue &&
1032
- !isValidInstanceValue(node.condition.rightConditionValue.value) &&
1033
- node.condition.leftConditionValue instanceof
1034
- QueryBuilderSimpleProjectionColumnState,
1030
+ !isValidInstanceValue(node.condition.rightConditionValue.value),
1035
1031
  );
1036
1032
  }
1037
1033