diff --git a/src/main/java/org/gridsuite/computation/utils/SpecificationUtils.java b/src/main/java/org/gridsuite/computation/utils/SpecificationUtils.java index 7a9d6b1..acc18ad 100644 --- a/src/main/java/org/gridsuite/computation/utils/SpecificationUtils.java +++ b/src/main/java/org/gridsuite/computation/utils/SpecificationUtils.java @@ -26,14 +26,14 @@ * @author Kevin Le Saulnier */ public final class SpecificationUtils { - /** - * Maximum values per IN clause chunk to avoid StackOverflow exceptions. - * Current value (500) is a safe default but can be changed - */ - public static final int MAX_IN_CLAUSE_SIZE = 500; public static final String FIELD_SEPARATOR = "."; + /** + Prefer less than 1000 for Oracle DB / good compromise for Postgres + */ + public static final int MAX_IN_CLAUSE_SIZE = 10000; + // Utility class, so no constructor private SpecificationUtils() { } @@ -51,6 +51,11 @@ public static Specification notEqual(String field, String value) { return (root, cq, cb) -> cb.notEqual(getColumnPath(root, field), value); } + public static Specification in(String field, List values) { + return (root, cq, cb) -> + cb.upper(getColumnPath(root, field).as(String.class)).in(values); + } + public static Specification contains(String field, String value) { return (root, cq, cb) -> cb.like(cb.upper(getColumnPath(root, field).as(String.class)), "%" + EscapeCharacter.DEFAULT.escape(value).toUpperCase() + "%", EscapeCharacter.DEFAULT.getEscapeCharacter()); } @@ -135,15 +140,22 @@ private static Specification appendTextFilterToSpecification(Specificatio // implicitly an IN resourceFilter type because only IN may have value lists as filter value List inValues = valueList.stream() .map(Object::toString) + .map(String::toUpperCase) .toList(); completedSpecification = completedSpecification.and( + resourceFilter.type() == ResourceFilterDTO.Type.NOT_EQUAL ? + not(generateInSpecification(resourceFilter.column(), inValues)) : generateInSpecification(resourceFilter.column(), inValues) ); } else if (resourceFilter.value() == null) { // if the value is null, we build an impossible specification (trick to remove later on ?) completedSpecification = completedSpecification.and(not(completedSpecification)); } else { - completedSpecification = completedSpecification.and(equals(resourceFilter.column(), resourceFilter.value().toString())); + completedSpecification = completedSpecification.and( + resourceFilter.type() == ResourceFilterDTO.Type.NOT_EQUAL ? + notEqual(resourceFilter.column(), resourceFilter.value().toString()) : + equals(resourceFilter.column(), resourceFilter.value().toString()) + ); } } case CONTAINS -> { @@ -176,32 +188,17 @@ private static Specification appendTextFilterToSpecification(Specificatio * @return a specification for the IN clause */ private static Specification generateInSpecification(String column, List inPossibleValues) { - - if (inPossibleValues.size() > MAX_IN_CLAUSE_SIZE) { - // there are too many values for only one call to anyOf() : it might cause a StackOverflow - // => the specification is divided into several specifications which have an OR between them : - List> chunksOfInValues = Lists.partition(inPossibleValues, MAX_IN_CLAUSE_SIZE); - Specification containerSpec = null; - for (List chunk : chunksOfInValues) { - Specification multiOrEqualSpec = anyOf( - chunk - .stream() - .map(value -> SpecificationUtils.equals(column, value)) - .toList() - ); - if (containerSpec == null) { - containerSpec = multiOrEqualSpec; - } else { - containerSpec = containerSpec.or(multiOrEqualSpec); - } + List> chunksOfInValues = Lists.partition(inPossibleValues, MAX_IN_CLAUSE_SIZE); + Specification containerSpec = null; + for (List chunk : chunksOfInValues) { + Specification multiOrEqualSpec = Specification.anyOf(in(column, chunk)); + if (containerSpec == null) { + containerSpec = multiOrEqualSpec; + } else { + containerSpec = containerSpec.or(multiOrEqualSpec); } - return containerSpec; } - return anyOf(inPossibleValues - .stream() - .map(value -> SpecificationUtils.equals(column, value)) - .toList() - ); + return containerSpec; } @NotNull diff --git a/src/test/java/org/gridsuite/computation/specification/CommonSpecificationBuilderTest.java b/src/test/java/org/gridsuite/computation/specification/CommonSpecificationBuilderTest.java index 389b68a..3538780 100644 --- a/src/test/java/org/gridsuite/computation/specification/CommonSpecificationBuilderTest.java +++ b/src/test/java/org/gridsuite/computation/specification/CommonSpecificationBuilderTest.java @@ -79,6 +79,8 @@ void testBuildSpecification() { // test data List resourceFilters = List.of( new ResourceFilterDTO(TEXT, EQUALS, "dummyColumnValue", "dummyColumn"), + new ResourceFilterDTO(TEXT, NOT_EQUAL, "dummyColumnValue", "dummyColumn"), + new ResourceFilterDTO(TEXT, NOT_EQUAL, List.of("dummyColumnValue", "otherDummyColumnValue"), "dummyColumn"), new ResourceFilterDTO(TEXT, STARTS_WITH, "dum", "dummyColumn"), new ResourceFilterDTO(TEXT, IN, List.of("dummyColumnValue", "otherDummyColumnValue"), "dummyColumn"), new ResourceFilterDTO(TEXT, IN, tooManyInValues, "dummyColumn"),