@finos/legend-query-builder 4.15.2 → 4.15.3

Sign up to get free protection for your applications and to get access to all the features.
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
  }