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<T> execute() {
         return (List<T>) 
query.getResultList().stream().map(mapper).collect(toList());
     }
 
-    protected Function<AttributeFilter<?>, Predicate> 
filterPredicateFunction(Root<E> 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<AttributeFilter<?>> nestedFilters = 
(List<AttributeFilter<?>>) 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<AttributeFilter<?>, Predicate> 
filterPredicateFunction(Root<E> 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<E> root, CriteriaBuilder builder) {
+    protected final Predicate buildPredicateFunction(AttributeFilter filter, 
Root<E> 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]

Reply via email to