@finos/legend-query-builder 4.1.15 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. package/lib/components/filter/QueryBuilderFilterPanel.d.ts +6 -1
  2. package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
  3. package/lib/components/filter/QueryBuilderFilterPanel.js +189 -13
  4. package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
  5. package/lib/index.css +2 -2
  6. package/lib/index.css.map +1 -1
  7. package/lib/package.json +1 -1
  8. package/lib/stores/QueryBuilderStateHashUtils.d.ts +1 -0
  9. package/lib/stores/QueryBuilderStateHashUtils.d.ts.map +1 -1
  10. package/lib/stores/QueryBuilderStateHashUtils.js +1 -0
  11. package/lib/stores/QueryBuilderStateHashUtils.js.map +1 -1
  12. package/lib/stores/filter/QueryBuilderFilterOperator.d.ts +1 -1
  13. package/lib/stores/filter/QueryBuilderFilterOperator.d.ts.map +1 -1
  14. package/lib/stores/filter/QueryBuilderFilterOperator.js.map +1 -1
  15. package/lib/stores/filter/QueryBuilderFilterState.d.ts +16 -5
  16. package/lib/stores/filter/QueryBuilderFilterState.d.ts.map +1 -1
  17. package/lib/stores/filter/QueryBuilderFilterState.js +78 -18
  18. package/lib/stores/filter/QueryBuilderFilterState.js.map +1 -1
  19. package/lib/stores/filter/QueryBuilderFilterStateBuilder.d.ts.map +1 -1
  20. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js +125 -2
  21. package/lib/stores/filter/QueryBuilderFilterStateBuilder.js.map +1 -1
  22. package/lib/stores/filter/QueryBuilderFilterValueSpecificationBuilder.d.ts.map +1 -1
  23. package/lib/stores/filter/QueryBuilderFilterValueSpecificationBuilder.js +62 -3
  24. package/lib/stores/filter/QueryBuilderFilterValueSpecificationBuilder.js.map +1 -1
  25. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.d.ts +1 -1
  26. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.d.ts.map +1 -1
  27. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js +12 -192
  28. package/lib/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.js.map +1 -1
  29. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts +2 -2
  30. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.d.ts.map +1 -1
  31. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js +4 -4
  32. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Contain.js.map +1 -1
  33. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts +2 -2
  34. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.d.ts.map +1 -1
  35. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js +4 -4
  36. package/lib/stores/filter/operators/QueryBuilderFilterOperator_EndWith.js.map +1 -1
  37. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts +2 -2
  38. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
  39. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js +4 -4
  40. package/lib/stores/filter/operators/QueryBuilderFilterOperator_Equal.js.map +1 -1
  41. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts +1 -1
  42. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
  43. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js +2 -2
  44. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
  45. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts +1 -1
  46. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  47. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js +2 -2
  48. package/lib/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
  49. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts +2 -2
  50. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.d.ts.map +1 -1
  51. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js +4 -4
  52. package/lib/stores/filter/operators/QueryBuilderFilterOperator_In.js.map +1 -1
  53. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts +2 -2
  54. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.d.ts.map +1 -1
  55. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js +4 -4
  56. package/lib/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.js.map +1 -1
  57. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts +1 -1
  58. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
  59. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js +2 -2
  60. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
  61. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts +1 -1
  62. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
  63. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js +2 -2
  64. package/lib/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
  65. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts +2 -2
  66. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.d.ts.map +1 -1
  67. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js +4 -4
  68. package/lib/stores/filter/operators/QueryBuilderFilterOperator_StartWith.js.map +1 -1
  69. package/package.json +1 -1
  70. package/src/components/filter/QueryBuilderFilterPanel.tsx +355 -24
  71. package/src/stores/QueryBuilderStateHashUtils.ts +1 -0
  72. package/src/stores/filter/QueryBuilderFilterOperator.ts +1 -0
  73. package/src/stores/filter/QueryBuilderFilterState.ts +115 -27
  74. package/src/stores/filter/QueryBuilderFilterStateBuilder.ts +244 -0
  75. package/src/stores/filter/QueryBuilderFilterValueSpecificationBuilder.ts +94 -1
  76. package/src/stores/filter/operators/QueryBuilderFilterOperatorValueSpecificationBuilder.ts +37 -377
  77. package/src/stores/filter/operators/QueryBuilderFilterOperator_Contain.ts +7 -1
  78. package/src/stores/filter/operators/QueryBuilderFilterOperator_EndWith.ts +7 -1
  79. package/src/stores/filter/operators/QueryBuilderFilterOperator_Equal.ts +7 -1
  80. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThan.ts +2 -0
  81. package/src/stores/filter/operators/QueryBuilderFilterOperator_GreaterThanEqual.ts +2 -0
  82. package/src/stores/filter/operators/QueryBuilderFilterOperator_In.ts +7 -1
  83. package/src/stores/filter/operators/QueryBuilderFilterOperator_IsEmpty.ts +7 -1
  84. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThan.ts +2 -0
  85. package/src/stores/filter/operators/QueryBuilderFilterOperator_LessThanEqual.ts +2 -0
  86. package/src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts +7 -1
