@finos/legend-query-builder 4.14.63 → 4.14.65

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.
Files changed (92) hide show
  1. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts +3 -0
  2. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts.map +1 -1
  3. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js +12 -1
  4. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js.map +1 -1
  5. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts.map +1 -1
  6. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +20 -19
  7. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
  8. package/lib/components/filter/QueryBuilderFilterPanel.d.ts +7 -1
  9. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  10. package/lib/components/filter/QueryBuilderFilterPanel.js +137 -54
  11. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  12. package/lib/components/shared/QueryBuilderFilterHelper.d.ts +19 -0
  13. package/lib/components/shared/QueryBuilderFilterHelper.d.ts.map +1 -0
  14. package/lib/components/shared/QueryBuilderFilterHelper.js +34 -0
  15. package/lib/components/shared/QueryBuilderFilterHelper.js.map +1 -0
  16. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js +1 -1
  17. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js.map +1 -1
  18. package/lib/index.css +17 -1
  19. package/lib/index.css.map +1 -1
  20. package/lib/package.json +3 -3
  21. package/lib/stores/QueryBuilderStateHashUtils.d.ts +2 -0
  22. package/lib/stores/QueryBuilderStateHashUtils.d.ts.map +1 -1
  23. package/lib/stores/QueryBuilderStateHashUtils.js +2 -0
  24. package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
  25. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js +1 -1
  26. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js.map +1 -1
  27. package/lib/stores/filter/QueryBuilderFilterState.d.ts +35 -5
  28. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  29. package/lib/stores/filter/QueryBuilderFilterState.js +181 -52
  30. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  31. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js +2 -2
  32. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js.map +1 -1
  33. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.d.ts.map +1 -1
  34. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js +21 -6
  35. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js.map +1 -1
  36. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts.map +1 -1
  37. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js +4 -3
  38. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js.map +1 -1
  39. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts.map +1 -1
  40. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js +4 -3
  41. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js.map +1 -1
  42. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
  43. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js +5 -4
  44. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js.map +1 -1
  45. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
  46. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js +5 -4
  47. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
  48. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  49. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js +5 -4
  50. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
  51. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts +1 -1
  52. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts.map +1 -1
  53. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +33 -28
  54. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  55. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js +1 -1
  56. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js.map +1 -1
  57. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
  58. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js +5 -4
  59. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
  60. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
  61. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js +5 -4
  62. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
  63. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts.map +1 -1
  64. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js +4 -3
  65. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js.map +1 -1
  66. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +2 -1
  67. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
  68. package/lib/stores/shared/ValueSpecificationEditorHelper.js +9 -3
  69. package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
  70. package/package.json +11 -11
  71. package/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx +25 -0
  72. package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +29 -26
  73. package/src/components/filter/QueryBuilderFilterPanel.tsx +335 -106
  74. package/src/components/shared/QueryBuilderFilterHelper.ts +51 -0
  75. package/src/components/shared/QueryBuilderPropertyInfoTooltip.tsx +1 -1
  76. package/src/stores/QueryBuilderStateHashUtils.ts +2 -0
  77. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.ts +1 -1
  78. package/src/stores/filter/QueryBuilderFilterState.ts +284 -74
  79. package/src/stores/filter/QueryBuilderFilterStateBuilder.ts +2 -2
  80. package/src/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.ts +48 -9
  81. package/src/stores/filter/operators/QueryBuilderFilterOperator_Contain.ts +5 -4
  82. package/src/stores/filter/operators/QueryBuilderFilterOperator_EndWith.ts +5 -4
  83. package/src/stores/filter/operators/QueryBuilderFilterOperator_Equal.ts +4 -4
  84. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.ts +5 -7
  85. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.ts +5 -7
  86. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +52 -47
  87. package/src/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.ts +1 -1
  88. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThan.ts +5 -7
  89. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.ts +5 -7
  90. package/src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts +5 -4
  91. package/src/stores/shared/ValueSpecificationEditorHelper.ts +28 -0
  92. package/tsconfig.json +1 -0
@@ -79,7 +79,7 @@ export class QueryBuilderPostFilterOperator_In
79
79
  postFilterConditionState.leftConditionValue.getColumnType(),
