diff options
author | Christoph Büscher <christoph@elastic.co> | 2017-04-25 13:42:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-25 13:42:15 +0200 |
commit | 768420db554479d7853a3d63548b2cb8cd507f7a (patch) | |
tree | 8db687f4478794b1280c90489d257584b657e0b2 /core | |
parent | 1cb34b5eba41ee0fd48a1c63a41e231830d5d60e (diff) |
Add parsing for InternalStats (#24239)
Diffstat (limited to 'core')
8 files changed, 230 insertions, 48 deletions
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 452fe9dcb0..6942b6aec5 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/ParsedAggregation.java @@ -22,6 +22,8 @@ package org.elasticsearch.search.aggregations; import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; import java.io.IOException; import java.util.Collections; @@ -77,4 +79,17 @@ public abstract class ParsedAggregation implements Aggregation, ToXContent { } protected abstract XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException; + + /** + * Parse a token of type XContentParser.Token.VALUE_NUMBER or XContentParser.Token.STRING to a double. + * In other cases the default value is returned instead. + */ + protected static double parseDouble(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; + } + } }
\ No newline at end of file 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 index 5eb0a7223a..3105a785e1 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/ParsedSingleValueNumericMetricsAggregation.java @@ -20,54 +20,41 @@ 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); - } - } + protected double value; + protected String valueAsString; - @Override - public double value() { - return value; + @Override + public String getValueAsString() { + if (valueAsString != null) { + return valueAsString; + } else { + return Double.toString(value); } + } - protected void setValue(double value) { - this.value = value; - } + @Override + public double value() { + return value; + } - protected void setValueAsString(String valueAsString) { - this.valueAsString = valueAsString; - } + protected void setValue(double value) { + this.value = value; + } - 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 void setValueAsString(String valueAsString) { + this.valueAsString = valueAsString; + } protected static void declareSingleValueFields(ObjectParser<? extends ParsedSingleValueNumericMetricsAggregation, Void> objectParser, double defaultNullValue) { declareAggregationFields(objectParser); objectParser.declareField(ParsedSingleValueNumericMetricsAggregation::setValue, - (parser, context) -> parseValue(parser, defaultNullValue), CommonFields.VALUE, ValueType.DOUBLE_OR_NULL); + (parser, context) -> parseDouble(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/stats/InternalStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java index b0b2ea73d3..a29754ea55 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/InternalStats.java @@ -177,21 +177,28 @@ public class InternalStats extends InternalNumericMetricsAggregation.MultiValue @Override public XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { builder.field(Fields.COUNT, count); - builder.field(Fields.MIN, count != 0 ? min : null); - builder.field(Fields.MAX, count != 0 ? max : null); - builder.field(Fields.AVG, count != 0 ? getAvg() : null); - builder.field(Fields.SUM, count != 0 ? sum : null); - if (count != 0 && format != DocValueFormat.RAW) { - builder.field(Fields.MIN_AS_STRING, format.format(min)); - builder.field(Fields.MAX_AS_STRING, format.format(max)); - builder.field(Fields.AVG_AS_STRING, format.format(getAvg())); - builder.field(Fields.SUM_AS_STRING, format.format(sum)); + if (count != 0) { + builder.field(Fields.MIN, min); + builder.field(Fields.MAX, max); + builder.field(Fields.AVG, getAvg()); + builder.field(Fields.SUM, sum); + if (format != DocValueFormat.RAW) { + builder.field(Fields.MIN_AS_STRING, format.format(min)); + builder.field(Fields.MAX_AS_STRING, format.format(max)); + builder.field(Fields.AVG_AS_STRING, format.format(getAvg())); + builder.field(Fields.SUM_AS_STRING, format.format(sum)); + } + } else { + builder.nullField(Fields.MIN); + builder.nullField(Fields.MAX); + builder.nullField(Fields.AVG); + builder.nullField(Fields.SUM); } - otherStatsToXCotent(builder, params); + otherStatsToXContent(builder, params); return builder; } - protected XContentBuilder otherStatsToXCotent(XContentBuilder builder, Params params) throws IOException { + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { return builder; } diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java new file mode 100644 index 0000000000..28ca3418a9 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/ParsedStats.java @@ -0,0 +1,147 @@ +/* + * 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.stats; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ObjectParser; +import org.elasticsearch.common.xcontent.ObjectParser.ValueType; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.ParsedAggregation; +import org.elasticsearch.search.aggregations.metrics.stats.InternalStats.Fields; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class ParsedStats extends ParsedAggregation implements Stats { + + protected long count; + protected double min; + protected double max; + protected double sum; + protected double avg; + + protected final Map<String, String> valueAsString = new HashMap<>(); + + @Override + public long getCount() { + return count; + } + + @Override + public double getMin() { + return min; + } + + @Override + public double getMax() { + return max; + } + + @Override + public double getAvg() { + return avg; + } + + @Override + public double getSum() { + return sum; + } + + @Override + public String getMinAsString() { + return valueAsString.getOrDefault(Fields.MIN_AS_STRING, Double.toString(min)); + } + + @Override + public String getMaxAsString() { + return valueAsString.getOrDefault(Fields.MAX_AS_STRING, Double.toString(max)); + } + + @Override + public String getAvgAsString() { + return valueAsString.getOrDefault(Fields.AVG_AS_STRING, Double.toString(avg)); + } + + @Override + public String getSumAsString() { + return valueAsString.getOrDefault(Fields.SUM_AS_STRING, Double.toString(sum)); + } + + @Override + protected String getType() { + return StatsAggregationBuilder.NAME; + } + + @Override + protected XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException { + builder.field(Fields.COUNT, count); + if (count != 0) { + builder.field(Fields.MIN, min); + builder.field(Fields.MAX, max); + builder.field(Fields.AVG, avg); + builder.field(Fields.SUM, sum); + if (valueAsString.get(Fields.MIN_AS_STRING) != null) { + builder.field(Fields.MIN_AS_STRING, getMinAsString()); + builder.field(Fields.MAX_AS_STRING, getMaxAsString()); + builder.field(Fields.AVG_AS_STRING, getAvgAsString()); + builder.field(Fields.SUM_AS_STRING, getSumAsString()); + } + } else { + builder.nullField(Fields.MIN); + builder.nullField(Fields.MAX); + builder.nullField(Fields.AVG); + builder.nullField(Fields.SUM); + } + otherStatsToXContent(builder, params); + return builder; + } + + private static final ObjectParser<ParsedStats, Void> PARSER = new ObjectParser<>(ParsedStats.class.getSimpleName(), true, + ParsedStats::new); + + static { + declareAggregationFields(PARSER); + PARSER.declareLong((agg, value) -> agg.count = value, new ParseField(Fields.COUNT)); + PARSER.declareField((agg, value) -> agg.min = value, (parser, context) -> parseDouble(parser, Double.POSITIVE_INFINITY), + new ParseField(Fields.MIN), ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.max = value, (parser, context) -> parseDouble(parser, Double.NEGATIVE_INFINITY), + new ParseField(Fields.MAX), ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.avg = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.AVG), + ValueType.DOUBLE_OR_NULL); + PARSER.declareField((agg, value) -> agg.sum = value, (parser, context) -> parseDouble(parser, 0), new ParseField(Fields.SUM), + ValueType.DOUBLE_OR_NULL); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.MIN_AS_STRING, value), new ParseField(Fields.MIN_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.MAX_AS_STRING, value), new ParseField(Fields.MAX_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.AVG_AS_STRING, value), new ParseField(Fields.AVG_AS_STRING)); + PARSER.declareString((agg, value) -> agg.valueAsString.put(Fields.SUM_AS_STRING, value), new ParseField(Fields.SUM_AS_STRING)); + } + + public static ParsedStats fromXContent(XContentParser parser, final String name) { + ParsedStats parsedStats = PARSER.apply(parser, null); + parsedStats.setName(name); + return parsedStats; + } + + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { + return builder; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java index 370399bfbb..b769850b81 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/InternalExtendedStats.java @@ -169,7 +169,7 @@ public class InternalExtendedStats extends InternalStats implements ExtendedStat } @Override - protected XContentBuilder otherStatsToXCotent(XContentBuilder builder, Params params) throws IOException { + protected XContentBuilder otherStatsToXContent(XContentBuilder builder, Params params) throws IOException { builder.field(Fields.SUM_OF_SQRS, count != 0 ? sumOfSqrs : null); builder.field(Fields.VARIANCE, count != 0 ? getVariance() : null); builder.field(Fields.STD_DEVIATION, count != 0 ? getStdDeviation() : null); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java index ed463239b2..5469b8b65c 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/pipeline/derivative/ParsedDerivative.java @@ -54,7 +54,7 @@ public class ParsedDerivative extends ParsedSimpleValue implements Derivative { PARSER.declareField((agg, normalized) -> { agg.normalizedValue = normalized; agg.hasNormalizationFactor = true; - }, (parser, context) -> parseValue(parser, Double.NaN), NORMALIZED, ValueType.DOUBLE_OR_NULL); + }, (parser, context) -> parseDouble(parser, Double.NaN), NORMALIZED, ValueType.DOUBLE_OR_NULL); PARSER.declareString((agg, normalAsString) -> agg.normalizedAsString = normalAsString, NORMALIZED_AS_STRING); } 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 fd6b9ecdc4..63211f3db2 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -50,6 +50,8 @@ import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.Interna import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.InternalTDigestPercentiles; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentileRanks; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.ParsedTDigestPercentiles; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; +import org.elasticsearch.search.aggregations.metrics.stats.StatsAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.sum.ParsedSum; import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder; import org.elasticsearch.search.aggregations.metrics.valuecount.ParsedValueCount; @@ -99,6 +101,7 @@ public abstract class InternalAggregationTestCase<T extends InternalAggregation> namedXContents.put(InternalSimpleValue.NAME, (p, c) -> ParsedSimpleValue.fromXContent(p, (String) c)); namedXContents.put(DerivativePipelineAggregationBuilder.NAME, (p, c) -> ParsedDerivative.fromXContent(p, (String) c)); namedXContents.put(InternalBucketMetricValue.NAME, (p, c) -> ParsedBucketMetricValue.fromXContent(p, (String) c)); + namedXContents.put(StatsAggregationBuilder.NAME, (p, c) -> ParsedStats.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/metrics/InternalStatsTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java index c99d208581..c0c54d3a8a 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalStatsTests.java @@ -21,7 +21,9 @@ package org.elasticsearch.search.aggregations.metrics; 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.metrics.stats.InternalStats; +import org.elasticsearch.search.aggregations.metrics.stats.ParsedStats; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.Collections; @@ -29,9 +31,9 @@ import java.util.List; import java.util.Map; public class InternalStatsTests extends InternalAggregationTestCase<InternalStats> { + @Override - protected InternalStats createTestInstance(String name, List<PipelineAggregator> pipelineAggregators, - Map<String, Object> metaData) { + protected InternalStats createTestInstance(String name, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) { long count = frequently() ? randomIntBetween(1, Integer.MAX_VALUE) : 0; double min = randomDoubleBetween(-1000000, 1000000, true); double max = randomDoubleBetween(-1000000, 1000000, true); @@ -63,7 +65,28 @@ public class InternalStatsTests extends InternalAggregationTestCase<InternalStat } @Override + protected void assertFromXContent(InternalStats aggregation, ParsedAggregation parsedAggregation) { + assertTrue(parsedAggregation instanceof ParsedStats); + ParsedStats parsed = (ParsedStats) parsedAggregation; + long count = aggregation.getCount(); + assertEquals(count, parsed.getCount()); + // for count == 0, fields are rendered as `null`, so we test that we parse to default values used also in the reduce phase + assertEquals(count > 0 ? aggregation.getMin() : Double.POSITIVE_INFINITY , parsed.getMin(), 0); + assertEquals(count > 0 ? aggregation.getMax() : Double.NEGATIVE_INFINITY, parsed.getMax(), 0); + assertEquals(count > 0 ? aggregation.getSum() : 0, parsed.getSum(), 0); + assertEquals(count > 0 ? aggregation.getAvg() : 0, parsed.getAvg(), 0); + // also as_string values are only rendered for count != 0 + if (count > 0) { + assertEquals(aggregation.getMinAsString(), parsed.getMinAsString()); + assertEquals(aggregation.getMaxAsString(), parsed.getMaxAsString()); + assertEquals(aggregation.getSumAsString(), parsed.getSumAsString()); + assertEquals(aggregation.getAvgAsString(), parsed.getAvgAsString()); + } + } + + @Override protected Writeable.Reader<InternalStats> instanceReader() { return InternalStats::new; } } + |