@finos/legend-query-builder 4.14.91 → 4.14.93

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. package/lib/components/QueryBuilderDiffPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderDiffPanel.js +2 -1
  3. package/lib/components/QueryBuilderDiffPanel.js.map +1 -1
  4. package/lib/components/QueryBuilderTextEditor.d.ts.map +1 -1
  5. package/lib/components/QueryBuilderTextEditor.js +2 -1
  6. package/lib/components/QueryBuilderTextEditor.js.map +1 -1
  7. package/lib/components/QueryLoader.d.ts.map +1 -1
  8. package/lib/components/QueryLoader.js +2 -1
  9. package/lib/components/QueryLoader.js.map +1 -1
  10. package/lib/components/execution-plan/ExecutionPlanViewer.d.ts.map +1 -1
  11. package/lib/components/execution-plan/ExecutionPlanViewer.js +2 -1
  12. package/lib/components/execution-plan/ExecutionPlanViewer.js.map +1 -1
  13. package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts.map +1 -1
  14. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +2 -1
  15. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
  16. package/lib/components/filter/QueryBuilderFilterPanel.js +2 -2
  17. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  18. package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -1
  19. package/lib/components/result/QueryBuilderResultPanel.js +2 -1
  20. package/lib/components/result/QueryBuilderResultPanel.js.map +1 -1
  21. package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts.map +1 -1
  22. package/lib/components/result/tds/QueryBuilderTDSGridResult.js +3 -2
  23. package/lib/components/result/tds/QueryBuilderTDSGridResult.js.map +1 -1
  24. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts +2 -2
  25. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts.map +1 -1
  26. package/lib/components/result/tds/QueryBuilderTDSResultShared.js +155 -19
  27. package/lib/components/result/tds/QueryBuilderTDSResultShared.js.map +1 -1
  28. package/lib/components/shared/LambdaEditor.js +2 -1
  29. package/lib/components/shared/LambdaEditor.js.map +1 -1
  30. package/lib/index.css +1 -1
  31. package/lib/package.json +6 -5
  32. package/lib/stores/filter/QueryBuilderFilterOperator.d.ts +1 -1
  33. package/lib/stores/filter/QueryBuilderFilterOperator.d.ts.map +1 -1
  34. package/lib/stores/filter/QueryBuilderFilterState.d.ts +1 -1
  35. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  36. package/lib/stores/filter/QueryBuilderFilterState.js +8 -3
  37. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  38. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js +1 -1
  39. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js.map +1 -1
  40. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts +2 -2
  41. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts.map +1 -1
  42. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js +3 -3
  43. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js.map +1 -1
  44. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts +2 -2
  45. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts.map +1 -1
  46. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js +3 -3
  47. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js.map +1 -1
  48. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts +2 -2
  49. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
  50. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js +2 -2
  51. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js.map +1 -1
  52. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts +1 -1
  53. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
  54. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js +2 -2
  55. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
  56. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts +1 -1
  57. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  58. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js +2 -2
  59. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
  60. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts +2 -2
  61. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts.map +1 -1
  62. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +2 -2
  63. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  64. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts +2 -2
  65. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts.map +1 -1
  66. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js +2 -2
  67. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js.map +1 -1
  68. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts +1 -1
  69. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
  70. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js +2 -2
  71. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
  72. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts +1 -1
  73. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
  74. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js +2 -2
  75. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
  76. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts +2 -2
  77. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts.map +1 -1
  78. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js +3 -3
  79. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js.map +1 -1
  80. package/package.json +14 -13
  81. package/src/components/QueryBuilderDiffPanel.tsx +2 -5
  82. package/src/components/QueryBuilderTextEditor.tsx +2 -4
  83. package/src/components/QueryLoader.tsx +2 -4
  84. package/src/components/execution-plan/ExecutionPlanViewer.tsx +2 -4
  85. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +2 -4
  86. package/src/components/filter/QueryBuilderFilterPanel.tsx +2 -2
  87. package/src/components/result/QueryBuilderResultPanel.tsx +2 -4
  88. package/src/components/result/tds/QueryBuilderTDSGridResult.tsx +3 -2
  89. package/src/components/result/tds/QueryBuilderTDSResultShared.tsx +332 -26
  90. package/src/components/shared/LambdaEditor.tsx +2 -2
  91. package/src/stores/filter/QueryBuilderFilterOperator.ts +1 -1
  92. package/src/stores/filter/QueryBuilderFilterState.ts +10 -5
  93. package/src/stores/filter/QueryBuilderFilterStateBuilder.ts +1 -1
  94. package/src/stores/filter/operators/QueryBuilderFilterOperator_Contain.ts +3 -5
  95. package/src/stores/filter/operators/QueryBuilderFilterOperator_EndWith.ts +3 -5
  96. package/src/stores/filter/operators/QueryBuilderFilterOperator_Equal.ts +2 -2
  97. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.ts +2 -4
  98. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.ts +2 -4
  99. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +2 -2
  100. package/src/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.ts +2 -2
  101. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThan.ts +2 -4
  102. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.ts +2 -4
  103. package/src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts +3 -5
