@finos/legend-query-builder 4.14.26 → 4.14.28
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/components/result/tds/QueryBuilderTDSGridResult.d.ts +4 -0
- package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts.map +1 -1
- package/lib/components/result/tds/QueryBuilderTDSGridResult.js +126 -37
- package/lib/components/result/tds/QueryBuilderTDSGridResult.js.map +1 -1
- package/lib/index.css +17 -1
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilderResultState.d.ts +12 -0
- package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderResultState.js +68 -4
- package/lib/stores/QueryBuilderResultState.js.map +1 -1
- package/lib/stores/QueryBuilderState.d.ts +2 -1
- package/lib/stores/QueryBuilderState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderState.js +10 -2
- package/lib/stores/QueryBuilderState.js.map +1 -1
- package/package.json +3 -3
- package/src/components/result/tds/QueryBuilderTDSGridResult.tsx +188 -11
- package/src/stores/QueryBuilderResultState.ts +82 -4
- package/src/stores/QueryBuilderState.ts +21 -0
@@ -14,11 +14,10 @@
|
|
14
14
|
* limitations under the License.
|
15
15
|
*/
|
16
16
|
|
17
|
-
import { clsx } from '@finos/legend-art';
|
18
17
|
import { observer } from 'mobx-react-lite';
|
19
18
|
import type { QueryBuilderState } from '../../../stores/QueryBuilderState.js';
|
20
19
|
import { PRIMITIVE_TYPE, type TDSExecutionResult } from '@finos/legend-graph';
|
21
|
-
import { useState, useCallback } from 'react';
|
20
|
+
import { useState, useCallback, useEffect } from 'react';
|
22
21
|
import {
|
23
22
|
DataGrid,
|
24
23
|
type DataGridApi,
|
@@ -28,23 +27,45 @@ import {
|
|
28
27
|
type DataGridGetContextMenuItemsParams,
|
29
28
|
type DataGridIRowNode,
|
30
29
|
type DataGridMenuItemDef,
|
30
|
+
type DataGridIAggFuncParams,
|
31
31
|
} from '@finos/legend-lego/data-grid';
|
32
32
|
import {
|
33
33
|
getRowDataFromExecutionResult,
|
34
34
|
type IQueryRendererParamsWithGridType,
|
35
35
|
filterByOrOutValues,
|
36
36
|
} from './QueryBuilderTDSResultShared.js';
|
37
|
-
import
|
38
|
-
QueryBuilderResultState,
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
import {
|
38
|
+
type QueryBuilderResultState,
|
39
|
+
QueryBuilderResultWavgAggregationState,
|
40
|
+
type QueryBuilderTDSResultCellData,
|
41
|
+
type QueryBuilderTDSResultCellDataType,
|
42
|
+
type QueryBuilderTDSRowDataType,
|
42
43
|
} from '../../../stores/QueryBuilderResultState.js';
|
43
44
|
import { QueryBuilderTDSState } from '../../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
|
44
45
|
import { DEFAULT_LOCALE } from '../../../graph-manager/QueryBuilderConst.js';
|
45
|
-
import {
|
46
|
+
import {
|
47
|
+
assertErrorThrown,
|
48
|
+
isNumber,
|
49
|
+
isString,
|
50
|
+
isValidURL,
|
51
|
+
} from '@finos/legend-shared';
|
46
52
|
import { useApplicationStore } from '@finos/legend-application';
|
47
53
|
import { QUERY_BUILDER_TEST_ID } from '../../../__lib__/QueryBuilderTesting.js';
|
54
|
+
import {
|
55
|
+
clsx,
|
56
|
+
Modal,
|
57
|
+
ModalBody,
|
58
|
+
ModalHeader,
|
59
|
+
ModalFooter,
|
60
|
+
ModalFooterButton,
|
61
|
+
Dialog,
|
62
|
+
CustomSelectorInput,
|
63
|
+
} from '@finos/legend-art';
|
64
|
+
|
65
|
+
export const enum QueryBuilderDataGridCustomAggregationFunction {
|
66
|
+
wavg = 'wavg',
|
67
|
+
WAVG = 'WAVG',
|
68
|
+
}
|
48
69
|
|
49
70
|
const getAggregationTDSColumnCustomizations = (
|
50
71
|
result: TDSExecutionResult,
|
@@ -71,7 +92,7 @@ const getAggregationTDSColumnCustomizations = (
|
|
71
92
|
case PRIMITIVE_TYPE.FLOAT:
|
72
93
|
return {
|
73
94
|
filter: 'agNumberColumnFilter',
|
74
|
-
allowedAggFuncs: ['count', 'sum', 'max', 'min', 'avg'],
|
95
|
+
allowedAggFuncs: ['count', 'sum', 'max', 'min', 'avg', 'wavg'],
|
75
96
|
};
|
76
97
|
default:
|
77
98
|
return {
|
@@ -222,16 +243,19 @@ export const QueryBuilderTDSGridResult = observer(
|
|
222
243
|
}) => {
|
223
244
|
const { executionResult, queryBuilderState } = props;
|
224
245
|
const applicationStore = useApplicationStore();
|
246
|
+
const darkMode =
|
247
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled;
|
225
248
|
const [columnAPi, setColumnApi] = useState<DataGridColumnApi | undefined>(
|
226
249
|
undefined,
|
227
250
|
);
|
251
|
+
const [aggFuncParams, setAggFuncParams] = useState<
|
252
|
+
DataGridIAggFuncParams | undefined
|
253
|
+
>(undefined);
|
228
254
|
const resultState = queryBuilderState.resultState;
|
229
255
|
const isLocalModeEnabled = queryBuilderState.isLocalModeEnabled;
|
230
256
|
const colDefs = isLocalModeEnabled
|
231
257
|
? getLocalColDefs(executionResult, resultState)
|
232
258
|
: getColDefs(executionResult, resultState);
|
233
|
-
const darkMode =
|
234
|
-
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled;
|
235
259
|
|
236
260
|
const onSaveGridColumnState = (): void => {
|
237
261
|
if (!columnAPi) {
|
@@ -242,6 +266,10 @@ export const QueryBuilderTDSGridResult = observer(
|
|
242
266
|
isPivotModeEnabled: columnAPi.isPivotMode(),
|
243
267
|
isLocalModeEnabled: true,
|
244
268
|
previewLimit: resultState.previewLimit,
|
269
|
+
...(resultState.wavgAggregationState?.weightedColumnIdPairs && {
|
270
|
+
weightedColumnPairs:
|
271
|
+
resultState.wavgAggregationState.weightedColumnIdPairs,
|
272
|
+
}),
|
245
273
|
});
|
246
274
|
};
|
247
275
|
|
@@ -336,6 +364,103 @@ export const QueryBuilderTDSGridResult = observer(
|
|
336
364
|
],
|
337
365
|
);
|
338
366
|
|
367
|
+
const weightedColumnOptions = columnAPi
|
368
|
+
?.getColumns()
|
369
|
+
?.filter((c) => c.getColDef().cellDataType === 'number')
|
370
|
+
.map((col) => ({
|
371
|
+
label: col.getColId(),
|
372
|
+
value: col.getColId(),
|
373
|
+
}));
|
374
|
+
|
375
|
+
const selectedWeightedColumn =
|
376
|
+
aggFuncParams?.colDef.field &&
|
377
|
+
resultState.wavgAggregationState?.weightedColumnIdPairs.get(
|
378
|
+
aggFuncParams.colDef.field,
|
379
|
+
)
|
380
|
+
? {
|
381
|
+
label: resultState.wavgAggregationState.weightedColumnIdPairs.get(
|
382
|
+
aggFuncParams.colDef.field,
|
383
|
+
),
|
384
|
+
value: resultState.wavgAggregationState.weightedColumnIdPairs.get(
|
385
|
+
aggFuncParams.colDef.field,
|
386
|
+
),
|
387
|
+
}
|
388
|
+
: null;
|
389
|
+
|
390
|
+
const onWeightedColumnOptionChange = async (
|
391
|
+
option: { label: string; value: string } | null,
|
392
|
+
): Promise<void> => {
|
393
|
+
if (aggFuncParams?.colDef.field && option?.value) {
|
394
|
+
resultState.wavgAggregationState?.addWeightedColumnIdPair(
|
395
|
+
aggFuncParams.colDef.field,
|
396
|
+
option.value,
|
397
|
+
);
|
398
|
+
}
|
399
|
+
};
|
400
|
+
|
401
|
+
const weightedAverage = (param: DataGridIAggFuncParams): void => {
|
402
|
+
if (param.colDef.field) {
|
403
|
+
if (!resultState.wavgAggregationState) {
|
404
|
+
resultState.setWavgAggregationState(
|
405
|
+
new QueryBuilderResultWavgAggregationState(),
|
406
|
+
);
|
407
|
+
}
|
408
|
+
resultState.wavgAggregationState?.addWeightedColumnIdPair(
|
409
|
+
param.colDef.field,
|
410
|
+
param.colDef.field,
|
411
|
+
);
|
412
|
+
resultState.wavgAggregationState?.setIsApplyingWavg(true);
|
413
|
+
setAggFuncParams(param);
|
414
|
+
} else {
|
415
|
+
applicationStore.notificationService.notifyError(
|
416
|
+
'The id of this column can`t be retrieved to perform weighted average',
|
417
|
+
);
|
418
|
+
}
|
419
|
+
};
|
420
|
+
|
421
|
+
const weightedAverageHelper = (param: DataGridIAggFuncParams): number => {
|
422
|
+
try {
|
423
|
+
const column = param.colDef.field;
|
424
|
+
if (column) {
|
425
|
+
const weightedColumnId =
|
426
|
+
resultState.wavgAggregationState?.weightedColumnIdPairs.get(column);
|
427
|
+
if (weightedColumnId) {
|
428
|
+
const weightedColumnSum = param.rowNode.allLeafChildren
|
429
|
+
.map((node) => node.data[weightedColumnId])
|
430
|
+
.reduce((a, b) => a + b) as number;
|
431
|
+
const weightedColumnMultiply = param.rowNode.allLeafChildren
|
432
|
+
.map((node) => node.data[weightedColumnId] * node.data[column])
|
433
|
+
.reduce((a, b) => a + b);
|
434
|
+
if (weightedColumnSum !== 0) {
|
435
|
+
onSaveGridColumnState();
|
436
|
+
return weightedColumnMultiply / weightedColumnSum;
|
437
|
+
} else {
|
438
|
+
applicationStore.notificationService.notifyError(
|
439
|
+
'The weighted column sum is 0',
|
440
|
+
);
|
441
|
+
}
|
442
|
+
} else {
|
443
|
+
applicationStore.notificationService.notifyError(
|
444
|
+
'The weighted column Id is not defined',
|
445
|
+
);
|
446
|
+
}
|
447
|
+
}
|
448
|
+
} catch (error) {
|
449
|
+
assertErrorThrown(error);
|
450
|
+
applicationStore.notificationService.notifyError(error);
|
451
|
+
}
|
452
|
+
return -1;
|
453
|
+
};
|
454
|
+
|
455
|
+
useEffect(() => {
|
456
|
+
if (aggFuncParams) {
|
457
|
+
aggFuncParams.columnApi.setColumnAggFunc(
|
458
|
+
aggFuncParams.colDef.field!,
|
459
|
+
QueryBuilderDataGridCustomAggregationFunction.WAVG,
|
460
|
+
);
|
461
|
+
}
|
462
|
+
}, [resultState.wavgAggregationState, aggFuncParams]);
|
463
|
+
|
339
464
|
return (
|
340
465
|
<div
|
341
466
|
data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_VALUES_TDS}
|
@@ -372,6 +497,10 @@ export const QueryBuilderTDSGridResult = observer(
|
|
372
497
|
suppressFieldDotNotation={true}
|
373
498
|
suppressContextMenu={false}
|
374
499
|
columnDefs={colDefs}
|
500
|
+
aggFuncs={{
|
501
|
+
wavg: weightedAverage,
|
502
|
+
WAVG: weightedAverageHelper,
|
503
|
+
}}
|
375
504
|
sideBar={['columns', 'filters']}
|
376
505
|
onColumnVisible={onSaveGridColumnState}
|
377
506
|
onColumnPinned={onSaveGridColumnState}
|
@@ -411,6 +540,54 @@ export const QueryBuilderTDSGridResult = observer(
|
|
411
540
|
}
|
412
541
|
/>
|
413
542
|
)}
|
543
|
+
{resultState.wavgAggregationState?.isApplyingWavg && (
|
544
|
+
<Dialog
|
545
|
+
open={resultState.wavgAggregationState.isApplyingWavg}
|
546
|
+
onClose={() =>
|
547
|
+
resultState.wavgAggregationState?.setIsApplyingWavg(false)
|
548
|
+
}
|
549
|
+
classes={{
|
550
|
+
root: 'editor-modal__root-container',
|
551
|
+
container: 'editor-modal__container',
|
552
|
+
paper: 'editor-modal__content',
|
553
|
+
}}
|
554
|
+
>
|
555
|
+
<Modal
|
556
|
+
darkMode={
|
557
|
+
!applicationStore.layoutService
|
558
|
+
.TEMPORARY__isLightColorThemeEnabled
|
559
|
+
}
|
560
|
+
className="query-editor__blocking-alert"
|
561
|
+
>
|
562
|
+
<ModalHeader title="Applying Weighted Average" />
|
563
|
+
<ModalBody>
|
564
|
+
<div className="query-builder__result__tds-grid__text">
|
565
|
+
choose a weighted column from dropdown
|
566
|
+
</div>
|
567
|
+
<CustomSelectorInput
|
568
|
+
options={weightedColumnOptions}
|
569
|
+
onChange={onWeightedColumnOptionChange}
|
570
|
+
value={selectedWeightedColumn}
|
571
|
+
placeholder={'Choose a weighted column'}
|
572
|
+
darkMode={
|
573
|
+
!applicationStore.layoutService
|
574
|
+
.TEMPORARY__isLightColorThemeEnabled
|
575
|
+
}
|
576
|
+
/>
|
577
|
+
</ModalBody>
|
578
|
+
<ModalFooter>
|
579
|
+
<ModalFooterButton
|
580
|
+
onClick={() => {
|
581
|
+
resultState.wavgAggregationState?.setIsApplyingWavg(
|
582
|
+
false,
|
583
|
+
);
|
584
|
+
}}
|
585
|
+
text="Apply"
|
586
|
+
/>
|
587
|
+
</ModalFooter>
|
588
|
+
</Modal>
|
589
|
+
</Dialog>
|
590
|
+
)}
|
414
591
|
</div>
|
415
592
|
</div>
|
416
593
|
);
|
@@ -48,6 +48,7 @@ import { QUERY_BUILDER_EVENT } from '../__lib__/QueryBuilderEvent.js';
|
|
48
48
|
import { ExecutionPlanState } from './execution-plan/ExecutionPlanState.js';
|
49
49
|
import type { DataGridColumnState } from '@finos/legend-lego/data-grid';
|
50
50
|
import { downloadStream } from '@finos/legend-application';
|
51
|
+
import { QueryBuilderDataGridCustomAggregationFunction } from '../components/result/tds/QueryBuilderTDSGridResult.js';
|
51
52
|
|
52
53
|
export const DEFAULT_LIMIT = 1000;
|
53
54
|
|
@@ -83,8 +84,37 @@ type QueryBuilderDataGridConfig = {
|
|
83
84
|
isPivotModeEnabled: boolean | undefined;
|
84
85
|
isLocalModeEnabled: boolean | undefined;
|
85
86
|
previewLimit?: number | undefined;
|
87
|
+
weightedColumnPairs?: Map<string, string> | undefined;
|
86
88
|
};
|
87
89
|
|
90
|
+
export class QueryBuilderResultWavgAggregationState {
|
91
|
+
isApplyingWavg = false;
|
92
|
+
weightedColumnIdPairs: Map<string, string>;
|
93
|
+
|
94
|
+
constructor() {
|
95
|
+
makeObservable(this, {
|
96
|
+
isApplyingWavg: observable,
|
97
|
+
weightedColumnIdPairs: observable,
|
98
|
+
setIsApplyingWavg: action,
|
99
|
+
addWeightedColumnIdPair: action,
|
100
|
+
removeWeightedColumnIdPair: action,
|
101
|
+
});
|
102
|
+
this.weightedColumnIdPairs = new Map<string, string>();
|
103
|
+
}
|
104
|
+
|
105
|
+
setIsApplyingWavg(val: boolean): void {
|
106
|
+
this.isApplyingWavg = val;
|
107
|
+
}
|
108
|
+
|
109
|
+
addWeightedColumnIdPair(col: string, weightedColumnId: string): void {
|
110
|
+
this.weightedColumnIdPairs.set(col, weightedColumnId);
|
111
|
+
}
|
112
|
+
|
113
|
+
removeWeightedColumnIdPair(col: string): void {
|
114
|
+
this.weightedColumnIdPairs.delete(col);
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
88
118
|
export class QueryBuilderResultState {
|
89
119
|
readonly queryBuilderState: QueryBuilderState;
|
90
120
|
readonly executionPlanState: ExecutionPlanState;
|
@@ -105,6 +135,7 @@ export class QueryBuilderResultState {
|
|
105
135
|
isSelectingCells: boolean;
|
106
136
|
|
107
137
|
gridConfig: QueryBuilderDataGridConfig | undefined;
|
138
|
+
wavgAggregationState: QueryBuilderResultWavgAggregationState | undefined;
|
108
139
|
|
109
140
|
constructor(queryBuilderState: QueryBuilderState) {
|
110
141
|
makeObservable(this, {
|
@@ -120,7 +151,9 @@ export class QueryBuilderResultState {
|
|
120
151
|
isSelectingCells: observable,
|
121
152
|
isQueryUsageViewerOpened: observable,
|
122
153
|
gridConfig: observable,
|
154
|
+
wavgAggregationState: observable,
|
123
155
|
setGridConfig: action,
|
156
|
+
setWavgAggregationState: action,
|
124
157
|
setIsSelectingCells: action,
|
125
158
|
setIsRunningQuery: action,
|
126
159
|
setExecutionResult: action,
|
@@ -153,6 +186,12 @@ export class QueryBuilderResultState {
|
|
153
186
|
this.gridConfig = val;
|
154
187
|
}
|
155
188
|
|
189
|
+
setWavgAggregationState(
|
190
|
+
val: QueryBuilderResultWavgAggregationState | undefined,
|
191
|
+
): void {
|
192
|
+
this.wavgAggregationState = val;
|
193
|
+
}
|
194
|
+
|
156
195
|
setIsSelectingCells(val: boolean): void {
|
157
196
|
this.isSelectingCells = val;
|
158
197
|
}
|
@@ -199,11 +238,50 @@ export class QueryBuilderResultState {
|
|
199
238
|
}
|
200
239
|
}
|
201
240
|
|
241
|
+
processWeightedColumnPairsMap(
|
242
|
+
config: QueryGridConfig,
|
243
|
+
): Map<string, string> | undefined {
|
244
|
+
if (config.weightedColumnPairs) {
|
245
|
+
const wavgColumns = config.columns
|
246
|
+
.filter(
|
247
|
+
(col) =>
|
248
|
+
(col as DataGridColumnState).aggFunc ===
|
249
|
+
QueryBuilderDataGridCustomAggregationFunction.WAVG,
|
250
|
+
)
|
251
|
+
.map((col) => (col as DataGridColumnState).colId);
|
252
|
+
const weightedColumnPairsMap = new Map<string, string>();
|
253
|
+
config.weightedColumnPairs.forEach((wc) => {
|
254
|
+
if (wc[0] && wc[1]) {
|
255
|
+
weightedColumnPairsMap.set(wc[0], wc[1]);
|
256
|
+
}
|
257
|
+
});
|
258
|
+
for (const wavgCol of weightedColumnPairsMap.keys()) {
|
259
|
+
if (!wavgColumns.includes(wavgCol)) {
|
260
|
+
weightedColumnPairsMap.delete(wavgCol);
|
261
|
+
}
|
262
|
+
}
|
263
|
+
return weightedColumnPairsMap;
|
264
|
+
}
|
265
|
+
return undefined;
|
266
|
+
}
|
267
|
+
|
202
268
|
handlePreConfiguredGridConfig(config: QueryGridConfig): void {
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
269
|
+
let newConfig;
|
270
|
+
const weightedColumnPairsMap = this.processWeightedColumnPairsMap(config);
|
271
|
+
if (weightedColumnPairsMap) {
|
272
|
+
this.wavgAggregationState = new QueryBuilderResultWavgAggregationState();
|
273
|
+
this.wavgAggregationState.weightedColumnIdPairs = weightedColumnPairsMap;
|
274
|
+
newConfig = {
|
275
|
+
...config,
|
276
|
+
weightedColumnPairs: weightedColumnPairsMap,
|
277
|
+
columns: config.columns as DataGridColumnState[],
|
278
|
+
};
|
279
|
+
} else {
|
280
|
+
newConfig = {
|
281
|
+
...config,
|
282
|
+
columns: config.columns as DataGridColumnState[],
|
283
|
+
};
|
284
|
+
}
|
207
285
|
if (config.previewLimit) {
|
208
286
|
this.setPreviewLimit(config.previewLimit);
|
209
287
|
}
|
@@ -32,6 +32,7 @@ import {
|
|
32
32
|
ActionState,
|
33
33
|
hashArray,
|
34
34
|
assertTrue,
|
35
|
+
assertNonNullable,
|
35
36
|
} from '@finos/legend-shared';
|
36
37
|
import { QueryBuilderFilterState } from './filter/QueryBuilderFilterState.js';
|
37
38
|
import { QueryBuilderFetchStructureState } from './fetch-structure/QueryBuilderFetchStructureState.js';
|
@@ -54,6 +55,7 @@ import {
|
|
54
55
|
type ValueSpecification,
|
55
56
|
type Type,
|
56
57
|
type QueryGridConfig,
|
58
|
+
type QueryExecutionContext,
|
57
59
|
GRAPH_MANAGER_EVENT,
|
58
60
|
CompilationError,
|
59
61
|
extractSourceInformationCoordinates,
|
@@ -73,6 +75,7 @@ import {
|
|
73
75
|
InstanceValue,
|
74
76
|
Multiplicity,
|
75
77
|
RuntimePointer,
|
78
|
+
QueryExplicitExecutionContext,
|
76
79
|
} from '@finos/legend-graph';
|
77
80
|
import { buildLambdaFunction } from './QueryBuilderValueSpecificationBuilder.js';
|
78
81
|
import type {
|
@@ -287,6 +290,24 @@ export abstract class QueryBuilderState implements CommandRegistrar {
|
|
287
290
|
return this.allVariables.map((e) => e.name);
|
288
291
|
}
|
289
292
|
|
293
|
+
getQueryExecutionContext(): QueryExecutionContext {
|
294
|
+
const queryExeContext = new QueryExplicitExecutionContext();
|
295
|
+
const runtimeValue = guaranteeType(
|
296
|
+
this.executionContextState.runtimeValue,
|
297
|
+
RuntimePointer,
|
298
|
+
'Query runtime must be of type runtime pointer',
|
299
|
+
);
|
300
|
+
assertNonNullable(
|
301
|
+
this.executionContextState.mapping,
|
302
|
+
'Query required mapping to update',
|
303
|
+
);
|
304
|
+
queryExeContext.mapping = PackageableElementExplicitReference.create(
|
305
|
+
this.executionContextState.mapping,
|
306
|
+
);
|
307
|
+
queryExeContext.runtime = runtimeValue.packageableRuntime;
|
308
|
+
return queryExeContext;
|
309
|
+
}
|
310
|
+
|
290
311
|
/**
|
291
312
|
* Gets information about the current queryBuilderState.
|
292
313
|
* This information can be used as a part of analytics
|