@@ -1 +1 @@
1
- {"version":3,"file":"QueryBuilderFilterOperator_StartWith.js","sourceRoot":"","sources":["../../../../src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"QueryBuilderFilterOperator_StartWith.js","sourceRoot":"","sources":["../../../../src/stores/filter/operators/QueryBuilderFilterOperator_StartWith.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-query-builder",
3
- "version": "4.1.15",
3
+ "version": "4.2.0",
4
4
  "description": "Legend query builder core",
5
5
  "keywords": [
6
6
  "legend",
@@ -55,6 +55,9 @@ import {
55
55
  QueryBuilderFilterTreeConditionNodeData,
56
56
  QueryBuilderFilterTreeBlankConditionNodeData,
57
57
  QueryBuilderFilterTreeGroupNodeData,
58
+ QueryBuilderFilterTreeExistsNodeData,
59
+ QueryBuilderFilterTreeOperationNodeData,
60
+ type QueryBuilderFilterState,
58
61
  } from '../../stores/filter/QueryBuilderFilterState.js';
59
62
  import { useDrag, useDragLayer, useDrop } from 'react-dnd';
60
63
  import {
@@ -67,12 +70,29 @@ import { QueryBuilderPropertyExpressionBadge } from '../QueryBuilderPropertyExpr
67
70
  import type { QueryBuilderState } from '../../stores/QueryBuilderState.js';
68
71
  import {
69
72
  assertErrorThrown,
73
+ assertTrue,
70
74
  debounce,
75
+ generateEnumerableNameFromToken,
76
+ getNullableFirstEntry,
77
+ guaranteeNonNullable,
78
+ guaranteeType,
71
79
  UnsupportedOperationError,
72
80
  } from '@finos/legend-shared';
73
81
  import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js';
74
- import { useApplicationStore } from '@finos/legend-application';
75
- import type { ValueSpecification } from '@finos/legend-graph';
82
+ import {
83
+ ActionAlertActionType,
84
+ ActionAlertType,
85
+ useApplicationStore,
86
+ } from '@finos/legend-application';
87
+ import {
88
+ AbstractPropertyExpression,
89
+ extractElementNameFromPath,
90
+ matchFunctionName,
91
+ Multiplicity,
92
+ SimpleFunctionExpression,
93
+ type ValueSpecification,
94
+ VariableExpression,
95
+ } from '@finos/legend-graph';
76
96
  import {
77
97
  type QueryBuilderProjectionColumnDragSource,
78
98
  QueryBuilderSimpleProjectionColumnState,
@@ -87,6 +107,282 @@ import {
87
107
  QUERY_BUILDER_VARIABLE_DND_TYPE,
88
108
  } from '../shared/BasicValueSpecificationEditor.js';
89
109
  import { QueryBuilderTelemetryHelper } from '../../__lib__/QueryBuilderTelemetryHelper.js';
110
+ import { getPropertyChainName } from '../../stores/QueryBuilderPropertyEditorState.js';
111
+ import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js';
112
+
113
+ const isCollectionProperty = (
114
+ propertyExpression: AbstractPropertyExpression,
115
+ ): boolean => {
116
+ let currentExpression: ValueSpecification | undefined = propertyExpression;
117
+ while (currentExpression instanceof AbstractPropertyExpression) {
118
+ // Check if the property chain can results in column that have multiple values
119
+ if (
120
+ currentExpression.func.value.multiplicity.upperBound === undefined ||
121
+ currentExpression.func.value.multiplicity.upperBound > 1
122
+ ) {
123
+ return true;
124
+ }
125
+ currentExpression = getNullableFirstEntry(
126
+ currentExpression.parametersValues,
127
+ );
128
+ // Take care of chains of subtype
129
+ while (
130
+ currentExpression instanceof SimpleFunctionExpression &&
131
+ matchFunctionName(
132
+ currentExpression.functionName,
133
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
134
+ )
135
+ ) {
136
+ currentExpression = getNullableFirstEntry(
137
+ currentExpression.parametersValues,
138
+ );
139
+ }
140
+ }
141
+ return false;
142
+ };
143
+
144
+ /**
145
+ * This function updates the filter state when we DnD a property that can accept multiple values.
146
+ */
147
+ export const buildFilterTreeWithExists = (
148
+ propertyExpression: AbstractPropertyExpression,
149
+ filterState: QueryBuilderFilterState,
150
+ targetDropNode?: QueryBuilderFilterTreeOperationNodeData,
151
+ ): void => {
152
+ // 1. Decompose property expression
153
+ const expressions: (AbstractPropertyExpression | SimpleFunctionExpression)[] =
154
+ [];
155
+ let currentPropertyExpression: ValueSpecification = propertyExpression;
156
+ while (
157
+ currentPropertyExpression instanceof AbstractPropertyExpression ||
158
+ (currentPropertyExpression instanceof SimpleFunctionExpression &&
159
+ matchFunctionName(
160
+ currentPropertyExpression.functionName,
161
+ QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE,
162
+ ))
163
+ ) {
164
+ let exp: AbstractPropertyExpression | SimpleFunctionExpression;
165
+ if (currentPropertyExpression instanceof SimpleFunctionExpression) {
166
+ exp = new SimpleFunctionExpression(
167
+ extractElementNameFromPath(QUERY_BUILDER_SUPPORTED_FUNCTIONS.SUBTYPE),
168
+ );
169
+ } else {
170
+ exp = new AbstractPropertyExpression('');
171
+ exp.func = currentPropertyExpression.func;
172
+ }
173
+ // NOTE: we must retain the rest of the parameters as those are derived property parameters
174
+ exp.parametersValues =
175
+ currentPropertyExpression.parametersValues.length > 1
176
+ ? currentPropertyExpression.parametersValues.slice(1)
177
+ : [];
178
+ expressions.push(exp);
179
+ currentPropertyExpression = guaranteeNonNullable(
180
+ currentPropertyExpression.parametersValues[0],
181
+ );
182
+ }
183
+ const rootVariable = guaranteeType(
184
+ currentPropertyExpression,
185
+ VariableExpression,
186
+ );
187
+
188
+ // 2. Traverse the list of decomposed property expression backward, every time we encounter a property of
189
+ // multiplicity many, create a new property expression and keep track of it to later form the lambda chain
190
+ let existsLambdaParamNames: string[] = [];
191
+ let existsLambdaPropertyChains: ValueSpecification[] = [rootVariable];
192
+ let currentParamNameIndex = 0;
193
+
194
+ for (let i = expressions.length - 1; i >= 0; --i) {
195
+ const exp = expressions[i] as
196
+ | AbstractPropertyExpression
197
+ | SimpleFunctionExpression;
198
+ // just keep adding to the property chain
199
+ exp.parametersValues.unshift(
200
+ existsLambdaPropertyChains[
201
+ existsLambdaPropertyChains.length - 1
202
+ ] as ValueSpecification,
203
+ );
204
+ existsLambdaPropertyChains[existsLambdaPropertyChains.length - 1] = exp;
205
+ // ... but if the property is of multiplicity multiple, start a new property chain
206
+ if (
207
+ exp instanceof AbstractPropertyExpression &&
208
+ (exp.func.value.multiplicity.upperBound === undefined ||
209
+ exp.func.value.multiplicity.upperBound > 1)
210
+ ) {
211
+ // NOTE: we need to find/generate the property chain variable name
212
+ if (currentParamNameIndex > existsLambdaParamNames.length - 1) {
213
+ existsLambdaParamNames.push(
214
+ generateEnumerableNameFromToken(
215
+ existsLambdaParamNames,
216
+ filterState.lambdaParameterName,
217
+ ),
218
+ );
219
+ assertTrue(currentParamNameIndex === existsLambdaParamNames.length - 1);
220
+ }
221
+ existsLambdaPropertyChains.push(
222
+ new VariableExpression(
223
+ existsLambdaParamNames[currentParamNameIndex] as string,
224
+ Multiplicity.ONE,
225
+ ),
226
+ );
227
+ currentParamNameIndex++;
228
+ }
229
+ }
230
+ let parentNode: QueryBuilderFilterTreeOperationNodeData | undefined =
231
+ undefined;
232
+ if (targetDropNode) {
233
+ if (targetDropNode instanceof QueryBuilderFilterTreeExistsNodeData) {
234
+ // Here we check if the target drop node is an exists tree node, if it is
235
+ // then we try to check in the lambda property chains that it contains the
236
+ // property expression of the target drop node. If we find any lambda property
237
+ // chain we just create exists filter for the rest of the property and add it to the
238
+ // target drop exists node, otherwise we just create exists for all the property chains.
239
+ // For example if we find property chain we create
240
+ // employees->exists($x.name == 'Bob' && $x.id == '1') instead of creating
241
+ // employess->exists($x.name == 'Bob) && employess->exists($x.id == '1')
242
+ const parentPropertyChainIndex = existsLambdaPropertyChains.findIndex(
243
+ (p) =>
244
+ p instanceof AbstractPropertyExpression &&
245
+ p.func.value === targetDropNode.propertyExpression.func.value &&
246
+ p.func.ownerReference.value.path ===
247
+ targetDropNode.propertyExpression.func.ownerReference.value.path,
248
+ );
249
+ if (parentPropertyChainIndex >= 0) {
250
+ parentNode = targetDropNode;
251
+ existsLambdaPropertyChains = existsLambdaPropertyChains.slice(
252
+ parentPropertyChainIndex + 1,
253
+ );
254
+ existsLambdaParamNames = existsLambdaParamNames.slice(
255
+ parentPropertyChainIndex + 1,
256
+ );
257
+ }
258
+ } else {
259
+ // Here the target drop node is a group operation tree node. So we try to find if there is
260
+ // any exists tree parent node of the target drop node. If there is any exists parent node,
261
+ // we try to check in the lambda property chains that it contains the
262
+ // property expression of the target drop node. If we find any lambda property
263
+ // chain we just create exists filter for the rest of the property and add it to the
264
+ // target drop exists node, otherwise we just create exists for all the property chains.
265
+ // For example if we find property chain we create
266
+ // employees->exists($x.name == 'Bob' && $x.id == '1' && $x.age == '30) instead of creating
267
+ // employess->exists($x.name == 'Bob) && employess->exists($x.id == '1') && employess->exists($x.age == '30')
268
+ let cn: QueryBuilderFilterTreeNodeData | undefined = targetDropNode;
269
+ let parentId = targetDropNode.parentId;
270
+ while (
271
+ parentId &&
272
+ !(cn instanceof QueryBuilderFilterTreeExistsNodeData)
273
+ ) {
274
+ cn = filterState.nodes.get(parentId);
275
+ parentId = cn?.parentId;
276
+ }
277
+ if (cn instanceof QueryBuilderFilterTreeExistsNodeData) {
278
+ const parentPropertyChainIndex = existsLambdaPropertyChains.findIndex(
279
+ (p) =>
280
+ p instanceof AbstractPropertyExpression &&
281
+ cn instanceof QueryBuilderFilterTreeExistsNodeData &&
282
+ p.func.value ===
283
+ guaranteeType(cn, QueryBuilderFilterTreeExistsNodeData)
284
+ .propertyExpression.func.value &&
285
+ p.func.ownerReference.value.path ===
286
+ cn.propertyExpression.func.ownerReference.value.path,
287
+ );
288
+ if (parentPropertyChainIndex >= 0) {
289
+ parentNode = targetDropNode;
290
+ existsLambdaPropertyChains = existsLambdaPropertyChains.slice(
291
+ parentPropertyChainIndex + 1,
292
+ );
293
+ existsLambdaParamNames = existsLambdaParamNames.slice(
294
+ parentPropertyChainIndex + 1,
295
+ );
296
+ }
297
+ } else if (!parentId) {
298
+ parentNode = targetDropNode;
299
+ }
300
+ }
301
+ }
302
+ // 3. Create exists tree node for all the property chains and add them to the filter tree
303
+ for (let i = 0; i < existsLambdaPropertyChains.length - 1; ++i) {
304
+ const existsNode: QueryBuilderFilterTreeExistsNodeData =
305
+ new QueryBuilderFilterTreeExistsNodeData(parentNode?.id);
306
+ existsNode.setPropertyExpression(
307
+ existsLambdaPropertyChains[i] as AbstractPropertyExpression,
308
+ );
309
+ existsNode.lambdaParameterName = existsLambdaParamNames[i];
310
+ filterState.nodes.set(existsNode.id, existsNode);
311
+ filterState.addNodeFromNode(existsNode, parentNode);
312
+ parentNode = existsNode;
313
+ }
314
+
315
+ // create the filter condition tree node data
316
+ const filterConditionState = new FilterConditionState(
317
+ filterState,
318
+ existsLambdaPropertyChains[
319
+ existsLambdaPropertyChains.length - 1
320
+ ] as AbstractPropertyExpression,
321
+ );
322
+ const treeNode = new QueryBuilderFilterTreeConditionNodeData(
323
+ undefined,
324
+ filterConditionState,
325
+ );
326
+ filterState.addNodeFromNode(treeNode, parentNode);
327
+ };
328
+
329
+ /**
330
+ * This function builds the filter tree when we DnD a node to the filter panel.
331
+ */
332
+ const buildFilterTree = (
333
+ propertyExpression: AbstractPropertyExpression,
334
+ filterState: QueryBuilderFilterState,
335
+ targetDropNode?: QueryBuilderFilterTreeOperationNodeData | undefined,
336
+ ): void => {
337
+ if (isCollectionProperty(propertyExpression)) {
338
+ const propertyChainName = getPropertyChainName(
339
+ propertyExpression,
340
+ filterState.queryBuilderState.explorerState.humanizePropertyName,
341
+ );
342
+ filterState.queryBuilderState.applicationStore.alertService.setActionAlertInfo(
343
+ {
344
+ message: `The property '${propertyChainName}' is a collection. As you are trying to filter on a collection, the filter created will be an exist filter. e.g. There exists at least one '${propertyChainName}' where 'Type' is the value specified.
345
+ If you are looking to create the filter where all values in this collection equal the value specified, rather than at least one value, consider creating post filter instead.`,
346
+ type: ActionAlertType.CAUTION,
347
+ actions: [
348
+ {
349
+ label: 'Cancel',
350
+ type: ActionAlertActionType.PROCEED_WITH_CAUTION,
351
+ default: true,
352
+ },
353
+ {
354
+ label: 'Proceed',
355
+ type: ActionAlertActionType.PROCEED,
356
+ handler:
357
+ filterState.queryBuilderState.applicationStore.guardUnhandledError(
358
+ async () =>
359
+ buildFilterTreeWithExists(
360
+ propertyExpression,
361
+ filterState,
362
+ targetDropNode,
363
+ ),
364
+ ),
365
+ },
366
+ ],
367
+ },
368
+ );
369
+ } else {
370
+ const filterConditionState = new FilterConditionState(
371
+ filterState,
372
+ propertyExpression,
373
+ );
374
+ const treeNode = new QueryBuilderFilterTreeConditionNodeData(
375
+ undefined,
376
+ filterConditionState,
377
+ );
378
+ filterState.addNodeFromNode(
379
+ treeNode,
380
+ targetDropNode instanceof QueryBuilderFilterTreeGroupNodeData
381
+ ? targetDropNode
382
+ : undefined,
383
+ );
384
+ }
385
+ };
90
386
 
91
387
  export const IS_DRAGGABLE_FILTER_DND_TYPES_FETCH_SUPPORTED = [
92
388
  QUERY_BUILDER_FILTER_DND_TYPE.CONDITION,
@@ -152,6 +448,42 @@ const QueryBuilderFilterGroupConditionEditor = observer(
152
448
  },
153
449
  );
154
450
 
451
+ const QueryBuilderFilterExistsConditionEditor = observer(
452
+ (props: {
453
+ node: QueryBuilderFilterTreeExistsNodeData;
454
+ humanizePropertyName: boolean;
455
+ isDragOver: boolean;
456
+ isDroppable: boolean;
457
+ }) => {
458
+ const { node, humanizePropertyName, isDragOver, isDroppable } = props;
459
+
460
+ return (
461
+ <div className="query-builder-filter-tree__node__label__content dnd__entry__container">
462
+ <PanelEntryDropZonePlaceholder
463
+ isDragOver={isDragOver}
464
+ isDroppable={isDroppable}
465
+ label={`Add to Exists Group`}
466
+ >
467
+ <div
468
+ className="query-builder-filter-tree__exists-node"
469
+ title="This is an exists filter on the collection property"
470
+ >
471
+ <div className="query-builder-filter-tree__exists-node__label">
472
+ {getPropertyChainName(
473
+ node.propertyExpression,
474
+ humanizePropertyName,
475
+ )}
476
+ </div>
477
+ <div className="query-builder-filter-tree__exists-node__exists--label">
478
+ exists
479
+ </div>
480
+ </div>
481
+ </PanelEntryDropZonePlaceholder>
482
+ </div>
483
+ );
484
+ },
485
+ );
486
+
155
487
  const QueryBuilderFilterConditionEditor = observer(
156
488
  (props: {
157
489
  node: QueryBuilderFilterTreeConditionNodeData;
@@ -401,7 +733,8 @@ const QueryBuilderFilterTreeNodeContainer = observer(
401
733
  useState(false);
402
734
  const applicationStore = useApplicationStore();
403
735
  const filterState = queryBuilderState.filterState;
404
- const isExpandable = node instanceof QueryBuilderFilterTreeGroupNodeData;
736
+ const isExpandable =
737
+ node instanceof QueryBuilderFilterTreeOperationNodeData;
405
738
  const selectNode = (): void => onNodeSelect?.(node);
406
739
  const toggleExpandNode = (): void => node.setIsOpen(!node.isOpen);
407
740
  const removeNode = (): void => filterState.removeNodeAndPruneBranch(node);
@@ -469,12 +802,10 @@ const QueryBuilderFilterTreeNodeContainer = observer(
469
802
  applicationStore.notificationService.notifyWarning(error.message);
470
803
  return;
471
804
  }
472
- if (node instanceof QueryBuilderFilterTreeGroupNodeData) {
473
- filterState.addNodeFromNode(
474
- new QueryBuilderFilterTreeConditionNodeData(
475
- undefined,
476
- filterConditionState,
477
- ),
805
+ if (node instanceof QueryBuilderFilterTreeOperationNodeData) {
806
+ buildFilterTree(
807
+ filterConditionState.propertyExpressionState.propertyExpression,
808
+ filterState,
478
809
  node,
479
810
  );
480
811
  } else if (node instanceof QueryBuilderFilterTreeConditionNodeData) {
@@ -609,6 +940,17 @@ const QueryBuilderFilterTreeNodeContainer = observer(
609
940
  isDragOver={isDragOver}
610
941
  />
611
942
  )}
943
+ {node instanceof QueryBuilderFilterTreeExistsNodeData && (
944
+ <QueryBuilderFilterExistsConditionEditor
945
+ node={node}
946
+ humanizePropertyName={
947
+ filterState.queryBuilderState.explorerState
948
+ .humanizePropertyName
949
+ }
950
+ isDroppable={isDroppable}
951
+ isDragOver={isDragOver}
952
+ />
953
+ )}
612
954
  {node instanceof QueryBuilderFilterTreeConditionNodeData && (
613
955
  <QueryBuilderFilterConditionEditor
614
956
  node={node}
@@ -697,7 +1039,7 @@ const QueryBuilderFilterTree = observer(
697
1039
  const getChildNodes = (
698
1040
  node: QueryBuilderFilterTreeNodeData,
699
1041
  ): QueryBuilderFilterTreeNodeData[] =>
700
- node instanceof QueryBuilderFilterTreeGroupNodeData
1042
+ node instanceof QueryBuilderFilterTreeOperationNodeData
701
1043
  ? node.childrenIds.map((id) => filterState.getNode(id))
702
1044
  : [];
703
1045
  return (
@@ -801,7 +1143,6 @@ export const QueryBuilderFilterPanel = observer(
801
1143
  // Drag and Drop
802
1144
  const handleDrop = useCallback(
803
1145
  (item: QueryBuilderFilterDropTarget, type: string): void => {
804
- let filterConditionState: FilterConditionState;
805
1146
  try {
806
1147
  let propertyExpression;
807
1148
  if (type === QUERY_BUILDER_PROJECTION_COLUMN_DND_TYPE) {
@@ -825,24 +1166,14 @@ export const QueryBuilderFilterPanel = observer(
825
1166
  filterState.queryBuilderState.explorerState,
826
1167
  );
827
1168
  }
828
- filterConditionState = new FilterConditionState(
829
- filterState,
830
- propertyExpression,
831
- );
1169
+ // NOTE: unfocus the current node when DnD a new node to the tree
1170
+ filterState.setSelectedNode(undefined);
1171
+ buildFilterTree(propertyExpression, filterState);
832
1172
  } catch (error) {
833
1173
  assertErrorThrown(error);
834
1174
  applicationStore.notificationService.notifyWarning(error.message);
835
1175
  return;
836
1176
  }
837
- // NOTE: unfocus the current node when DnD a new node to the tree
838
- filterState.setSelectedNode(undefined);
839
- filterState.addNodeFromNode(
840
- new QueryBuilderFilterTreeConditionNodeData(
841
- undefined,
842
- filterConditionState,
843
- ),
844
- undefined,
845
- );
846
1177
  },
847
1178
  [applicationStore, filterState],
848
1179
  );
@@ -84,6 +84,7 @@ export enum QUERY_BUILDER_STATE_HASH_STRUCTURE {
84
84
 
85
85
  // filter state
86
86
  FILTER_TREE_GROUP_NODE_DATA = 'FILTER_TREE_GROUP_NODE_DATA',
87
+ FILTER_TREE_EXISTS_NODE_DATA = 'FILTER_TREE_EXISTS_NODE_DATA',
87
88
  FILTER_TREE_CONDIITION_NODE_DATA = 'FILTER_TREE_CONDITION_NODE_DATA',
88
89
  FILTER_TREE_BLANK_CONDITION_NODE_DATA = 'FILTER_TREE_BLANK_CONDITION_NODE_DATA',
89
90
  FILTER_CONDITION_STATE = 'FILTER_CONDITION_STATE',
@@ -43,6 +43,7 @@ export abstract class QueryBuilderFilterOperator implements Hashable {
43
43
 
44
44
  abstract buildFilterConditionExpression(
45
45
  filterConditionState: FilterConditionState,
46
+ lambdaParameterName?: string | undefined,
46
47
  ): ValueSpecification;
47
48
 
48
49
  abstract buildFilterConditionState(