@finos/legend-query-builder 4.15.2 → 4.15.3

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 (81) hide show
  1. package/lib/components/data-cube/QueryBuilderDataCube.d.ts.map +1 -1
  2. package/lib/components/data-cube/QueryBuilderDataCube.js +6 -7
  3. package/lib/components/data-cube/QueryBuilderDataCube.js.map +1 -1
  4. package/lib/components/filter/QueryBuilderFilterPanel.js +2 -2
  5. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  6. package/lib/components/result/tds/QueryBuilderTDSGridResult.d.ts.map +1 -1
  7. package/lib/components/result/tds/QueryBuilderTDSGridResult.js +4 -3
  8. package/lib/components/result/tds/QueryBuilderTDSGridResult.js.map +1 -1
  9. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts +2 -2
  10. package/lib/components/result/tds/QueryBuilderTDSResultShared.d.ts.map +1 -1
  11. package/lib/components/result/tds/QueryBuilderTDSResultShared.js +157 -19
  12. package/lib/components/result/tds/QueryBuilderTDSResultShared.js.map +1 -1
  13. package/lib/index.css +1 -17
  14. package/lib/index.css.map +1 -1
  15. package/lib/package.json +1 -1
  16. package/lib/stores/filter/QueryBuilderFilterOperator.d.ts +1 -1
  17. package/lib/stores/filter/QueryBuilderFilterOperator.d.ts.map +1 -1
  18. package/lib/stores/filter/QueryBuilderFilterState.d.ts +1 -1
  19. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  20. package/lib/stores/filter/QueryBuilderFilterState.js +8 -3
  21. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  22. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js +1 -1
  23. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js.map +1 -1
  24. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts +2 -2
  25. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts.map +1 -1
  26. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js +3 -3
  27. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js.map +1 -1
  28. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts +2 -2
  29. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts.map +1 -1
  30. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js +3 -3
  31. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js.map +1 -1
  32. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts +2 -2
  33. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
  34. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js +2 -2
  35. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js.map +1 -1
  36. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts +1 -1
  37. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
  38. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js +2 -2
  39. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
  40. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts +1 -1
  41. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  42. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js +2 -2
  43. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
  44. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts +2 -2
  45. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts.map +1 -1
  46. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +2 -2
  47. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  48. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts +2 -2
  49. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts.map +1 -1
  50. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js +2 -2
  51. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js.map +1 -1
  52. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts +1 -1
  53. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
  54. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js +2 -2
  55. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
  56. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts +1 -1
  57. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
  58. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js +2 -2
  59. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
  60. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts +2 -2
  61. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts.map +1 -1
  62. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js +3 -3
  63. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js.map +1 -1
  64. package/package.json +5 -5
  65. package/src/components/data-cube/QueryBuilderDataCube.tsx +15 -25
  66. package/src/components/filter/QueryBuilderFilterPanel.tsx +2 -2
  67. package/src/components/result/tds/QueryBuilderTDSGridResult.tsx +7 -3
  68. package/src/components/result/tds/QueryBuilderTDSResultShared.tsx +334 -26
  69. package/src/stores/filter/QueryBuilderFilterOperator.ts +1 -1
  70. package/src/stores/filter/QueryBuilderFilterState.ts +10 -5
  71. package/src/stores/filter/QueryBuilderFilterStateBuilder.ts +1 -1
  72. package/src/stores/filter/operators/QueryBuilderFilterOperator_Contain.ts +3 -5
  73. package/src/stores/filter/operators/QueryBuilderFilterOperator_EndWith.ts +3 -5
  74. package/src/stores/filter/operators/QueryBuilderFilterOperator_Equal.ts +2 -2
  75. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.ts +2 -4
  76. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.ts +2 -4
  77. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +2 -2
  78. package/src/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.ts +2 -2
  79. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThan.ts +2 -4
  80. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.ts +2 -4
  81. package/src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts +3 -5
@@ -20,6 +20,7 @@ import { PRIMITIVE_TYPE, type TDSExecutionResult } from '@finos/legend-graph';
20
20
  import { useState, useCallback, useEffect } from 'react';
21
21
  import {
22
22
  DataGrid,
23
+ type DataGridCellSelectionChangedEvent,
23
24
  type DataGridApi,
24
25
  type DataGridCellRange,
25
26
  type DataGridColumnDefinition,
@@ -347,7 +348,7 @@ export const QueryBuilderTDSGridResult = observer(
347
348
  resultState.mousedOverCell,
348
349
  true,
349
350
  fetchStructureImplementation,
350
- );
351
+ ).catch(queryBuilderState.applicationStore.alertUnhandledError);
351
352
  },
352
353
  },
353
354
  {
@@ -358,7 +359,7 @@ export const QueryBuilderTDSGridResult = observer(
358
359
  resultState.mousedOverCell,
359
360
  false,
360
361
  fetchStructureImplementation,
361
- );
362
+ ).catch(queryBuilderState.applicationStore.alertUnhandledError);
362
363
  },
363
364
  },
364
365
  'copy',
@@ -377,6 +378,7 @@ export const QueryBuilderTDSGridResult = observer(
377
378
  applicationStore,
378
379
  resultState.mousedOverCell,
379
380
  resultState.queryBuilderState.fetchStructureState.implementation,
381
+ queryBuilderState.applicationStore.alertUnhandledError,
380
382
  ],
