@finos/legend-query-builder 4.1.14 → 4.2.0

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 (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 +17 -1
  6. package/lib/index.css.map +1 -1
  7. package/lib/package.json +6 -6
  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 +14 -14
  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.14",
3
+ "version": "4.2.0",
4
4
  "description": "Legend query builder core",
5
5
  "keywords": [
6
6
  "legend",
@@ -42,17 +42,17 @@
42
42
  "test:watch": "jest --watch"
43
43
  },
44
44
  "dependencies": {
45
- "@finos/legend-application": "15.0.35",
46
- "@finos/legend-art": "7.0.35",
47
- "@finos/legend-graph": "31.0.3",
48
- "@finos/legend-lego": "1.1.14",
49
- "@finos/legend-server-depot": "6.0.22",
50
- "@finos/legend-shared": "10.0.21",
51
- "@finos/legend-storage": "3.0.71",
45
+ "@finos/legend-application": "15.0.36",
46
+ "@finos/legend-art": "7.0.36",
47
+ "@finos/legend-graph": "31.0.4",
48
+ "@finos/legend-lego": "1.1.15",
49
+ "@finos/legend-server-depot": "6.0.23",
50
+ "@finos/legend-shared": "10.0.22",
51
+ "@finos/legend-storage": "3.0.72",
52
52
  "@testing-library/react": "14.0.0",
53
- "@types/react": "18.2.16",
53
+ "@types/react": "18.2.17",
54
54
  "@types/react-dom": "18.2.7",
55
- "chart.js": "4.3.1",
55
+ "chart.js": "4.3.2",
56
56
  "mathjs": "11.9.1",
57
57
  "mobx": "6.10.0",
58
58
  "mobx-react-lite": "3.4.3",
@@ -65,11 +65,11 @@
65
65
  "sql-formatter": "12.2.4"
66
66
  },
67
67
  "devDependencies": {
68
- "@finos/legend-dev-utils": "2.0.75",
69
- "@jest/globals": "29.6.1",
68
+ "@finos/legend-dev-utils": "2.0.76",
69
+ "@jest/globals": "29.6.2",
70
70
  "cross-env": "7.0.3",
71
- "eslint": "8.45.0",
72
- "jest": "29.6.1",
71
+ "eslint": "8.46.0",
72
+ "jest": "29.6.2",
73
73
  "npm-run-all": "4.1.5",
74
74
  "rimraf": "5.0.1",
75
75
  "sass": "1.64.1",
@@ -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(