Skip to content

Commit

Permalink
dynamic rounding of results for comparisons (#151)
Browse files Browse the repository at this point in the history
Signed-off-by: Mathieu DEHARBE <[email protected]>
  • Loading branch information
Mathieu-Deharbe authored Nov 26, 2024
1 parent 265a7a8 commit 832e0e3
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@
* @param type the type of filter (contains, startsWith...)
* @param value the value of the filter
* @param column the column / field on which the filter will be applied
* @param tolerance precision/tolerance used for the comparisons (simulates the rounding of the database values) Only useful for numbers.
* @author Kevin Le Saulnier <kevin.lesaulnier at rte-france.com>
*/
public record ResourceFilterDTO(DataType dataType, Type type, Object value, String column) {
public record ResourceFilterDTO(DataType dataType, Type type, Object value, String column, Double tolerance) {
public ResourceFilterDTO(DataType dataType, Type type, Object value, String column) {
this(dataType, type, value, column, null);
}

public enum DataType {
@JsonProperty("text")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,17 @@ public static <X> Specification<X> startsWith(String field, String value) {
return (root, cq, cb) -> cb.like(cb.upper(getColumnPath(root, field)), EscapeCharacter.DEFAULT.escape(value).toUpperCase() + "%", EscapeCharacter.DEFAULT.getEscapeCharacter());
}

/**
* Returns a specification where the field value is not equal within the given tolerance.
*/
public static <X> Specification<X> notEqual(String field, Double value, Double tolerance) {
return (root, cq, cb) -> {
Expression<Double> doubleExpression = getColumnPath(root, field).as(Double.class);
/**
* in order to be equal to doubleExpression, value has to fit :
* value - tolerance <= doubleExpression <= value + tolerance
* therefore in order to be different at least one of the opposite comparison needs to be true :
*/
return cb.or(
cb.greaterThan(doubleExpression, value + tolerance),
cb.lessThan(doubleExpression, value - tolerance)
Expand Down Expand Up @@ -137,13 +145,27 @@ private static <X> Specification<X> appendTextFilterToSpecification(Specificatio

@NotNull
private static <X> Specification<X> appendNumberFilterToSpecification(Specification<X> specification, ResourceFilterDTO resourceFilter) {
final double tolerance = 0.00001; // tolerance for comparison
String value = resourceFilter.value().toString();
return createNumberPredicate(specification, resourceFilter, value, tolerance);
return createNumberPredicate(specification, resourceFilter, value);
}

private static <X> Specification<X> createNumberPredicate(Specification<X> specification, ResourceFilterDTO resourceFilter, String value, double tolerance) {
Double valueDouble = Double.valueOf(value);
private static <X> Specification<X> createNumberPredicate(Specification<X> specification, ResourceFilterDTO resourceFilter, String filterValue) {
double tolerance;
if (resourceFilter.tolerance() != null) {
tolerance = resourceFilter.tolerance();
} else {
// the reference for the comparison is the number of digits after the decimal point in filterValue
// extra digits are ignored, but the user may add '0's after the decimal point in order to get a better precision
String[] splitValue = filterValue.split("\\.");
int numberOfDecimalAfterDot = 0;
if (splitValue.length > 1) {
numberOfDecimalAfterDot = splitValue[1].length();
}
// tolerance is multiplied by 0.5 to simulate the fact that the database value is rounded (in the front, from the user viewpoint)
// more than 13 decimal after dot will likely cause rounding errors due to double precision
tolerance = Math.pow(10, -numberOfDecimalAfterDot) * 0.5;
}
Double valueDouble = Double.valueOf(filterValue);
return switch (resourceFilter.type()) {
case NOT_EQUAL -> specification.and(notEqual(resourceFilter.column(), valueDouble, tolerance));
case LESS_THAN_OR_EQUAL ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ void findFilteredPrecontingencyLimitViolationResultsTest(List<ResourceFilterDTO>
List<PreContingencyLimitViolationResultDTO> preContingencyLimitViolation = securityAnalysisResultService.findNResult(resultEntity.getId(), filters, sort);

// assert subject ids to check parent filters
assertThat(preContingencyLimitViolation).extracting(SubjectLimitViolationEntity.Fields.subjectId).containsExactlyElementsOf(expectedResult.stream().map(PreContingencyLimitViolationResultDTO::getSubjectId).toList());
assertThat(preContingencyLimitViolation).extracting(SubjectLimitViolationEntity.Fields.subjectId)
.containsExactlyElementsOf(
expectedResult.stream().map(PreContingencyLimitViolationResultDTO::getSubjectId).toList()
);
assertSelectCount(expectedSelectCount);
}

Expand Down Expand Up @@ -110,9 +113,11 @@ private static Stream<Arguments> provideChildFilter() {

private static Stream<Arguments> provideChildFilterWithTolerance() {
return Stream.of(
Arguments.of(List.of(new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.NOT_EQUAL, "11.02425", AbstractLimitViolationEntity.Fields.value),
Arguments.of(List.of(
new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.NOT_EQUAL, "11.02425", AbstractLimitViolationEntity.Fields.value),
new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.LESS_THAN_OR_EQUAL, "10.51243", AbstractLimitViolationEntity.Fields.limit),
new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.GREATER_THAN_OR_EQUAL, "1.00001", AbstractLimitViolationEntity.Fields.limitReduction)), Sort.by(Sort.Direction.ASC, "limit"),
new ResourceFilterDTO(ResourceFilterDTO.DataType.NUMBER, ResourceFilterDTO.Type.GREATER_THAN_OR_EQUAL, "0.999999", AbstractLimitViolationEntity.Fields.limitReduction)
), Sort.by(Sort.Direction.ASC, "limit"),
getResultPreContingencyWithNestedFilter(p -> p.getLimitViolation().getLimit() <= 10.51243)
.stream().sorted(Comparator.comparing(x -> x.getLimitViolation().getLimit())).toList(), 2)
);
Expand Down

0 comments on commit 832e0e3

Please sign in to comment.