Skip to content

Commit 1dd2ab0

Browse files
committed
Add parsing for InternalPercentilesBucket
1 parent 1f22115 commit 1dd2ab0

File tree

6 files changed

+133
-7
lines changed

6 files changed

+133
-7
lines changed

core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/ParsedPercentiles.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
public abstract class ParsedPercentiles extends ParsedAggregation implements Iterable<Percentile> {
3535

36-
private final Map<Double, Double> percentiles = new LinkedHashMap<>();
37-
private final Map<Double, String> percentilesAsString = new HashMap<>();
36+
protected final Map<Double, Double> percentiles = new LinkedHashMap<>();
37+
protected final Map<Double, String> percentilesAsString = new HashMap<>();
3838

3939
private boolean keyed;
4040

@@ -130,7 +130,6 @@ protected static void declarePercentilesFields(ObjectParser<? extends ParsedPerc
130130
if (token.isValue()) {
131131
if (token == XContentParser.Token.VALUE_NUMBER) {
132132
aggregation.addPercentile(Double.valueOf(parser.currentName()), parser.doubleValue());
133-
134133
} else if (token == XContentParser.Token.VALUE_STRING) {
135134
int i = parser.currentName().indexOf("_as_string");
136135
if (i > 0) {
@@ -140,6 +139,8 @@ protected static void declarePercentilesFields(ObjectParser<? extends ParsedPerc
140139
aggregation.addPercentile(Double.valueOf(parser.currentName()), Double.valueOf(parser.text()));
141140
}
142141
}
142+
} else if (token == XContentParser.Token.VALUE_NULL) {
143+
aggregation.addPercentile(Double.valueOf(parser.currentName()), Double.NaN);
143144
}
144145
}
145146
} else if (token == XContentParser.Token.START_ARRAY) {
@@ -162,6 +163,8 @@ protected static void declarePercentilesFields(ObjectParser<? extends ParsedPerc
162163
} else if (CommonFields.VALUE_AS_STRING.getPreferredName().equals(currentFieldName)) {
163164
valueAsString = parser.text();
164165
}
166+
} else if (token == XContentParser.Token.VALUE_NULL) {
167+
value = Double.NaN;
165168
}
166169
}
167170
if (key != null) {
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile;
21+
22+
import org.elasticsearch.common.xcontent.ObjectParser;
23+
import org.elasticsearch.common.xcontent.XContentBuilder;
24+
import org.elasticsearch.common.xcontent.XContentParser;
25+
import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles;
26+
import org.elasticsearch.search.aggregations.metrics.percentiles.Percentiles;
27+
28+
import java.io.IOException;
29+
import java.util.Map.Entry;
30+
31+
public class ParsedPercentilesBucket extends ParsedPercentiles implements Percentiles {
32+
33+
@Override
34+
public String getType() {
35+
return PercentilesBucketPipelineAggregationBuilder.NAME;
36+
}
37+
38+
@Override
39+
public double percentile(double percent) throws IllegalArgumentException {
40+
Double value = percentiles.get(percent);
41+
if (value == null) {
42+
throw new IllegalArgumentException("Percent requested [" + String.valueOf(percent) + "] was not" +
43+
" one of the computed percentiles. Available keys are: " + percentiles.keySet());
44+
}
45+
return value;
46+
}
47+
48+
@Override
49+
public String percentileAsString(double percent) {
50+
double value = percentile(percent); // check availability as unformatted value
51+
String valueAsString = percentilesAsString.get(percent);
52+
if (valueAsString != null) {
53+
return valueAsString;
54+
} else {
55+
return Double.toString(value);
56+
}
57+
}
58+
59+
@Override
60+
public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException {
61+
builder.startObject("values");
62+
for (Entry<Double, Double> percent : percentiles.entrySet()) {
63+
double value = percent.getValue();
64+
boolean hasValue = !(Double.isNaN(value));
65+
Double key = percent.getKey();
66+
builder.field(Double.toString(key), hasValue ? value : null);
67+
String valueAsString = percentilesAsString.get(key);
68+
if (hasValue && valueAsString != null) {
69+
builder.field(key + "_as_string", valueAsString);
70+
}
71+
}
72+
builder.endObject();
73+
return builder;
74+
}
75+
76+
private static ObjectParser<ParsedPercentilesBucket, Void> PARSER =
77+
new ObjectParser<>(ParsedPercentilesBucket.class.getSimpleName(), true, ParsedPercentilesBucket::new);
78+
79+
static {
80+
ParsedPercentiles.declarePercentilesFields(PARSER);
81+
}
82+
83+
public static ParsedPercentilesBucket fromXContent(XContentParser parser, String name) throws IOException {
84+
ParsedPercentilesBucket aggregation = PARSER.parse(parser, null);
85+
aggregation.setName(name);
86+
return aggregation;
87+
}
88+
}

core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
6464
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.InternalBucketMetricValue;
6565
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.ParsedBucketMetricValue;
66+
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.ParsedPercentilesBucket;
67+
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.percentile.PercentilesBucketPipelineAggregationBuilder;
6668
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.ParsedStatsBucket;
6769
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.StatsBucketPipelineAggregationBuilder;
6870
import org.elasticsearch.search.aggregations.pipeline.bucketmetrics.stats.extended.ExtendedStatsBucketPipelineAggregationBuilder;
@@ -99,6 +101,7 @@ static List<NamedXContentRegistry.Entry> getNamedXContents() {
99101
namedXContents.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c));
100102
namedXContents.put(InternalTDigestPercentiles.NAME, (p, c) -> ParsedTDigestPercentiles.fromXContent(p, (String) c));
101103
namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c));
104+
namedXContents.put(PercentilesBucketPipelineAggregationBuilder.NAME, (p, c) -> ParsedPercentilesBucket.fromXContent(p, (String) c));
102105
namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c));
103106
namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c));
104107
namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c));

