Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3503623014
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +185,145 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, false, false);
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, true, false);
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+
+// Handle NOT with AND/OR: Apply De Morgan's Law by negating
and flipping the operator
+if (innerFilter.getCondition() == FilterCondition.AND ||
innerFilter.getCondition() == FilterCondition.OR) {
+// NOT (A AND B) = NOT A OR NOT B, NOT (A OR B) = NOT A
AND NOT B
+boolean flipToAnd = innerFilter.getCondition() ==
FilterCondition.OR;
+return buildGroupedPredicate(innerFilter, root, builder,
criteriaQuery, flipToAnd, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute()) &&
+(innerFilter.getCondition() ==
FilterCondition.CONTAINS ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ALL ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ANY ||
+innerFilter.getCondition() ==
FilterCondition.EQUAL)) {
+return buildCollectionPredicate(innerFilter, root,
builder, criteriaQuery, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute())) {
+return buildNegatedCollectionPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Builds predicates for AND/OR, grouping CONTAINS filters on same
collection into single EXISTS.
+ *
+ * @param isNegated if true, negates each predicate (for NOT operations
with De Morgan's Law)
+ */
+private Predicate buildGroupedPredicate(AttributeFilter filter, Root
root,
+CriteriaBuilder builder, CriteriaQuery criteriaQuery, boolean
isAnd, boolean isNegated) {
+
+List> nestedFilters = (List>)
filter.getValue();
+
+java.util.Map>> groups = new
java.util.HashMap<>();
+List otherPredicates = new ArrayList<>();
+
+for (AttributeFilter f : nestedFilters) {
+if (f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute()) &&
+(f.getCondition() == FilterCondition.CONTAINS ||
+f.getCondition() == FilterCondition.CONTAINS_ALL ||
+f.getCondition() == FilterCondition.CONTAINS_ANY ||
+f.getCondition() == FilterCondition.EQUAL)) {
+String collection = f.getAttribute().split("\\.")[0];
+groups.computeIfAbsent(collection, k -> new
ArrayList<>()).add(f);
+} else {
+Predicate pred = filterPredicateFunction(root, builder,
criteriaQuery).apply(f);
+otherPredicates.add(isNegated ? builder.not(pred) : pred);
+}
+}
+
+List allPredicates = new ArrayList<>(otherPredicates);
+for (List> groupFilters : groups.values()) {
+if (groupFilters.size() == 1) {
+
allPredicates.add(buildCollectionPredicate(groupFilters.get(0), root, builder,
criteriaQuery, isNegated));
+} else {
+
allPredicates.add(buildMultiFilterCollectionPredicate(groupFilters, root,
builder, criteriaQuery, isAnd, isNegated));
+}
Review Comment:
buildMultiFilterCollectionPredicate is reusable when groupFilters.size() ==
1 hence refactored
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubsc
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3503616755
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +185,145 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, false, false);
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, true, false);
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+
+// Handle NOT with AND/OR: Apply De Morgan's Law by negating
and flipping the operator
+if (innerFilter.getCondition() == FilterCondition.AND ||
innerFilter.getCondition() == FilterCondition.OR) {
+// NOT (A AND B) = NOT A OR NOT B, NOT (A OR B) = NOT A
AND NOT B
+boolean flipToAnd = innerFilter.getCondition() ==
FilterCondition.OR;
+return buildGroupedPredicate(innerFilter, root, builder,
criteriaQuery, flipToAnd, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute()) &&
+(innerFilter.getCondition() ==
FilterCondition.CONTAINS ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ALL ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ANY ||
+innerFilter.getCondition() ==
FilterCondition.EQUAL)) {
+return buildCollectionPredicate(innerFilter, root,
builder, criteriaQuery, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute())) {
+return buildNegatedCollectionPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Builds predicates for AND/OR, grouping CONTAINS filters on same
collection into single EXISTS.
+ *
+ * @param isNegated if true, negates each predicate (for NOT operations
with De Morgan's Law)
+ */
+private Predicate buildGroupedPredicate(AttributeFilter filter, Root
root,
+CriteriaBuilder builder, CriteriaQuery criteriaQuery, boolean
isAnd, boolean isNegated) {
+
+List> nestedFilters = (List>)
filter.getValue();
+
+java.util.Map>> groups = new
java.util.HashMap<>();
+List otherPredicates = new ArrayList<>();
+
+for (AttributeFilter f : nestedFilters) {
+if (f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute()) &&
+(f.getCondition() == FilterCondition.CONTAINS ||
Review Comment:
comment addressed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3503615603
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +185,145 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, false, false);
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, true, false);
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+
+// Handle NOT with AND/OR: Apply De Morgan's Law by negating
and flipping the operator
+if (innerFilter.getCondition() == FilterCondition.AND ||
innerFilter.getCondition() == FilterCondition.OR) {
+// NOT (A AND B) = NOT A OR NOT B, NOT (A OR B) = NOT A
AND NOT B
+boolean flipToAnd = innerFilter.getCondition() ==
FilterCondition.OR;
+return buildGroupedPredicate(innerFilter, root, builder,
criteriaQuery, flipToAnd, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute()) &&
+(innerFilter.getCondition() ==
FilterCondition.CONTAINS ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ALL ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ANY ||
+innerFilter.getCondition() ==
FilterCondition.EQUAL)) {
+return buildCollectionPredicate(innerFilter, root,
builder, criteriaQuery, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute())) {
+return buildNegatedCollectionPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Builds predicates for AND/OR, grouping CONTAINS filters on same
collection into single EXISTS.
+ *
+ * @param isNegated if true, negates each predicate (for NOT operations
with De Morgan's Law)
+ */
+private Predicate buildGroupedPredicate(AttributeFilter filter, Root
root,
+CriteriaBuilder builder, CriteriaQuery criteriaQuery, boolean
isAnd, boolean isNegated) {
+
+List> nestedFilters = (List>)
filter.getValue();
+
+java.util.Map>> groups = new
java.util.HashMap<>();
+List otherPredicates = new ArrayList<>();
+
+for (AttributeFilter f : nestedFilters) {
+if (f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute()) &&
+(f.getCondition() == FilterCondition.CONTAINS ||
+f.getCondition() == FilterCondition.CONTAINS_ALL ||
+f.getCondition() == FilterCondition.CONTAINS_ANY ||
+f.getCondition() == FilterCondition.EQUAL)) {
+String collection = f.getAttribute().split("\\.")[0];
+groups.computeIfAbsent(collection, k -> new
ArrayList<>()).add(f);
+} else {
+Predicate pred = filterPredicateFunction(root, builder,
criteriaQuery).apply(f);
+otherPredicates.add(isNegated ? builder.not(pred) : pred);
+}
+}
+
+List allPredicates = new ArrayList<>(otherPredicates);
+for (List> groupFilters : groups.values()) {
+if (groupFilters.size() == 1) {
+
allPredicates.add(buildCollectionPredicate(groupFilters.get(0), root, builder,
criteriaQuery, isNegated));
+} else {
+
allPredicates.add(buildMultiFilterCollectionPredicate(groupFilters, root,
builder, criteriaQuery, isAnd, isNegated));
+}
+}
+
+return isAnd ? builder.and(allPredicates.toArray(new Predicate[0]))
+: builder.or(allPredicates.toArray(new Predicate[0]));
+}
+
+/**
+ * Builds single EXISTS with HAVING for multiple CONTAINS filters on same
collection.
+ * Example: [{ comm
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3497381235
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +185,145 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, false, false);
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, true, false);
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+
+// Handle NOT with AND/OR: Apply De Morgan's Law by negating
and flipping the operator
+if (innerFilter.getCondition() == FilterCondition.AND ||
innerFilter.getCondition() == FilterCondition.OR) {
+// NOT (A AND B) = NOT A OR NOT B, NOT (A OR B) = NOT A
AND NOT B
+boolean flipToAnd = innerFilter.getCondition() ==
FilterCondition.OR;
+return buildGroupedPredicate(innerFilter, root, builder,
criteriaQuery, flipToAnd, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute()) &&
+(innerFilter.getCondition() ==
FilterCondition.CONTAINS ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ALL ||
+innerFilter.getCondition() ==
FilterCondition.CONTAINS_ANY ||
+innerFilter.getCondition() ==
FilterCondition.EQUAL)) {
+return buildCollectionPredicate(innerFilter, root,
builder, criteriaQuery, true);
+}
+
+if (innerFilter.getAttribute() != null &&
isCollectionAttribute(innerFilter.getAttribute())) {
+return buildNegatedCollectionPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Builds predicates for AND/OR, grouping CONTAINS filters on same
collection into single EXISTS.
+ *
+ * @param isNegated if true, negates each predicate (for NOT operations
with De Morgan's Law)
+ */
+private Predicate buildGroupedPredicate(AttributeFilter filter, Root
root,
+CriteriaBuilder builder, CriteriaQuery criteriaQuery, boolean
isAnd, boolean isNegated) {
+
+List> nestedFilters = (List>)
filter.getValue();
+
+java.util.Map>> groups = new
java.util.HashMap<>();
+List otherPredicates = new ArrayList<>();
+
+for (AttributeFilter f : nestedFilters) {
+if (f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute()) &&
+(f.getCondition() == FilterCondition.CONTAINS ||
Review Comment:
the same if condition is in the method above too, so I'd say it's a
categorization that we want to have explicitly defined in a single place.
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +185,145 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, false, false);
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return buildGroupedPredicate(filter, root, builder,
criteriaQuery, true, false);
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+
+// Handle NOT with AND/OR: Apply De Morgan's Law by negating
and flipping the operator
+
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3490615001
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +224,206 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operation that needs special
handling
+// Collection operations have attributes like "nodes.name" or
use CONTAINS/CONTAINS_ALL/CONTAINS_ANY
+if (isCollectionNotOperation(innerFilter)) {
+return buildCollectionNotPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Checks if the filter is a collection operation (CONTAINS, CONTAINS_ALL,
CONTAINS_ANY)
+ * or contains collection attributes that require special NOT handling
with subqueries.
+ */
+private boolean isCollectionNotOperation(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY -> true;
+case AND, OR -> {
+// Check if any nested filter is a collection operation or has
collection attribute
+List> nestedFilters =
(List>) filter.getValue();
+yield nestedFilters.stream().anyMatch(f ->
isCollectionNotOperation(f) ||
+(f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute(;
+}
+case NOT -> isCollectionNotOperation((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// Check if this filter has a collection attribute (e.g.,
"nodes.name")
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+/**
+ * Builds a NOT EXISTS subquery predicate for collection operations.
+ * This ensures entity-level negation instead of per-row negation.
+ */
+private Predicate buildCollectionNotPredicate(AttributeFilter filter,
Root root,
Review Comment:
comment addressed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3490611800
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
This has been addressed as discussed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3378886355
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
This is all new code, right? so the fact it was not correctly translating
query into SQL might be because of that and the actual logic. I expect that if
we try to merge into a single subquery, we need to properly adjust logic in the
inner scope of the logical condition. In your example, we'd basically need `NOT
EXISTS(... JOIN c1_0 ... JOIN c2_0 WHERE (c1_0.id=? AND c2_0.id=?)` ? instead
of OR?
I'd be interested in the resulting SQL Query plan really - if we merge
EXISTS clauses into one or we combine several using AND/OR - would it be
possible to attempt run EXPLAIN to get the query resulting query plans? If you
have such queries from previous runs then you could use those, if not perhaps
you could "simulate" only the SQL query without the need to actually implement
the changes just to test SQL query plan.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3378843363
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
@martinweiler I meant it the other way round actually - should we care about
backward compatibility if the behavior was not correct? It brings increased
complexity to code we need to maintain - all those extra fields below.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3372256829
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
@jstastny-cz I tried merging multiple NOT EXISTS subqueries into a single
one but was causing issues with NOT CONTAINS ALL and was giving unexpected
response for example:
Task has comment with id 243672d5-44a2-423b-901e-1f4130373ebe
Query: NOT CONTAINS_ALL [243672d5-44a2-423b-901e-1f4130373ebe,
another-comment-id]
Expected: Return task (missing another-comment-id)
this was resulting in a query : `NOT EXISTS(... JOIN c1_0 ... JOIN c2_0
WHERE (c1_0.id=? OR c2_0.id=?))` which return Returns 0 items hence followed
`NOT EXISTS(... JOIN c1_0 WHERE c1_0.id=?) OR NOT EXISTS(... JOIN c2_0 WHERE
c2_0.id=?)` and for maintaining the uniformity kept the same for other NOT
conditions and for positive cases the current implementation reuses the
existing getAttributePath(). Let me know if you have more thoughts
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
martinweiler commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3364348900
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
@jstastny-cz as @GopikaReghunath showed in the example, the fallback to
`containsAny` is how such queries were handled prior to this PR. The new
operators give you more control, but I wouldn't say they are "forcing" you into
anything.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r253902
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
of course, perhaps if the joins are between various tables in each
condition, it needs to be a separate subquery.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r246135
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
Right, there are 2 subselects each joining the same tables - why not to
merge the conditions into a single EXISTS query? And adjust AND/OR combinations
accordingly.
Would be
`SELECT ... FROM processes p_0 WHERE NOT EXISTS (SELECT 1 FROM processes p1
JOIN nodes n1 ON p1.id=n1.id WHERE p_0.id=p1.id AND (n1.name='Technical
Interview' OR n1.type='ErrorNode`)`?
so in simplified WHERE NOT EXISTS (both conditions via OR), similarly for
positive filters, why isn't it the same approach, also single subselect (which
results in the join anyways) and then combine the filters in a single set.
Having said that - can't actually be both positive and negative filters
handled within the same single subquery using EXISTS by just inverting the
conditions for the negative?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3325142381
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
Review Comment:
distinct is not need misunderstood on queries like the below:
```
ProcessInstances(
where: {
nodes: {
type: { equal: "ActionNode" }
}
}
)
```
when there are multiple nodes with same type works
Retested the scenario after removing distinct and is is working as expected
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3324101250
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +224,206 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operation that needs special
handling
+// Collection operations have attributes like "nodes.name" or
use CONTAINS/CONTAINS_ALL/CONTAINS_ANY
+if (isCollectionNotOperation(innerFilter)) {
+return buildCollectionNotPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Checks if the filter is a collection operation (CONTAINS, CONTAINS_ALL,
CONTAINS_ANY)
+ * or contains collection attributes that require special NOT handling
with subqueries.
+ */
+private boolean isCollectionNotOperation(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY -> true;
+case AND, OR -> {
+// Check if any nested filter is a collection operation or has
collection attribute
+List> nestedFilters =
(List>) filter.getValue();
+yield nestedFilters.stream().anyMatch(f ->
isCollectionNotOperation(f) ||
+(f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute(;
+}
+case NOT -> isCollectionNotOperation((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// Check if this filter has a collection attribute (e.g.,
"nodes.name")
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+/**
+ * Builds a NOT EXISTS subquery predicate for collection operations.
+ * This ensures entity-level negation instead of per-row negation.
+ */
+private Predicate buildCollectionNotPredicate(AttributeFilter filter,
Root root,
Review Comment:
The handling was added to ensures entity-level negation, rather than
incorrectly negating at the row level. Simply wrapping a positive predicate
with NOT would fail because it would negate individual JOIN results rather than
checking entity-level existence.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3324101250
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +224,206 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operation that needs special
handling
+// Collection operations have attributes like "nodes.name" or
use CONTAINS/CONTAINS_ALL/CONTAINS_ANY
+if (isCollectionNotOperation(innerFilter)) {
+return buildCollectionNotPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Checks if the filter is a collection operation (CONTAINS, CONTAINS_ALL,
CONTAINS_ANY)
+ * or contains collection attributes that require special NOT handling
with subqueries.
+ */
+private boolean isCollectionNotOperation(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY -> true;
+case AND, OR -> {
+// Check if any nested filter is a collection operation or has
collection attribute
+List> nestedFilters =
(List>) filter.getValue();
+yield nestedFilters.stream().anyMatch(f ->
isCollectionNotOperation(f) ||
+(f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute(;
+}
+case NOT -> isCollectionNotOperation((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// Check if this filter has a collection attribute (e.g.,
"nodes.name")
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+/**
+ * Builds a NOT EXISTS subquery predicate for collection operations.
+ * This ensures entity-level negation instead of per-row negation.
+ */
+private Predicate buildCollectionNotPredicate(AttributeFilter filter,
Root root,
Review Comment:
The handling was added to ensures entity-level negation: the entity is
excluded if it has ANY matching element, rather than incorrectly negating at
the row level. Simply wrapping a positive predicate with NOT would fail because
it would negate individual JOIN results rather than checking entity-level
existence.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3324034539
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
Multiple filters on array/collection attributes are always combined with AND
logic, ensuring only top-level entities matching ALL conditions are returned.
Positive filters (CONTAINS, EQUAL) use INNER JOINs, while NOT operations use
NOT EXISTS subqueries for proper entity-level negation. For example, the query
filtering not: { nodes: { name: { equal: "Technical Interview" } } } and not: {
nodes: { type: { equal: "ErrorNode" } } } generates:
`SELECT pie1_0.*
FROM processes pie1_0
WHERE NOT EXISTS (
SELECT 1 FROM processes pie2_0
JOIN nodes n1_0 ON pie2_0.id = n1_0.process_instance_id
WHERE pie2_0.id = pie1_0.id AND n1_0.name = 'Technical Interview'
)
AND NOT EXISTS (
SELECT 1 FROM processes pie3_0
JOIN nodes n2_0 ON pie3_0.id = n2_0.process_instance_id
WHERE pie3_0.id = pie1_0.id AND n2_0.type = 'ErrorNode'
);
`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3311373870
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
Review Comment:
When/why do we need distinct at all?
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -110,23 +121,80 @@ public List execute() {
return (List)
query.getResultList().stream().map(mapper).collect(toList());
}
-protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder) {
+/**
+ * Determines if DISTINCT is needed based on whether the query uses
collection attributes
+ * that would result in JOINs and potential duplicate rows.
+ */
+private boolean needsDistinct() {
+if (filters == null || filters.isEmpty()) {
+return false;
+}
+return filters.stream().anyMatch(this::filterNeedsDistinct);
+}
+
+/**
+ * Recursively checks if a filter needs DISTINCT due to collection
operations or attributes.
+ */
+private boolean filterNeedsDistinct(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY ->
+// Collection operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+case AND, OR -> {
+// Check nested filters recursively
+List> nestedFilters =
(List>) filter.getValue();
+yield
nestedFilters.stream().anyMatch(this::filterNeedsDistinct);
+}
+case NOT -> filterNeedsDistinct((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// These operations on collection attributes need DISTINCT
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+protected Function, Predicate>
filterPredicateFunction(Root root, CriteriaBuilder builder, CriteriaQuery
criteriaQuery) {
return filter -> jsonPredicateBuilder.filter(b ->
filter.isJson()).map(b -> b.buildPredicate(filter, root, builder))
-.orElseGet(() -> buildPredicateFunction(filter, root,
builder));
+.orElseGet(() -> buildPredicateFunction(filter, root, builder,
criteriaQuery));
}
-protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder) {
+protected final Predicate buildPredicateFunction(AttributeFilter filter,
Root root, CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
Review Comment:
so basically, each array attribute results in a separate WHERE EXISTS
(SELECT 1 FROM ... now the comparisons) ...
How are these EXISTS subqueries joined, is that an OR or AND?
If single query contains filters on multiple array arguments - they should
be evaluated together, returning only top-level items matching all conditions -
is that the case already?
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +224,206 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operat
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3287209796
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
So basically defaults to `containsAny`? IDK really, if "forcing" users to
use proper operators now is a problem, @martinweiler wdyt?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3287118998
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +224,206 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operation that needs special
handling
+// Collection operations have attributes like "nodes.name" or
use CONTAINS/CONTAINS_ALL/CONTAINS_ANY
+if (isCollectionNotOperation(innerFilter)) {
+return buildCollectionNotPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Checks if the filter is a collection operation (CONTAINS, CONTAINS_ALL,
CONTAINS_ANY)
+ * or contains collection attributes that require special NOT handling
with subqueries.
+ */
+private boolean isCollectionNotOperation(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY -> true;
+case AND, OR -> {
+// Check if any nested filter is a collection operation or has
collection attribute
+List> nestedFilters =
(List>) filter.getValue();
+yield nestedFilters.stream().anyMatch(f ->
isCollectionNotOperation(f) ||
+(f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute(;
+}
+case NOT -> isCollectionNotOperation((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// Check if this filter has a collection attribute (e.g.,
"nodes.name")
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+/**
+ * Builds a NOT EXISTS subquery predicate for collection operations.
+ * This ensures entity-level negation instead of per-row negation.
+ */
+private Predicate buildCollectionNotPredicate(AttributeFilter filter,
Root root,
+CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
+
+// Special handling for AND/OR: Apply De Morgan's Law
+// NOT (A AND B) = NOT A OR NOT B
+// NOT (A OR B) = NOT A AND NOT B
+if (filter.getCondition() ==
org.kie.kogito.persistence.api.query.FilterCondition.AND) {
Review Comment:
comment addressed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3279239125
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
allows filtering on array elements without explicitly using the contains,
containsAll, or containsAny operations. Behavior: Returns entities where ANY
array element matches the condition
example:
```
id: "Task-A"
"attachments": [
{
"id": "id-1",
"name": "google"
}
]
id: "Task-B"
"attachments": [
{
"id": "id-2",
"name": "google"
}
]
id: "Task-C"
"attachments": [
{
"id": "id-3",
"name": "comment"
}
]
Query :
UserTaskInstances(
where: {
attachments: {
name: { equal: "google" }
}
}
)
Return Task-A and Task-B
```
@jstastny-cz let me know if you think this is not needed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3279239125
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
allows filtering on array elements without explicitly using the contains,
containsAll, or containsAny operations. Behavior: Returns entities where ANY
array element matches the condition
example:
```
id: "process-A"
nodes: [
{ id: "node-1", name: "Start" },
{ id: "node-2", name: "Task" },
{ id: "node-3", name: "End" }
]
id: "process-B"
nodes: [
{ id: "node-10", name: "Start" },
{ id: "node-20", name: "Task" }
]
Query : {
ProcessInstances(where: { nodes: { id: { equal: "node-2" } } })
}
Process A is RETURNED because at least ONE node (node-2) matched
```
@jstastny-cz let me know if you think this is not needed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3279239125
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
allows filtering on array elements without explicitly using the contains,
containsAll, or containsAny operations. Behavior: Returns entities where ANY
array element matches the condition
example: nodes: { id: { equal: "123" } }
@jstastny-cz let me know if you think this is not needed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3279239125
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
allows filtering on array elements without explicitly using the contains,
containsAll, or containsAny operations.
example: nodes: { id: { equal: "123" } }
@jstastny-cz let me know if you think this is not needed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3279159478
##
data-index/data-index-graphql/src/main/resources/graphql/basic.schema.graphqls:
##
@@ -252,6 +252,28 @@ input NodeInstanceArgument {
slaDueDate: DateArgument
}
+input NodeInstanceArrayArgument {
+# Array-specific operations
+contains: NodeInstanceArgument
+containsAll: [NodeInstanceArgument!]
+containsAny: [NodeInstanceArgument!]
+isNull: Boolean
+and: [NodeInstanceArrayArgument!]
+or: [NodeInstanceArrayArgument!]
+not: NodeInstanceArrayArgument
+# Backward compatibility:
Review Comment:
Backward compatibility of what actually? Do we want to allow queries on
arrays that define nested values, e.g. the id below, or? Would it then pick the
first, all matching, or what are the semantics?
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +224,206 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operation that needs special
handling
+// Collection operations have attributes like "nodes.name" or
use CONTAINS/CONTAINS_ALL/CONTAINS_ANY
+if (isCollectionNotOperation(innerFilter)) {
+return buildCollectionNotPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Checks if the filter is a collection operation (CONTAINS, CONTAINS_ALL,
CONTAINS_ANY)
+ * or contains collection attributes that require special NOT handling
with subqueries.
+ */
+private boolean isCollectionNotOperation(AttributeFilter filter) {
+return switch (filter.getCondition()) {
+case CONTAINS, CONTAINS_ALL, CONTAINS_ANY -> true;
+case AND, OR -> {
+// Check if any nested filter is a collection operation or has
collection attribute
+List> nestedFilters =
(List>) filter.getValue();
+yield nestedFilters.stream().anyMatch(f ->
isCollectionNotOperation(f) ||
+(f.getAttribute() != null &&
isCollectionAttribute(f.getAttribute(;
+}
+case NOT -> isCollectionNotOperation((AttributeFilter)
filter.getValue());
+case EQUAL, LIKE, IN, GT, GTE, LT, LTE, BETWEEN, IS_NULL, NOT_NULL
->
+// Check if this filter has a collection attribute (e.g.,
"nodes.name")
+filter.getAttribute() != null &&
isCollectionAttribute(filter.getAttribute());
+default -> false;
+};
+}
+
+/**
+ * Builds a NOT EXISTS subquery predicate for collection operations.
+ * This ensures entity-level negation instead of per-row negation.
+ */
+private Predicate buildCollectionNotPredicate(AttributeFilter filter,
Root root,
+CriteriaBuilder builder, CriteriaQuery criteriaQuery) {
+
+// Special handling for AND/OR: Apply De Morgan's Law
+// NOT (A AND B) = NOT A OR NOT B
+// NOT (A OR B) = NOT A AND NOT B
+if (filter.getCondition() ==
org.kie.kogito.persistence.api.query.FilterCondition.AND) {
Review Comment:
why isn't this a switch on filter.getCondition() ? Should be possible, right?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: commits
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3241247604
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -82,9 +83,9 @@ public GraphQLQueryParser apply(GraphQLInputObjectType type) {
parser.mapAttribute(field.getName(),
mapEnumArgument(field.getName()));
} else if (isListOfType(field.getType(), type.getName())) {
parser.mapAttribute(field.getName(),
mapRecursiveListArgument(field.getName(), parser));
-} else if (((GraphQLNamedType)
field.getType()).getName().equals(type.getName())) {
+} else if (field.getType() instanceof GraphQLNamedType &&
((GraphQLNamedType) field.getType()).getName().equals(type.getName())) {
parser.mapAttribute(field.getName(),
mapRecursiveArgument(field.getName(), parser));
-} else {
+} else if (field.getType() instanceof GraphQLNamedType) {
String name = ((GraphQLNamedType)
field.getType()).getName();
Review Comment:
changes done as per the comment
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3241248527
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -82,9 +83,9 @@ public GraphQLQueryParser apply(GraphQLInputObjectType type) {
parser.mapAttribute(field.getName(),
mapEnumArgument(field.getName()));
} else if (isListOfType(field.getType(), type.getName())) {
parser.mapAttribute(field.getName(),
mapRecursiveListArgument(field.getName(), parser));
-} else if (((GraphQLNamedType)
field.getType()).getName().equals(type.getName())) {
+} else if (field.getType() instanceof GraphQLNamedType &&
((GraphQLNamedType) field.getType()).getName().equals(type.getName())) {
Review Comment:
changes done as per comment
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3241246268
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -116,7 +117,13 @@ public GraphQLQueryParser apply(GraphQLInputObjectType
type) {
break;
default:
if (field.getType() instanceof
GraphQLInputObjectType) {
-parser.mapAttribute(field.getName(),
mapSubEntityArgument(field.getName(), new
GraphQLQueryMapper().apply((GraphQLInputObjectType) field.getType(;
+GraphQLInputObjectType inputType =
(GraphQLInputObjectType) field.getType();
Review Comment:
comment addressed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3241244605
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -229,6 +236,161 @@ private Function>>
mapSubEntityArgument(String
});
}
+private Function>>
mapArrayArgument(String attribute, GraphQLInputObjectType arrayArgType) {
+return argument -> {
+Map argMap = (Map) argument;
+
+// Get the element type from the "contains" field
+GraphQLInputType containsFieldType =
arrayArgType.getField("contains") != null ?
arrayArgType.getField("contains").getType() : null;
+
+if (containsFieldType == null) {
+LOGGER.warn("Array argument type {} does not have a 'contains'
field", arrayArgType.getName());
+return Stream.empty();
+}
+
+// Unwrap the element type and get the parser for it
+GraphQLType unwrappedType = unwrapNonNull(containsFieldType);
+if (!(unwrappedType instanceof GraphQLInputObjectType)) {
+LOGGER.warn("Contains field type is not an input object type:
{}", simplePrint(unwrappedType));
+return Stream.empty();
+}
+
+GraphQLInputObjectType elementType = (GraphQLInputObjectType)
unwrappedType;
+GraphQLQueryParser elementParser = new
GraphQLQueryMapper().apply(elementType);
+
+// Separate array operations from backward-compatible fields
+Map backwardCompatFields = new
java.util.HashMap<>();
+List>> arrayOpStreams = new
java.util.ArrayList<>();
+
+for (Map.Entry entry : argMap.entrySet()) {
+String operation = entry.getKey();
+Object value = entry.getValue();
Review Comment:
done
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLInputObjectTypeMapper.java:
##
@@ -108,7 +112,20 @@ private GraphQLInputType
getInputTypeByField(GraphQLFieldDefinition field) {
return getInputObjectType("BooleanArgument");
case "DateTime":
return getInputObjectType("DateArgument");
+case "StringArray":
+return getInputObjectType("StringArrayArgument");
default:
+// For array fields, try to use ArrayArgument type first
+if (isArray) {
+String arrayTypeName = name + "ArrayArgument";
Review Comment:
done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3241230916
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -229,6 +236,161 @@ private Function>>
mapSubEntityArgument(String
});
}
+private Function>>
mapArrayArgument(String attribute, GraphQLInputObjectType arrayArgType) {
+return argument -> {
+Map argMap = (Map) argument;
+
+// Get the element type from the "contains" field
Review Comment:
changes done to resolve this
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3241227910
##
data-index/data-index-storage/data-index-storage-jpa-common/src/main/java/org/kie/kogito/index/jpa/storage/JPAQuery.java:
##
@@ -156,17 +238,221 @@ protected final Predicate
buildPredicateFunction(AttributeFilter filter, Root
return builder
.lessThanOrEqualTo(getAttributePath(root,
filter.getAttribute()), (Comparable) filter.getValue());
case OR:
-return builder.or(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.or(getRecursivePredicate(filter, root, builder,
criteriaQuery).toArray(new Predicate[] {}));
case AND:
-return builder.and(getRecursivePredicate(filter, root,
builder).toArray(new Predicate[] {}));
+return builder.and(getRecursivePredicate(filter, root,
builder, criteriaQuery).toArray(new Predicate[] {}));
case NOT:
-return builder.not(filterPredicateFunction(root,
builder).apply((AttributeFilter) filter.getValue()));
+AttributeFilter innerFilter = (AttributeFilter)
filter.getValue();
+// Check if this is a collection operation that needs special
handling
+// Collection operations have attributes like "nodes.name" or
use CONTAINS/CONTAINS_ALL/CONTAINS_ANY
+if (isCollectionNotOperation(innerFilter)) {
+return buildCollectionNotPredicate(innerFilter, root,
builder, criteriaQuery);
+}
+return builder.not(filterPredicateFunction(root, builder,
criteriaQuery).apply(innerFilter));
default:
return null;
}
}
+/**
+ * Checks if the filter is a collection operation (CONTAINS, CONTAINS_ALL,
CONTAINS_ANY)
+ * or contains collection attributes that require special NOT handling
with subqueries.
+ */
+private boolean isCollectionNotOperation(AttributeFilter filter) {
+switch (filter.getCondition()) {
+case CONTAINS:
+case CONTAINS_ALL:
+case CONTAINS_ANY:
+return true;
Review Comment:
comment addressed changes done
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
jstastny-cz commented on code in PR #2328:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2328#discussion_r3234140452
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -82,9 +83,9 @@ public GraphQLQueryParser apply(GraphQLInputObjectType type) {
parser.mapAttribute(field.getName(),
mapEnumArgument(field.getName()));
} else if (isListOfType(field.getType(), type.getName())) {
parser.mapAttribute(field.getName(),
mapRecursiveListArgument(field.getName(), parser));
-} else if (((GraphQLNamedType)
field.getType()).getName().equals(type.getName())) {
+} else if (field.getType() instanceof GraphQLNamedType &&
((GraphQLNamedType) field.getType()).getName().equals(type.getName())) {
parser.mapAttribute(field.getName(),
mapRecursiveArgument(field.getName(), parser));
-} else {
+} else if (field.getType() instanceof GraphQLNamedType) {
String name = ((GraphQLNamedType)
field.getType()).getName();
Review Comment:
```suggestion
} else if (field.getType() instanceof GraphQLNamedType
namedType) {
String name = namedType.getName();
```
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -82,9 +83,9 @@ public GraphQLQueryParser apply(GraphQLInputObjectType type) {
parser.mapAttribute(field.getName(),
mapEnumArgument(field.getName()));
} else if (isListOfType(field.getType(), type.getName())) {
parser.mapAttribute(field.getName(),
mapRecursiveListArgument(field.getName(), parser));
-} else if (((GraphQLNamedType)
field.getType()).getName().equals(type.getName())) {
+} else if (field.getType() instanceof GraphQLNamedType &&
((GraphQLNamedType) field.getType()).getName().equals(type.getName())) {
Review Comment:
wouldn't this work too?
```suggestion
} else if (field.getType() instanceof GraphQLNamedType
namedType && namedType.getName().equals(type.getName())) {
```
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -116,7 +117,13 @@ public GraphQLQueryParser apply(GraphQLInputObjectType
type) {
break;
default:
if (field.getType() instanceof
GraphQLInputObjectType) {
-parser.mapAttribute(field.getName(),
mapSubEntityArgument(field.getName(), new
GraphQLQueryMapper().apply((GraphQLInputObjectType) field.getType(;
+GraphQLInputObjectType inputType =
(GraphQLInputObjectType) field.getType();
Review Comment:
also here we could use pattern matching
##
data-index/data-index-graphql/src/main/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapper.java:
##
@@ -229,6 +236,161 @@ private Function>>
mapSubEntityArgument(String
});
}
+private Function>>
mapArrayArgument(String attribute, GraphQLInputObjectType arrayArgType) {
+return argument -> {
+Map argMap = (Map) argument;
+
+// Get the element type from the "contains" field
+GraphQLInputType containsFieldType =
arrayArgType.getField("contains") != null ?
arrayArgType.getField("contains").getType() : null;
+
+if (containsFieldType == null) {
+LOGGER.warn("Array argument type {} does not have a 'contains'
field", arrayArgType.getName());
+return Stream.empty();
+}
+
+// Unwrap the element type and get the parser for it
+GraphQLType unwrappedType = unwrapNonNull(containsFieldType);
+if (!(unwrappedType instanceof GraphQLInputObjectType)) {
+LOGGER.warn("Contains field type is not an input object type:
{}", simplePrint(unwrappedType));
+return Stream.empty();
+}
+
+GraphQLInputObjectType elementType = (GraphQLInputObjectType)
unwrappedType;
+GraphQLQueryParser elementParser = new
GraphQLQueryMapper().apply(elementType);
+
+// Separate array operations from backward-compatible fields
+Map backwardCompatFields = new
java.util.HashMap<>();
+List>> arrayOpStreams = new
java.util.ArrayList<>();
+
+for (Map.Entry entry : argMap.entrySet()) {
+String operation = entry.getKey();
+Object value = entry.getValue();
Review Comment:
we should use as other methods:
```
FilterCondition condition =
FilterCondition.fromLabel(entry.ge
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath closed pull request #2324: [Incubator-kie-issues#2323] Allow filtering schema on nested arrays URL: https://github.com/apache/incubator-kie-kogito-apps/pull/2324 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] - To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on PR #2324: URL: https://github.com/apache/incubator-kie-kogito-apps/pull/2324#issuecomment-4370450957 closing this due to PR : https://github.com/apache/incubator-kie-kogito-apps/pull/2328 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] - To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on PR #2324: URL: https://github.com/apache/incubator-kie-kogito-apps/pull/2324#issuecomment-4370453098 closing this due to PR : https://github.com/apache/incubator-kie-kogito-apps/pull/2328 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] - To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
GopikaReghunath commented on PR #2328: URL: https://github.com/apache/incubator-kie-kogito-apps/pull/2328#issuecomment-4351734373 > Thanks @GopikaReghunath for the PR, Can you please share the steps how to test this? done -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] - To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
Kusuma04-dev commented on PR #2328: URL: https://github.com/apache/incubator-kie-kogito-apps/pull/2328#issuecomment-4350340444 Hi @GopikaReghunath , Thanks for the PR, Can you please share the steps how to test this? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] - To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
martinweiler commented on PR #2324: URL: https://github.com/apache/incubator-kie-kogito-apps/pull/2324#issuecomment-4297841263 @GopikaReghunath thanks for this PR. I am seeing some local test failures in `ProcessInstanceEntityQueryIT`, could you please take a look? Example: ``` [ERROR] org.kie.kogito.index.postgresql.query.ProcessInstanceEntityQueryIT.testNotContainsAllOnCollection -- Time elapsed: 0.061 s <<< ERROR! org.hibernate.query.SemanticException: Operand of 'member of' operator must be a plural path at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.createSqmMemberOfPredicate(SqmCriteriaNodeBuilder.java:2764) at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.isMember(SqmCriteriaNodeBuilder.java:2746) at org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder.isMember(SqmCriteriaNodeBuilder.java:186) at org.kie.kogito.index.jpa.storage.JPAQuery.buildCollectionNotPredicate(JPAQuery.java:269) at org.kie.kogito.index.jpa.storage.JPAQuery.buildPredicateFunction(JPAQuery.java:168) at org.kie.kogito.index.jpa.storage.JPAQuery.lambda$filterPredicateFunction$3(JPAQuery.java:116) at java.base/java.util.Optional.orElseGet(Optional.java:364) at org.kie.kogito.index.jpa.storage.JPAQuery.lambda$filterPredicateFunction$4(JPAQuery.java:116) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:720) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575) at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616) at org.kie.kogito.index.jpa.storage.JPAQuery.addWhere(JPAQuery.java:359) at org.kie.kogito.index.jpa.storage.JPAQuery.execute(JPAQuery.java:96) at org.kie.kogito.index.jpa.query.AbstractProcessInstanceEntityQueryIT.testNotContainsAllOnCollection(AbstractProcessInstanceEntityQueryIT.java:142) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:1000) at io.quarkus.test.junit.QuarkusTestExtension.interceptTestMethod(QuarkusTestExtension.java:848) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] - To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
martinweiler commented on code in PR #2324:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2324#discussion_r3124789580
##
data-index/data-index-graphql/src/test/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapperTest.java:
##
@@ -115,4 +122,281 @@ void testJsonMapperNotNull() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata",
Map.of("number", Map.of("isNull", false).containsExactly(
jsonFilter(notNull("variables.workflowdata.number")));
}
+
+// == Array Argument Tests ==
Review Comment:
@GopikaReghunath great addition with these test cases! Would it be possible
to extend them even more to assess the actual result of the filter being
applied with some mock test data? Thanks!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
Re: [PR] [Incubator-kie-issues#2323] Allow filtering schema on nested arrays [incubator-kie-kogito-apps]
martinweiler commented on code in PR #2324:
URL:
https://github.com/apache/incubator-kie-kogito-apps/pull/2324#discussion_r3124789580
##
data-index/data-index-graphql/src/test/java/org/kie/kogito/index/graphql/query/GraphQLQueryMapperTest.java:
##
@@ -115,4 +122,281 @@ void testJsonMapperNotNull() {
assertThat(mapper.mapJsonArgument("variables").apply(Map.of("workflowdata",
Map.of("number", Map.of("isNull", false).containsExactly(
jsonFilter(notNull("variables.workflowdata.number")));
}
+
+// == Array Argument Tests ==
Review Comment:
@GopikaReghunath great addition with these test cases! Would it be possible
to extend them even more to assess the actual result of the filter being
applied with some mock test data? Thanks!
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
-
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
