@finos/legend-query-builder 4.10.0 → 4.10.1
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__/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
|
}
|