diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java index 0a7e71c6f061e..a3a886dadfd8b 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/ObjectParser.java @@ -448,6 +448,7 @@ public enum ValueType { FLOAT(VALUE_NUMBER, VALUE_STRING), FLOAT_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), DOUBLE(VALUE_NUMBER, VALUE_STRING), + DOUBLE_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), LONG(VALUE_NUMBER, VALUE_STRING), LONG_OR_NULL(VALUE_NUMBER, VALUE_STRING, VALUE_NULL), INT(VALUE_NUMBER, VALUE_STRING), diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java index ae620ba601ea1..452fe9dcb0853 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -33,7 +33,7 @@ */ public abstract class ParsedAggregation implements Aggregation, ToXContent { - protected static void declareCommonFields(ObjectParser objectParser) { + protected static void declareAggregationFields(ObjectParser objectParser) { objectParser.declareObject((parsedAgg, metadata) -> parsedAgg.metadata = Collections.unmodifiableMap(metadata), (parser, context) -> parser.map(), InternalAggregation.CommonFields.META); } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java new file mode 100644 index 0000000000000..11da4c09f5754 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java @@ -0,0 +1,72 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.search.aggregations.metrics; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; + +public abstract class ParsedSingleValueNumericMetricsAggregation extends ParsedAggregation implements NumericMetricsAggregation.SingleValue { + + protected double value; + protected String valueAsString; + + @Override + public String getValueAsString() { + if (valueAsString != null) { + return valueAsString; + } else { + return Double.toString(value); + } + } + + @Override + public double value() { + return value; + } + + protected void setValue(double value) { + this.value = value; + } + + protected void setValueAsString(String valueAsString) { + this.valueAsString = valueAsString; + } + + protected static double parseValue(XContentParser parser, double defaultNullValue) throws IOException { + Token currentToken = parser.currentToken(); + if (currentToken == XContentParser.Token.VALUE_NUMBER || currentToken == XContentParser.Token.VALUE_STRING) { + return parser.doubleValue(); + } else { + return defaultNullValue; + } + } + + protected static void declareSingeValueFields(ObjectParser objectParser, + double defaultNullValue) { + declareAggregationFields(objectParser); + objectParser.declareField(ParsedSingleValueNumericMetricsAggregation::setValue, + (parser, context) -> parseValue(parser, defaultNullValue), CommonFields.VALUE, ValueType.DOUBLE_OR_NULL); + objectParser.declareString(ParsedSingleValueNumericMetricsAggregation::setValueAsString, CommonFields.VALUE_AS_STRING); + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java new file mode 100644 index 0000000000000..ed4b9416908ee --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/ParsedAvg.java @@ -0,0 +1,64 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.metrics.avg; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedAvg extends ParsedSingleValueNumericMetricsAggregation implements Avg { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return AvgAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + // InternalAvg renders value only if the avg normalizer (count) is not 0. + // We parse back `null` as Double.POSITIVE_INFINITY so we check for that value here to get the same xContent output + boolean hasValue = value != Double.POSITIVE_INFINITY; + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedAvg.class.getSimpleName(), true, ParsedAvg::new); + + static { + declareSingeValueFields(PARSER, Double.POSITIVE_INFINITY); + } + + public static ParsedAvg fromXContent(XContentParser parser, final String name) { + ParsedAvg avg = PARSER.apply(parser, null); + avg.setName(name); + return avg; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java index cac367cc7a213..77356db519705 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/ParsedCardinality.java @@ -49,21 +49,17 @@ public long getValue() { return cardinalityValue; } - private void setValue(long cardinalityValue) { - this.cardinalityValue = cardinalityValue; - } - @Override protected String getType() { return CardinalityAggregationBuilder.NAME; } private static final ObjectParser PARSER = new ObjectParser<>( - CardinalityAggregationBuilder.NAME, true, ParsedCardinality::new); + ParsedCardinality.class.getSimpleName(), true, ParsedCardinality::new); static { - declareCommonFields(PARSER); - PARSER.declareLong(ParsedCardinality::setValue, CommonFields.VALUE); + declareAggregationFields(PARSER); + PARSER.declareLong((agg, value) -> agg.cardinalityValue = value, CommonFields.VALUE); } public static ParsedCardinality fromXContent(XContentParser parser, final String name) { diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java new file mode 100644 index 0000000000000..f3a4ff85f80d4 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/max/ParsedMax.java @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.metrics.max; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedMax extends ParsedSingleValueNumericMetricsAggregation implements Max { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return MaxAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + boolean hasValue = !Double.isInfinite(value); + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedMax.class.getSimpleName(), true, ParsedMax::new); + + static { + declareSingeValueFields(PARSER, Double.NEGATIVE_INFINITY); + } + + public static ParsedMax fromXContent(XContentParser parser, final String name) { + ParsedMax max = PARSER.apply(parser, null); + max.setName(name); + return max; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java new file mode 100644 index 0000000000000..5be0444e47870 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/min/ParsedMin.java @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.metrics.min; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedMin extends ParsedSingleValueNumericMetricsAggregation implements Min { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return MinAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + boolean hasValue = !Double.isInfinite(value); + builder.field(CommonFields.VALUE.getPreferredName(), hasValue ? value : null); + if (hasValue && valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedMin.class.getSimpleName(), true, ParsedMin::new); + + static { + declareSingeValueFields(PARSER, Double.POSITIVE_INFINITY); + } + + public static ParsedMin fromXContent(XContentParser parser, final String name) { + ParsedMin min = PARSER.apply(parser, null); + min.setName(name); + return min; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java index a45d089cc79bc..e3152bac3a36c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/AbstractParsedPercentiles.java @@ -120,7 +120,7 @@ protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) } protected static void declarePercentilesFields(ObjectParser objectParser) { - ParsedAggregation.declareCommonFields(objectParser); + ParsedAggregation.declareAggregationFields(objectParser); objectParser.declareField((parser, aggregation, context) -> { XContentParser.Token token = parser.currentToken(); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java new file mode 100644 index 0000000000000..15a85aa3f87ba --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/ParsedSum.java @@ -0,0 +1,61 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.metrics.sum; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.metrics.ParsedSingleValueNumericMetricsAggregation; + +import java.io.IOException; + +public class ParsedSum extends ParsedSingleValueNumericMetricsAggregation implements Sum { + + @Override + public double getValue() { + return value(); + } + + @Override + protected String getType() { + return SumAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.VALUE.getPreferredName(), value); + if (valueAsString != null) { + builder.field(CommonFields.VALUE_AS_STRING.getPreferredName(), valueAsString); + } + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedSum.class.getSimpleName(), true, ParsedSum::new); + + static { + declareSingeValueFields(PARSER, Double.NEGATIVE_INFINITY); + } + + public static ParsedSum fromXContent(XContentParser parser, final String name) { + ParsedSum sum = PARSER.apply(parser, null); + sum.setName(name); + return sum; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java new file mode 100644 index 0000000000000..012323f0041e8 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ParsedValueCount.java @@ -0,0 +1,74 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.metrics.valuecount; + +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedAggregation; + +import java.io.IOException; + +public class ParsedValueCount extends ParsedAggregation implements ValueCount { + + private long valueCount; + + @Override + public double value() { + return getValue(); + } + + @Override + public long getValue() { + return valueCount; + } + + @Override + public String getValueAsString() { + // InternalValueCount doesn't print "value_as_string", but you can get a formatted value using + // getValueAsString() using the raw formatter and converting the value to double + return Double.toString(valueCount); + } + + @Override + protected String getType() { + return ValueCountAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(CommonFields.VALUE.getPreferredName(), valueCount); + return builder; + } + + private static final ObjectParser PARSER = new ObjectParser<>(ParsedValueCount.class.getSimpleName(), true, + ParsedValueCount::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareLong((agg, value) -> agg.valueCount = value, CommonFields.VALUE); + } + + public static ParsedValueCount fromXContent(XContentParser parser, final String name) { + ParsedValueCount sum = PARSER.apply(parser, null); + sum.setName(name); + return sum; + } +} \ No newline at end of file diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index e6c5098761c05..04c0ed58f3900 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -33,12 +33,22 @@ import org.elasticsearch.rest.action.search.RestSearchAction; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.SearchModule; +import org.elasticsearch.search.aggregations.metrics.avg.AvgAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.avg.ParsedAvg; import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.cardinality.ParsedCardinality; +import org.elasticsearch.search.aggregations.metrics.max.MaxAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; +import org.elasticsearch.search.aggregations.metrics.min.MinAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.min.ParsedMin; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.InternalHDRPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.hdr.ParsedHDRPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; +import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; +import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; +import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; +import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.AbstractWireSerializingTestCase; @@ -67,6 +77,11 @@ static List getNamedXContents() { namedXContents.put(CardinalityAggregationBuilder.NAME, (p, c) -> ParsedCardinality.fromXContent(p, (String) c)); namedXContents.put(InternalHDRPercentileRanks.NAME, (p, c) -> ParsedHDRPercentileRanks.fromXContent(p, (String) c)); namedXContents.put(InternalTDigestPercentileRanks.NAME, (p, c) -> ParsedTDigestPercentileRanks.fromXContent(p, (String) c)); + namedXContents.put(MinAggregationBuilder.NAME, (p, c) -> ParsedMin.fromXContent(p, (String) c)); + namedXContents.put(MaxAggregationBuilder.NAME, (p, c) -> ParsedMax.fromXContent(p, (String) c)); + namedXContents.put(SumAggregationBuilder.NAME, (p, c) -> ParsedSum.fromXContent(p, (String) c)); + namedXContents.put(AvgAggregationBuilder.NAME, (p, c) -> ParsedAvg.fromXContent(p, (String) c)); + namedXContents.put(ValueCountAggregationBuilder.NAME, (p, c) -> ParsedValueCount.fromXContent(p, (String) c)); return namedXContents.entrySet().stream() .map(entry -> new NamedXContentRegistry.Entry(Aggregation.class, new ParseField(entry.getKey()), entry.getValue())) diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java index a79ccf5c91f07..f56ae175a86a9 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/ParsedAggregationTests.java @@ -90,7 +90,7 @@ private static class TestParsedAggregation extends ParsedAggregation { private static ObjectParser PARSER = new ObjectParser<>("testAggParser", TestParsedAggregation::new); static { - ParsedAggregation.declareCommonFields(PARSER); + ParsedAggregation.declareAggregationFields(PARSER); } @Override diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java index de045ff533ea3..ec178feab71a4 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java @@ -22,18 +22,21 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; +import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; import java.util.Map; public class InternalMaxTests extends InternalAggregationTestCase { + @Override protected InternalMax createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalMax(name, randomDouble(), - randomFrom(DocValueFormat.BOOLEAN, DocValueFormat.GEOHASH, DocValueFormat.IP, DocValueFormat.RAW), pipelineAggregators, - metaData); + double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + return new InternalMax(name, value, formatter, pipelineAggregators, metaData); } @Override @@ -45,4 +48,17 @@ protected Reader instanceReader() { protected void assertReduced(InternalMax reduced, List inputs) { assertEquals(inputs.stream().mapToDouble(InternalMax::value).max().getAsDouble(), reduced.value(), 0); } + + @Override + protected void assertFromXContent(InternalMax max, ParsedAggregation parsedAggregation) { + ParsedMax parsed = ((ParsedMax) parsedAggregation); + if (Double.isInfinite(max.getValue()) == false) { + assertEquals(max.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(max.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.getValue(), Double.NEGATIVE_INFINITY, 0); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java index 0600d7299b46f..bfdf37d5775de 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -31,9 +32,9 @@ public class InternalAvgTests extends InternalAggregationTestCase { @Override protected InternalAvg createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalAvg(name, randomDoubleBetween(0, 100000, true), randomNonNegativeLong() % 100000, - randomFrom(DocValueFormat.BOOLEAN, DocValueFormat.GEOHASH, DocValueFormat.IP, DocValueFormat.RAW), pipelineAggregators, - metaData); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + long count = frequently() ? randomNonNegativeLong() % 100000 : 0; + return new InternalAvg(name, randomDoubleBetween(0, 100000, true), count, formatter, pipelineAggregators, metaData); } @Override @@ -53,4 +54,14 @@ protected void assertReduced(InternalAvg reduced, List inputs) { assertEquals(sum, reduced.getSum(), 0.0000001); assertEquals(sum / counts, reduced.value(), 0.0000001); } + + @Override + protected void assertFromXContent(InternalAvg avg, ParsedAggregation parsedAggregation) { + ParsedAvg parsed = ((ParsedAvg) parsedAggregation); + assertEquals(avg.getValue(), parsed.getValue(), Double.MIN_VALUE); + // we don't print out VALUE_AS_STRING for avg.getCount() == 0, so we cannot get the exact same value back + if (avg.getCount() != 0) { + assertEquals(avg.getValueAsString(), parsed.getValueAsString()); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java index f93e7c5c81e41..82ba4dec5efcc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java @@ -22,6 +22,7 @@ import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -30,9 +31,9 @@ public class InternalMinTests extends InternalAggregationTestCase { @Override protected InternalMin createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalMin(name, randomDouble(), - randomFrom(DocValueFormat.BOOLEAN, DocValueFormat.GEOHASH, DocValueFormat.IP, DocValueFormat.RAW), pipelineAggregators, - metaData); + double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + return new InternalMin(name, value, formatter, pipelineAggregators, metaData); } @Override @@ -44,4 +45,17 @@ protected Reader instanceReader() { protected void assertReduced(InternalMin reduced, List inputs) { assertEquals(inputs.stream().mapToDouble(InternalMin::value).min().getAsDouble(), reduced.value(), 0); } + + @Override + protected void assertFromXContent(InternalMin min, ParsedAggregation parsedAggregation) { + ParsedMin parsed = ((ParsedMin) parsedAggregation); + if (Double.isInfinite(min.getValue()) == false) { + assertEquals(min.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(min.getValueAsString(), parsed.getValueAsString()); + } else { + // we write Double.NEGATIVE_INFINITY and Double.POSITIVE_INFINITY to xContent as 'null', so we + // cannot differentiate between them. Also we cannot recreate the exact String representation + assertEquals(parsed.getValue(), Double.POSITIVE_INFINITY, 0); + } + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java index 44e5ea940adeb..6fb61257a9e92 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/sum/InternalSumTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -30,7 +31,9 @@ public class InternalSumTests extends InternalAggregationTestCase { @Override protected InternalSum createTestInstance(String name, List pipelineAggregators, Map metaData) { - return new InternalSum(name, randomDouble(), DocValueFormat.RAW, pipelineAggregators, metaData); + double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); + DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + return new InternalSum(name, value, formatter, pipelineAggregators, metaData); } @Override @@ -43,4 +46,11 @@ protected void assertReduced(InternalSum reduced, List inputs) { double expectedSum = inputs.stream().mapToDouble(InternalSum::getValue).sum(); assertEquals(expectedSum, reduced.getValue(), 0.0001d); } + + @Override + protected void assertFromXContent(InternalSum sum, ParsedAggregation parsedAggregation) { + ParsedSum parsed = ((ParsedSum) parsedAggregation); + assertEquals(sum.getValue(), parsed.getValue(), Double.MIN_VALUE); + assertEquals(sum.getValueAsString(), parsed.getValueAsString()); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java index 3de87898778d4..17df4b89dc641 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/valuecount/InternalValueCountTests.java @@ -21,6 +21,7 @@ import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; @@ -29,9 +30,8 @@ public class InternalValueCountTests extends InternalAggregationTestCase { @Override - protected InternalValueCount createTestInstance(String name, - List pipelineAggregators, - Map metaData) { + protected InternalValueCount createTestInstance(String name, List pipelineAggregators, + Map metaData) { return new InternalValueCount(name, randomIntBetween(0, 100), pipelineAggregators, metaData); } @@ -44,4 +44,10 @@ protected void assertReduced(InternalValueCount reduced, List instanceReader() { return InternalValueCount::new; } + + @Override + protected void assertFromXContent(InternalValueCount valueCount, ParsedAggregation parsedAggregation) { + assertEquals(valueCount.getValue(), ((ParsedValueCount) parsedAggregation).getValue(), 0); + assertEquals(valueCount.getValueAsString(), ((ParsedValueCount) parsedAggregation).getValueAsString()); + } }