@finos/legend-query-builder 4.10.0 → 4.10.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/QueryBuilderResultPanel.d.ts.map +1 -1
- package/lib/components/QueryBuilderResultPanel.js +44 -57
- package/lib/components/QueryBuilderResultPanel.js.map +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.d.ts +3 -2
- package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.js +145 -8
- package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
- package/lib/index.css +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilderResultState.d.ts +3 -0
- package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderResultState.js +31 -2
- package/lib/stores/QueryBuilderResultState.js.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterState.d.ts +7 -1
- package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterState.js +27 -0
- package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
- package/package.json +5 -5
- package/src/__lib__/QueryBuilderTesting.ts +1 -0
- package/src/components/QueryBuilderResultPanel.tsx +63 -76
- package/src/components/filter/QueryBuilderFilterPanel.tsx +239 -21
- package/src/stores/QueryBuilderResultState.ts +36 -1
- package/src/stores/filter/QueryBuilderFilterState.ts +40 -1
@@ -52,7 +52,6 @@ import {
|
|
52
52
|
EnumValueInstanceValue,
|
53
53
|
EnumValueExplicitReference,
|
54
54
|
RelationalExecutionActivities,
|
55
|
-
getTDSRowRankByColumnInAsc,
|
56
55
|
} from '@finos/legend-graph';
|
57
56
|
import {
|
58
57
|
ActionAlertActionType,
|
@@ -63,8 +62,6 @@ import {
|
|
63
62
|
import {
|
64
63
|
assertErrorThrown,
|
65
64
|
guaranteeNonNullable,
|
66
|
-
isBoolean,
|
67
|
-
type PlainObject,
|
68
65
|
prettyDuration,
|
69
66
|
filterByType,
|
70
67
|
isValidURL,
|
@@ -420,9 +417,7 @@ const QueryBuilderGridResultContextMenu = observer(
|
|
420
417
|
),
|
421
418
|
);
|
422
419
|
|
423
|
-
const
|
424
|
-
rowIndex: number,
|
425
|
-
): (string | number | boolean | null)[] => {
|
420
|
+
const findSelectedCellRowData = (): string => {
|
426
421
|
if (
|
427
422
|
!tdsState.queryBuilderState.resultState.executionResult ||
|
428
423
|
!(
|
@@ -430,21 +425,31 @@ const QueryBuilderGridResultContextMenu = observer(
|
|
430
425
|
TDSExecutionResult
|
431
426
|
)
|
432
427
|
) {
|
433
|
-
return
|
428
|
+
return '';
|
434
429
|
}
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
430
|
+
const rowData = tdsState.queryBuilderState.resultState.rowData.find(
|
431
|
+
(rData) =>
|
432
|
+
rData.rowNumber ===
|
433
|
+
tdsState.queryBuilderState.resultState.selectedCells[0]?.coordinates
|
434
|
+
.rowIndex,
|
439
435
|
);
|
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 '';
|
440
448
|
};
|
441
449
|
|
442
450
|
const handleCopyRowValue = applicationStore.guardUnhandledError(() =>
|
443
451
|
applicationStore.clipboardService.copyTextToClipboard(
|
444
|
-
|
445
|
-
tdsState.queryBuilderState.resultState.selectedCells[0]?.coordinates
|
446
|
-
.rowIndex ?? 0,
|
447
|
-
).toString(),
|
452
|
+
findSelectedCellRowData(),
|
448
453
|
),
|
449
454
|
);
|
450
455
|
|
@@ -502,18 +507,18 @@ const QueryResultCellRenderer = observer(
|
|
502
507
|
isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
|
503
508
|
const columnName = params.column?.getColId() ?? '';
|
504
509
|
const findCoordinatesFromResultValue = (
|
505
|
-
|
510
|
+
colName: string,
|
506
511
|
rowNumber: number,
|
507
512
|
): QueryBuilderTDSResultCellCoordinate => {
|
508
513
|
const colIndex = tdsExecutionResult.result.columns.findIndex(
|
509
|
-
(col) => col ===
|
514
|
+
(col) => col === colName,
|
510
515
|
);
|
511
516
|
return { rowIndex: rowNumber, colIndex: colIndex };
|
512
517
|
};
|
513
518
|
|
514
519
|
const currentCellCoordinates = findCoordinatesFromResultValue(
|
515
520
|
columnName,
|
516
|
-
params.
|
521
|
+
params.data.rowNumber,
|
517
522
|
);
|
518
523
|
const cellInFilteredResults = resultState.selectedCells.some(
|
519
524
|
(result) =>
|
@@ -521,9 +526,9 @@ const QueryResultCellRenderer = observer(
|
|
521
526
|
result.coordinates.rowIndex === currentCellCoordinates.rowIndex,
|
522
527
|
);
|
523
528
|
|
524
|
-
const
|
529
|
+
const findColumnNameFromColumnIndex = (
|
525
530
|
colIndex: number,
|
526
|
-
): string |
|
531
|
+
): string | undefined => {
|
527
532
|
if (
|
528
533
|
!resultState.executionResult ||
|
529
534
|
!(resultState.executionResult instanceof TDSExecutionResult)
|
@@ -534,30 +539,12 @@ const QueryResultCellRenderer = observer(
|
|
534
539
|
};
|
535
540
|
|
536
541
|
const findResultValueFromCoordinates = (
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
!resultState.executionResult ||
|
544
|
-
!(resultState.executionResult instanceof TDSExecutionResult)
|
545
|
-
) {
|
546
|
-
return undefined;
|
547
|
-
}
|
548
|
-
if (params.columnApi.getColumnState()[colIndex]?.sort === 'asc') {
|
549
|
-
resultState.executionResult.result.rows.sort((a, b) =>
|
550
|
-
getTDSRowRankByColumnInAsc(a, b, colIndex),
|
551
|
-
);
|
552
|
-
} else if (params.columnApi.getColumnState()[colIndex]?.sort === 'desc') {
|
553
|
-
resultState.executionResult.result.rows.sort((a, b) =>
|
554
|
-
getTDSRowRankByColumnInAsc(b, a, colIndex),
|
555
|
-
);
|
556
|
-
}
|
557
|
-
return resultState.executionResult.result.rows[rowIndex]?.values[
|
558
|
-
colIndex
|
559
|
-
];
|
560
|
-
};
|
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;
|
561
548
|
|
562
549
|
const isCoordinatesSelected = (
|
563
550
|
resultCoordinate: QueryBuilderTDSResultCellCoordinate,
|
@@ -574,12 +561,12 @@ const QueryResultCellRenderer = observer(
|
|
574
561
|
if (event.shiftKey) {
|
575
562
|
const coordinates = findCoordinatesFromResultValue(
|
576
563
|
columnName,
|
577
|
-
params.
|
564
|
+
params.data.rowNumber,
|
578
565
|
);
|
579
|
-
const actualValue = findResultValueFromCoordinates(
|
566
|
+
const actualValue = findResultValueFromCoordinates(
|
580
567
|
coordinates.rowIndex,
|
581
|
-
|
582
|
-
|
568
|
+
columnName,
|
569
|
+
);
|
583
570
|
resultState.addSelectedCell({
|
584
571
|
value: actualValue,
|
585
572
|
columnName: columnName,
|
@@ -593,12 +580,12 @@ const QueryResultCellRenderer = observer(
|
|
593
580
|
resultState.setSelectedCells([]);
|
594
581
|
const coordinates = findCoordinatesFromResultValue(
|
595
582
|
columnName,
|
596
|
-
params.
|
583
|
+
params.data.rowNumber,
|
597
584
|
);
|
598
|
-
const actualValue = findResultValueFromCoordinates(
|
585
|
+
const actualValue = findResultValueFromCoordinates(
|
599
586
|
coordinates.rowIndex,
|
600
|
-
|
601
|
-
|
587
|
+
columnName,
|
588
|
+
);
|
602
589
|
resultState.setSelectedCells([
|
603
590
|
{
|
604
591
|
value: actualValue,
|
@@ -612,14 +599,14 @@ const QueryResultCellRenderer = observer(
|
|
612
599
|
if (event.button === 2) {
|
613
600
|
const coordinates = findCoordinatesFromResultValue(
|
614
601
|
columnName,
|
615
|
-
params.
|
602
|
+
params.data.rowNumber,
|
616
603
|
);
|
617
604
|
const isInSelected = isCoordinatesSelected(coordinates);
|
618
605
|
if (!isInSelected) {
|
619
|
-
const actualValue = findResultValueFromCoordinates(
|
606
|
+
const actualValue = findResultValueFromCoordinates(
|
620
607
|
coordinates.rowIndex,
|
621
|
-
|
622
|
-
|
608
|
+
columnName,
|
609
|
+
);
|
623
610
|
resultState.setSelectedCells([
|
624
611
|
{
|
625
612
|
value: actualValue,
|
@@ -649,7 +636,7 @@ const QueryResultCellRenderer = observer(
|
|
649
636
|
const firstCorner = results.coordinates;
|
650
637
|
const secondCorner = findCoordinatesFromResultValue(
|
651
638
|
columnName,
|
652
|
-
params.
|
639
|
+
params.data.rowNumber,
|
653
640
|
);
|
654
641
|
|
655
642
|
resultState.setSelectedCells([results]);
|
@@ -661,11 +648,13 @@ const QueryResultCellRenderer = observer(
|
|
661
648
|
|
662
649
|
for (let x = minRow; x <= maxRow; x++) {
|
663
650
|
for (let y = minCol; y <= maxCol; y++) {
|
664
|
-
const actualValue = findResultValueFromCoordinates(
|
665
|
-
|
651
|
+
const actualValue = findResultValueFromCoordinates(
|
652
|
+
x,
|
653
|
+
findColumnNameFromColumnIndex(y) as string,
|
654
|
+
);
|
666
655
|
const valueAndColumnId = {
|
667
656
|
value: actualValue,
|
668
|
-
columnName:
|
657
|
+
columnName: findColumnNameFromColumnIndex(y),
|
669
658
|
coordinates: {
|
670
659
|
rowIndex: x,
|
671
660
|
colIndex: y,
|
@@ -684,7 +673,6 @@ const QueryResultCellRenderer = observer(
|
|
684
673
|
}
|
685
674
|
}
|
686
675
|
}
|
687
|
-
|
688
676
|
resultState.setMouseOverCell(resultState.selectedCells[0] ?? null);
|
689
677
|
};
|
690
678
|
|
@@ -741,20 +729,6 @@ const QueryBuilderGridResult = observer(
|
|
741
729
|
|
742
730
|
const resultState = queryBuilderState.resultState;
|
743
731
|
|
744
|
-
const rowData = executionResult.result.rows.map((_row, rowIdx) => {
|
745
|
-
const row: PlainObject = {};
|
746
|
-
const cols = executionResult.result.columns;
|
747
|
-
_row.values.forEach((value, colIdx) => {
|
748
|
-
// `ag-grid` shows `false` value as empty string so we have
|
749
|
-
// call `.toString()` to avoid this behavior.
|
750
|
-
// See https://github.com/finos/legend-studio/issues/1008
|
751
|
-
row[cols[colIdx] as string] = isBoolean(value) ? String(value) : value;
|
752
|
-
});
|
753
|
-
|
754
|
-
row.rowNumber = rowIdx;
|
755
|
-
return row;
|
756
|
-
});
|
757
|
-
|
758
732
|
return (
|
759
733
|
<div className="query-builder__result__values__table">
|
760
734
|
<div
|
@@ -763,7 +737,20 @@ const QueryBuilderGridResult = observer(
|
|
763
737
|
)}
|
764
738
|
>
|
765
739
|
<DataGrid
|
766
|
-
rowData={
|
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
|
+
}}
|
767
754
|
gridOptions={{
|
768
755
|
suppressScrollOnNewData: true,
|
769
756
|
getRowId: (data) => data.data.rowNumber,
|
@@ -113,6 +113,7 @@ import {
|
|
113
113
|
import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
|
114
114
|
import { getPropertyChainName } from '../../stores/QueryBuilderPropertyEditorState.js';
|
115
115
|
import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
|
116
|
+
import { buildPropertyExpressionChain } from '../../stores/QueryBuilderValueSpecificationBuilderHelper.js';
|
116
117
|
|
117
118
|
const isCollectionProperty = (
|
118
119
|
propertyExpression: AbstractPropertyExpression,
|
@@ -151,7 +152,7 @@ const isCollectionProperty = (
|
|
151
152
|
export const buildFilterTreeWithExists = (
|
152
153
|
propertyExpression: AbstractPropertyExpression,
|
153
154
|
filterState: QueryBuilderFilterState,
|
154
|
-
targetDropNode?:
|
155
|
+
targetDropNode?: QueryBuilderFilterTreeNodeData,
|
155
156
|
): void => {
|
156
157
|
// 1. Decompose property expression
|
157
158
|
const expressions: (AbstractPropertyExpression | SimpleFunctionExpression)[] =
|
@@ -294,7 +295,21 @@ export const buildFilterTreeWithExists = (
|
|
294
295
|
.value.path,
|
295
296
|
);
|
296
297
|
if (parentPropertyChainIndex >= 0) {
|
297
|
-
parentNode
|
298
|
+
// Here we choose parentNode based on what the type of targetDropNode
|
299
|
+
// 1. QueryBuilderFilterTreeConditionNodeData: In this case we would want to
|
300
|
+
// add a new group condition from the targetDropNode and add new exists node
|
301
|
+
// getting created as it's child so the parent would be the new group node
|
302
|
+
// 2. QueryBuilderFilterTreeGroupNodeData: Parent node would be same as
|
303
|
+
// targetDropNode
|
304
|
+
// 3. QueryBuilderFilterTreeBlankConditionNodeData: parentNode would be the
|
305
|
+
// parent of the targetDropNode. At the end we would deelete this blank node
|
306
|
+
// that got created.
|
307
|
+
parentNode =
|
308
|
+
targetDropNode instanceof QueryBuilderFilterTreeConditionNodeData
|
309
|
+
? filterState.newGroupConditionFromNode(targetDropNode)
|
310
|
+
: targetDropNode instanceof QueryBuilderFilterTreeGroupNodeData
|
311
|
+
? targetDropNode
|
312
|
+
: filterState.getParentNode(targetDropNode);
|
298
313
|
existsLambdaPropertyChains = existsLambdaPropertyChains.slice(
|
299
314
|
parentPropertyChainIndex + 1,
|
300
315
|
);
|
@@ -303,7 +318,12 @@ export const buildFilterTreeWithExists = (
|
|
303
318
|
);
|
304
319
|
}
|
305
320
|
} else if (!parentId) {
|
306
|
-
parentNode =
|
321
|
+
parentNode =
|
322
|
+
targetDropNode instanceof QueryBuilderFilterTreeConditionNodeData
|
323
|
+
? filterState.newGroupConditionFromNode(targetDropNode)
|
324
|
+
: targetDropNode instanceof QueryBuilderFilterTreeGroupNodeData
|
325
|
+
? targetDropNode
|
326
|
+
: filterState.getParentNode(targetDropNode);
|
307
327
|
}
|
308
328
|
}
|
309
329
|
}
|
@@ -332,6 +352,127 @@ export const buildFilterTreeWithExists = (
|
|
332
352
|
filterConditionState,
|
333
353
|
);
|
334
354
|
filterState.addNodeFromNode(treeNode, parentNode);
|
355
|
+
if (targetDropNode instanceof QueryBuilderFilterTreeBlankConditionNodeData) {
|
356
|
+
filterState.removeNodeAndPruneBranch(targetDropNode);
|
357
|
+
}
|
358
|
+
};
|
359
|
+
|
360
|
+
export const buildPropertyExpressionFromExistsNode = (
|
361
|
+
filterState: QueryBuilderFilterState,
|
362
|
+
existsNode: QueryBuilderFilterTreeExistsNodeData,
|
363
|
+
node: QueryBuilderFilterTreeConditionNodeData,
|
364
|
+
): AbstractPropertyExpression => {
|
365
|
+
let nodeParent = filterState.getParentNode(node);
|
366
|
+
let existsLambdaParameterNames: string[] = [];
|
367
|
+
let existsLambdaExpressions: AbstractPropertyExpression[] = [];
|
368
|
+
existsLambdaExpressions.push(
|
369
|
+
node.condition.propertyExpressionState.propertyExpression,
|
370
|
+
);
|
371
|
+
existsLambdaParameterNames.push(
|
372
|
+
nodeParent?.lambdaParameterName ?? filterState.lambdaParameterName,
|
373
|
+
);
|
374
|
+
while (nodeParent && nodeParent.id !== existsNode.id) {
|
375
|
+
if (nodeParent instanceof QueryBuilderFilterTreeExistsNodeData) {
|
376
|
+
existsLambdaExpressions.push(
|
377
|
+
nodeParent.propertyExpressionState.propertyExpression,
|
378
|
+
);
|
379
|
+
existsLambdaParameterNames.push(
|
380
|
+
nodeParent.lambdaParameterName ?? filterState.lambdaParameterName,
|
381
|
+
);
|
382
|
+
}
|
383
|
+
nodeParent = filterState.getParentNode(nodeParent);
|
384
|
+
}
|
385
|
+
if (nodeParent?.id === existsNode.id) {
|
386
|
+
existsLambdaExpressions.push(
|
387
|
+
existsNode.propertyExpressionState.propertyExpression,
|
388
|
+
);
|
389
|
+
existsLambdaParameterNames.push(
|
390
|
+
existsNode.lambdaParameterName ?? filterState.lambdaParameterName,
|
391
|
+
);
|
392
|
+
}
|
393
|
+
existsLambdaParameterNames = existsLambdaParameterNames.reverse();
|
394
|
+
existsLambdaExpressions = existsLambdaExpressions.reverse();
|
395
|
+
|
396
|
+
const initialPropertyExpression = guaranteeNonNullable(
|
397
|
+
existsLambdaExpressions[0],
|
398
|
+
);
|
399
|
+
existsLambdaParameterNames = existsLambdaParameterNames.slice(1);
|
400
|
+
existsLambdaExpressions = existsLambdaExpressions.slice(1);
|
401
|
+
|
402
|
+
let flattenedPropertyExpressionChain = new AbstractPropertyExpression('');
|
403
|
+
flattenedPropertyExpressionChain.func = initialPropertyExpression.func;
|
404
|
+
flattenedPropertyExpressionChain.parametersValues = [
|
405
|
+
...initialPropertyExpression.parametersValues,
|
406
|
+
];
|
407
|
+
for (const exp of existsLambdaExpressions) {
|
408
|
+
// when rebuilding the property expression chain, disregard the initial variable that starts the chain
|
409
|
+
const expressions: (
|
410
|
+
| AbstractPropertyExpression
|
411
|
+
| SimpleFunctionExpression
|
412
|
+
)[] = [];
|
413
|
+
let currentExpression: ValueSpecification = exp;
|
414
|
+
while (
|
415
|
+
currentExpression instanceof AbstractPropertyExpression ||
|
416
|
+
(currentExpression instanceof SimpleFunctionExpression &&
|
417
|
+
matchFunctionName(
|
418
|
+
currentExpression.functionName,
|
419
|
+
QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
|
420
|
+
))
|
421
|
+
) {
|
422
|
+
if (currentExpression instanceof SimpleFunctionExpression) {
|
423
|
+
const functionExpression = new SimpleFunctionExpression(
|
424
|
+
extractElementNameFromPath(QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE),
|
425
|
+
);
|
426
|
+
functionExpression.parametersValues.unshift(
|
427
|
+
guaranteeNonNullable(currentExpression.parametersValues[1]),
|
428
|
+
);
|
429
|
+
expressions.push(functionExpression);
|
430
|
+
} else if (currentExpression instanceof AbstractPropertyExpression) {
|
431
|
+
const propertyExp = new AbstractPropertyExpression('');
|
432
|
+
propertyExp.func = currentExpression.func;
|
433
|
+
// NOTE: we must retain the rest of the parameters as those are derived property parameters
|
434
|
+
propertyExp.parametersValues =
|
435
|
+
currentExpression.parametersValues.length > 1
|
436
|
+
? currentExpression.parametersValues.slice(1)
|
437
|
+
: [];
|
438
|
+
expressions.push(propertyExp);
|
439
|
+
}
|
440
|
+
currentExpression = guaranteeNonNullable(
|
441
|
+
currentExpression.parametersValues[0],
|
442
|
+
);
|
443
|
+
}
|
444
|
+
assertTrue(
|
445
|
+
expressions.length > 0,
|
446
|
+
`Can't process exists() expression: exists() usage with non-chain property expression is not supported`,
|
447
|
+
);
|
448
|
+
for (let i = 0; i < expressions.length - 1; ++i) {
|
449
|
+
(
|
450
|
+
expressions[i] as AbstractPropertyExpression | SimpleFunctionExpression
|
451
|
+
).parametersValues.unshift(
|
452
|
+
expressions[i + 1] as
|
453
|
+
| AbstractPropertyExpression
|
454
|
+
| SimpleFunctionExpression,
|
455
|
+
);
|
456
|
+
}
|
457
|
+
(
|
458
|
+
expressions[expressions.length - 1] as
|
459
|
+
| AbstractPropertyExpression
|
460
|
+
| SimpleFunctionExpression
|
461
|
+
).parametersValues.unshift(flattenedPropertyExpressionChain);
|
462
|
+
flattenedPropertyExpressionChain = guaranteeType(
|
463
|
+
expressions[0],
|
464
|
+
AbstractPropertyExpression,
|
465
|
+
`Can't process exists() expression: can't flatten to a property expression`,
|
466
|
+
);
|
467
|
+
}
|
468
|
+
return guaranteeType(
|
469
|
+
buildPropertyExpressionChain(
|
470
|
+
flattenedPropertyExpressionChain,
|
471
|
+
filterState.queryBuilderState,
|
472
|
+
existsNode.lambdaParameterName ?? filterState.lambdaParameterName,
|
473
|
+
),
|
474
|
+
AbstractPropertyExpression,
|
475
|
+
);
|
335
476
|
};
|
336
477
|
|
337
478
|
/**
|
@@ -340,7 +481,7 @@ export const buildFilterTreeWithExists = (
|
|
340
481
|
const buildFilterTree = (
|
341
482
|
propertyExpression: AbstractPropertyExpression,
|
342
483
|
filterState: QueryBuilderFilterState,
|
343
|
-
targetDropNode?:
|
484
|
+
targetDropNode?: QueryBuilderFilterTreeNodeData | undefined,
|
344
485
|
): void => {
|
345
486
|
if (isCollectionProperty(propertyExpression)) {
|
346
487
|
const propertyChainName = getPropertyChainName(
|
@@ -383,12 +524,88 @@ const buildFilterTree = (
|
|
383
524
|
undefined,
|
384
525
|
filterConditionState,
|
385
526
|
);
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
527
|
+
// Check if there are any exists node present in the parent nodes of the target.
|
528
|
+
// This would change the way we build the filter tree
|
529
|
+
let cn: QueryBuilderFilterTreeNodeData | undefined = targetDropNode;
|
530
|
+
let existsNode: QueryBuilderFilterTreeExistsNodeData | undefined =
|
531
|
+
undefined;
|
532
|
+
let parentId = targetDropNode?.parentId;
|
533
|
+
while (parentId) {
|
534
|
+
cn = filterState.nodes.get(parentId);
|
535
|
+
parentId = cn?.parentId;
|
536
|
+
if (cn instanceof QueryBuilderFilterTreeExistsNodeData) {
|
537
|
+
existsNode = cn;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
if (cn instanceof QueryBuilderFilterTreeExistsNodeData) {
|
541
|
+
existsNode = cn;
|
542
|
+
}
|
543
|
+
if (targetDropNode instanceof QueryBuilderFilterTreeGroupNodeData) {
|
544
|
+
if (existsNode) {
|
545
|
+
filterState.newGroupConditionFromNode(
|
546
|
+
existsNode,
|
547
|
+
treeNode,
|
548
|
+
targetDropNode.groupOperation,
|
549
|
+
);
|
550
|
+
} else {
|
551
|
+
filterState.addNodeFromNode(treeNode, targetDropNode);
|
552
|
+
}
|
553
|
+
} else if (
|
554
|
+
targetDropNode instanceof QueryBuilderFilterTreeBlankConditionNodeData
|
555
|
+
) {
|
556
|
+
if (existsNode) {
|
557
|
+
filterState.queryBuilderState.applicationStore.notificationService.notifyError(
|
558
|
+
`Can't drag and drop here: property expression of target and source doesn't match`,
|
559
|
+
);
|
560
|
+
} else {
|
561
|
+
filterState.replaceBlankNodeWithNode(treeNode, targetDropNode);
|
562
|
+
}
|
563
|
+
} else if (
|
564
|
+
existsNode &&
|
565
|
+
targetDropNode instanceof QueryBuilderFilterTreeExistsNodeData
|
566
|
+
) {
|
567
|
+
filterState.newGroupConditionFromNode(existsNode, treeNode);
|
568
|
+
} else if (
|
569
|
+
targetDropNode instanceof QueryBuilderFilterTreeConditionNodeData
|
570
|
+
) {
|
571
|
+
const parentNode = filterState.getParentNode(targetDropNode);
|
572
|
+
if (
|
573
|
+
existsNode &&
|
574
|
+
parentNode instanceof QueryBuilderFilterTreeExistsNodeData &&
|
575
|
+
parentNode.childrenIds.length === 1
|
576
|
+
) {
|
577
|
+
filterState.newGroupConditionFromNode(
|
578
|
+
existsNode,
|
579
|
+
treeNode,
|
580
|
+
QUERY_BUILDER_GROUP_OPERATION.AND,
|
581
|
+
);
|
582
|
+
} else if (
|
583
|
+
existsNode &&
|
584
|
+
parentNode instanceof QueryBuilderFilterTreeGroupNodeData
|
585
|
+
) {
|
586
|
+
const propertyExpression1 = buildPropertyExpressionFromExistsNode(
|
587
|
+
filterState,
|
588
|
+
existsNode,
|
589
|
+
targetDropNode,
|
590
|
+
);
|
591
|
+
filterState.removeNodeAndPruneBranch(targetDropNode);
|
592
|
+
filterState.newGroupConditionFromNode(
|
593
|
+
existsNode,
|
594
|
+
treeNode,
|
595
|
+
parentNode.groupOperation,
|
596
|
+
);
|
597
|
+
const newParentNode = filterState.getParentNode(treeNode);
|
598
|
+
buildFilterTreeWithExists(
|
599
|
+
propertyExpression1,
|
600
|
+
filterState,
|
601
|
+
newParentNode,
|
602
|
+
);
|
603
|
+
} else {
|
604
|
+
filterState.newGroupWithConditionFromNode(treeNode, targetDropNode);
|
605
|
+
}
|
606
|
+
} else {
|
607
|
+
filterState.addNodeFromNode(treeNode, undefined);
|
608
|
+
}
|
392
609
|
}
|
393
610
|
};
|
394
611
|
|
@@ -604,7 +821,12 @@ const QueryBuilderFilterConditionEditor = observer(
|
|
604
821
|
};
|
605
822
|
|
606
823
|
return (
|
607
|
-
<div
|
824
|
+
<div
|
825
|
+
className="query-builder-filter-tree__node__label__content dnd__entry__container"
|
826
|
+
data-testid={
|
827
|
+
QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER_TREE_CONDITION_NODE_CONTENT
|
828
|
+
}
|
829
|
+
>
|
608
830
|
<PanelEntryDropZonePlaceholder
|
609
831
|
isDragOver={isDragOver}
|
610
832
|
label="Add New Logical Group"
|
@@ -845,21 +1067,17 @@ const QueryBuilderFilterTreeNodeContainer = observer(
|
|
845
1067
|
node,
|
846
1068
|
);
|
847
1069
|
} else if (node instanceof QueryBuilderFilterTreeConditionNodeData) {
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
filterConditionState,
|
852
|
-
),
|
1070
|
+
buildFilterTree(
|
1071
|
+
filterConditionState.propertyExpressionState.propertyExpression,
|
1072
|
+
filterState,
|
853
1073
|
node,
|
854
1074
|
);
|
855
1075
|
} else if (
|
856
1076
|
node instanceof QueryBuilderFilterTreeBlankConditionNodeData
|
857
1077
|
) {
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
filterConditionState,
|
862
|
-
),
|
1078
|
+
buildFilterTree(
|
1079
|
+
filterConditionState.propertyExpressionState.propertyExpression,
|
1080
|
+
filterState,
|
863
1081
|
node,
|
864
1082
|
);
|
865
1083
|
}
|
@@ -17,14 +17,15 @@
|
|
17
17
|
import { action, flow, makeObservable, observable } from 'mobx';
|
18
18
|
import {
|
19
19
|
type GeneratorFn,
|
20
|
+
type ContentType,
|
20
21
|
assertErrorThrown,
|
21
22
|
LogEvent,
|
22
23
|
guaranteeNonNullable,
|
23
|
-
type ContentType,
|
24
24
|
downloadFileUsingDataURI,
|
25
25
|
ActionState,
|
26
26
|
StopWatch,
|
27
27
|
getContentTypeFileExtension,
|
28
|
+
isBoolean,
|
28
29
|
} from '@finos/legend-shared';
|
29
30
|
import type { QueryBuilderState } from './QueryBuilderState.js';
|
30
31
|
import {
|
@@ -37,6 +38,7 @@ import {
|
|
37
38
|
buildRawLambdaFromLambdaFunction,
|
38
39
|
reportGraphAnalytics,
|
39
40
|
extractExecutionResultValues,
|
41
|
+
TDSExecutionResult,
|
40
42
|
} from '@finos/legend-graph';
|
41
43
|
import { buildLambdaFunction } from './QueryBuilderValueSpecificationBuilder.js';
|
42
44
|
import { DEFAULT_TAB_SIZE } from '@finos/legend-application';
|
@@ -81,6 +83,7 @@ export class QueryBuilderResultState {
|
|
81
83
|
latestRunHashCode?: string | undefined;
|
82
84
|
queryRunPromise: Promise<ExecutionResult> | undefined = undefined;
|
83
85
|
isQueryUsageViewerOpened = false;
|
86
|
+
rowData: Record<string, string | number | boolean | null>[] = [];
|
84
87
|
|
85
88
|
selectedCells: QueryBuilderTDSResultCellData[];
|
86
89
|
mousedOverCell: QueryBuilderTDSResultCellData | null = null;
|
@@ -110,6 +113,8 @@ export class QueryBuilderResultState {
|
|
110
113
|
setQueryRunPromise: action,
|
111
114
|
setIsQueryUsageViewerOpened: action,
|
112
115
|
exportData: flow,
|
116
|
+
getRowData: action,
|
117
|
+
setRowData: action,
|
113
118
|
runQuery: flow,
|
114
119
|
cancelQuery: flow,
|
115
120
|
generatePlan: flow,
|
@@ -124,6 +129,36 @@ export class QueryBuilderResultState {
|
|
124
129
|
);
|
125
130
|
}
|
126
131
|
|
132
|
+
getRowData(): Record<string, string | number | boolean | null>[] {
|
133
|
+
if (
|
134
|
+
this.executionResult &&
|
135
|
+
this.executionResult instanceof TDSExecutionResult
|
136
|
+
) {
|
137
|
+
const data = this.executionResult.result.rows.map((_row, rowIdx) => {
|
138
|
+
const row: Record<string, string | number | boolean | null> = {};
|
139
|
+
const cols = (this.executionResult as TDSExecutionResult).result
|
140
|
+
.columns;
|
141
|
+
_row.values.forEach((value, colIdx) => {
|
142
|
+
// `ag-grid` shows `false` value as empty string so we have
|
143
|
+
// call `.toString()` to avoid this behavior.
|
144
|
+
// See https://github.com/finos/legend-studio/issues/1008
|
145
|
+
row[cols[colIdx] as string] = isBoolean(value)
|
146
|
+
? String(value)
|
147
|
+
: value;
|
148
|
+
});
|
149
|
+
row.rowNumber = rowIdx;
|
150
|
+
return row;
|
151
|
+
});
|
152
|
+
this.rowData = data;
|
153
|
+
return data;
|
154
|
+
}
|
155
|
+
return [];
|
156
|
+
}
|
157
|
+
|
158
|
+
setRowData(val: Record<string, string | number | boolean | null>[]): void {
|
159
|
+
this.rowData = val;
|
160
|
+
}
|
161
|
+
|
127
162
|
setIsSelectingCells(val: boolean): void {
|
128
163
|
this.isSelectingCells = val;
|
129
164
|
}
|