@finos/legend-query-builder 4.1.15 → 4.2.0
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/filter/QueryBuilderFilterPanel.d.ts +6 -1
- package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.js +189 -13
- package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +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/filter/QueryBuilderFilterOperator.d.ts +1 -1
- package/lib/stores/filter/QueryBuilderFilterOperator.d.ts.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterOperator.js.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterState.d.ts +16 -5
- package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterState.js +78 -18
- package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterStateBuilder.d.ts.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterStateBuilder.js +125 -2
- package/lib/stores/filter/QueryBuilderFilterStateBuilder.js.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterValueSpecificationBuilder.d.ts.map +1 -1
- package/lib/stores/filter/QueryBuilderFilterValueSpecificationBuilder.js +62 -3
- package/lib/stores/filter/QueryBuilderFilterValueSpecificationBuilder.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.d.ts +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js +12 -192
- package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js +4 -4
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js +4 -4
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js +4 -4
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +4 -4
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js +4 -4
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts +2 -2
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts.map +1 -1
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js +4 -4
- package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js.map +1 -1
- package/package.json +1 -1
- package/src/components/filter/QueryBuilderFilterPanel.tsx +355 -24
- package/src/stores/QueryBuilderStateHashUtils.ts +1 -0
- package/src/stores/filter/QueryBuilderFilterOperator.ts +1 -0
- package/src/stores/filter/QueryBuilderFilterState.ts +115 -27
- package/src/stores/filter/QueryBuilderFilterStateBuilder.ts +244 -0
- package/src/stores/filter/QueryBuilderFilterValueSpecificationBuilder.ts +94 -1
- package/src/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.ts +37 -377
- package/src/stores/filter/operators/QueryBuilderFilterOperator_Contain.ts +7 -1
- package/src/stores/filter/operators/QueryBuilderFilterOperator_EndWith.ts +7 -1
- package/src/stores/filter/operators/QueryBuilderFilterOperator_Equal.ts +7 -1
- package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.ts +2 -0
- package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.ts +2 -0
- package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +7 -1
- package/src/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.ts +7 -1
- package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThan.ts +2 -0
- package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.ts +2 -0
- package/src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts +7 -1
|
@@ -257,47 +257,36 @@ export abstract class QueryBuilderFilterTreeNodeData
|
|
|
257
257
|
abstract get hashCode(): string;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
export class
|
|
260
|
+
export abstract class QueryBuilderFilterTreeOperationNodeData
|
|
261
261
|
extends QueryBuilderFilterTreeNodeData
|
|
262
262
|
implements Hashable
|
|
263
263
|
{
|
|
264
|
-
groupOperation: QUERY_BUILDER_GROUP_OPERATION;
|
|
265
264
|
childrenIds: string[] = [];
|
|
265
|
+
lambdaParameterName?: string | undefined;
|
|
266
266
|
|
|
267
|
-
constructor(
|
|
268
|
-
parentId: string | undefined,
|
|
269
|
-
groupOperation: QUERY_BUILDER_GROUP_OPERATION,
|
|
270
|
-
) {
|
|
267
|
+
constructor(parentId: string | undefined) {
|
|
271
268
|
super(parentId);
|
|
272
269
|
|
|
273
270
|
makeObservable(this, {
|
|
274
|
-
groupOperation: observable,
|
|
275
271
|
childrenIds: observable,
|
|
276
|
-
setGroupOperation: action,
|
|
277
272
|
addChildNode: action,
|
|
278
273
|
removeChildNode: action,
|
|
279
274
|
dragPreviewLabel: computed,
|
|
280
275
|
});
|
|
281
276
|
|
|
282
|
-
this.groupOperation = groupOperation;
|
|
283
277
|
this.isOpen = true;
|
|
284
278
|
}
|
|
285
279
|
|
|
286
|
-
get dragPreviewLabel(): string {
|
|
287
|
-
return `${this.groupOperation.toUpperCase()} group`;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
setGroupOperation(val: QUERY_BUILDER_GROUP_OPERATION): void {
|
|
291
|
-
this.groupOperation = val;
|
|
292
|
-
}
|
|
293
280
|
addChildNode(node: QueryBuilderFilterTreeNodeData): void {
|
|
294
281
|
addUniqueEntry(this.childrenIds, node.id);
|
|
295
282
|
node.setParentId(this.id);
|
|
296
283
|
}
|
|
284
|
+
|
|
297
285
|
removeChildNode(node: QueryBuilderFilterTreeNodeData): void {
|
|
298
286
|
deleteEntry(this.childrenIds, node.id);
|
|
299
287
|
node.setParentId(undefined);
|
|
300
288
|
}
|
|
289
|
+
|
|
301
290
|
addChildNodeAt(node: QueryBuilderFilterTreeNodeData, idx: number): void {
|
|
302
291
|
if (!this.childrenIds.find((childId) => childId === node.id)) {
|
|
303
292
|
idx = Math.max(0, Math.min(idx, this.childrenIds.length - 1));
|
|
@@ -305,6 +294,36 @@ export class QueryBuilderFilterTreeGroupNodeData
|
|
|
305
294
|
node.setParentId(this.id);
|
|
306
295
|
}
|
|
307
296
|
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export class QueryBuilderFilterTreeGroupNodeData
|
|
300
|
+
extends QueryBuilderFilterTreeOperationNodeData
|
|
301
|
+
implements Hashable
|
|
302
|
+
{
|
|
303
|
+
groupOperation: QUERY_BUILDER_GROUP_OPERATION;
|
|
304
|
+
|
|
305
|
+
constructor(
|
|
306
|
+
parentId: string | undefined,
|
|
307
|
+
groupOperation: QUERY_BUILDER_GROUP_OPERATION,
|
|
308
|
+
) {
|
|
309
|
+
super(parentId);
|
|
310
|
+
|
|
311
|
+
makeObservable(this, {
|
|
312
|
+
groupOperation: observable,
|
|
313
|
+
setGroupOperation: action,
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
this.groupOperation = groupOperation;
|
|
317
|
+
this.isOpen = true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
get dragPreviewLabel(): string {
|
|
321
|
+
return `${this.groupOperation.toUpperCase()} group`;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
setGroupOperation(val: QUERY_BUILDER_GROUP_OPERATION): void {
|
|
325
|
+
this.groupOperation = val;
|
|
326
|
+
}
|
|
308
327
|
|
|
309
328
|
get hashCode(): string {
|
|
310
329
|
return hashArray([
|
|
@@ -312,6 +331,43 @@ export class QueryBuilderFilterTreeGroupNodeData
|
|
|
312
331
|
this.parentId ?? '',
|
|
313
332
|
hashArray(this.childrenIds),
|
|
314
333
|
this.groupOperation,
|
|
334
|
+
this.lambdaParameterName ?? '',
|
|
335
|
+
]);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export class QueryBuilderFilterTreeExistsNodeData
|
|
340
|
+
extends QueryBuilderFilterTreeOperationNodeData
|
|
341
|
+
implements Hashable
|
|
342
|
+
{
|
|
343
|
+
propertyExpression!: AbstractPropertyExpression;
|
|
344
|
+
|
|
345
|
+
constructor(parentId: string | undefined) {
|
|
346
|
+
super(parentId);
|
|
347
|
+
|
|
348
|
+
makeObservable(this, {
|
|
349
|
+
propertyExpression: observable,
|
|
350
|
+
setPropertyExpression: action,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
this.isOpen = true;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
get dragPreviewLabel(): string {
|
|
357
|
+
return `exists`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
setPropertyExpression(val: AbstractPropertyExpression): void {
|
|
361
|
+
this.propertyExpression = val;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
get hashCode(): string {
|
|
365
|
+
return hashArray([
|
|
366
|
+
QUERY_BUILDER_STATE_HASH_STRUCTURE.FILTER_TREE_EXISTS_NODE_DATA,
|
|
367
|
+
this.parentId ?? '',
|
|
368
|
+
hashArray(this.childrenIds),
|
|
369
|
+
this.propertyExpression,
|
|
370
|
+
this.lambdaParameterName ?? '',
|
|
315
371
|
]);
|
|
316
372
|
}
|
|
317
373
|
}
|
|
@@ -449,11 +505,11 @@ export class QueryBuilderFilterState
|
|
|
449
505
|
|
|
450
506
|
private getParentNode(
|
|
451
507
|
node: QueryBuilderFilterTreeNodeData,
|
|
452
|
-
):
|
|
508
|
+
): QueryBuilderFilterTreeOperationNodeData | undefined {
|
|
453
509
|
return node.parentId
|
|
454
510
|
? guaranteeType(
|
|
455
511
|
this.nodes.get(node.parentId),
|
|
456
|
-
|
|
512
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
457
513
|
)
|
|
458
514
|
: undefined;
|
|
459
515
|
}
|
|
@@ -465,7 +521,8 @@ export class QueryBuilderFilterState
|
|
|
465
521
|
rootNode.addChildNode(node);
|
|
466
522
|
} else if (
|
|
467
523
|
rootNode instanceof QueryBuilderFilterTreeConditionNodeData ||
|
|
468
|
-
rootNode instanceof QueryBuilderFilterTreeBlankConditionNodeData
|
|
524
|
+
rootNode instanceof QueryBuilderFilterTreeBlankConditionNodeData ||
|
|
525
|
+
rootNode instanceof QueryBuilderFilterTreeExistsNodeData
|
|
469
526
|
) {
|
|
470
527
|
// if the root node is condition node, form a group between the root node and the new node and nominate the group node as the new root
|
|
471
528
|
const groupNode = new QueryBuilderFilterTreeGroupNodeData(
|
|
@@ -489,6 +546,31 @@ export class QueryBuilderFilterState
|
|
|
489
546
|
if (fromNode instanceof QueryBuilderFilterTreeGroupNodeData) {
|
|
490
547
|
this.nodes.set(node.id, node);
|
|
491
548
|
fromNode.addChildNode(node);
|
|
549
|
+
} else if (fromNode instanceof QueryBuilderFilterTreeExistsNodeData) {
|
|
550
|
+
// Here we check if there are any child nodes for exists node. The rationale
|
|
551
|
+
// behind doing this check is if there are no childs we can just add a child
|
|
552
|
+
// node to this otherwise we need to add a group condition for the existing
|
|
553
|
+
// child node and the node we are trying to add.
|
|
554
|
+
if (!fromNode.childrenIds.length) {
|
|
555
|
+
this.nodes.set(node.id, node);
|
|
556
|
+
fromNode.addChildNode(node);
|
|
557
|
+
} else {
|
|
558
|
+
this.nodes.set(node.id, node);
|
|
559
|
+
const groupNode = new QueryBuilderFilterTreeGroupNodeData(
|
|
560
|
+
undefined,
|
|
561
|
+
QUERY_BUILDER_GROUP_OPERATION.AND,
|
|
562
|
+
);
|
|
563
|
+
groupNode.addChildNode(
|
|
564
|
+
guaranteeNonNullable(
|
|
565
|
+
this.nodes.get(guaranteeNonNullable(fromNode.childrenIds[0])),
|
|
566
|
+
),
|
|
567
|
+
);
|
|
568
|
+
groupNode.addChildNode(node);
|
|
569
|
+
groupNode.lambdaParameterName = fromNode.lambdaParameterName;
|
|
570
|
+
this.nodes.set(groupNode.id, groupNode);
|
|
571
|
+
fromNode.childrenIds = [];
|
|
572
|
+
fromNode.addChildNode(groupNode);
|
|
573
|
+
}
|
|
492
574
|
} else if (
|
|
493
575
|
fromNode instanceof QueryBuilderFilterTreeConditionNodeData ||
|
|
494
576
|
fromNode instanceof QueryBuilderFilterTreeBlankConditionNodeData
|
|
@@ -563,6 +645,7 @@ export class QueryBuilderFilterState
|
|
|
563
645
|
this.nodes.set(newGroupNode.id, newGroupNode);
|
|
564
646
|
newGroupNode.addChildNode(fromNode);
|
|
565
647
|
newGroupNode.addChildNode(newNode);
|
|
648
|
+
newGroupNode.lambdaParameterName = fromNodeParent.lambdaParameterName;
|
|
566
649
|
fromNodeParent.addChildNodeAt(newGroupNode, fromNodeIdx);
|
|
567
650
|
} else {
|
|
568
651
|
this.addRootNode(newNode);
|
|
@@ -573,7 +656,7 @@ export class QueryBuilderFilterState
|
|
|
573
656
|
private removeNode(node: QueryBuilderFilterTreeNodeData): void {
|
|
574
657
|
this.nodes.delete(node.id);
|
|
575
658
|
// remove relationship with children nodes
|
|
576
|
-
if (node instanceof
|
|
659
|
+
if (node instanceof QueryBuilderFilterTreeOperationNodeData) {
|
|
577
660
|
// NOTE: we are deleting child node, i.e. modifying `childrenIds` as we iterate
|
|
578
661
|
[...node.childrenIds].forEach((childId) =>
|
|
579
662
|
node.removeChildNode(this.getNode(childId)),
|
|
@@ -644,9 +727,13 @@ export class QueryBuilderFilterState
|
|
|
644
727
|
// squash parent node after the current node is deleted
|
|
645
728
|
if (parentNode) {
|
|
646
729
|
parentNode.removeChildNode(node);
|
|
647
|
-
let currentParentNode:
|
|
648
|
-
|
|
649
|
-
|
|
730
|
+
let currentParentNode:
|
|
731
|
+
| QueryBuilderFilterTreeOperationNodeData
|
|
732
|
+
| undefined = parentNode;
|
|
733
|
+
while (
|
|
734
|
+
currentParentNode &&
|
|
735
|
+
currentParentNode instanceof QueryBuilderFilterTreeGroupNodeData
|
|
736
|
+
) {
|
|
650
737
|
if (currentParentNode.childrenIds.length >= 2) {
|
|
651
738
|
break;
|
|
652
739
|
}
|
|
@@ -724,11 +811,12 @@ export class QueryBuilderFilterState
|
|
|
724
811
|
if (!node.parentId || !this.nodes.has(node.parentId)) {
|
|
725
812
|
return false;
|
|
726
813
|
}
|
|
727
|
-
const parentGroupNode =
|
|
728
|
-
|
|
729
|
-
|
|
814
|
+
const parentGroupNode = this.nodes.get(node.parentId);
|
|
815
|
+
|
|
816
|
+
return (
|
|
817
|
+
parentGroupNode instanceof QueryBuilderFilterTreeGroupNodeData &&
|
|
818
|
+
parentGroupNode.groupOperation === node.groupOperation
|
|
730
819
|
);
|
|
731
|
-
return parentGroupNode.groupOperation === node.groupOperation;
|
|
732
820
|
});
|
|
733
821
|
// Squash these unnecessary group nodes
|
|
734
822
|
let nodesToProcess = getUnnecessaryNodes();
|
|
@@ -16,9 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
AbstractPropertyExpression,
|
|
19
|
+
extractElementNameFromPath,
|
|
20
|
+
LambdaFunction,
|
|
19
21
|
LambdaFunctionInstanceValue,
|
|
20
22
|
matchFunctionName,
|
|
21
23
|
SimpleFunctionExpression,
|
|
24
|
+
type ValueSpecification,
|
|
22
25
|
VariableExpression,
|
|
23
26
|
} from '@finos/legend-graph';
|
|
24
27
|
import {
|
|
@@ -37,8 +40,35 @@ import {
|
|
|
37
40
|
type QueryBuilderFilterState,
|
|
38
41
|
QueryBuilderFilterTreeConditionNodeData,
|
|
39
42
|
QueryBuilderFilterTreeGroupNodeData,
|
|
43
|
+
QueryBuilderFilterTreeExistsNodeData,
|
|
44
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
40
45
|
} from './QueryBuilderFilterState.js';
|
|
41
46
|
|
|
47
|
+
const getPropertyExpressionChainVariable = (
|
|
48
|
+
propertyExpression: AbstractPropertyExpression,
|
|
49
|
+
): VariableExpression => {
|
|
50
|
+
let currentExpression: ValueSpecification = propertyExpression;
|
|
51
|
+
while (currentExpression instanceof AbstractPropertyExpression) {
|
|
52
|
+
currentExpression = guaranteeNonNullable(
|
|
53
|
+
currentExpression.parametersValues[0],
|
|
54
|
+
);
|
|
55
|
+
// Take care of chains of subtype (a pattern that is not useful, but we want to support and rectify)
|
|
56
|
+
// $x.employees->subType(@Person)->subType(@Staff)
|
|
57
|
+
while (
|
|
58
|
+
currentExpression instanceof SimpleFunctionExpression &&
|
|
59
|
+
matchFunctionName(
|
|
60
|
+
currentExpression.functionName,
|
|
61
|
+
QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
|
|
62
|
+
)
|
|
63
|
+
) {
|
|
64
|
+
currentExpression = guaranteeNonNullable(
|
|
65
|
+
currentExpression.parametersValues[0],
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return guaranteeType(currentExpression, VariableExpression);
|
|
70
|
+
};
|
|
71
|
+
|
|
42
72
|
const processFilterTree = (
|
|
43
73
|
expression: SimpleFunctionExpression,
|
|
44
74
|
filterState: QueryBuilderFilterState,
|
|
@@ -58,6 +88,12 @@ const processFilterTree = (
|
|
|
58
88
|
toGroupOperation(expression.functionName),
|
|
59
89
|
);
|
|
60
90
|
filterState.nodes.set(groupNode.id, groupNode);
|
|
91
|
+
if (parentNode) {
|
|
92
|
+
groupNode.lambdaParameterName = guaranteeType(
|
|
93
|
+
parentNode,
|
|
94
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
95
|
+
).lambdaParameterName;
|
|
96
|
+
}
|
|
61
97
|
expression.parametersValues.forEach((filterExpression) =>
|
|
62
98
|
processFilterTree(
|
|
63
99
|
guaranteeType(
|
|
@@ -70,6 +106,200 @@ const processFilterTree = (
|
|
|
70
106
|
),
|
|
71
107
|
);
|
|
72
108
|
filterState.addNodeFromNode(groupNode, parentNode);
|
|
109
|
+
} else if (
|
|
110
|
+
matchFunctionName(expression.functionName, [
|
|
111
|
+
QUERY_BUILDER_SUPPORTED_FUNCTIONS.EXISTS,
|
|
112
|
+
])
|
|
113
|
+
) {
|
|
114
|
+
const propertyExpression = guaranteeType(
|
|
115
|
+
expression.parametersValues[0],
|
|
116
|
+
AbstractPropertyExpression,
|
|
117
|
+
);
|
|
118
|
+
if (
|
|
119
|
+
propertyExpression.func.value.multiplicity.upperBound === undefined ||
|
|
120
|
+
propertyExpression.func.value.multiplicity.upperBound > 1
|
|
121
|
+
) {
|
|
122
|
+
const existsNode = new QueryBuilderFilterTreeExistsNodeData(
|
|
123
|
+
parentFilterNodeId,
|
|
124
|
+
);
|
|
125
|
+
const lambdaFunctionInstance = guaranteeType(
|
|
126
|
+
expression.parametersValues[1],
|
|
127
|
+
LambdaFunctionInstanceValue,
|
|
128
|
+
`Can't process filter expression: only supports exists with second paramter as LambdaFunctionInstanceValue`,
|
|
129
|
+
);
|
|
130
|
+
const lambdaFunction = guaranteeType(
|
|
131
|
+
lambdaFunctionInstance.values[0],
|
|
132
|
+
LambdaFunction,
|
|
133
|
+
);
|
|
134
|
+
const filterExpression = guaranteeType(
|
|
135
|
+
lambdaFunction.expressionSequence[0],
|
|
136
|
+
SimpleFunctionExpression,
|
|
137
|
+
);
|
|
138
|
+
existsNode.setPropertyExpression(propertyExpression);
|
|
139
|
+
existsNode.lambdaParameterName =
|
|
140
|
+
lambdaFunction.functionType.parameters[0]?.name;
|
|
141
|
+
filterState.nodes.set(existsNode.id, existsNode);
|
|
142
|
+
processFilterTree(filterExpression, filterState, existsNode.id);
|
|
143
|
+
filterState.addNodeFromNode(existsNode, parentNode);
|
|
144
|
+
} else {
|
|
145
|
+
// Build property chain if we have exists filter() and the multiplicity of the property
|
|
146
|
+
// is of multiplicity less than or equal to one. This will auto-fix the exists into the
|
|
147
|
+
// desired filter expression
|
|
148
|
+
|
|
149
|
+
// 1. Decompose the exists() lambda chain into property expression chains
|
|
150
|
+
const existsLambdaParameterNames: string[] = [];
|
|
151
|
+
|
|
152
|
+
const existsLambdaExpressions: AbstractPropertyExpression[] = [];
|
|
153
|
+
let mainFilterExpression: SimpleFunctionExpression = expression;
|
|
154
|
+
while (
|
|
155
|
+
matchFunctionName(
|
|
156
|
+
mainFilterExpression.functionName,
|
|
157
|
+
QUERY_BUILDER_SUPPORTED_FUNCTIONS.EXISTS,
|
|
158
|
+
)
|
|
159
|
+
) {
|
|
160
|
+
const existsLambda = guaranteeNonNullable(
|
|
161
|
+
guaranteeType(
|
|
162
|
+
mainFilterExpression.parametersValues[1],
|
|
163
|
+
LambdaFunctionInstanceValue,
|
|
164
|
+
).values[0],
|
|
165
|
+
`Can't process exists() expression: exists() lambda is missing`,
|
|
166
|
+
);
|
|
167
|
+
assertTrue(
|
|
168
|
+
existsLambda.expressionSequence.length === 1,
|
|
169
|
+
`Can't process exists() expression: exists() lambda body should hold an expression`,
|
|
170
|
+
);
|
|
171
|
+
mainFilterExpression = guaranteeType(
|
|
172
|
+
existsLambda.expressionSequence[0],
|
|
173
|
+
SimpleFunctionExpression,
|
|
174
|
+
`Can't process exists() expression: exists() lambda body should hold an expression`,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// record the lambda parameter name
|
|
178
|
+
assertTrue(
|
|
179
|
+
existsLambda.functionType.parameters.length === 1,
|
|
180
|
+
`Can't process exists() expression: exists() lambda should have 1 parameter`,
|
|
181
|
+
);
|
|
182
|
+
existsLambdaParameterNames.push(
|
|
183
|
+
guaranteeType(
|
|
184
|
+
existsLambda.functionType.parameters[0],
|
|
185
|
+
VariableExpression,
|
|
186
|
+
`Can't process exists() expression: exists() lambda should have 1 parameter`,
|
|
187
|
+
).name,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// record the lambda property expression
|
|
191
|
+
if (
|
|
192
|
+
mainFilterExpression.parametersValues[0] instanceof
|
|
193
|
+
AbstractPropertyExpression &&
|
|
194
|
+
!(
|
|
195
|
+
mainFilterExpression.parametersValues[0].func.value.multiplicity
|
|
196
|
+
.upperBound === undefined ||
|
|
197
|
+
mainFilterExpression.parametersValues[0].func.value.multiplicity
|
|
198
|
+
.upperBound > 1
|
|
199
|
+
)
|
|
200
|
+
) {
|
|
201
|
+
existsLambdaExpressions.push(
|
|
202
|
+
mainFilterExpression.parametersValues[0],
|
|
203
|
+
);
|
|
204
|
+
} else {
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 2. Build the property expression
|
|
210
|
+
const initialPropertyExpression = guaranteeType(
|
|
211
|
+
expression.parametersValues[0],
|
|
212
|
+
AbstractPropertyExpression,
|
|
213
|
+
);
|
|
214
|
+
let flattenedPropertyExpressionChain = new AbstractPropertyExpression('');
|
|
215
|
+
flattenedPropertyExpressionChain.func = initialPropertyExpression.func;
|
|
216
|
+
flattenedPropertyExpressionChain.parametersValues = [
|
|
217
|
+
...initialPropertyExpression.parametersValues,
|
|
218
|
+
];
|
|
219
|
+
for (const exp of existsLambdaExpressions) {
|
|
220
|
+
// when rebuilding the property expression chain, disregard the initial variable that starts the chain
|
|
221
|
+
const expressions: (
|
|
222
|
+
| AbstractPropertyExpression
|
|
223
|
+
| SimpleFunctionExpression
|
|
224
|
+
)[] = [];
|
|
225
|
+
let currentExpression: ValueSpecification = exp;
|
|
226
|
+
while (
|
|
227
|
+
currentExpression instanceof AbstractPropertyExpression ||
|
|
228
|
+
(currentExpression instanceof SimpleFunctionExpression &&
|
|
229
|
+
matchFunctionName(
|
|
230
|
+
currentExpression.functionName,
|
|
231
|
+
QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
|
|
232
|
+
))
|
|
233
|
+
) {
|
|
234
|
+
if (currentExpression instanceof SimpleFunctionExpression) {
|
|
235
|
+
const functionExpression = new SimpleFunctionExpression(
|
|
236
|
+
extractElementNameFromPath(
|
|
237
|
+
QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
functionExpression.parametersValues.unshift(
|
|
241
|
+
guaranteeNonNullable(currentExpression.parametersValues[1]),
|
|
242
|
+
);
|
|
243
|
+
expressions.push(functionExpression);
|
|
244
|
+
} else if (currentExpression instanceof AbstractPropertyExpression) {
|
|
245
|
+
const propertyExp = new AbstractPropertyExpression('');
|
|
246
|
+
propertyExp.func = currentExpression.func;
|
|
247
|
+
// NOTE: we must retain the rest of the parameters as those are derived property parameters
|
|
248
|
+
propertyExp.parametersValues =
|
|
249
|
+
currentExpression.parametersValues.length > 1
|
|
250
|
+
? currentExpression.parametersValues.slice(1)
|
|
251
|
+
: [];
|
|
252
|
+
expressions.push(propertyExp);
|
|
253
|
+
}
|
|
254
|
+
currentExpression = guaranteeNonNullable(
|
|
255
|
+
currentExpression.parametersValues[0],
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
assertTrue(
|
|
259
|
+
expressions.length > 0,
|
|
260
|
+
`Can't process exists() expression: exists() usage with non-chain property expression is not supported`,
|
|
261
|
+
);
|
|
262
|
+
for (let i = 0; i < expressions.length - 1; ++i) {
|
|
263
|
+
(
|
|
264
|
+
expressions[i] as
|
|
265
|
+
| AbstractPropertyExpression
|
|
266
|
+
| SimpleFunctionExpression
|
|
267
|
+
).parametersValues.unshift(
|
|
268
|
+
expressions[i + 1] as
|
|
269
|
+
| AbstractPropertyExpression
|
|
270
|
+
| SimpleFunctionExpression,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
(
|
|
274
|
+
expressions[expressions.length - 1] as
|
|
275
|
+
| AbstractPropertyExpression
|
|
276
|
+
| SimpleFunctionExpression
|
|
277
|
+
).parametersValues.unshift(flattenedPropertyExpressionChain);
|
|
278
|
+
flattenedPropertyExpressionChain = guaranteeType(
|
|
279
|
+
expressions[0],
|
|
280
|
+
AbstractPropertyExpression,
|
|
281
|
+
`Can't process exists() expression: can't flatten to a property expression`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
mainFilterExpression.parametersValues =
|
|
285
|
+
mainFilterExpression.parametersValues.map((p) => {
|
|
286
|
+
if (
|
|
287
|
+
p instanceof SimpleFunctionExpression &&
|
|
288
|
+
p.parametersValues[0] instanceof AbstractPropertyExpression
|
|
289
|
+
) {
|
|
290
|
+
p.parametersValues[0].parametersValues[0] =
|
|
291
|
+
flattenedPropertyExpressionChain;
|
|
292
|
+
} else if (p instanceof AbstractPropertyExpression) {
|
|
293
|
+
p.parametersValues[0] = guaranteeNonNullable(
|
|
294
|
+
flattenedPropertyExpressionChain.parametersValues[0],
|
|
295
|
+
);
|
|
296
|
+
} else if (p instanceof VariableExpression) {
|
|
297
|
+
return flattenedPropertyExpressionChain;
|
|
298
|
+
}
|
|
299
|
+
return p;
|
|
300
|
+
});
|
|
301
|
+
processFilterTree(mainFilterExpression, filterState, parentFilterNodeId);
|
|
302
|
+
}
|
|
73
303
|
} else {
|
|
74
304
|
const propertyExpression = expression.parametersValues[0];
|
|
75
305
|
if (propertyExpression instanceof AbstractPropertyExpression) {
|
|
@@ -89,6 +319,20 @@ const processFilterTree = (
|
|
|
89
319
|
operator.buildFilterConditionState(filterState, expression),
|
|
90
320
|
);
|
|
91
321
|
if (filterConditionState) {
|
|
322
|
+
const variableName = getPropertyExpressionChainVariable(
|
|
323
|
+
filterConditionState.propertyExpressionState.propertyExpression,
|
|
324
|
+
).name;
|
|
325
|
+
const parentLambdaVariableName =
|
|
326
|
+
parentNode instanceof QueryBuilderFilterTreeOperationNodeData &&
|
|
327
|
+
parentNode.lambdaParameterName
|
|
328
|
+
? parentNode.lambdaParameterName
|
|
329
|
+
: filterState.lambdaParameterName;
|
|
330
|
+
assertTrue(
|
|
331
|
+
parentLambdaVariableName === variableName,
|
|
332
|
+
`Can't process ${extractElementNameFromPath(
|
|
333
|
+
filterConditionState.operator.getLabel(filterConditionState),
|
|
334
|
+
)}() expression: expects variable used in lambda body '${variableName}' to match lambda parameter '${parentLambdaVariableName}'`,
|
|
335
|
+
);
|
|
92
336
|
filterState.addNodeFromNode(
|
|
93
337
|
new QueryBuilderFilterTreeConditionNodeData(
|
|
94
338
|
undefined,
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
isNonNullable,
|
|
19
|
+
guaranteeNonNullable,
|
|
20
|
+
guaranteeType,
|
|
21
|
+
} from '@finos/legend-shared';
|
|
18
22
|
import {
|
|
19
23
|
type ValueSpecification,
|
|
20
24
|
type LambdaFunction,
|
|
@@ -28,16 +32,26 @@ import {
|
|
|
28
32
|
QueryBuilderFilterTreeConditionNodeData,
|
|
29
33
|
QueryBuilderFilterTreeGroupNodeData,
|
|
30
34
|
type QueryBuilderFilterTreeNodeData,
|
|
35
|
+
QueryBuilderFilterTreeExistsNodeData,
|
|
36
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
31
37
|
} from './QueryBuilderFilterState.js';
|
|
32
38
|
import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
|
|
39
|
+
import { DEFAULT_LAMBDA_VARIABLE_NAME } from '../QueryBuilderConfig.js';
|
|
33
40
|
|
|
34
41
|
const buildFilterConditionExpression = (
|
|
35
42
|
filterState: QueryBuilderFilterState,
|
|
36
43
|
node: QueryBuilderFilterTreeNodeData,
|
|
37
44
|
): ValueSpecification | undefined => {
|
|
38
45
|
if (node instanceof QueryBuilderFilterTreeConditionNodeData) {
|
|
46
|
+
const parentNode = node.parentId
|
|
47
|
+
? guaranteeType(
|
|
48
|
+
filterState.nodes.get(node.parentId),
|
|
49
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
50
|
+
)
|
|
51
|
+
: undefined;
|
|
39
52
|
return node.condition.operator.buildFilterConditionExpression(
|
|
40
53
|
node.condition,
|
|
54
|
+
parentNode?.lambdaParameterName,
|
|
41
55
|
);
|
|
42
56
|
} else if (node instanceof QueryBuilderFilterTreeGroupNodeData) {
|
|
43
57
|
const func = new SimpleFunctionExpression(
|
|
@@ -75,6 +89,85 @@ const buildFilterConditionExpression = (
|
|
|
75
89
|
func.parametersValues = clauses;
|
|
76
90
|
}
|
|
77
91
|
return func.parametersValues.length ? func : undefined;
|
|
92
|
+
} else if (node instanceof QueryBuilderFilterTreeExistsNodeData) {
|
|
93
|
+
const func = new SimpleFunctionExpression(
|
|
94
|
+
extractElementNameFromPath(QUERY_BUILDER_SUPPORTED_FUNCTIONS.EXISTS),
|
|
95
|
+
);
|
|
96
|
+
let parentNode = node.parentId
|
|
97
|
+
? guaranteeType(
|
|
98
|
+
filterState.nodes.get(guaranteeNonNullable(node.parentId)),
|
|
99
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
100
|
+
)
|
|
101
|
+
: undefined;
|
|
102
|
+
while (
|
|
103
|
+
parentNode &&
|
|
104
|
+
!(parentNode instanceof QueryBuilderFilterTreeGroupNodeData)
|
|
105
|
+
) {
|
|
106
|
+
parentNode = parentNode.parentId
|
|
107
|
+
? guaranteeType(
|
|
108
|
+
filterState.nodes.get(guaranteeNonNullable(parentNode.parentId)),
|
|
109
|
+
QueryBuilderFilterTreeOperationNodeData,
|
|
110
|
+
)
|
|
111
|
+
: undefined;
|
|
112
|
+
}
|
|
113
|
+
const propertyExpression = guaranteeNonNullable(node.propertyExpression);
|
|
114
|
+
const clauses = node.childrenIds
|
|
115
|
+
.map((e) => filterState.nodes.get(e))
|
|
116
|
+
.filter(isNonNullable)
|
|
117
|
+
.map((e) => buildFilterConditionExpression(filterState, e))
|
|
118
|
+
.filter(isNonNullable);
|
|
119
|
+
/**
|
|
120
|
+
* NOTE: Due to a limitation (or perhaps design decision) in the engine, group operations
|
|
121
|
+
* like and/or do not take more than 2 parameters, as such, if we have more than 2, we need
|
|
122
|
+
* to create a chain of this operation to accomondate.
|
|
123
|
+
*
|
|
124
|
+
* This means that in the read direction, we might need to flatten the chains down to group with
|
|
125
|
+
* multiple clauses. This means user's intended grouping will not be kept.
|
|
126
|
+
*/
|
|
127
|
+
let parametersValues;
|
|
128
|
+
if (clauses.length > 2) {
|
|
129
|
+
const firstClause = clauses[0] as ValueSpecification;
|
|
130
|
+
let currentClause: ValueSpecification = clauses[
|
|
131
|
+
clauses.length - 1
|
|
132
|
+
] as ValueSpecification;
|
|
133
|
+
for (let i = clauses.length - 2; i > 0; --i) {
|
|
134
|
+
const clause1 = clauses[i] as ValueSpecification;
|
|
135
|
+
const clause2 = currentClause;
|
|
136
|
+
const groupClause = new SimpleFunctionExpression(
|
|
137
|
+
guaranteeNonNullable(parentNode).groupOperation,
|
|
138
|
+
);
|
|
139
|
+
groupClause.parametersValues = [clause1, clause2];
|
|
140
|
+
currentClause = groupClause;
|
|
141
|
+
}
|
|
142
|
+
parametersValues = [firstClause, currentClause];
|
|
143
|
+
} else if (clauses.length === 1) {
|
|
144
|
+
const lambdaFunctionInstanceValue =
|
|
145
|
+
buildGenericLambdaFunctionInstanceValue(
|
|
146
|
+
node.lambdaParameterName ?? DEFAULT_LAMBDA_VARIABLE_NAME,
|
|
147
|
+
clauses,
|
|
148
|
+
filterState.queryBuilderState.graphManagerState.graph,
|
|
149
|
+
);
|
|
150
|
+
func.parametersValues = [propertyExpression, lambdaFunctionInstanceValue];
|
|
151
|
+
return func;
|
|
152
|
+
} else {
|
|
153
|
+
parametersValues = clauses;
|
|
154
|
+
}
|
|
155
|
+
if (!parametersValues.length) {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
const simp = new SimpleFunctionExpression(
|
|
159
|
+
extractElementNameFromPath(
|
|
160
|
+
fromGroupOperation(guaranteeNonNullable(parentNode).groupOperation),
|
|
161
|
+
),
|
|
162
|
+
);
|
|
163
|
+
simp.parametersValues = parametersValues;
|
|
164
|
+
const lambdaFunctionInstanceValue = buildGenericLambdaFunctionInstanceValue(
|
|
165
|
+
node.lambdaParameterName ?? DEFAULT_LAMBDA_VARIABLE_NAME,
|
|
166
|
+
[simp],
|
|
167
|
+
filterState.queryBuilderState.graphManagerState.graph,
|
|
168
|
+
);
|
|
169
|
+
func.parametersValues = [propertyExpression, lambdaFunctionInstanceValue];
|
|
170
|
+
return func;
|
|
78
171
|
}
|
|
79
172
|
return undefined;
|
|
80
173
|
};
|