Skip to content

Commit dc73e5c

Browse files
committed
Extract methodes implementing IN clause in gridsuite-computation
Signed-off-by: Igor PIROG <[email protected]>
1 parent cd63c50 commit dc73e5c

File tree

2 files changed

+86
-1
lines changed

2 files changed

+86
-1
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.0.0</version>
22+
<version>1.0.1-SNAPSHOT</version>
2323

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

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

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import com.google.common.collect.Lists;
1717
import jakarta.persistence.criteria.*;
18+
import lombok.extern.slf4j.Slf4j;
1819
import org.gridsuite.computation.dto.ResourceFilterDTO;
1920
import org.jetbrains.annotations.NotNull;
2021
import org.springframework.data.jpa.domain.Specification;
@@ -32,6 +33,7 @@
3233
*
3334
* @author Kevin Le Saulnier <[email protected]>
3435
*/
36+
@Slf4j
3537
public final class SpecificationUtils {
3638
/**
3739
* Maximum values per IN clause chunk to avoid StackOverflow exceptions.
@@ -41,6 +43,11 @@ public final class SpecificationUtils {
4143

4244
public static final String FIELD_SEPARATOR = ".";
4345

46+
public static final int CHUNK_SIZE = 10000; // prefer less than 1000 for Oracle DB / good compromise for Postgres
47+
48+
private static final String WARN_UNEXPECTED_TYPE_ENCOUNTERED_FOR = "Unexpected type encountered for {} : {} - {}";
49+
50+
4451
// Utility class, so no constructor
4552
private SpecificationUtils() { }
4653

@@ -54,6 +61,11 @@ public static <X> Specification<X> equals(String field, String value) {
5461
);
5562
}
5663

64+
public static <X> Specification<X> in(String field, List<String> values) {
65+
return (root, cq, cb) ->
66+
getColumnPath(root, field).in(values);
67+
}
68+
5769
public static <X> Specification<X> notEqual(String field, String value) {
5870
return (root, cq, cb) -> cb.notEqual(getColumnPath(root, field), value);
5971
}
@@ -131,6 +143,75 @@ public static <X> Specification<X> appendFiltersToSpecification(Specification<X>
131143
return completedSpecification;
132144
}
133145

146+
/**
147+
* Better use and abuse from built-in performance of IN clause in Postgresql
148+
* TODO : suppress when reported in appendFiltersToSpecification - test phase for shortcircuit results
149+
* @param specification : specification to complement
150+
* @param resourceFilters : filters to add in IN clause
151+
* @return : new specification with "AND (field IN (?, ?, ..., ?))"
152+
* @param <X>
153+
*/
154+
public static <X> Specification<X> appendInTextClauseToSpecification(Specification<X> specification, List<ResourceFilterDTO> resourceFilters) {
155+
Objects.requireNonNull(specification);
156+
if (resourceFilters != null && !resourceFilters.isEmpty()) {
157+
Specification<X> completedSpecification = specification;
158+
159+
for (ResourceFilterDTO resourceFilter : resourceFilters) {
160+
if (resourceFilter.dataType() == ResourceFilterDTO.DataType.TEXT) {
161+
completedSpecification = appendInTextFilterToSpecification(completedSpecification, resourceFilter);
162+
} else {
163+
doLogWarn(resourceFilter);
164+
}
165+
}
166+
167+
return completedSpecification;
168+
} else {
169+
return specification;
170+
}
171+
}
172+
173+
/*
174+
TODO : suppress when reported in appendTextFilterToSpecification - test phase for shortcircuit results
175+
*/
176+
private static <X> Specification<X> appendInTextFilterToSpecification(Specification<X> specification, ResourceFilterDTO resourceFilter) {
177+
Specification<X> completedSpecification = specification;
178+
if (resourceFilter.type() == ResourceFilterDTO.Type.IN) {
179+
if (resourceFilter.value() instanceof Collection<?> valueList) {
180+
List<String> inValues = valueList.stream().map(Object::toString).toList();
181+
completedSpecification = specification.and(generateInClauseSpecification(resourceFilter.column(), inValues));
182+
} else {
183+
doLogWarn(resourceFilter);
184+
}
185+
} else {
186+
doLogWarn(resourceFilter);
187+
}
188+
189+
return completedSpecification;
190+
}
191+
192+
/*
193+
TODO : suppress when reported in generateInSpecification - test phase for shortcircuit results
194+
*/
195+
private static <X> Specification<X> generateInClauseSpecification(String column, List<String> inPossibleValues) {
196+
if (inPossibleValues.size() > CHUNK_SIZE) {
197+
List<List<String>> chunksOfInValues = Lists.partition(inPossibleValues, CHUNK_SIZE);
198+
Specification<X> containerSpec = null;
199+
200+
for (List<String> chunk : chunksOfInValues) {
201+
Specification<X> multiOrEqualSpec = Specification.anyOf(in(column, chunk));
202+
if (containerSpec == null) {
203+
containerSpec = multiOrEqualSpec;
204+
} else {
205+
containerSpec = containerSpec.or(multiOrEqualSpec);
206+
}
207+
}
208+
209+
return containerSpec;
210+
} else {
211+
return Specification.anyOf(in(column, inPossibleValues));
212+
}
213+
}
214+
134215
@NotNull
135216
private static <X> Specification<X> appendTextFilterToSpecification(Specification<X> specification, ResourceFilterDTO resourceFilter) {
136217
Specification<X> completedSpecification = specification;
@@ -264,4 +345,8 @@ private static <X, Y> Path<Y> getColumnPath(Root<X> root, String dotSeparatedFie
264345
return root.get(dotSeparatedFields);
265346
}
266347
}
348+
349+
private static void doLogWarn(ResourceFilterDTO resourceFilter) {
350+
log.warn(WARN_UNEXPECTED_TYPE_ENCOUNTERED_FOR, resourceFilter.column(), resourceFilter.type(), resourceFilter.dataType());
351+
}
267352
}

0 commit comments

Comments
 (0)