@@ -68,10 +68,8 @@ import { useRef, useState } from 'react';
68
68
  import { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js';
69
69
  import { PARAMETER_SUBMIT_ACTION } from '../../stores/shared/LambdaParameterState.js';
70
70
  import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
71
- import {
72
- CODE_EDITOR_LANGUAGE,
73
- CodeEditor,
74
- } from '@finos/legend-lego/code-editor';
71
+ import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
72
+ import { CodeEditor } from '@finos/legend-lego/code-editor';
75
73
  import { ExecutionPlanViewer } from '../execution-plan/ExecutionPlanViewer.js';
76
74
  import { QueryUsageViewer } from '../QueryUsageViewer.js';
77
75
  import { DocumentationLink } from '@finos/legend-lego/application';
@@ -346,7 +346,7 @@ export const QueryBuilderTDSGridResult = observer(
346
346
  resultState.mousedOverCell,
347
347
  true,
348
348
  fetchStructureImplementation,
349
- );
349
+ ).catch(queryBuilderState.applicationStore.alertUnhandledError);
350
350
  },
351
351
  },
352
352
  {
@@ -357,7 +357,7 @@ export const QueryBuilderTDSGridResult = observer(
357
357
  resultState.mousedOverCell,
358
358
  false,
359
359
  fetchStructureImplementation,
360
- );
360
+ ).catch(queryBuilderState.applicationStore.alertUnhandledError);
361
361
  },
362
362
  },
363
363
  'copy',
@@ -376,6 +376,7 @@ export const QueryBuilderTDSGridResult = observer(
376
376
  applicationStore,
377
377
  resultState.mousedOverCell,
378
378
  resultState.queryBuilderState.fetchStructureState.implementation,
379
+ queryBuilderState.applicationStore.alertUnhandledError,
379
380
  ],
380
381
  );
381
382
 