core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractPercentilesTestCase.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.junit.Before;
2727

2828
import java.io.IOException;
29+
import java.util.Arrays;
2930
import java.util.Iterator;
3031
import java.util.List;
3132
import java.util.Map;
@@ -39,7 +40,7 @@ public abstract class AbstractPercentilesTestCase<T extends InternalAggregation
3940

4041
@Before
4142
public void init() {
42-
percents = randomPercents();
43+
percents = randomPercents(false);
4344
keyed = randomBoolean();
4445
docValueFormat = randomNumericDocValueFormat();
4546
}
@@ -70,12 +71,15 @@ public void testPercentilesIterators() throws IOException {
7071
}
7172
}
7273

73-
protected static double[] randomPercents() {
74+
public static double[] randomPercents(boolean sorted) {
7475
List<Double> randomCdfValues = randomSubsetOf(randomIntBetween(1, 7), 0.01d, 0.05d, 0.25d, 0.50d, 0.75d, 0.95d, 0.99d);
7576
double[] percents = new double[randomCdfValues.size()];
7677
for (int i = 0; i < randomCdfValues.size(); i++) {
7778
percents[i] = randomCdfValues.get(i);
7879
}
80+
if (sorted) {
81+
Arrays.sort(percents);
82+
}
7983
return percents;
8084
}
8185
}

core/src/test/java/org/elasticsearch/search/aggregations/metrics/percentiles/hdr/InternalHDRPercentilesTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import org.elasticsearch.common.io.stream.Writeable;
2424
import org.elasticsearch.search.DocValueFormat;
2525
import org.elasticsearch.search.aggregations.metrics.percentiles.InternalPercentilesTestCase;
26-
import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile;
2726
import org.elasticsearch.search.aggregations.metrics.percentiles.ParsedPercentiles;
27+
import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile;
2828
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
2929

3030
import java.util.Arrays;
@@ -70,7 +70,7 @@ protected Class<? extends ParsedPercentiles> implementationClass() {
7070
}
7171

7272
public void testIterator() {
73-
final double[] percents = randomPercents();
73+
final double[] percents = randomPercents(false);
7474
final double[] values = new double[frequently() ? randomIntBetween(1, 10) : 0];
7575
for (int i = 0; i < values.length; ++i) {
7676
values[i] = randomDouble();

core/src/test/java/org/elasticsearch/search/aggregations/pipeline/bucketmetrics/percentile/InternalPercentilesBucketTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import org.elasticsearch.common.io.stream.Writeable;
2323
import org.elasticsearch.search.DocValueFormat;
2424
import org.elasticsearch.search.aggregations.InternalAggregationTestCase;
25+
import org.elasticsearch.search.aggregations.ParsedAggregation;
2526
import org.elasticsearch.search.aggregations.metrics.percentiles.Percentile;
2627
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
2728

29+
import java.io.IOException;
2830
import java.util.Collections;
2931
import java.util.Iterator;
3032
import java.util.List;
@@ -66,6 +68,22 @@ protected Writeable.Reader<InternalPercentilesBucket> instanceReader() {
6668
return InternalPercentilesBucket::new;
6769
}
6870

71+
@Override
72+
protected final void assertFromXContent(InternalPercentilesBucket aggregation, ParsedAggregation parsedAggregation) {
73+
assertTrue(parsedAggregation instanceof ParsedPercentilesBucket);
74+
ParsedPercentilesBucket parsedPercentiles = (ParsedPercentilesBucket) parsedAggregation;
75+
76+
for (Percentile percentile : aggregation) {
77+
Double percent = percentile.getPercent();
78+
assertEquals(aggregation.percentile(percent), parsedPercentiles.percentile(percent), 0);
79+
// we cannot ensure we get the same as_string output for Double.NaN values since they are rendered as
80+
// null and we don't have a formatted string representation in the rest output
81+
if (Double.isNaN(aggregation.percentile(percent)) == false) {
82+
assertEquals(aggregation.percentileAsString(percent), parsedPercentiles.percentileAsString(percent));
83+
}
84+
}
85+
}
86+
6987
/**
7088
* check that we don't rely on the percent array order and that the iterator returns the values in the original order
7189
*/
@@ -89,4 +107,14 @@ public void testErrorOnDifferentArgumentSize() {
89107
assertEquals("The number of provided percents and percentiles didn't match. percents: [0.1, 0.2, 0.3], percentiles: [0.1, 0.2]",
90108
e.getMessage());
91109
}
110+
111+
public void testParsedAggregationIteratorOrder() throws IOException {
112+
final InternalPercentilesBucket aggregation = createTestInstance();
113+
final Iterable<Percentile> parsedAggregation = parseAndAssert(aggregation, false);
114+
Iterator<Percentile> it = aggregation.iterator();
115+
Iterator<Percentile> parsedIt = parsedAggregation.iterator();
116+
while (it.hasNext()) {
117+
assertEquals(it.next(), parsedIt.next());
118+
}
119+
}
92120
}

0 commit comments

Comments
 (0)