@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.
- package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts +3 -3
- package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderTesting.d.ts +1 -0
- package/lib/__lib__/QueryBuilderTesting.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderTesting.js +1 -0
- package/lib/__lib__/QueryBuilderTesting.js.map +1 -1
- package/lib/components/QueryBuilder.d.ts.map +1 -1
- package/lib/components/QueryBuilder.js +5 -2
- package/lib/components/QueryBuilder.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderFetchStructurePanel.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +93 -2
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
- package/lib/graph/QueryBuilderMetaModelConst.d.ts +1 -0
- package/lib/graph/QueryBuilderMetaModelConst.d.ts.map +1 -1
- package/lib/graph/QueryBuilderMetaModelConst.js +1 -0
- package/lib/graph/QueryBuilderMetaModelConst.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/package.json +5 -4
- package/lib/stores/QueryBuilderState.d.ts +5 -5
- package/lib/stores/QueryBuilderState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderState.js.map +1 -1
- package/lib/stores/QueryBuilderStateHashUtils.d.ts +1 -0
- package/lib/stores/QueryBuilderStateHashUtils.d.ts.map +1 -1
- package/lib/stores/QueryBuilderStateHashUtils.js +1 -0
- package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
- package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js +2 -0
- package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.d.ts +2 -0
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js +8 -0
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.js.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.js +2 -0
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.js.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.d.ts +1 -0
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js +7 -3
- package/lib/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperatorValueSpecificationBuilder.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperatorValueSpecificationBuilder.js.map +1 -1
- package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.d.ts +37 -0
- package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.d.ts.map +1 -0
- package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.js +123 -0
- package/lib/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.js.map +1 -0
- package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js +3 -7
- package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
- package/package.json +13 -12
- package/src/__lib__/QueryBuilderTelemetryHelper.ts +3 -3
- package/src/__lib__/QueryBuilderTesting.ts +1 -0
- package/src/components/QueryBuilder.tsx +5 -2
- package/src/components/fetch-structure/QueryBuilderFetchStructurePanel.tsx +0 -1
- package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +208 -4
- package/src/graph/QueryBuilderMetaModelConst.ts +1 -0
- package/src/index.ts +1 -1
- package/src/stores/QueryBuilderState.ts +5 -5
- package/src/stores/QueryBuilderStateHashUtils.ts +1 -0
- package/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts +5 -0
- package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperator.ts +10 -0
- package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregateOperatorLoader.ts +2 -0
- package/src/stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.ts +9 -3
- package/src/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperatorValueSpecificationBuilder.ts +0 -1
- package/src/stores/fetch-structure/tds/aggregation/operators/QueryBuilderAggregateOperator_Percentile.ts +241 -0
- package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +3 -7
- 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-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
|
114
|
+
export interface QueryableSourceInfo {}
|
|
115
115
|
|
|
116
|
-
export type
|
|
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?:
|
|
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?:
|
|
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():
|
|
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,
|
|
@@ -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.
|
|
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
|
|