80
80
  );
81
81
  const rightSide = postFilterConditionState.rightConditionValue;
82
- // `in`/`not in` doest not support right hand value being column state as the multipliticy for columns are [0..1]
82
+ // `in`/`not in` does not support right hand value being column state as the multipliticy for columns are [0..1]
83
83
  if (rightSide instanceof PostFilterValueSpecConditionValueState) {
84
84
  const valueSpec = rightSide.value;
85
85
  if (valueSpec instanceof CollectionInstanceValue) {
@@ -38,12 +38,15 @@ import { QueryBuilderPropertyExpressionState } from '../QueryBuilderPropertyEdit
38
38
  import type { QueryBuilderState } from '../QueryBuilderState.js';
39
39
  import {
40
40
  type ExecutionResult,
41
- type AbstractPropertyExpression,
41
+ AbstractPropertyExpression,
42
42
  type ValueSpecification,
43
43
  type VariableExpression,
44
+ type Type,
44
45
  observe_ValueSpecification,
45
46
  CollectionInstanceValue,
46
47
  InstanceValue,
48
+ SimpleFunctionExpression,
49
+ matchFunctionName,
47
50
  } from '@finos/legend-graph';
48
51
  import { DEFAULT_LAMBDA_VARIABLE_NAME } from '../QueryBuilderConfig.js';
49
52
  import type { QueryBuilderProjectionColumnDragSource } from '../fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
@@ -56,10 +59,14 @@ import type { QueryBuilderFilterOperator } from './QueryBuilderFilterOperator.js
56
59
  import { QUERY_BUILDER_GROUP_OPERATION } from '../QueryBuilderGroupOperationHelper.js';
57
60
  import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from '../QueryBuilderStateHashUtils.js';
58
61
  import {
62
+ getCollectionValueSpecificationType,
63
+ getNonCollectionValueSpecificationType,
59
64
  isValidInstanceValue,
60
65
  isValueExpressionReferencedInValue,
61
66
  } from '../QueryBuilderValueSpecificationHelper.js';
62
67
  import { instanceValue_setValues } from '../shared/ValueSpecificationModifierHelper.js';
68
+ import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
69
+ import type { QueryBuilderVariableDragSource } from '../../components/shared/BasicValueSpecificationEditor.js';
63
70
 
64
71
  export enum QUERY_BUILDER_FILTER_DND_TYPE {
65
72
  GROUP_CONDITION = 'QUERY_BUILDER_FILTER_DND_TYPE.GROUP_CONDITION',
@@ -71,18 +78,167 @@ export interface QueryBuilderFilterConditionDragSource {
71
78
  node: QueryBuilderFilterTreeNodeData;
72
79
  }
73
80
 
74
- export type QueryBuilderFilterDropTarget =
81
+ export type QueryBuilderFilterNodeDropTarget =
75
82
  | QueryBuilderExplorerTreeDragSource
76
83
  | QueryBuilderProjectionColumnDragSource
77
84
  | QueryBuilderFilterConditionDragSource;
85
+
86
+ export type QueryBuilderFilterValueDropTarget =
87
+ | QueryBuilderVariableDragSource
88
+ | QueryBuilderProjectionColumnDragSource
89
+ | QueryBuilderExplorerTreeDragSource;
90
+
78
91
  export type QueryBuilderFilterConditionRearrangeDropTarget =
79
92
  QueryBuilderFilterConditionDragSource;
80
93
 
94
+ export const isCollectionProperty = (
95
+ propertyExpression: AbstractPropertyExpression,
96
+ ): boolean => {
97
+ let currentExpression: ValueSpecification | undefined = propertyExpression;
98
+ while (currentExpression instanceof AbstractPropertyExpression) {
99
+ // Check if the property chain can results in column that have multiple values
100
+ if (
101
+ currentExpression.func.value.multiplicity.upperBound === undefined ||
102
+ currentExpression.func.value.multiplicity.upperBound > 1
103
+ ) {
104
+ return true;
105
+ }
106
+ currentExpression = getNullableFirstEntry(
107
+ currentExpression.parametersValues,
108
+ );
109
+ // Take care of chains of subtype
110
+ while (
111
+ currentExpression instanceof SimpleFunctionExpression &&
112
+ matchFunctionName(
113
+ currentExpression.functionName,
114
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
115
+ )
116
+ ) {
117
+ currentExpression = getNullableFirstEntry(
118
+ currentExpression.parametersValues,
119
+ );
120
+ }
121
+ }
122
+ return false;
123
+ };
124
+
125
+ export abstract class FilterConditionValueState implements Hashable {
126
+ conditionState: FilterConditionState;
127
+
128
+ constructor(conditionState: FilterConditionState) {
129
+ this.conditionState = conditionState;
130
+ }
131
+
132
+ get type(): Type | undefined {
133
+ return undefined;
134
+ }
135
+
136
+ get isCollection(): boolean {
137
+ return false;
138
+ }
139
+
140
+ get hashCode(): string {
141
+ return hashArray([
142
+ QUERY_BUILDER_STATE_HASH_STRUCTURE.FILTER_CONDITION_RIGHT_VALUE,
143
+ ]);
144
+ }
145
+ }
146
+
147
+ // This class is used to represent the value of a filter condition when the value is
148
+ // a ValueSpecification.
149
+ export class FilterValueSpecConditionValueState extends FilterConditionValueState {
150
+ value?: ValueSpecification | undefined;
151
+
152
+ constructor(
153
+ conditionState: FilterConditionState,
154
+ value?: ValueSpecification | undefined,
155
+ ) {
156
+ super(conditionState);
157
+ makeObservable(this, {
158
+ value: observable,
159
+ setValue: action,
160
+ });
161
+ this.value = this.setValue(value);
162
+ }
163
+
164
+ override get type(): Type | undefined {
165
+ if (this.value instanceof CollectionInstanceValue) {
166
+ return getCollectionValueSpecificationType(
167
+ this.conditionState.filterState.queryBuilderState.graphManagerState
168
+ .graph,
169
+ this.value.values,
170
+ );
171
+ }
172
+ return this.value
173
+ ? getNonCollectionValueSpecificationType(this.value)
174
+ : undefined;
175
+ }
176
+
177
+ setValue(
178
+ val: ValueSpecification | undefined,
179
+ ): ValueSpecification | undefined {
180
+ this.value = val
181
+ ? observe_ValueSpecification(
182
+ val,
183
+ this.conditionState.filterState.queryBuilderState.observerContext,
184
+ )
185
+ : undefined;
186
+ return this.value;
187
+ }
188
+
189
+ override get isCollection(): boolean {
190
+ return this.value instanceof CollectionInstanceValue;
191
+ }
192
+
193
+ override get hashCode(): string {
194
+ return hashArray([
195
+ QUERY_BUILDER_STATE_HASH_STRUCTURE.FILTER_CONDITION_RIGHT_VALUE_SPEC,
196
+ this.value,
197
+ ]);
198
+ }
199
+ }
200
+
201
+ // This class is used to represent the value of a filter condition when the value is a
202
+ // PropertyExpressionState (which comes from DND a property node from the explorer tree
203
+ // or a column from the fetch structure panel).
204
+ export class FilterPropertyExpressionStateConditionValueState extends FilterConditionValueState {
205
+ propertyExpressionState: QueryBuilderPropertyExpressionState;
206
+
207
+ constructor(
208
+ conditionState: FilterConditionState,
209
+ propertyExpressionState: QueryBuilderPropertyExpressionState,
210
+ ) {
211
+ super(conditionState);
212
+ makeObservable(this, {
213
+ propertyExpressionState: observable,
214
+ changePropertyExpressionState: action,
215
+ });
216
+ this.propertyExpressionState = propertyExpressionState;
217
+ }
218
+
219
+ override get type(): Type {
220
+ return this.propertyExpressionState.propertyExpression.func.value
221
+ .genericType.value.rawType;
222
+ }
223
+
224
+ override get isCollection(): boolean {
225
+ return isCollectionProperty(
226
+ this.propertyExpressionState.propertyExpression,
227
+ );
228
+ }
229
+
230
+ changePropertyExpressionState(
231
+ propertyExpressionState: QueryBuilderPropertyExpressionState,
232
+ ): void {
233
+ this.propertyExpressionState = propertyExpressionState;
234
+ }
235
+ }
236
+
81
237
  export class FilterConditionState implements Hashable {
82
238
  readonly filterState: QueryBuilderFilterState;
83
239
  propertyExpressionState: QueryBuilderPropertyExpressionState;
84
240
  operator!: QueryBuilderFilterOperator;
85
- value?: ValueSpecification | undefined;
241
+ rightConditionValue?: FilterConditionValueState | undefined;
86
242
  existsLambdaParamNames: string[] = [];
87
243
  typeaheadSearchResults: string[] | undefined;
88
244
  typeaheadSearchState = ActionState.create();
@@ -94,16 +250,17 @@ export class FilterConditionState implements Hashable {
94
250
  makeObservable(this, {
95
251
  propertyExpressionState: observable,
96
252
  operator: observable,
97
- value: observable,
253
+ rightConditionValue: observable,
98
254
  existsLambdaParamNames: observable,
99
255
  typeaheadSearchResults: observable,
100
- operators: computed,
101
- changeProperty: action,
102
256
  changeOperator: action,
103
257
  setOperator: action,
104
- setValue: action,
258
+ setRightConditionValue: action,
105
259
  addExistsLambdaParamNames: action,
260
+ buildRightConditionValueFromValueSpec: action,
261
+ buildRightConditionValueFromPropertyExpressionState: action,
106
262
  handleTypeaheadSearch: flow,
263
+ operators: computed,
107
264
  hashCode: computed,
108
265
  });
109
266
 
@@ -119,7 +276,9 @@ export class FilterConditionState implements Hashable {
119
276
  `Can't find an operator for property '${this.propertyExpressionState.path}': no operators registered`,
120
277
  );
121
278
  this.operator = this.operators[0] as QueryBuilderFilterOperator;
122
- this.setValue(this.operator.getDefaultFilterConditionValue(this));
279
+ this.buildRightConditionValueFromValueSpec(
280
+ this.operator.getDefaultFilterConditionValue(this),
281
+ );
123
282
  }
124
283
 
125
284
  get operators(): QueryBuilderFilterOperator[] {
@@ -131,10 +290,14 @@ export class FilterConditionState implements Hashable {
131
290
  *handleTypeaheadSearch(
132
291
  searchValue?: ValueSpecification | undefined,
133
292
  ): GeneratorFn<void> {
134
- const value = searchValue ?? this.value;
135
293
  try {
136
294
  this.typeaheadSearchState.inProgress();
137
295
  this.typeaheadSearchResults = undefined;
296
+ const rightConditionValue = guaranteeType(
297
+ this.rightConditionValue,
298
+ FilterValueSpecConditionValueState,
299
+ );
300
+ const value = searchValue ?? rightConditionValue.value;
138
301
  if (performTypeahead(value)) {
139
302
  const result =
140
303
  (yield this.filterState.queryBuilderState.graphManagerState.graphManager.runQuery(
@@ -164,65 +327,34 @@ export class FilterConditionState implements Hashable {
164
327
  }
165
328
  }
166
329
 
167
- changeProperty(propertyExpression: AbstractPropertyExpression): void {
168
- try {
169
- // first, check if the new property is supported
170
- new FilterConditionState(this.filterState, propertyExpression);
171
- } catch (error) {
172
- assertErrorThrown(error);
173
- this.filterState.queryBuilderState.applicationStore.notificationService.notifyError(
174
- error,
175
- );
176
- return;
177
- }
178
-
179
- // observe the property expression
180
- observe_ValueSpecification(
181
- propertyExpression,
182
- this.filterState.queryBuilderState.observerContext,
183
- );
184
-
185
- this.propertyExpressionState = new QueryBuilderPropertyExpressionState(
186
- this.filterState.queryBuilderState,
187
- propertyExpression,
188
- );
189
-
190
- const newCompatibleOperators = this.operators;
191
- assertTrue(
192
- newCompatibleOperators.length !== 0,
193
- `Can't find an operator for property '${this.propertyExpressionState.path}': no operators registered`,
194
- );
195
- if (!newCompatibleOperators.includes(this.operator)) {
196
- this.changeOperator(
197
- newCompatibleOperators[0] as QueryBuilderFilterOperator,
198
- );
199
- } else if (!this.operator.isCompatibleWithFilterConditionValue(this)) {
200
- this.setValue(this.operator.getDefaultFilterConditionValue(this));
201
- }
202
- }
203
-
204
330
  changeOperator(val: QueryBuilderFilterOperator): void {
205
331
  this.setOperator(val);
206
332
  if (!this.operator.isCompatibleWithFilterConditionValue(this)) {
207
333
  let defaultValue = this.operator.getDefaultFilterConditionValue(this);
334
+ // Don't allow invalid InstanceValues or empty strings to be set as list element
208
335
  if (
209
336
  defaultValue instanceof CollectionInstanceValue &&
210
- this.value instanceof InstanceValue &&
211
- isValidInstanceValue(this.value)
337
+ this.rightConditionValue instanceof
338
+ FilterValueSpecConditionValueState &&
339
+ this.rightConditionValue.value instanceof InstanceValue &&
340
+ isValidInstanceValue(this.rightConditionValue.value) &&
341
+ this.rightConditionValue.value.values[0] !== ''
212
342
  ) {
213
343
  instanceValue_setValues(
214
344
  defaultValue,
215
- [this.value],
345
+ [this.rightConditionValue.value],
216
346
  this.filterState.queryBuilderState.observerContext,
217
347
  );
218
348
  } else if (
219
349
  defaultValue instanceof InstanceValue &&
220
- this.value instanceof CollectionInstanceValue &&
221
- this.value.values.length
350
+ this.rightConditionValue instanceof
351
+ FilterValueSpecConditionValueState &&
352
+ this.rightConditionValue.value instanceof CollectionInstanceValue &&
353
+ this.rightConditionValue.value.values.length
222
354
  ) {
223
- defaultValue = this.value.values[0];
355
+ defaultValue = this.rightConditionValue.value.values[0];
224
356
  }
225
- this.setValue(defaultValue);
357
+ this.buildRightConditionValueFromValueSpec(defaultValue);
226
358
  }
227
359
  }
228
360
 
@@ -230,24 +362,53 @@ export class FilterConditionState implements Hashable {
230
362
  this.operator = val;
231
363
  }
232
364
 
233
- setValue(val: ValueSpecification | undefined): void {
234
- this.value = val
235
- ? observe_ValueSpecification(
236
- val,
237
- this.filterState.queryBuilderState.observerContext,
238
- )
239
- : undefined;
365
+ setRightConditionValue(val: FilterConditionValueState | undefined): void {
366
+ this.rightConditionValue = val;
240
367
  }
241
368
 
242
369
  addExistsLambdaParamNames(val: string): void {
243
370
  this.existsLambdaParamNames.push(val);
244
371
  }
245
372
 
373
+ buildRightConditionValueFromValueSpec(
374
+ val: ValueSpecification | undefined,
375
+ ): void {
376
+ if (
377
+ this.rightConditionValue instanceof FilterValueSpecConditionValueState
378
+ ) {
379
+ this.rightConditionValue.setValue(val);
380
+ } else {
381
+ this.setRightConditionValue(
382
+ new FilterValueSpecConditionValueState(this, val),
383
+ );
384
+ }
385
+ }
386
+
387
+ buildRightConditionValueFromPropertyExpressionState(
388
+ propertyExpressionState: QueryBuilderPropertyExpressionState,
389
+ ): void {
390
+ if (
391
+ this.rightConditionValue instanceof
392
+ FilterPropertyExpressionStateConditionValueState
393
+ ) {
394
+ this.rightConditionValue.changePropertyExpressionState(
395
+ propertyExpressionState,
396
+ );
397
+ } else {
398
+ this.setRightConditionValue(
399
+ new FilterPropertyExpressionStateConditionValueState(
400
+ this,
401
+ propertyExpressionState,
402
+ ),
403
+ );
404
+ }
405
+ }
406
+
246
407
  get hashCode(): string {
247
408
  return hashArray([
248
409
  QUERY_BUILDER_STATE_HASH_STRUCTURE.FILTER_CONDITION_STATE,
249
410
  this.propertyExpressionState,
250
- this.value ?? '',
411
+ this.rightConditionValue ?? '',
251
412
  this.operator,
252
413
  ]);
253
414
  }
@@ -435,6 +596,17 @@ export class QueryBuilderFilterTreeConditionNodeData
435
596
  this.isNewlyAdded = val;
436
597
  }
437
598
 
599
+ get isExistsNodeChild(): boolean {
600
+ let parentNode = this.condition.filterState.getParentNode(this);
601
+ while (parentNode !== undefined) {
602
+ if (parentNode instanceof QueryBuilderFilterTreeExistsNodeData) {
603
+ return true;
604
+ }
605
+ parentNode = this.condition.filterState.getParentNode(parentNode);
606
+ }
607
+ return false;
608
+ }
609
+
438
610
  get dragPreviewLabel(): string {
439
611
  return this.condition.propertyExpressionState.title;
440
612
  }
@@ -965,48 +1137,86 @@ export class QueryBuilderFilterState
965
1137
  return Boolean(
966
1138
  Array.from(this.nodes.values())
967
1139
  .filter(filterByType(QueryBuilderFilterTreeConditionNodeData))
968
- .map((node) => node.condition.value)
1140
+ .map((node) =>
1141
+ node.condition.rightConditionValue instanceof
1142
+ FilterValueSpecConditionValueState
1143
+ ? node.condition.rightConditionValue.value
1144
+ : undefined,
1145
+ )
969
1146
  .filter(isNonNullable)
970
1147
  .find((value) => isValueExpressionReferencedInValue(variable, value)),
971
1148
  );
972
1149
  }
973
1150
 
1151
+ isInvalidFilterPropertyExpressionState(
1152
+ node: QueryBuilderFilterTreeNodeData,
1153
+ ): boolean {
1154
+ return (
1155
+ node instanceof QueryBuilderFilterTreeConditionNodeData &&
1156
+ !node.condition.propertyExpressionState.isValid
1157
+ );
1158
+ }
1159
+
1160
+ isInvalidValueSpecFilterValue(node: QueryBuilderFilterTreeNodeData): boolean {
1161
+ return (
1162
+ node instanceof QueryBuilderFilterTreeConditionNodeData &&
1163
+ node.condition.rightConditionValue instanceof
1164
+ FilterValueSpecConditionValueState &&
1165
+ node.condition.rightConditionValue.value instanceof InstanceValue &&
1166
+ !isValidInstanceValue(node.condition.rightConditionValue.value)
1167
+ );
1168
+ }
1169
+
1170
+ isInvalidPropertyExpressionStateFilterValue(
1171
+ node: QueryBuilderFilterTreeNodeData,
1172
+ ): boolean {
1173
+ return (
1174
+ node instanceof QueryBuilderFilterTreeConditionNodeData &&
1175
+ node.condition.rightConditionValue instanceof
1176
+ FilterPropertyExpressionStateConditionValueState &&
1177
+ !node.condition.rightConditionValue.propertyExpressionState.isValid
1178
+ );
1179
+ }
1180
+
974
1181
  get allValidationIssues(): string[] {
975
1182
  const validationIssues: string[] = [];
976
1183
  Array.from(this.nodes.values()).forEach((node) => {
977
1184
  if (node instanceof QueryBuilderFilterTreeConditionNodeData) {
978
- if (
979
- node.condition.value instanceof InstanceValue &&
980
- !isValidInstanceValue(node.condition.value)
981
- ) {
1185
+ if (this.isInvalidValueSpecFilterValue(node)) {
982
1186
  validationIssues.push(
983
1187
  `Filter value for ${node.condition.propertyExpressionState.title} is missing or invalid`,
984
1188
  );
985
1189
  }
986
- if (!node.condition.propertyExpressionState.isValid) {
1190
+ if (this.isInvalidFilterPropertyExpressionState(node)) {
987
1191
  validationIssues.push(
988
1192
  `Derived property parameter value for ${node.condition.propertyExpressionState.title} is missing or invalid`,
989
1193
  );
990
1194
  }
1195
+ if (
1196
+ node.condition.rightConditionValue instanceof
1197
+ FilterPropertyExpressionStateConditionValueState &&
1198
+ this.isInvalidPropertyExpressionStateFilterValue(node)
1199
+ ) {
1200
+ validationIssues.push(
1201
+ `Derived property parameter value for ${node.condition.rightConditionValue.propertyExpressionState.title} is missing or invalid`,
1202
+ );
1203
+ }
991
1204
  }
992
1205
  });
993
1206
  return validationIssues;
994
1207
  }
995
1208
 
996
1209
  get hasInvalidFilterValues(): boolean {
997
- return Array.from(this.nodes.values()).some(
998
- (node) =>
999
- node instanceof QueryBuilderFilterTreeConditionNodeData &&
1000
- node.condition.value instanceof InstanceValue &&
1001
- !isValidInstanceValue(node.condition.value),
1210
+ return Array.from(this.nodes.values()).some((node) =>
1211
+ this.isInvalidValueSpecFilterValue(node),
1002
1212
  );
1003
1213
  }
1004
1214
 
1005
1215
  get hasInvalidDerivedPropertyParameters(): boolean {
1006
1216
  return Array.from(this.nodes.values()).some(
1007
1217
  (node) =>
1008
- node instanceof QueryBuilderFilterTreeConditionNodeData &&
1009
- !node.condition.propertyExpressionState.isValid,
1218
+ this.isInvalidFilterPropertyExpressionState(node) ||
1219
+ this.isInvalidPropertyExpressionStateFilterValue(node),
1010
1220
  );
1011
1221
  }
1012
1222
 
@@ -314,8 +314,8 @@ const processFilterTree = (
314
314
  }
315
315
  }
316
316
  for (const operator of filterState.operators) {
317
- // NOTE: this allow plugin author to either return `undefined` or throw error
318
- // if there is a problem with building the lambda. Either case, the plugin is
317
+ // NOTE: this allows plugin author to either return `undefined` or throw error
318
+ // if there is a problem with building the lambda. In either case, the plugin is
319
319
  // considered as not supporting the lambda.
320
320
  const filterConditionState = returnUndefOnError(() =>
321
321
  operator.buildFilterConditionState(filterState, expression),
@@ -30,12 +30,15 @@ import {
30
30
  } from '@finos/legend-shared';
31
31
  import {
32
32
  FilterConditionState,
33
+ FilterPropertyExpressionStateConditionValueState,
34
+ FilterValueSpecConditionValueState,
33
35
  type QueryBuilderFilterState,
34
36
  } from '../QueryBuilderFilterState.js';
35
37
  import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../../graph/QueryBuilderMetaModelConst.js';
36
38
  import { simplifyValueExpression } from '../../QueryBuilderValueSpecificationHelper.js';
37
39
  import type { QueryBuilderFilterOperator } from '../QueryBuilderFilterOperator.js';
38
40
  import { buildPropertyExpressionChain } from '../../QueryBuilderValueSpecificationBuilderHelper.js';
41
+ import { QueryBuilderPropertyExpressionState } from '../../QueryBuilderPropertyEditorState.js';
39
42
 
40
43
  export const buildFilterConditionExpression = (
41
44
  filterConditionState: FilterConditionState,
@@ -52,8 +55,28 @@ export const buildFilterConditionExpression = (
52
55
  );
53
56
  expression.parametersValues.push(guaranteeNonNullable(propertyExpression));
54
57
  // NOTE: there are simple operators which do not require any params (e.g. isEmpty)
55
- if (filterConditionState.value) {
56
- expression.parametersValues.push(filterConditionState.value);
58
+ if (
59
+ filterConditionState.rightConditionValue &&
60
+ filterConditionState.rightConditionValue instanceof
61
+ FilterValueSpecConditionValueState &&
62
+ filterConditionState.rightConditionValue.value !== undefined
63
+ ) {
64
+ expression.parametersValues.push(
65
+ filterConditionState.rightConditionValue.value,
66
+ );
67
+ } else if (
68
+ filterConditionState.rightConditionValue &&
69
+ filterConditionState.rightConditionValue instanceof
70
+ FilterPropertyExpressionStateConditionValueState
71
+ ) {
72
+ const rightConditionPropertyExpression = buildPropertyExpressionChain(
73
+ filterConditionState.rightConditionValue.propertyExpressionState
74
+ .propertyExpression,
75
+ filterConditionState.propertyExpressionState.queryBuilderState,
76
+ lambdaParameterName ??
77
+ filterConditionState.filterState.lambdaParameterName,
78
+ );
79
+ expression.parametersValues.push(rightConditionPropertyExpression);
57
80
  }
58
81
  return expression;
59
82
  };
@@ -155,18 +178,34 @@ export const buildFilterConditionState = (
155
178
  // value
156
179
  const value = mainExpressionWithOperator.parametersValues[1];
157
180
  if (hasNoValue || !value) {
158
- filterConditionState.setValue(undefined);
181
+ filterConditionState.setRightConditionValue(undefined);
182
+ } else if (value instanceof AbstractPropertyExpression) {
183
+ filterConditionState.setRightConditionValue(
184
+ new FilterPropertyExpressionStateConditionValueState(
185
+ filterConditionState,
186
+ new QueryBuilderPropertyExpressionState(
187
+ filterState.queryBuilderState,
188
+ value,
189
+ ),
190
+ ),
191
+ );
159
192
  } else {
160
- filterConditionState.setValue(
161
- simplifyValueExpression(
162
- value,
163
- filterConditionState.filterState.queryBuilderState.observerContext,
193
+ filterConditionState.setRightConditionValue(
194
+ new FilterValueSpecConditionValueState(
195
+ filterConditionState,
196
+ simplifyValueExpression(
197
+ value,
198
+ filterConditionState.filterState.queryBuilderState.observerContext,
199
+ ),
164
200
  ),
165
201
  );
166
202
  }
167
203
  if (!operator.isCompatibleWithFilterConditionValue(filterConditionState)) {
168
- filterConditionState.setValue(
169
- operator.getDefaultFilterConditionValue(filterConditionState),
204
+ filterConditionState.setRightConditionValue(
205
+ new FilterValueSpecConditionValueState(
206
+ filterConditionState,
207
+ operator.getDefaultFilterConditionValue(filterConditionState),
208
+ ),
170
209
  );
171
210
  }
172
211
  return filterConditionState;
@@ -36,7 +36,6 @@ import {
36
36
  } from './QueryBuilderFilterOperatorValueSpecificationBuilder.js';
37
37
  import {
38
38
  buildNotExpression,
39
- getNonCollectionValueSpecificationType,
40
39
  unwrapNotExpression,
41
40
  } from '../../QueryBuilderValueSpecificationHelper.js';
42
41
  import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../../graph/QueryBuilderMetaModelConst.js';
@@ -64,9 +63,11 @@ export class QueryBuilderFilterOperator_Contain
64
63
  isCompatibleWithFilterConditionValue(
65
64
  filterConditionState: FilterConditionState,
66
65
  ): boolean {
67
- const type = filterConditionState.value
68
- ? getNonCollectionValueSpecificationType(filterConditionState.value)
69
- : undefined;
66
+ const type =
67
+ filterConditionState.rightConditionValue &&
68
+ !filterConditionState.rightConditionValue.isCollection
69
+ ? filterConditionState.rightConditionValue.type
70
+ : undefined;
70
71
  return PrimitiveType.STRING === type;
71
72
  }
72
73
 
@@ -36,7 +36,6 @@ import {
36
36
  } from './QueryBuilderFilterOperatorValueSpecificationBuilder.js';
37
37
  import {
38
38
  buildNotExpression,
39
- getNonCollectionValueSpecificationType,
40
39
  unwrapNotExpression,
41
40
  } from '../../QueryBuilderValueSpecificationHelper.js';
42
41
  import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../../graph/QueryBuilderMetaModelConst.js';
@@ -64,9 +63,11 @@ export class QueryBuilderFilterOperator_EndWith
64
63
  isCompatibleWithFilterConditionValue(
65
64
  filterConditionState: FilterConditionState,
66
65
  ): boolean {
67
- const type = filterConditionState.value
68
- ? getNonCollectionValueSpecificationType(filterConditionState.value)
69
- : undefined;
66
+ const type =
67
+ filterConditionState.rightConditionValue &&
68
+ !filterConditionState.rightConditionValue.isCollection
69
+ ? filterConditionState.rightConditionValue.type
70
+ : undefined;
70
71
  return PrimitiveType.STRING === type;
71
72
  }
72
73