Skip to content
This repository was archived by the owner on Jun 20, 2025. It is now read-only.

Commit 8ca27ef

Browse files
authored
Fix range facets (#679)
1 parent d771b57 commit 8ca27ef

File tree

3 files changed

+55
-26
lines changed

3 files changed

+55
-26
lines changed

common/app/com/commercetools/sunrise/search/facetedsearch/RangeUtils.java

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.apache.commons.lang3.tuple.Pair;
99

1010
import javax.annotation.Nullable;
11+
import java.math.BigDecimal;
1112
import java.util.HashMap;
1213
import java.util.Map;
1314
import java.util.Optional;
@@ -21,11 +22,15 @@ public final class RangeUtils {
2122
private RangeUtils() {
2223
}
2324

24-
public static Map<FacetRange<String>, RangeStats> mapRangeToStats(final RangeFacetResult facetResult) {
25-
final Map<FacetRange<String>, RangeStats> rangeToStats = new HashMap<>();
25+
public static Map<String, RangeStats> mapRangeToStats(final RangeFacetResult facetResult) {
26+
final Map<String, RangeStats> rangeToStats = new HashMap<>();
2627
facetResult.getRanges()
27-
.forEach(stats -> parseFacetRange(stats.getLowerEndpoint(), stats.getUpperEndpoint())
28-
.ifPresent(range -> rangeToStats.put(range, stats)));
28+
.forEach(stats -> {
29+
final String lowerEndpoint = cleanEndpointDecimal(stats.getLowerEndpoint());
30+
final String upperEndpoint = cleanEndpointDecimal(stats.getUpperEndpoint());
31+
parseFacetRange(lowerEndpoint, upperEndpoint)
32+
.ifPresent(range -> rangeToStats.put(range.toString(), stats));
33+
});
2934
return rangeToStats;
3035
}
3136

@@ -41,6 +46,7 @@ public static Optional<FacetRange<String>> parseFacetRange(@Nullable final Strin
4146

4247
/**
4348
* Parses a range of the form {@code (x to y)} to a {@link FilterRange}.
49+
* Ranges of the form {@code (* to *)} are not supported.
4450
* @param rangeAsString range of the form {@code (x to y)}
4551
* @return the {@link FilterRange} corresponding to that range, or empty if it could not be parsed
4652
*/
@@ -55,11 +61,13 @@ public static Optional<FilterRange<String>> parseFilterRange(final String rangeA
5561
} else {
5662
return FilterRange.atMost(pair.getRight());
5763
}
58-
});
64+
})
65+
.filter(RangeUtils::hasNoBounds);
5966
}
6067

6168
/**
6269
* Parses a range of the form {@code (x to y)} to a {@link FacetRange}.
70+
* Ranges of the form {@code (* to *)} are not supported.
6371
* @param rangeAsString range of the form {@code (x to y)}
6472
* @return the {@link FacetRange} corresponding to that range, or empty if it could not be parsed
6573
*/
@@ -74,7 +82,8 @@ public static Optional<FacetRange<String>> parseFacetRange(final String rangeAsS
7482
} else {
7583
return FacetRange.lessThan(pair.getRight());
7684
}
77-
});
85+
})
86+
.filter(RangeUtils::hasNoBounds);
7887
}
7988

8089
@Nullable
@@ -89,17 +98,48 @@ private static Pair<String, String> parseRangeToPair(final String rangeAsString)
8998
}
9099

91100
private static String buildRange(@Nullable final String lowerEndpoint, @Nullable final String upperEndpoint) {
92-
return String.format("(\"%s\" to \"%s\")",
101+
return String.format("(%s to %s)",
93102
boundEndpointOrAsterisk(lowerEndpoint),
94103
boundEndpointOrAsterisk(upperEndpoint));
95104
}
96105

97106
@Nullable
98107
private static String boundEndpointOrNull(@Nullable final String endpoint) {
99-
return endpoint == null || endpoint.equals("*") ? null : endpoint;
108+
return isEndpointBoundless(endpoint) ? null : endpoint;
100109
}
101110

102111
private static String boundEndpointOrAsterisk(@Nullable final String endpoint) {
103-
return endpoint == null ? "*" : endpoint;
112+
return isEndpointBoundless(endpoint) ? "*" : endpoint;
113+
}
114+
115+
private static boolean isEndpointBoundless(@Nullable final String endpoint) {
116+
return endpoint == null || endpoint.equals("*");
117+
}
118+
119+
private static <T extends Comparable<T>> boolean hasNoBounds(final FacetRange<T> range) {
120+
return range.lowerBound() != null || range.upperBound() != null;
121+
}
122+
123+
private static <T extends Comparable<T>> boolean hasNoBounds(final FilterRange<T> range) {
124+
return range.lowerBound() != null || range.upperBound() != null;
125+
}
126+
127+
/**
128+
* Cleans the decimal part of the endpoint value due to the backend answering with decimals but not accepting them.
129+
* For example, the backend might answer with 9002.0, but it needs to be converted to 9002.
130+
* @param endpointValue the value of the endpoint to be cleaned
131+
* @return the value of the endpoint without the decimal part, in case the value was a number
132+
*/
133+
@Nullable
134+
private static String cleanEndpointDecimal(@Nullable final String endpointValue) {
135+
if (endpointValue != null) {
136+
try {
137+
final int value = new BigDecimal(endpointValue).intValue();
138+
return String.valueOf(value);
139+
} catch (NumberFormatException e) {
140+
// Fallbacks to default case
141+
}
142+
}
143+
return endpointValue;
104144
}
105145
}

