@finos/legend-query-builder 4.14.62 → 4.14.64

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. package/lib/components/QueryBuilderSideBar.js +1 -1
  2. package/lib/components/QueryBuilderSideBar.js.map +1 -1
  3. package/lib/components/QueryLoader.js +1 -1
  4. package/lib/components/QueryLoader.js.map +1 -1
  5. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts +3 -0
  6. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.d.ts.map +1 -1
  7. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js +12 -1
  8. package/lib/components/__test-utils__/QueryBuilderComponentTestUtils.js.map +1 -1
  9. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts.map +1 -1
  10. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +20 -19
  11. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
  12. package/lib/components/filter/QueryBuilderFilterPanel.d.ts +7 -1
  13. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  14. package/lib/components/filter/QueryBuilderFilterPanel.js +137 -54
  15. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  16. package/lib/components/shared/CustomDatePicker.js +1 -1
  17. package/lib/components/shared/CustomDatePicker.js.map +1 -1
  18. package/lib/components/shared/QueryBuilderFilterHelper.d.ts +19 -0
  19. package/lib/components/shared/QueryBuilderFilterHelper.d.ts.map +1 -0
  20. package/lib/components/shared/QueryBuilderFilterHelper.js +34 -0
  21. package/lib/components/shared/QueryBuilderFilterHelper.js.map +1 -0
  22. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js +1 -1
  23. package/lib/components/shared/QueryBuilderPropertyInfoTooltip.js.map +1 -1
  24. package/lib/index.css +2 -2
  25. package/lib/index.css.map +1 -1
  26. package/lib/package.json +6 -6
  27. package/lib/stores/QueryBuilderStateHashUtils.d.ts +2 -0
  28. package/lib/stores/QueryBuilderStateHashUtils.d.ts.map +1 -1
  29. package/lib/stores/QueryBuilderStateHashUtils.js +2 -0
  30. package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
  31. package/lib/stores/explorer/QueryBuilderExplorerState.js +1 -1
  32. package/lib/stores/explorer/QueryBuilderExplorerState.js.map +1 -1
  33. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js +1 -1
  34. package/lib/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.js.map +1 -1
  35. package/lib/stores/filter/QueryBuilderFilterState.d.ts +35 -5
  36. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  37. package/lib/stores/filter/QueryBuilderFilterState.js +181 -52
  38. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  39. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js +2 -2
  40. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js.map +1 -1
  41. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.d.ts.map +1 -1
  42. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js +21 -6
  43. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js.map +1 -1
  44. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts.map +1 -1
  45. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js +4 -3
  46. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js.map +1 -1
  47. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts.map +1 -1
  48. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js +4 -3
  49. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js.map +1 -1
  50. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
  51. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js +5 -4
  52. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js.map +1 -1
  53. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
  54. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js +5 -4
  55. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
  56. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  57. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js +5 -4
  58. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
  59. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts +1 -1
  60. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts.map +1 -1
  61. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +33 -28
  62. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  63. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js +1 -1
  64. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js.map +1 -1
  65. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
  66. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js +5 -4
  67. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
  68. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
  69. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js +5 -4
  70. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
  71. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts.map +1 -1
  72. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js +4 -3
  73. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js.map +1 -1
  74. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +2 -1
  75. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
  76. package/lib/stores/shared/ValueSpecificationEditorHelper.js +9 -3
  77. package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
  78. package/package.json +14 -14
  79. package/src/components/QueryBuilderSideBar.tsx +1 -1
  80. package/src/components/QueryLoader.tsx +2 -2
  81. package/src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx +25 -0
  82. package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +29 -26
  83. package/src/components/filter/QueryBuilderFilterPanel.tsx +335 -106
  84. package/src/components/shared/CustomDatePicker.tsx +1 -1
  85. package/src/components/shared/QueryBuilderFilterHelper.ts +51 -0
  86. package/src/components/shared/QueryBuilderPropertyInfoTooltip.tsx +1 -1
  87. package/src/stores/QueryBuilderStateHashUtils.ts +2 -0
  88. package/src/stores/explorer/QueryBuilderExplorerState.ts +1 -1
  89. package/src/stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_In.ts +1 -1
  90. package/src/stores/filter/QueryBuilderFilterState.ts +284 -74
  91. package/src/stores/filter/QueryBuilderFilterStateBuilder.ts +2 -2
  92. package/src/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.ts +48 -9
  93. package/src/stores/filter/operators/QueryBuilderFilterOperator_Contain.ts +5 -4
  94. package/src/stores/filter/operators/QueryBuilderFilterOperator_EndWith.ts +5 -4
  95. package/src/stores/filter/operators/QueryBuilderFilterOperator_Equal.ts +4 -4
  96. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.ts +5 -7
  97. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.ts +5 -7
  98. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +52 -47
  99. package/src/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.ts +1 -1
  100. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThan.ts +5 -7
  101. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.ts +5 -7
  102. package/src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts +5 -4
  103. package/src/stores/shared/ValueSpecificationEditorHelper.ts +28 -0
  104. package/tsconfig.json +1 -0
@@ -91,6 +91,8 @@ export enum QUERY_BUILDER_STATE_HASH_STRUCTURE {
91
91
  FILTER_TREE_CONDIITION_NODE_DATA = 'FILTER_TREE_CONDITION_NODE_DATA',
92
92
  FILTER_TREE_BLANK_CONDITION_NODE_DATA = 'FILTER_TREE_BLANK_CONDITION_NODE_DATA',
93
93
  FILTER_CONDITION_STATE = 'FILTER_CONDITION_STATE',
94
+ FILTER_CONDITION_RIGHT_VALUE = 'FILTER_CONDITION_RIGHT_VALUE',
95
+ FILTER_CONDITION_RIGHT_VALUE_SPEC = 'FILTER_CONDITION_RIGHT_VALUE_SPEC',
94
96
  FILTER_OPERATOR_CONTAIN = 'FILTER_OPERATOR_CONTAIN',
95
97
  FILTER_OPERATOR_NOT_CONTAIN = 'FILTER_OPERATOR_NOT_CONTAIN',
96
98
  FILTER_OPERATOR_END_WITH = 'FILTER_OPERATOR_END_WITH',
@@ -765,7 +765,7 @@ export class QueryBuilderExplorerState {
765
765
  }
766
766
  nodeToOpen =
767
767
  nodeToOpen instanceof QueryBuilderExplorerTreePropertyNodeData
768
- ? this.treeData?.nodes.get(nodeToOpen.parentId) ?? null
768
+ ? (this.treeData?.nodes.get(nodeToOpen.parentId) ?? null)
769
769
  : null;
770
770
  }
771
771
  this.refreshTree();
@@ -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