Skip to content

Commit 8b9e18e

Browse files
author
Hendrik Muhs
authored
Percentiles aggregation validation checks for range (#51871)
disallow to specify percentile out of range [0,100]. This also fixes a problem in transform by failing validation if an invalid percentile configuration is used.
1 parent c82a050 commit 8b9e18e

File tree

5 files changed

+35
-17
lines changed

5 files changed

+35
-17
lines changed

server/src/main/java/org/elasticsearch/search/aggregations/metrics/PercentilesAggregationBuilder.java

+6
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ public PercentilesAggregationBuilder percentiles(double... percents) {
181181
}
182182
double[] sortedPercents = Arrays.copyOf(percents, percents.length);
183183
Arrays.sort(sortedPercents);
184+
for (double percent : sortedPercents) {
185+
if (percent < 0.0 || percent > 100.0) {
186+
throw new IllegalArgumentException("percent must be in [0,100], got [" + percent + "]: [" + name + "]");
187+
}
188+
}
189+
184190
this.percents = sortedPercents;
185191
return this;
186192
}

server/src/test/java/org/elasticsearch/search/aggregations/metrics/HDRPercentilesIT.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,21 @@
2626
import org.elasticsearch.script.Script;
2727
import org.elasticsearch.script.ScriptType;
2828
import org.elasticsearch.search.aggregations.AggregationTestScriptsPlugin;
29+
import org.elasticsearch.search.aggregations.BucketOrder;
2930
import org.elasticsearch.search.aggregations.InternalAggregation;
3031
import org.elasticsearch.search.aggregations.bucket.filter.Filter;
3132
import org.elasticsearch.search.aggregations.bucket.global.Global;
3233
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
3334
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
34-
import org.elasticsearch.search.aggregations.BucketOrder;
3535

3636
import java.util.Arrays;
3737
import java.util.Collection;
3838
import java.util.Collections;
3939
import java.util.HashMap;
40+
import java.util.HashSet;
4041
import java.util.List;
4142
import java.util.Map;
43+
import java.util.Set;
4244

4345
import static java.util.Collections.emptyMap;
4446
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
@@ -67,21 +69,21 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
6769