@@ -22,12 +22,13 @@ import {
22
22
  import { observer } from 'mobx-react-lite';
23
23
  import { flowResult } from 'mobx';
24
24
  import {
25
- type TDSExecutionResult,
25
+ type AbstractPropertyExpression,
26
26
  type Enumeration,
27
- InstanceValue,
28
- EnumValueInstanceValue,
29
- EnumValueExplicitReference,
30
27
  type ExecutionResult,
28
+ type TDSExecutionResult,
29
+ EnumValueExplicitReference,
30
+ EnumValueInstanceValue,
31
+ InstanceValue,
31
32
  RelationalExecutionActivities,
32
33
  } from '@finos/legend-graph';
33
34
  import { format as formatSQL } from 'sql-formatter';
@@ -47,6 +48,7 @@ import { forwardRef } from 'react';
47
48
  import {
48
49
  QueryBuilderDerivationProjectionColumnState,
49
50
  QueryBuilderProjectionColumnState,
51
+ QueryBuilderSimpleProjectionColumnState,
50
52
  } from '../../../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
51
53
  import {
52
54
  type QueryBuilderPostFilterTreeNodeData,
@@ -81,6 +83,30 @@ import {
81
83
  } from '../../../stores/fetch-structure/tds/post-filter/operators/QueryBuilderPostFilterOperator_IsEmpty.js';
82
84
  import { getTDSColumnState } from '../../../stores/fetch-structure/tds/QueryBuilderTDSHelper.js';
83
85
  import type { QueryBuilderTDSColumnState } from '../../../stores/fetch-structure/tds/QueryBuilderTDSColumnState.js';
86
+ import {
87
+ type QueryBuilderFilterState,
88
+ type QueryBuilderFilterTreeNodeData,
89
+ FilterConditionState,
90
+ FilterValueSpecConditionValueState,
91
+ isCollectionProperty,
92
+ QueryBuilderFilterTreeConditionNodeData,
93
+ } from '../../../stores/filter/QueryBuilderFilterState.js';
94
+ import { QueryBuilderAggregateColumnState } from '../../../stores/fetch-structure/tds/aggregation/QueryBuilderAggregationState.js';
95
+ import type { QueryBuilderFilterOperator } from '../../../stores/filter/QueryBuilderFilterOperator.js';
96
+ import {
97
+ QueryBuilderFilterOperator_Equal,
98
+ QueryBuilderFilterOperator_NotEqual,
99
+ } from '../../../stores/filter/operators/QueryBuilderFilterOperator_Equal.js';
100
+ import {
101
+ QueryBuilderFilterOperator_In,
102
+ QueryBuilderFilterOperator_NotIn,
103
+ } from '../../../stores/filter/operators/QueryBuilderFilterOperator_In.js';
104
+ import {
105
+ QueryBuilderFilterOperator_IsEmpty,
106
+ QueryBuilderFilterOperator_IsNotEmpty,
107
+ } from '../../../stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js';
108
+ import type { QueryBuilderState } from '../../../stores/QueryBuilderState.js';
109
+ import type { QueryBuilderPropertyExpressionState } from '../../../stores/QueryBuilderPropertyEditorState.js';
84
110
 
85
111
  export const tryToFormatSql = (sql: string): string => {
86
112
  try {
@@ -139,6 +165,13 @@ export type IQueryRendererParamsWithGridType = DataGridCellRendererParams & {
139
165
  tdsExecutionResult: TDSExecutionResult;
140
166
  };
141
167
 
168
+ const filterEqualOperator = new QueryBuilderFilterOperator_Equal();
169
+ const filterNotEqualOperator = new QueryBuilderFilterOperator_NotEqual();
170
+ const filterInOperator = new QueryBuilderFilterOperator_In();
171
+ const filterNotInOperator = new QueryBuilderFilterOperator_NotIn();
172
+ const filterEmptyOperator = new QueryBuilderFilterOperator_IsEmpty();
173
+ const filterNotEmptyOperator = new QueryBuilderFilterOperator_IsNotEmpty();
174
+
142
175
  const postFilterEqualOperator = new QueryBuilderPostFilterOperator_Equal();
143
176
  const postFilterInOperator = new QueryBuilderPostFilterOperator_In();
144
177
  const postFilterEmptyOperator = new QueryBuilderPostFilterOperator_IsEmpty();
@@ -148,6 +181,22 @@ const postFilterNotEqualOperator =
148
181
  new QueryBuilderPostFilterOperator_NotEqual();
149
182
  const postFilterNotInOperator = new QueryBuilderPostFilterOperator_NotIn();
150
183
 
184
+ const getExistingFilterNode = (
185
+ operators: QueryBuilderFilterOperator[],
186
+ propertyExpressionState: QueryBuilderPropertyExpressionState | undefined,
187
+ filterState: QueryBuilderFilterState,
188
+ ): QueryBuilderFilterTreeNodeData | undefined =>
189
+ Array.from(filterState.nodes.values())
190
+ .filter(filterByType(QueryBuilderFilterTreeConditionNodeData))
191
+ .filter(
192
+ (node) =>
193
+ node.condition.propertyExpressionState.path ===
194
+ propertyExpressionState?.path &&
195
+ operators
196
+ .map((op) => op.getLabel())
197
+ .includes(node.condition.operator.getLabel()),
198
+ )[0];
199
+
151
200
  const getExistingPostFilterNode = (
152
201
  operators: QueryBuilderPostFilterOperator[],
153
202
  projectionColumnName: string | undefined,
@@ -173,7 +222,7 @@ const getExistingPostFilterNode = (
173
222
  const updateFilterConditionValue = (
174
223
  conditionValue: InstanceValue,
175
224
  _cellData: QueryBuilderTDSResultCellData,
176
- tdsState: QueryBuilderTDSState,
225
+ queryBuilderState: QueryBuilderState,
177
226
  ): void => {
178
227
  if (_cellData.value) {
179
228
  instanceValue_setValue(
@@ -188,11 +237,58 @@ const updateFilterConditionValue = (
188
237
  )
189
238
  : _cellData.value,
190
239
  0,
191
- tdsState.queryBuilderState.observerContext,
240
+ queryBuilderState.observerContext,
192
241
  );
193
242
  }
194
243
  };
195
244
 
245
+ const generateNewFilterConditionNodeData = (
246
+ applicationStore: ApplicationStore<
247
+ LegendApplicationConfig,
248
+ LegendApplicationPluginManager<LegendApplicationPlugin>
249
+ >,
250
+ operator: QueryBuilderFilterOperator,
251
+ _cellData: QueryBuilderTDSResultCellData,
252
+ filterState: QueryBuilderFilterState,
253
+ propertyExpression: AbstractPropertyExpression | undefined,
254
+ ): void => {
255
+ let filterConditionState: FilterConditionState;
256
+ try {
257
+ if (propertyExpression) {
258
+ filterConditionState = new FilterConditionState(
259
+ filterState,
260
+ propertyExpression,
261
+ operator,
262
+ );
263
+
264
+ const defaultFilterConditionValue =
265
+ filterConditionState.operator.getDefaultFilterConditionValue(
266
+ filterConditionState,
267
+ );
268
+
269
+ filterConditionState.buildRightConditionValueFromValueSpec(
270
+ defaultFilterConditionValue,
271
+ );
272
+ updateFilterConditionValue(
273
+ defaultFilterConditionValue as InstanceValue,
274
+ _cellData,
275
+ filterState.queryBuilderState,
276
+ );
277
+ filterState.addNodeFromNode(
278
+ new QueryBuilderFilterTreeConditionNodeData(
279
+ undefined,
280
+ filterConditionState,
281
+ ),
282
+ undefined,
283
+ );
284
+ }
285
+ } catch (error) {
286
+ assertErrorThrown(error);
287
+ applicationStore.notificationService.notifyWarning(error.message);
288
+ return;
289
+ }
290
+ };
291
+
196
292
  const generateNewPostFilterConditionNodeData = async (
197
293
  applicationStore: ApplicationStore<
198
294
  LegendApplicationConfig,
@@ -235,7 +331,7 @@ const generateNewPostFilterConditionNodeData = async (
235
331
  updateFilterConditionValue(
236
332
  defaultFilterConditionValue as InstanceValue,
237
333
  _cellData,
238
- tdsState,
334
+ tdsState.queryBuilderState,
239
335
  );
240
336
  tdsState.postFilterState.addNodeFromNode(
241
337
  new QueryBuilderPostFilterTreeConditionNodeData(
@@ -252,6 +348,91 @@ const generateNewPostFilterConditionNodeData = async (
252
348
  }
253
349
  };
254
350
 
351
+ const updateExistingFilterConditionNodeData = (
352
+ existingPreFilterNode: QueryBuilderFilterTreeNodeData,
353
+ isFilterBy: boolean,
354
+ _cellData: QueryBuilderTDSResultCellData,
355
+ operator: QueryBuilderFilterOperator,
356
+ data: QueryBuilderTDSResultCellData | null,
357
+ queryBuilderState: QueryBuilderState,
358
+ ): void => {
359
+ if (operator === filterEmptyOperator || operator === filterNotEmptyOperator) {
360
+ const conditionState = (
361
+ existingPreFilterNode as QueryBuilderFilterTreeConditionNodeData
362
+ ).condition;
363
+ if (conditionState.operator.getLabel() !== operator.getLabel()) {
364
+ conditionState.changeOperator(
365
+ isFilterBy ? filterEmptyOperator : filterNotEmptyOperator,
366
+ );
367
+ }
368
+ return;
369
+ }
370
+ const conditionState = (
371
+ existingPreFilterNode as QueryBuilderFilterTreeConditionNodeData
372
+ ).condition;
373
+
374
+ const rightSide = conditionState.rightConditionValue;
375
+ if (rightSide instanceof FilterValueSpecConditionValueState) {
376
+ if (conditionState.operator.getLabel() === operator.getLabel()) {
377
+ const doesValueAlreadyExist =
378
+ rightSide.value instanceof InstanceValue &&
379
+ (rightSide.value instanceof EnumValueInstanceValue
380
+ ? rightSide.value.values.map((ef) => ef.value.name)
381
+ : rightSide.value.values
382
+ ).includes(_cellData.value);
383
+
384
+ if (!doesValueAlreadyExist) {
385
+ const currentValueSpecificaton = rightSide.value;
386
+ const newValueSpecification =
387
+ conditionState.operator.getDefaultFilterConditionValue(
388
+ conditionState,
389
+ );
390
+ updateFilterConditionValue(
391
+ newValueSpecification as InstanceValue,
392
+ _cellData,
393
+ queryBuilderState,
394
+ );
395
+ conditionState.changeOperator(
396
+ isFilterBy ? filterInOperator : filterNotInOperator,
397
+ );
398
+ instanceValue_setValues(
399
+ rightSide.value as InstanceValue,
400
+ [currentValueSpecificaton, newValueSpecification],
401
+ queryBuilderState.observerContext,
402
+ );
403
+ }
404
+ } else {
405
+ const doesValueAlreadyExist =
406
+ rightSide.value instanceof InstanceValue &&
407
+ rightSide.value.values
408
+ .filter((v) => v instanceof InstanceValue)
409
+ .map((v) =>
410
+ v instanceof EnumValueInstanceValue
411
+ ? v.values.map((ef) => ef.value.name)
412
+ : v.values,
413
+ )
414
+ .flat()
415
+ .includes(_cellData.value ?? data?.value);
416
+
417
+ if (!doesValueAlreadyExist) {
418
+ const newValueSpecification = (
419
+ isFilterBy ? filterEqualOperator : filterNotEqualOperator
420
+ ).getDefaultFilterConditionValue(conditionState);
421
+ updateFilterConditionValue(
422
+ newValueSpecification as InstanceValue,
423
+ _cellData,
424
+ queryBuilderState,
425
+ );
426
+ instanceValue_setValues(
427
+ rightSide.value as InstanceValue,
428
+ [...(rightSide.value as InstanceValue).values, newValueSpecification],
429
+ queryBuilderState.observerContext,
430
+ );
431
+ }
432
+ }
433
+ }
434
+ };
435
+
255
436
  const updateExistingPostFilterConditionNodeData = (
256
437
  existingPostFilterNode: QueryBuilderPostFilterTreeNodeData,
257
438
  isFilterBy: boolean,
@@ -297,7 +478,7 @@ const updateExistingPostFilterConditionNodeData = (
297
478
  updateFilterConditionValue(
298
479
  newValueSpecification as InstanceValue,
299
480
  _cellData,
300
- tdsState,
481
+ tdsState.queryBuilderState,
301
482
  );
302
483
  conditionState.changeOperator(
303
484
  isFilterBy ? postFilterInOperator : postFilterNotInOperator,
@@ -328,7 +509,7 @@ const updateExistingPostFilterConditionNodeData = (
328
509
  updateFilterConditionValue(
329
510
  newValueSpecification as InstanceValue,
330
511
  _cellData,
331
- tdsState,
512
+ tdsState.queryBuilderState,
332
513
  );
333
514
  instanceValue_setValues(
334
515
  rightSide.value as InstanceValue,
@@ -343,6 +524,25 @@ const updateExistingPostFilterConditionNodeData = (
343
524
  const getFilterOperator = (
344
525
  isFilterBy: boolean,
345
526
  _cellData: QueryBuilderTDSResultCellData,
527
+ ): QueryBuilderFilterOperator => {
528
+ if (isFilterBy) {
529
+ if (_cellData.value === null) {
530
+ return filterEmptyOperator;
531
+ } else {
532
+ return filterEqualOperator;
533
+ }
534
+ } else {
535
+ if (_cellData.value === null) {
536
+ return filterNotEmptyOperator;
537
+ } else {
538
+ return filterNotEqualOperator;
539
+ }
540
+ }
541
+ };
542
+
543
+ const getPostFilterOperator = (
544
+ isFilterBy: boolean,
545
+ _cellData: QueryBuilderTDSResultCellData,
346
546
  ): QueryBuilderPostFilterOperator => {
347
547
  if (isFilterBy) {
348
548
  if (_cellData.value === null) {
@@ -359,7 +559,7 @@ const getFilterOperator = (
359
559
  }
360
560
  };
361
561
 
362
- const filterByOrOutValue = (
562
+ const preFilterByOrOutValue = (
363
563
  applicationStore: ApplicationStore<
364
564
  LegendApplicationConfig,
365
565
  LegendApplicationPluginManager<LegendApplicationPlugin>
@@ -367,13 +567,58 @@ const filterByOrOutValue = (
367
567
  isFilterBy: boolean,
368
568
  _cellData: QueryBuilderTDSResultCellData,
369
569
  data: QueryBuilderTDSResultCellData | null,
370
- tdsState: QueryBuilderTDSState,
570
+ propertyExpressionState: QueryBuilderPropertyExpressionState,
571
+ queryBuilderState: QueryBuilderState,
371
572
  ): void => {
372
- tdsState.setShowPostFilterPanel(true);
573
+ queryBuilderState.filterState.setShowPanel(true);
373
574
  const operator = getFilterOperator(isFilterBy, _cellData);
374
- const tdsColState = data?.columnName
375
- ? getTDSColumnState(tdsState, data.columnName)
376
- : undefined;
575
+ const existingPreFilterNode = getExistingFilterNode(
576
+ _cellData.value === null
577
+ ? [filterEmptyOperator, filterNotEmptyOperator]
578
+ : isFilterBy
579
+ ? [filterEqualOperator, filterInOperator]
580
+ : [filterNotEqualOperator, filterNotInOperator],
581
+ propertyExpressionState,
582
+ queryBuilderState.filterState,
583
+ );
584
+ if (existingPreFilterNode) {
585
+ updateExistingFilterConditionNodeData(
586
+ existingPreFilterNode,
587
+ isFilterBy,
588
+ _cellData,
589
+ operator,
590
+ data,
591
+ queryBuilderState,
592
+ );
593
+ } else {
594
+ try {
595
+ generateNewFilterConditionNodeData(
596
+ applicationStore,
597
+ operator,
598
+ _cellData,
599
+ queryBuilderState.filterState,
600
+ propertyExpressionState.propertyExpression,
601
+ );
602
+ } catch (error) {
603
+ assertErrorThrown(error);
604
+ applicationStore.alertUnhandledError(error);
605
+ }
606
+ }
607
+ };
608
+
609
+ const postFilterByOrOutValue = async (
610
+ applicationStore: ApplicationStore<
611
+ LegendApplicationConfig,
612
+ LegendApplicationPluginManager<LegendApplicationPlugin>
613
+ >,
614
+ isFilterBy: boolean,
615
+ _cellData: QueryBuilderTDSResultCellData,
616
+ data: QueryBuilderTDSResultCellData | null,
617
+ tdsColState: QueryBuilderTDSColumnState,
618
+ tdsState: QueryBuilderTDSState,
619
+ ): Promise<void> => {
620
+ tdsState.setShowPostFilterPanel(true);
621
+ const operator = getPostFilterOperator(isFilterBy, _cellData);
377
622
  const existingPostFilterNode = getExistingPostFilterNode(
378
623
  _cellData.value === null
379
624
  ? [postFilterEmptyOperator, postFilterNotEmptyOperator]
@@ -394,17 +639,67 @@ const filterByOrOutValue = (
394
639
  tdsState,
395
640
  );
396
641
  } else {
397
- generateNewPostFilterConditionNodeData(
642
+ try {
643
+ await generateNewPostFilterConditionNodeData(
644
+ applicationStore,
645
+ operator,
646
+ _cellData,
647
+ tdsState,
648
+ tdsColState,
649
+ );
650
+ } catch (error) {
651
+ assertErrorThrown(error);
652
+ applicationStore.alertUnhandledError(error);
653
+ }
654
+ }
655
+ };
656
+
657
+ const filterByOrOutValue = async (
658
+ applicationStore: ApplicationStore<
659
+ LegendApplicationConfig,
660
+ LegendApplicationPluginManager<LegendApplicationPlugin>
661
+ >,
662
+ isFilterBy: boolean,
663
+ _cellData: QueryBuilderTDSResultCellData,
664
+ data: QueryBuilderTDSResultCellData | null,
665
+ tdsState: QueryBuilderTDSState,
666
+ ): Promise<void> => {
667
+ const tdsColState = data?.columnName
668
+ ? getTDSColumnState(tdsState, data.columnName)
669
+ : undefined;
670
+ if (
671
+ tdsColState instanceof QueryBuilderDerivationProjectionColumnState ||
672
+ tdsColState instanceof QueryBuilderAggregateColumnState ||
673
+ (tdsColState instanceof QueryBuilderSimpleProjectionColumnState &&
674
+ isCollectionProperty(
675
+ tdsColState.propertyExpressionState.propertyExpression,
676
+ ))
677
+ ) {
678
+ await postFilterByOrOutValue(
398
679
  applicationStore,
399
- operator,
680
+ isFilterBy,
400
681
  _cellData,
401
- tdsState,
682
+ data,
402
683
  tdsColState,
403
- ).catch(applicationStore.alertUnhandledError);
684
+ tdsState,
685
+ );
686
+ } else if (tdsColState instanceof QueryBuilderSimpleProjectionColumnState) {
687
+ preFilterByOrOutValue(
688
+ applicationStore,
689
+ isFilterBy,
690
+ _cellData,
691
+ data,
692
+ tdsColState.propertyExpressionState,
693
+ tdsState.queryBuilderState,
694
+ );
695
+ } else {
696
+ applicationStore.notificationService.notifyError(
697
+ `Can't filter column '${data?.columnName}'`,
698
+ );
404
699
  }
405
700
  };
406
701
 
407
- export const filterByOrOutValues = (
702
+ export const filterByOrOutValues = async (
408
703
  applicationStore: ApplicationStore<
409
704
  LegendApplicationConfig,
410
705
  LegendApplicationPluginManager<LegendApplicationPlugin>
@@ -412,10 +707,17 @@ export const filterByOrOutValues = (
412
707
  data: QueryBuilderTDSResultCellData | null,
413
708
  isFilterBy: boolean,
414
709
  tdsState: QueryBuilderTDSState,
415
- ): void => {
416
- tdsState.queryBuilderState.resultState.selectedCells.forEach((_cellData) => {
417
- filterByOrOutValue(applicationStore, isFilterBy, _cellData, data, tdsState);
418
- });
710
+ ): Promise<void> => {
711
+ for (const _cellData of tdsState.queryBuilderState.resultState
712
+ .selectedCells) {
713
+ await filterByOrOutValue(
714
+ applicationStore,
715
+ isFilterBy,
716
+ _cellData,
717
+ data,
718
+ tdsState,
719
+ );
720
+ }
419
721
  };
420
722
 
421
723
  export const QueryBuilderGridResultContextMenu = observer(
@@ -440,7 +742,9 @@ export const QueryBuilderGridResultContextMenu = observer(
440
742
  <MenuContentItem
441
743
  disabled={!tdsColState}
442
744
  onClick={(): void => {
443
- filterByOrOutValues(applicationStore, data, true, tdsState);
745
+ filterByOrOutValues(applicationStore, data, true, tdsState).catch(
746
+ tdsState.queryBuilderState.applicationStore.alertUnhandledError,
747
+ );
444
748
  }}
445
749
  >
446
750
  Filter By
@@ -448,7 +752,9 @@ export const QueryBuilderGridResultContextMenu = observer(
448
752
  <MenuContentItem
449
753
  disabled={!tdsColState}
450
754
  onClick={(): void => {
451
- filterByOrOutValues(applicationStore, data, false, tdsState);
755
+ filterByOrOutValues(applicationStore, data, false, tdsState).catch(
756
+ tdsState.queryBuilderState.applicationStore.alertUnhandledError,
757
+ );
452
758
  }}
453
759
  >
454
760
  Filter Out
@@ -31,7 +31,6 @@ import {
31
31
  ModalFooterButton,
32
32
  } from '@finos/legend-art';
33
33
  import {
34
- disposeCodeEditor,
35
34
  getBaseCodeEditorOptions,
36
35
  getCodeEditorValue,
37
36
  normalizeLineEnding,
@@ -39,7 +38,8 @@ import {
39
38
  setErrorMarkers,
40
39
  CODE_EDITOR_LANGUAGE,
41
40
  CODE_EDITOR_THEME,
42
- } from '@finos/legend-lego/code-editor';
41
+ } from '@finos/legend-code-editor';
42
+ import { disposeCodeEditor } from '@finos/legend-lego/code-editor';
43
43
  import type { LambdaEditorState } from '../../stores/shared/LambdaEditorState.js';
44
44
  import {
45
45
  debounce,
@@ -27,7 +27,7 @@ import type {
27
27
  export abstract class QueryBuilderFilterOperator implements Hashable {
28
28
  readonly uuid = uuid();
29
29
 
30
- abstract getLabel(filterConditionState: FilterConditionState): string;
30
+ abstract getLabel(): string;
31
31
 
32
32
  abstract isCompatibleWithFilterConditionProperty(
33
33
  filterConditionState: FilterConditionState,
@@ -246,6 +246,7 @@ export class FilterConditionState implements Hashable {
246
246
  constructor(
247
247
  filterState: QueryBuilderFilterState,
248
248
  propertyExpression: AbstractPropertyExpression,
249
+ operator?: QueryBuilderFilterOperator,
249
250
  ) {
250
251
  makeObservable(this, {
251
252
  propertyExpressionState: observable,
@@ -271,11 +272,15 @@ export class FilterConditionState implements Hashable {
271
272
  );
272
273
 
273
274
  // operator
274
- assertTrue(
275
- this.operators.length !== 0,
276
- `Can't find an operator for property '${this.propertyExpressionState.path}': no operators registered`,
277
- );
278
- this.operator = this.operators[0] as QueryBuilderFilterOperator;
275
+ if (operator) {
276
+ this.operator = operator;
277
+ } else {
278
+ assertTrue(
279
+ this.operators.length !== 0,
280
+ `Can't find an operator for property '${this.propertyExpressionState.path}': no operators registered`,
281
+ );
282
+ this.operator = this.operators[0] as QueryBuilderFilterOperator;
283
+ }
279
284
  this.buildRightConditionValueFromValueSpec(
280
285
  this.operator.getDefaultFilterConditionValue(this),
281
286
  );
@@ -332,7 +332,7 @@ const processFilterTree = (
332
332
  assertTrue(
333
333
  parentLambdaVariableName === variableName,
334
334
  `Can't process ${extractElementNameFromPath(
335
- filterConditionState.operator.getLabel(filterConditionState),
335
+ filterConditionState.operator.getLabel(),
336
336
  )}() expression: expects variable used in lambda body '${variableName}' to match lambda parameter '${parentLambdaVariableName}'`,
337
337
  );
338
338
  filterState.addNodeFromNode(
@@ -46,7 +46,7 @@ export class QueryBuilderFilterOperator_Contain
46
46
  extends QueryBuilderFilterOperator
47
47
  implements Hashable
48
48
  {
49
- getLabel(filterConditionState: FilterConditionState): string {
49
+ getLabel(): string {
50
50
  return 'contains';
51
51
  }
52
52
 
@@ -90,9 +90,7 @@ export class QueryBuilderFilterOperator_Contain
90
90
  }
91
91
  default:
92
92
  throw new UnsupportedOperationError(
93
- `Can't get default value for filter operator '${this.getLabel(
94
- filterConditionState,
95
- )}' when the LHS property is of type '${propertyType.path}'`,
93
+ `Can't get default value for filter operator '${this.getLabel()}' when the LHS property is of type '${propertyType.path}'`,
96
94
  );
97
95
  }
98
96
  }
@@ -128,7 +126,7 @@ export class QueryBuilderFilterOperator_Contain
128
126
  }
129
127
 
130
128
  export class QueryBuilderFilterOperator_NotContain extends QueryBuilderFilterOperator_Contain {
131
- override getLabel(filterConditionState: FilterConditionState): string {
129
+ override getLabel(): string {
132
130
  return `doesn't contain`;
133
131
  }
134
132
 
@@ -46,7 +46,7 @@ export class QueryBuilderFilterOperator_EndWith
46
46
  extends QueryBuilderFilterOperator
47
47
  implements Hashable
48
48
  {
49
- getLabel(filterConditionState: FilterConditionState): string {
49
+ getLabel(): string {
50
50
  return 'ends with';
51
51
  }
52
52
 
@@ -90,9 +90,7 @@ export class QueryBuilderFilterOperator_EndWith
90
90
  }
91
91
  default:
92
92
  throw new UnsupportedOperationError(
93
- `Can't get default value for filter operator '${this.getLabel(
94
- filterConditionState,
95
- )}' when the LHS property is of type '${propertyType.path}'`,
93
+ `Can't get default value for filter operator '${this.getLabel()}' when the LHS property is of type '${propertyType.path}'`,
96
94
  );
97
95
  }
98
96
  }
@@ -128,7 +126,7 @@ export class QueryBuilderFilterOperator_EndWith
128
126
  }
129
127
 
130
128
  export class QueryBuilderFilterOperator_NotEndWith extends QueryBuilderFilterOperator_EndWith {
131
- override getLabel(filterConditionState: FilterConditionState): string {
129
+ override getLabel(): string {
132
130
  return `doesn't end with`;
133
131
  }
134
132
 
@@ -44,7 +44,7 @@ export class QueryBuilderFilterOperator_Equal
44
44
  extends QueryBuilderFilterOperator
45
45
  implements Hashable
46
46
  {
47
- getLabel(filterConditionState: FilterConditionState): string {
47
+ getLabel(): string {
48
48
  return 'is';
49
49
  }
50
50
 
@@ -143,7 +143,7 @@ export class QueryBuilderFilterOperator_Equal
143
143
  }
144
144
 
145
145
  export class QueryBuilderFilterOperator_NotEqual extends QueryBuilderFilterOperator_Equal {
146
- override getLabel(filterConditionState: FilterConditionState): string {
146
+ override getLabel(): string {
147
147
  return `is not`;
148
148
  }
149
149