common/app/com/commercetools/sunrise/search/facetedsearch/bucketranges/BucketRangeUtils.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,17 @@ public final class BucketRangeUtils {
1414

1515
public static List<FilterRange<String>> optionsToFilterRange(final List<BucketRangeFacetedSearchFormOption> rangeOptions) {
1616
return rangeOptions.stream()
17-
.map(BucketRangeUtils::optionToFilterRange)
17+
.map(option -> parseFilterRange(option.getValue()))
1818
.filter(Optional::isPresent)
1919
.map(Optional::get)
2020
.collect(toList());
2121
}
2222

2323
public static List<FacetRange<String>> optionsToFacetRange(final List<BucketRangeFacetedSearchFormOption> rangeOptions) {
2424
return rangeOptions.stream()
25-
.map(BucketRangeUtils::optionToFacetRange)
25+
.map(option -> parseFacetRange(option.getValue()))
2626
.filter(Optional::isPresent)
2727
.map(Optional::get)
2828
.collect(toList());
2929
}
30-
31-
public static Optional<FilterRange<String>> optionToFilterRange(final BucketRangeFacetedSearchFormOption rangeOption) {
32-
return parseFilterRange(rangeOption.getValue())
33-
.filter(range -> range.isBounded());
34-
}
35-
36-
public static Optional<FacetRange<String>> optionToFacetRange(final BucketRangeFacetedSearchFormOption rangeOption) {
37-
return parseFacetRange(rangeOption.getValue())
38-
.filter(range -> range.isBounded());
39-
}
4030
}

common/app/com/commercetools/sunrise/search/facetedsearch/bucketranges/viewmodels/BucketRangeFacetViewModelFactory.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.commercetools.sunrise.search.facetedsearch.viewmodels.AbstractFacetWithOptionsViewModelFactory;
88
import com.commercetools.sunrise.search.facetedsearch.viewmodels.FacetOptionViewModel;
99
import io.sphere.sdk.search.RangeFacetResult;
10-
import io.sphere.sdk.search.model.FacetRange;
1110
import io.sphere.sdk.search.model.RangeStats;
1211
import play.mvc.Http;
1312

@@ -18,7 +17,7 @@
1817
import java.util.Objects;
1918

2019
import static com.commercetools.sunrise.search.facetedsearch.RangeUtils.mapRangeToStats;
21-
import static com.commercetools.sunrise.search.facetedsearch.bucketranges.BucketRangeUtils.optionToFacetRange;
20+
import static com.commercetools.sunrise.search.facetedsearch.RangeUtils.parseFacetRange;
2221
import static java.util.stream.Collectors.toList;
2322

2423
@RequestScoped
@@ -67,11 +66,11 @@ protected void fillLimitedOptions(final BucketRangeFacetViewModel viewModel, fin
6766
final List<String> selectedValues = settings.getAllSelectedOptions(Http.Context.current()).stream()
6867
.map(FormOption::getFieldValue)
6968
.collect(toList());
70-
final Map<FacetRange<String>, RangeStats> rangeToStatsMap = mapRangeToStats(facetResult);
69+
final Map<String, RangeStats> rangeToStatsMap = mapRangeToStats(facetResult);
7170
final List<FacetOptionViewModel> options = new ArrayList<>();
7271
settings.getOptions()
73-
.forEach(option -> optionToFacetRange(option)
74-
.map(rangeToStatsMap::get)
72+
.forEach(option -> parseFacetRange(option.getValue())
73+
.map(range -> rangeToStatsMap.get(range.toString()))
7574
.filter(Objects::nonNull)
7675
.ifPresent(rangeStats -> options.add(bucketRangeFacetOptionViewModelFactory.create(rangeStats, option, selectedValues))));
7776
viewModel.setLimitedOptions(options);

0 commit comments

Comments
 (0)