6870
private static double[] randomPercentiles() {
6971
final int length = randomIntBetween(1, 20);
70-
final double[] percentiles = new double[length];
71-
for (int i = 0; i < percentiles.length; ++i) {
72+
final Set<Double> uniquedPercentiles = new HashSet<>();
73+
while (uniquedPercentiles.size() < length) {
7274
switch (randomInt(20)) {
7375
case 0:
74-
percentiles[i] = 0;
76+
uniquedPercentiles.add(0.0);
7577
break;
7678
case 1:
77-
percentiles[i] = 100;
79+
uniquedPercentiles.add(100.0);
7880
break;
7981
default:
80-
percentiles[i] = randomDouble() * 100;
82+
uniquedPercentiles.add(randomDouble() * 100);
8183
break;
8284
}
8385
}
84-
Arrays.sort(percentiles);
86+
double[] percentiles= uniquedPercentiles.stream().mapToDouble(Double::doubleValue).sorted().toArray();
8587
LogManager.getLogger(HDRPercentilesIT.class).info("Using percentiles={}", Arrays.toString(percentiles));
8688
return percentiles;
8789
}

server/src/test/java/org/elasticsearch/search/aggregations/metrics/PercentilesTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ public void testNullOrEmptyPercentilesThrows() throws IOException {
7070
assertEquals("[percents] must not be empty: [testAgg]", ex.getMessage());
7171
}
7272

73+
public void testOutOfRangePercentilesThrows() throws IOException {
74+
PercentilesAggregationBuilder builder = new PercentilesAggregationBuilder("testAgg");
75+
IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> builder.percentiles(-0.4));
76+
assertEquals("percent must be in [0,100], got [-0.4]: [testAgg]", ex.getMessage());
77+
78+
ex = expectThrows(IllegalArgumentException.class, () -> builder.percentiles(104));
79+
assertEquals("percent must be in [0,100], got [104.0]: [testAgg]", ex.getMessage());
80+
}
81+
7382
public void testExceptionMultipleMethods() throws IOException {
7483
final String illegalAgg = "{\n" +
7584
" \"percentiles\": {\n" +

server/src/test/java/org/elasticsearch/search/aggregations/metrics/TDigestPercentilesIT.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,21 @@
2626
import org.elasticsearch.script.Script;
2727
import org.elasticsearch.script.ScriptType;
2828
import org.elasticsearch.search.aggregations.AggregationTestScriptsPlugin;
29+
import org.elasticsearch.search.aggregations.BucketOrder;
2930
import org.elasticsearch.search.aggregations.InternalAggregation;
3031
import org.elasticsearch.search.aggregations.bucket.filter.Filter;
3132
import org.elasticsearch.search.aggregations.bucket.global.Global;
3233
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
3334
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
34-
import org.elasticsearch.search.aggregations.BucketOrder;
3535

3636
import java.util.Arrays;
3737
import java.util.Collection;
3838
import java.util.Collections;
3939
import java.util.HashMap;
40+
import java.util.HashSet;
4041
import java.util.List;
4142
import java.util.Map;
43+
import java.util.Set;
4244

4345
import static java.util.Collections.emptyMap;
4446
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
@@ -66,21 +68,21 @@ protected Collection<Class<? extends Plugin>> nodePlugins() {
6668

6769
private static double[] randomPercentiles() {
6870
final int length = randomIntBetween(1, 20);
69-
final double[] percentiles = new double[length];
70-
for (int i = 0; i < percentiles.length; ++i) {
71+
final Set<Double> uniquedPercentiles = new HashSet<>();
72+
for (int i = 0; i < length; ++i) {
7173
switch (randomInt(20)) {
7274
case 0:
73-
percentiles[i] = 0;
75+
uniquedPercentiles.add(0.0);
7476
break;
7577
case 1:
76-
percentiles[i] = 100;
78+
uniquedPercentiles.add(100.0);
7779
break;
7880
default:
79-
percentiles[i] = randomDouble() * 100;
81+
uniquedPercentiles.add(randomDouble() * 100);
8082
break;
8183
}
8284
}
83-
Arrays.sort(percentiles);
85+
double[] percentiles= uniquedPercentiles.stream().mapToDouble(Double::doubleValue).sorted().toArray();
8486
LogManager.getLogger(TDigestPercentilesIT.class).info("Using percentiles={}", Arrays.toString(percentiles));
8587
return percentiles;
8688
}

x-pack/plugin/analytics/src/test/java/org/elasticsearch/xpack/analytics/mapper/HistogramPercentileAggregationTests.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,18 @@
77

88

99
import com.tdunning.math.stats.Centroid;
10+
1011
import org.HdrHistogram.DoubleHistogram;
1112
import org.HdrHistogram.DoubleHistogramIterationValue;
1213
import org.apache.lucene.util.TestUtil;
1314
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
14-
1515
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
1616
import org.elasticsearch.action.bulk.BulkRequest;
1717
import org.elasticsearch.action.index.IndexRequest;
1818
import org.elasticsearch.action.search.SearchResponse;
1919
import org.elasticsearch.common.xcontent.XContentBuilder;
2020
import org.elasticsearch.common.xcontent.XContentFactory;
2121
import org.elasticsearch.plugins.Plugin;
22-
2322
import org.elasticsearch.search.aggregations.AggregationBuilders;
2423
import org.elasticsearch.search.aggregations.metrics.InternalHDRPercentiles;
2524
import org.elasticsearch.search.aggregations.metrics.InternalTDigestPercentiles;
@@ -222,7 +221,7 @@ public void testTDigestHistogram() throws Exception {
222221

223222
PercentilesAggregationBuilder builder =
224223
AggregationBuilders.percentiles("agg").field("inner.data").method(PercentilesMethod.TDIGEST)
225-
.compression(compression).percentiles(10, 25, 500, 75);
224+
.compression(compression).percentiles(10, 25, 50, 75);
226225

227226
SearchResponse responseRaw = client().prepareSearch("raw").addAggregation(builder).get();
228227
SearchResponse responsePreAgg = client().prepareSearch("pre_agg").addAggregation(builder).get();

0 commit comments

Comments
 (0)