Skip to content

Commit ac8812b

Browse files
authored
Merge branch 'main' into short_circuit_support_one_bus
2 parents d71408e + 6b05710 commit ac8812b

File tree

3 files changed

+34
-39
lines changed

3 files changed

+34
-39
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
<groupId>org.gridsuite</groupId>
2121
<artifactId>gridsuite-computation</artifactId>
22-
<version>1.2.0-SNAPSHOT</version>
22+
<version>1.4.0-SNAPSHOT</version>
2323

2424
<packaging>jar</packaging>
2525
<name>Computation library</name>

src/main/java/org/gridsuite/computation/utils/SpecificationUtils.java

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
/**
2-
* Copyright (c) 2025, RTE (http://www.rte-france.com)
3-
* This Source Code Form is subject to the terms of the Mozilla Public
4-
* License, v. 2.0. If a copy of the MPL was not distributed with this
5-
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6-
*/
7-
81
/**
92
* Copyright (c) 2025, RTE (http://www.rte-france.com)
103
* This Source Code Form is subject to the terms of the Mozilla Public
@@ -33,14 +26,14 @@
3326
* @author Kevin Le Saulnier <[email protected]>
3427
*/
3528
public final class SpecificationUtils {
36-
/**
37-
* Maximum values per IN clause chunk to avoid StackOverflow exceptions.
38-
* Current value (500) is a safe default but can be changed
39-
*/
40-
public static final int MAX_IN_CLAUSE_SIZE = 500;
4129

4230
public static final String FIELD_SEPARATOR = ".";
4331

32+
/**
33+
Prefer less than 1000 for Oracle DB / good compromise for Postgres
34+
*/
35+
public static final int MAX_IN_CLAUSE_SIZE = 10000;
36+
4437
// Utility class, so no constructor
4538
private SpecificationUtils() { }
4639

@@ -58,6 +51,11 @@ public static <X> Specification<X> notEqual(String field, String value) {
5851
return (root, cq, cb) -> cb.notEqual(getColumnPath(root, field), value);
5952
}
6053

54+
public static <X> Specification<X> in(String field, List<String> values) {
55+
return (root, cq, cb) ->
56+
cb.upper(getColumnPath(root, field).as(String.class)).in(values);
57+
}
58+
6159
public static <X> Specification<X> contains(String field, String value) {
6260
return (root, cq, cb) -> cb.like(cb.upper(getColumnPath(root, field).as(String.class)), "%" + EscapeCharacter.DEFAULT.escape(value).toUpperCase() + "%", EscapeCharacter.DEFAULT.getEscapeCharacter());
6361
}
@@ -142,15 +140,22 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio
142140
// implicitly an IN resourceFilter type because only IN may have value lists as filter value
143141
List<String> inValues = valueList.stream()
144142
.map(Object::toString)
143+
.map(String::toUpperCase)
145144
.toList();
146145
completedSpecification = completedSpecification.and(
146+
resourceFilter.type() == ResourceFilterDTO.Type.NOT_EQUAL ?
147+
not(generateInSpecification(resourceFilter.column(), inValues)) :
147148
generateInSpecification(resourceFilter.column(), inValues)
148149
);
149150
} else if (resourceFilter.value() == null) {
150151
// if the value is null, we build an impossible specification (trick to remove later on ?)
151152
completedSpecification = completedSpecification.and(not(completedSpecification));
152153
} else {
153-
completedSpecification = completedSpecification.and(equals(resourceFilter.column(), resourceFilter.value().toString()));
154+
completedSpecification = completedSpecification.and(
155+
resourceFilter.type() == ResourceFilterDTO.Type.NOT_EQUAL ?
156+
notEqual(resourceFilter.column(), resourceFilter.value().toString()) :
157+
equals(resourceFilter.column(), resourceFilter.value().toString())
158+
);
154159
}
155160
}
156161
case CONTAINS -> {
@@ -183,32 +188,17 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio
183188
* @return a specification for the IN clause
184189
*/
185190
private static <X> Specification<X> generateInSpecification(String column, List<String> inPossibleValues) {
186-
187-
if (inPossibleValues.size() > MAX_IN_CLAUSE_SIZE) {
188-
// there are too many values for only one call to anyOf() : it might cause a StackOverflow
189-
// => the specification is divided into several specifications which have an OR between them :
190-
List<List<String>> chunksOfInValues = Lists.partition(inPossibleValues, MAX_IN_CLAUSE_SIZE);
191-
Specification<X> containerSpec = null;
192-
for (List<String> chunk : chunksOfInValues) {
193-
Specification<X> multiOrEqualSpec = anyOf(
194-
chunk
195-
.stream()
196-
.map(value -> SpecificationUtils.<X>equals(column, value))
197-
.toList()
198-
);
199-
if (containerSpec == null) {
200-
containerSpec = multiOrEqualSpec;
201-
} else {
202-
containerSpec = containerSpec.or(multiOrEqualSpec);
203-
}
191+
List<List<String>> chunksOfInValues = Lists.partition(inPossibleValues, MAX_IN_CLAUSE_SIZE);
192+
Specification<X> containerSpec = null;
193+
for (List<String> chunk : chunksOfInValues) {
194+
Specification<X> multiOrEqualSpec = Specification.anyOf(in(column, chunk));
195+
if (containerSpec == null) {
196+
containerSpec = multiOrEqualSpec;
197+
} else {
198+
containerSpec = containerSpec.or(multiOrEqualSpec);
204199
}
205-
return containerSpec;
206200
}
207-
return anyOf(inPossibleValues
208-
.stream()
209-
.map(value -> SpecificationUtils.<X>equals(column, value))
210-
.toList()
211-
);
201+
return containerSpec;
212202
}
213203

214204
@NotNull
@@ -236,6 +226,8 @@ private static <X> Specification<X> appendNumberFilterToSpecification(Specificat
236226
specification.and(lessThanOrEqual(resourceFilter.column(), valueDouble, tolerance));
237227
case GREATER_THAN_OR_EQUAL ->
238228
specification.and(greaterThanOrEqual(resourceFilter.column(), valueDouble, tolerance));
229+
case EQUALS -> specification.and(greaterThanOrEqual(resourceFilter.column(), valueDouble, tolerance))
230+
.and(lessThanOrEqual(resourceFilter.column(), valueDouble, tolerance));
239231
default ->
240232
throw new IllegalArgumentException("The filter type " + resourceFilter.type() + " is not supported with the data type " + resourceFilter.dataType());
241233
};

src/test/java/org/gridsuite/computation/specification/CommonSpecificationBuilderTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,17 @@ void testBuildSpecification() {
7979
// test data
8080
List<ResourceFilterDTO> resourceFilters = List.of(
8181
new ResourceFilterDTO(TEXT, EQUALS, "dummyColumnValue", "dummyColumn"),
82+
new ResourceFilterDTO(TEXT, NOT_EQUAL, "dummyColumnValue", "dummyColumn"),
83+
new ResourceFilterDTO(TEXT, NOT_EQUAL, List.of("dummyColumnValue", "otherDummyColumnValue"), "dummyColumn"),
8284
new ResourceFilterDTO(TEXT, STARTS_WITH, "dum", "dummyColumn"),
8385
new ResourceFilterDTO(TEXT, IN, List.of("dummyColumnValue", "otherDummyColumnValue"), "dummyColumn"),
8486
new ResourceFilterDTO(TEXT, IN, tooManyInValues, "dummyColumn"),
8587
new ResourceFilterDTO(TEXT, CONTAINS, "partialValue", "dummyColumn"),
8688
new ResourceFilterDTO(TEXT, CONTAINS, List.of("partialValue1", "partialValue2"), "dummyColumn"),
8789
new ResourceFilterDTO(NUMBER, LESS_THAN_OR_EQUAL, 100.0157, "dummyNumberColumn"),
8890
new ResourceFilterDTO(NUMBER, GREATER_THAN_OR_EQUAL, 10, "dummyNumberColumn", 0.1),
89-
new ResourceFilterDTO(NUMBER, NOT_EQUAL, 10, "parent.dummyNumberColumn")
91+
new ResourceFilterDTO(NUMBER, NOT_EQUAL, 10, "parent.dummyNumberColumn"),
92+
new ResourceFilterDTO(NUMBER, EQUALS, 42.000, "dummyNumberColumn")
9093
);
9194
List<ResourceFilterDTO> resourceFiltersWithChildren = List.of(
9295
new ResourceFilterDTO(NUMBER, NOT_EQUAL, 10, "parent.dummyNumberColumn")

0 commit comments

Comments
 (0)