381
383
  );
382
384
 
@@ -554,7 +556,9 @@ export const QueryBuilderTDSGridResult = observer(
554
556
  onRowDataUpdated={(params) => {
555
557
  params.api.refreshCells({ force: true });
556
558
  }}
557
- onRangeSelectionChanged={(event) => {
559
+ onCellSelectionChanged={(
560
+ event: DataGridCellSelectionChangedEvent<QueryBuilderTDSRowDataType>,
561
+ ) => {
558
562
  const selectedCells = getSelectedCells(event.api);
559
563
  resultState.setSelectedCells([]);
560
564
  selectedCells.forEach((cell) =>
@@ -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,69 @@ 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, _cellData.columnName)
669
+ : _cellData.columnName
670
+ ? getTDSColumnState(tdsState, _cellData.columnName)
671
+ : undefined;
672
+ if (
673
+ tdsColState instanceof QueryBuilderDerivationProjectionColumnState ||
674
+ tdsColState instanceof QueryBuilderAggregateColumnState ||
675
+ (tdsColState instanceof QueryBuilderSimpleProjectionColumnState &&
676
+ isCollectionProperty(
677
+ tdsColState.propertyExpressionState.propertyExpression,
678
+ ))
679
+ ) {
680
+ await postFilterByOrOutValue(
398
681
  applicationStore,
399
- operator,
682
+ isFilterBy,
400
683
  _cellData,
401
- tdsState,
684
+ data,
402
685
  tdsColState,
403
- ).catch(applicationStore.alertUnhandledError);
686
+ tdsState,
687
+ );
688
+ } else if (tdsColState instanceof QueryBuilderSimpleProjectionColumnState) {
689
+ preFilterByOrOutValue(
690
+ applicationStore,
691
+ isFilterBy,
692
+ _cellData,
693
+ data,
694
+ tdsColState.propertyExpressionState,
695
+ tdsState.queryBuilderState,
696
+ );
697
+ } else {
698
+ applicationStore.notificationService.notifyError(
699
+ `Can't filter column '${data?.columnName ? data.columnName : _cellData.columnName}'`,
700
+ );
404
701
  }
405
702
  };
406
703
 
407
- export const filterByOrOutValues = (
704
+ export const filterByOrOutValues = async (
408
705
  applicationStore: ApplicationStore<
409
706
  LegendApplicationConfig,
410
707
  LegendApplicationPluginManager<LegendApplicationPlugin>
@@ -412,10 +709,17 @@ export const filterByOrOutValues = (
412
709
  data: QueryBuilderTDSResultCellData | null,
413
710
  isFilterBy: boolean,
414
711
  tdsState: QueryBuilderTDSState,
415
- ): void => {
416
- tdsState.queryBuilderState.resultState.selectedCells.forEach((_cellData) => {
417
- filterByOrOutValue(applicationStore, isFilterBy, _cellData, data, tdsState);
418
- });
712
+ ): Promise<void> => {
713
+ for (const _cellData of tdsState.queryBuilderState.resultState
714
+ .selectedCells) {
715
+ await filterByOrOutValue(
716
+ applicationStore,
717
+ isFilterBy,
718
+ _cellData,
719
+ data,
720
+ tdsState,
721
+ );
722
+ }
419
723
  };
420
724
 
421
725
  export const QueryBuilderGridResultContextMenu = observer(
@@ -440,7 +744,9 @@ export const QueryBuilderGridResultContextMenu = observer(
440
744
  <MenuContentItem
441
745
  disabled={!tdsColState}
442
746
  onClick={(): void => {
443
- filterByOrOutValues(applicationStore, data, true, tdsState);
747
+ filterByOrOutValues(applicationStore, data, true, tdsState).catch(
748
+ tdsState.queryBuilderState.applicationStore.alertUnhandledError,
749
+ );
444
750
  }}
445
751
  >
446
752
  Filter By
@@ -448,7 +754,9 @@ export const QueryBuilderGridResultContextMenu = observer(
448
754
  <MenuContentItem
449
755
  disabled={!tdsColState}
450
756
  onClick={(): void => {
451
- filterByOrOutValues(applicationStore, data, false, tdsState);
757
+ filterByOrOutValues(applicationStore, data, false, tdsState).catch(
758
+ tdsState.queryBuilderState.applicationStore.alertUnhandledError,
759
+ );
452
760
  }}
453
761
  >
454
762
  Filter Out
@@ -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
 
@@ -43,7 +43,7 @@ export class QueryBuilderFilterOperator_GreaterThan
43
43
  extends QueryBuilderFilterOperator
44
44
  implements Hashable
45
45
  {
46
- getLabel(filterConditionState: FilterConditionState): string {
46
+ getLabel(): string {
47
47
  return '>';
48
48
  }
49
49
 
@@ -104,9 +104,7 @@ export class QueryBuilderFilterOperator_GreaterThan
104
104
  }
105
105
  default:
106
106
  throw new UnsupportedOperationError(
107
- `Can't get default value for filter operator '${this.getLabel(
108
- filterConditionState,
109
- )}' when the LHS property is of type '${propertyType.path}'`,
107
+ `Can't get default value for filter operator '${this.getLabel()}' when the LHS property is of type '${propertyType.path}'`,
110
108
  );
111
109
  }
112
110
  }