From c9b0b04f55c3a5d5502ed15e95114ff6b8a01d6b Mon Sep 17 00:00:00 2001 From: uboness Date: Thu, 3 Apr 2014 12:00:35 +0200 Subject: [PATCH] Aggregation cleanup - consolidated value source parsing under a single parser that is reused in all the value source aggs parsers - consolidated include/exclude parsing under a single parser - cleaned up value format handling, to have consistent behaviour across all values source aggs --- .../common/geo/GeoHashUtils.java | 12 +- .../aggregations/AggregatorFactories.java | 2 +- .../aggregations/InternalAggregation.java | 5 + .../bucket/geogrid/GeoHashGridParser.java | 36 +-- .../bucket/histogram/DateHistogramParser.java | 86 +----- .../bucket/histogram/ExtendedBounds.java | 5 +- .../bucket/histogram/HistogramAggregator.java | 17 +- .../bucket/histogram/HistogramParser.java | 68 +---- .../histogram/InternalDateHistogram.java | 15 +- .../bucket/histogram/InternalHistogram.java | 66 +++-- .../bucket/missing/MissingParser.java | 32 +-- .../bucket/range/InternalRange.java | 22 +- .../bucket/range/RangeAggregator.java | 54 ++-- .../bucket/range/RangeParser.java | 69 +---- .../bucket/range/date/DateRangeParser.java | 79 +----- .../bucket/range/date/InternalDateRange.java | 3 +- .../range/geodistance/GeoDistanceParser.java | 89 ++---- .../geodistance/InternalGeoDistance.java | 7 +- .../bucket/range/ipv4/InternalIPv4Range.java | 19 +- .../bucket/range/ipv4/IpRangeParser.java | 69 +---- .../significant/SignificantLongTerms.java | 17 +- .../SignificantLongTermsAggregator.java | 10 +- .../SignificantTermsAggregatorFactory.java | 17 +- .../significant/SignificantTermsParser.java | 132 ++------- .../bucket/terms/DoubleTerms.java | 19 +- .../bucket/terms/DoubleTermsAggregator.java | 6 +- .../aggregations/bucket/terms/LongTerms.java | 15 +- .../bucket/terms/LongTermsAggregator.java | 8 +- .../aggregations/bucket/terms/Terms.java | 11 +- .../bucket/terms/TermsAggregatorFactory.java | 12 +- .../bucket/terms/TermsParser.java | 192 ++----------- .../bucket/terms/support/IncludeExclude.java | 88 ++++++ ...icValuesSourceMetricsAggregatorParser.java | 74 +++++ .../ValuesSourceMetricsAggregatorParser.java | 111 -------- .../aggregations/metrics/avg/AvgParser.java | 9 +- .../cardinality/CardinalityParser.java | 67 +---- .../aggregations/metrics/max/MaxParser.java | 9 +- .../aggregations/metrics/min/MinParser.java | 9 +- .../percentiles/PercentilesParser.java | 71 +---- .../metrics/stats/StatsParser.java | 9 +- .../stats/extended/ExtendedStatsParser.java | 9 +- .../aggregations/metrics/sum/SumParser.java | 9 +- .../valuecount/ValueCountAggregator.java | 12 +- .../metrics/valuecount/ValueCountParser.java | 61 +--- .../aggregations/support/FieldContext.java | 9 +- .../aggregations/support/GeoPointParser.java | 103 +++++++ .../aggregations/support/ScriptValueType.java | 48 ---- .../aggregations/support/ValueType.java | 151 ++++++++++ .../aggregations/support/ValuesSource.java | 4 +- .../ValuesSourceAggregatorFactory.java | 23 +- .../support/ValuesSourceConfig.java | 34 +-- .../support/ValuesSourceParser.java | 266 ++++++++++++++++++ .../support/format/ValueFormat.java | 103 +++++++ .../support/format/ValueFormatter.java | 4 + .../support/format/ValueParser.java | 7 + .../bucket/DateHistogramTests.java | 3 +- .../bucket/SignificantTermsTests.java | 2 - 57 files changed, 1199 insertions(+), 1290 deletions(-) create mode 100644 src/main/java/org/elasticsearch/search/aggregations/metrics/NumericValuesSourceMetricsAggregatorParser.java delete mode 100644 src/main/java/org/elasticsearch/search/aggregations/metrics/ValuesSourceMetricsAggregatorParser.java create mode 100644 src/main/java/org/elasticsearch/search/aggregations/support/GeoPointParser.java delete mode 100644 src/main/java/org/elasticsearch/search/aggregations/support/ScriptValueType.java create mode 100644 src/main/java/org/elasticsearch/search/aggregations/support/ValueType.java create mode 100644 src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParser.java create mode 100644 src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormat.java diff --git a/src/main/java/org/elasticsearch/common/geo/GeoHashUtils.java b/src/main/java/org/elasticsearch/common/geo/GeoHashUtils.java index 7c97e0614b5..17d16391349 100644 --- a/src/main/java/org/elasticsearch/common/geo/GeoHashUtils.java +++ b/src/main/java/org/elasticsearch/common/geo/GeoHashUtils.java @@ -449,12 +449,12 @@ public class GeoHashUtils { */ public static String toString(long geohashAsLong) { - int precision= (int) (geohashAsLong&15); - char[] chars=new char[precision]; - geohashAsLong>>=4; - for (int i = precision-1; i >=0 ; i--) { - chars[i]= BASE_32[(int) (geohashAsLong&31)]; - geohashAsLong>>=5; + int precision = (int) (geohashAsLong&15); + char[] chars = new char[precision]; + geohashAsLong >>= 4; + for (int i = precision - 1; i >= 0 ; i--) { + chars[i] = BASE_32[(int) (geohashAsLong & 31)]; + geohashAsLong >>= 5; } return new String(chars); } diff --git a/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java b/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java index 5e5553f9297..40d8f53fd5e 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java +++ b/src/main/java/org/elasticsearch/search/aggregations/AggregatorFactories.java @@ -39,7 +39,7 @@ public class AggregatorFactories { public static final AggregatorFactories EMPTY = new Empty(); - private final AggregatorFactory[] factories; + private AggregatorFactory[] factories; public static Builder builder() { return new Builder(); diff --git a/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java b/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java index a2e61516659..30a5215c2da 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java +++ b/src/main/java/org/elasticsearch/search/aggregations/InternalAggregation.java @@ -74,6 +74,11 @@ public abstract class InternalAggregation implements Aggregation, ToXContent, St public BytesReference stream() { return stream; } + + @Override + public String toString() { + return name; + } } protected static class ReduceContext { diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridParser.java index 9fb24fdf827..e6ea8a81b7f 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/geogrid/GeoHashGridParser.java @@ -21,8 +21,10 @@ package org.elasticsearch.search.aggregations.bucket.geogrid; import org.elasticsearch.common.geo.GeoHashUtils; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.*; -import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.fielddata.BytesValues; +import org.elasticsearch.index.fielddata.DoubleValues; +import org.elasticsearch.index.fielddata.GeoPointValues; +import org.elasticsearch.index.fielddata.LongValues; import org.elasticsearch.index.query.GeoBoundingBoxFilterBuilder; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; @@ -53,21 +55,19 @@ public class GeoHashGridParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - String field = null; + ValuesSourceParser vsParser = ValuesSourceParser.geoPoint(aggregationName, InternalGeoHashGrid.TYPE, context).build(); + int precision = DEFAULT_PRECISION; int requiredSize = DEFAULT_MAX_NUM_CELLS; int shardSize = -1; - XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_NUMBER) { if ("precision".equals(currentFieldName)) { precision = parser.intValue(); @@ -76,7 +76,6 @@ public class GeoHashGridParser implements Aggregator.Parser { } else if ("shard_size".equals(currentFieldName) || "shardSize".equals(currentFieldName)) { shardSize = parser.intValue(); } - } } @@ -97,20 +96,8 @@ public class GeoHashGridParser implements Aggregator.Parser { shardSize = requiredSize; } - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.GeoPoint.class); - if (field == null) { - return new GeoGridFactory(aggregationName, config, precision, requiredSize, shardSize); - } + return new GeoGridFactory(aggregationName, vsParser.config(), precision, requiredSize, shardSize); - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new GeoGridFactory(aggregationName, config, precision, requiredSize, shardSize); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return new GeoGridFactory(aggregationName, config, precision, requiredSize, shardSize); } @@ -120,9 +107,8 @@ public class GeoHashGridParser implements Aggregator.Parser { private int requiredSize; private int shardSize; - public GeoGridFactory(String name, ValuesSourceConfig valueSourceConfig, - int precision, int requiredSize, int shardSize) { - super(name, InternalGeoHashGrid.TYPE.name(), valueSourceConfig); + public GeoGridFactory(String name, ValuesSourceConfig config, int precision, int requiredSize, int shardSize) { + super(name, InternalGeoHashGrid.TYPE.name(), config); this.precision = precision; this.requiredSize = requiredSize; this.shardSize = shardSize; diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramParser.java index a2101e98a38..3148382c0a8 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/DateHistogramParser.java @@ -21,28 +21,19 @@ package org.elasticsearch.search.aggregations.bucket.histogram; import com.google.common.collect.ImmutableMap; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.collect.MapBuilder; -import org.elasticsearch.common.joda.DateMathParser; import org.elasticsearch.common.rounding.DateTimeUnit; import org.elasticsearch.common.rounding.TimeZoneRounding; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.core.DateFieldMapper; -import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.ValueType; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import org.joda.time.DateTimeZone; import java.io.IOException; -import java.util.Map; /** * @@ -82,12 +73,12 @@ public class DateHistogramParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalDateHistogram.TYPE, context) + .targetValueType(ValueType.DATE) + .requiresSortedValues(true) + .formattable(true) + .build(); - String field = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; boolean keyed = false; long minDocCount = 1; ExtendedBounds extendedBounds = null; @@ -96,24 +87,18 @@ public class DateHistogramParser implements Aggregator.Parser { boolean preZoneAdjustLargeInterval = false; DateTimeZone preZone = DateTimeZone.UTC; DateTimeZone postZone = DateTimeZone.UTC; - String format = null; long preOffset = 0; long postOffset = 0; - boolean assumeSorted = false; XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else if ("time_zone".equals(currentFieldName) || "timeZone".equals(currentFieldName)) { + if ("time_zone".equals(currentFieldName) || "timeZone".equals(currentFieldName)) { preZone = parseZone(parser.text()); } else if ("pre_zone".equals(currentFieldName) || "preZone".equals(currentFieldName)) { preZone = parseZone(parser.text()); @@ -125,16 +110,12 @@ public class DateHistogramParser implements Aggregator.Parser { postOffset = parseOffset(parser.text()); } else if ("interval".equals(currentFieldName)) { interval = parser.text(); - } else if ("format".equals(currentFieldName)) { - format = parser.text(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } } else if (token == XContentParser.Token.VALUE_BOOLEAN) { if ("keyed".equals(currentFieldName)) { keyed = parser.booleanValue(); - } else if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); } else if ("pre_zone_adjust_large_interval".equals(currentFieldName) || "preZoneAdjustLargeInterval".equals(currentFieldName)) { preZoneAdjustLargeInterval = parser.booleanValue(); } else { @@ -153,9 +134,7 @@ public class DateHistogramParser implements Aggregator.Parser { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else if ("order".equals(currentFieldName)) { + if ("order".equals(currentFieldName)) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -204,17 +183,6 @@ public class DateHistogramParser implements Aggregator.Parser { throw new SearchParseException(context, "Missing required field [interval] for histogram aggregation [" + aggregationName + "]"); } - SearchScript searchScript = null; - if (script != null) { - searchScript = context.scriptService().search(context.lookup(), scriptLang, script, scriptParams); - config.script(searchScript); - } - - if (!assumeSorted) { - // we need values to be sorted and unique for efficiency - config.ensureSorted(true); - } - TimeZoneRounding.Builder tzRoundingBuilder; DateTimeUnit dateTimeUnit = dateFieldUnits.get(interval); if (dateTimeUnit != null) { @@ -230,38 +198,8 @@ public class DateHistogramParser implements Aggregator.Parser { .preOffset(preOffset).postOffset(postOffset) .build(); - ValueFormatter valueFormatter = format != null ? new ValueFormatter.DateTime(format) : null; + return new HistogramAggregator.Factory(aggregationName, vsParser.config(), rounding, order, keyed, minDocCount, extendedBounds, InternalDateHistogram.FACTORY); - if (field == null) { - - if (searchScript != null) { - ValueParser valueParser = new ValueParser.DateMath(new DateMathParser(DateFieldMapper.Defaults.DATE_TIME_FORMATTER, DateFieldMapper.Defaults.TIME_UNIT)); - return new HistogramAggregator.Factory(aggregationName, config, valueFormatter, valueParser, rounding, order, keyed, minDocCount, extendedBounds, InternalDateHistogram.FACTORY); - } - - // falling back on the get field data context - return new HistogramAggregator.Factory(aggregationName, config, valueFormatter, null, rounding, order, keyed, minDocCount, extendedBounds, InternalDateHistogram.FACTORY); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - valueFormatter = format == null ? new ValueFormatter.DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER) : null; - ValueParser valueParser = new ValueParser.DateMath(new DateMathParser(DateFieldMapper.Defaults.DATE_TIME_FORMATTER, DateFieldMapper.Defaults.TIME_UNIT)); - return new HistogramAggregator.Factory(aggregationName, config, valueFormatter, valueParser, rounding, order, keyed, minDocCount, extendedBounds, InternalDateHistogram.FACTORY); - } - - if (!(mapper instanceof DateFieldMapper)) { - throw new SearchParseException(context, "date histogram can only be aggregated on date fields but [" + field + "] is not a date field"); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - if (format == null) { - valueFormatter = new ValueFormatter.DateTime(((DateFieldMapper) mapper).dateTimeFormatter()); - } - ValueParser valueParser = new ValueParser.DateMath(new DateMathParser(((DateFieldMapper) mapper).dateTimeFormatter(), DateFieldMapper.Defaults.TIME_UNIT)); - return new HistogramAggregator.Factory(aggregationName, config, valueFormatter, valueParser, rounding, order, keyed, minDocCount, extendedBounds, InternalDateHistogram.FACTORY); } private static InternalOrder resolveOrder(String key, boolean asc) { diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ExtendedBounds.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ExtendedBounds.java index 30325460686..b041ef34fdb 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ExtendedBounds.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/ExtendedBounds.java @@ -47,11 +47,12 @@ public class ExtendedBounds { } void processAndValidate(String aggName, SearchContext context, ValueParser parser) { + assert parser != null; if (minAsStr != null) { - min = parser != null ? parser.parseLong(minAsStr, context) : Long.parseLong(minAsStr); + min = parser.parseLong(minAsStr, context); } if (maxAsStr != null) { - max = parser != null ? parser.parseLong(maxAsStr, context) : Long.parseLong(maxAsStr); + max = parser.parseLong(maxAsStr, context); } if (min != null && max != null && min.compareTo(max) > 0) { throw new SearchParseException(context, "[extended_bounds.min][" + min + "] cannot be greater than " + diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java index ebee7e705a0..fab705e25f0 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregator.java @@ -44,11 +44,11 @@ import java.util.List; public class HistogramAggregator extends BucketsAggregator { private final ValuesSource.Numeric valuesSource; - private final ValueFormatter formatter; - private final ValueParser parser; + private final @Nullable ValueFormatter formatter; private final Rounding rounding; private final InternalOrder order; private final boolean keyed; + private final long minDocCount; private final ExtendedBounds extendedBounds; private final InternalHistogram.Factory histogramFactory; @@ -58,7 +58,7 @@ public class HistogramAggregator extends BucketsAggregator { public HistogramAggregator(String name, AggregatorFactories factories, Rounding rounding, InternalOrder order, boolean keyed, long minDocCount, @Nullable ExtendedBounds extendedBounds, - @Nullable ValuesSource.Numeric valuesSource, ValueFormatter formatter, ValueParser parser, + @Nullable ValuesSource.Numeric valuesSource, @Nullable ValueFormatter formatter, long initialCapacity, InternalHistogram.Factory histogramFactory, AggregationContext aggregationContext, Aggregator parent) { @@ -70,7 +70,6 @@ public class HistogramAggregator extends BucketsAggregator { this.extendedBounds = extendedBounds; this.valuesSource = valuesSource; this.formatter = formatter; - this.parser = parser; this.histogramFactory = histogramFactory; bucketOrds = new LongHash(initialCapacity, aggregationContext.bigArrays()); @@ -147,11 +146,11 @@ public class HistogramAggregator extends BucketsAggregator { private final ExtendedBounds extendedBounds; private final InternalHistogram.Factory histogramFactory; - public Factory(String name, ValuesSourceConfig config, ValueFormatter formatter, ValueParser parser, + public Factory(String name, ValuesSourceConfig config, Rounding rounding, InternalOrder order, boolean keyed, long minDocCount, ExtendedBounds extendedBounds, InternalHistogram.Factory histogramFactory) { - super(name, histogramFactory.type(), config, formatter, parser); + super(name, histogramFactory.type(), config); this.rounding = rounding; this.order = order; this.keyed = keyed; @@ -162,7 +161,7 @@ public class HistogramAggregator extends BucketsAggregator { @Override protected Aggregator createUnmapped(AggregationContext aggregationContext, Aggregator parent) { - return new HistogramAggregator(name, factories, rounding, order, keyed, minDocCount, null, null, null, null, 0, histogramFactory, aggregationContext, parent); + return new HistogramAggregator(name, factories, rounding, order, keyed, minDocCount, null, null, config.formatter(), 0, histogramFactory, aggregationContext, parent); } @Override @@ -175,10 +174,10 @@ public class HistogramAggregator extends BucketsAggregator { ExtendedBounds roundedBounds = null; if (extendedBounds != null) { // we need to process & validate here using the parser - extendedBounds.processAndValidate(name, aggregationContext.searchContext(), parser); + extendedBounds.processAndValidate(name, aggregationContext.searchContext(), config.parser()); roundedBounds = extendedBounds.round(rounding); } - return new HistogramAggregator(name, factories, rounding, order, keyed, minDocCount, roundedBounds, valuesSource, formatter, parser, 50, histogramFactory, aggregationContext, parent); + return new HistogramAggregator(name, factories, rounding, order, keyed, minDocCount, roundedBounds, valuesSource, config.formatter(), 50, histogramFactory, aggregationContext, parent); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramParser.java index 6159dd95191..98e010ff215 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramParser.java @@ -21,20 +21,15 @@ package org.elasticsearch.search.aggregations.bucket.histogram; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.rounding.Rounding; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; +import org.elasticsearch.search.aggregations.support.ValueType; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.aggregations.support.format.ValueParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Map; /** * Parses the histogram request @@ -51,18 +46,16 @@ public class HistogramParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalHistogram.TYPE, context) + .requiresSortedValues(true) + .targetValueType(ValueType.NUMERIC) + .formattable(true) + .build(); - String field = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; boolean keyed = false; long minDocCount = 1; InternalOrder order = (InternalOrder) InternalOrder.KEY_ASC; long interval = -1; - boolean assumeSorted = false; - String format = null; ExtendedBounds extendedBounds = null; XContentParser.Token token; @@ -70,16 +63,8 @@ public class HistogramParser implements Aggregator.Parser { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in aggregation [" + aggregationName + "]: [" + currentFieldName + "]."); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_NUMBER) { if ("interval".equals(currentFieldName)) { interval = parser.longValue(); @@ -91,15 +76,11 @@ public class HistogramParser implements Aggregator.Parser { } else if (token == XContentParser.Token.VALUE_BOOLEAN) { if ("keyed".equals(currentFieldName)) { keyed = parser.booleanValue(); - } else if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in aggregation [" + aggregationName + "]: [" + currentFieldName + "]."); } } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else if ("order".equals(currentFieldName)) { + if ("order".equals(currentFieldName)) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); @@ -143,35 +124,10 @@ public class HistogramParser implements Aggregator.Parser { if (extendedBounds != null) { // with numeric histogram, we can process here and fail fast if the bounds are invalid - extendedBounds.processAndValidate(aggregationName, context, null); + extendedBounds.processAndValidate(aggregationName, context, ValueParser.RAW); } - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeSorted) { - // we need values to be sorted and unique for efficiency - config.ensureSorted(true); - } - - if (field == null) { - return new HistogramAggregator.Factory(aggregationName, config, null, null, rounding, order, keyed, minDocCount, extendedBounds, InternalHistogram.FACTORY); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new HistogramAggregator.Factory(aggregationName, config, null, null, rounding, order, keyed, minDocCount, extendedBounds, InternalHistogram.FACTORY); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - - ValueFormatter valueFormatter = format == null ? ValueFormatter.RAW : new ValueFormatter.Number.Pattern(format); - ValueParser valueParser = format == null ? ValueParser.RAW : new ValueParser.Number.Pattern(format); - - return new HistogramAggregator.Factory(aggregationName, config, valueFormatter, valueParser, rounding, order, keyed, minDocCount, extendedBounds, InternalHistogram.FACTORY); + return new HistogramAggregator.Factory(aggregationName, vsParser.config(), rounding, order, keyed, minDocCount, extendedBounds, InternalHistogram.FACTORY); } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java index 4859f748c2d..bb093261e91 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java @@ -19,8 +19,8 @@ package org.elasticsearch.search.aggregations.bucket.histogram; import com.carrotsearch.hppc.ObjectObjectOpenHashMap; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.index.mapper.core.DateFieldMapper; import org.elasticsearch.search.aggregations.AggregationStreams; import org.elasticsearch.search.aggregations.InternalAggregations; import org.elasticsearch.search.aggregations.support.format.ValueFormatter; @@ -53,11 +53,8 @@ public class InternalDateHistogram extends InternalHistogram buckets, InternalOrder order, - long minDocCount, EmptyBucketInfo emptyBucketInfo, ValueFormatter formatter, boolean keyed) { + long minDocCount, EmptyBucketInfo emptyBucketInfo, @Nullable ValueFormatter formatter, boolean keyed) { return new InternalDateHistogram(name, buckets, order, minDocCount, emptyBucketInfo, formatter, keyed); } @Override - public InternalDateHistogram.Bucket createBucket(long key, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { + public InternalDateHistogram.Bucket createBucket(long key, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { return new Bucket(key, docCount, aggregations, formatter); } } @@ -103,7 +100,7 @@ public class InternalDateHistogram extends InternalHistogram buckets, InternalOrder order, long minDocCount, - EmptyBucketInfo emptyBucketInfo, ValueFormatter formatter, boolean keyed) { + EmptyBucketInfo emptyBucketInfo, @Nullable ValueFormatter formatter, boolean keyed) { super(name, buckets, order, minDocCount, emptyBucketInfo, formatter, keyed); } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java index b23f25c32d7..c64554d14a2 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogram.java @@ -22,6 +22,7 @@ import com.carrotsearch.hppc.LongObjectOpenHashMap; import com.google.common.collect.Lists; import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.Version; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.rounding.Rounding; @@ -68,17 +69,19 @@ public class InternalHistogram extends Inter long key; long docCount; + protected transient final @Nullable ValueFormatter formatter; InternalAggregations aggregations; - public Bucket(long key, long docCount, InternalAggregations aggregations) { + public Bucket(long key, long docCount, @Nullable ValueFormatter formatter, InternalAggregations aggregations) { this.key = key; this.docCount = docCount; + this.formatter = formatter; this.aggregations = aggregations; } @Override public String getKey() { - return String.valueOf(key); + return formatter != null ? formatter.format(key) : ValueFormatter.RAW.format(key); } @Override @@ -121,6 +124,28 @@ public class InternalHistogram extends Inter reduced.aggregations = InternalAggregations.reduce(aggregations, bigArrays); return (B) reduced; } + + void toXContent(XContentBuilder builder, Params params, boolean keyed, @Nullable ValueFormatter formatter) throws IOException { + if (formatter != null) { + Text keyTxt = new StringText(formatter.format(key)); + if (keyed) { + builder.startObject(keyTxt.string()); + } else { + builder.startObject(); + } + builder.field(CommonFields.KEY_AS_STRING, keyTxt); + } else { + if (keyed) { + builder.startObject(String.valueOf(getKeyAsNumber())); + } else { + builder.startObject(); + } + } + builder.field(CommonFields.KEY, key); + builder.field(CommonFields.DOC_COUNT, docCount); + aggregations.toXContentInternal(builder, params); + builder.endObject(); + } } static class EmptyBucketInfo { @@ -173,12 +198,12 @@ public class InternalHistogram extends Inter } public InternalHistogram create(String name, List buckets, InternalOrder order, long minDocCount, - EmptyBucketInfo emptyBucketInfo, ValueFormatter formatter, boolean keyed) { + EmptyBucketInfo emptyBucketInfo, @Nullable ValueFormatter formatter, boolean keyed) { return new InternalHistogram<>(name, buckets, order, minDocCount, emptyBucketInfo, formatter, keyed); } - public B createBucket(long key, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { - return (B) new Bucket(key, docCount, aggregations); + public B createBucket(long key, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { + return (B) new Bucket(key, docCount, formatter, aggregations); } } @@ -186,14 +211,15 @@ public class InternalHistogram extends Inter protected List buckets; private LongObjectOpenHashMap bucketsMap; private InternalOrder order; - private ValueFormatter formatter; + private @Nullable ValueFormatter formatter; private boolean keyed; private long minDocCount; private EmptyBucketInfo emptyBucketInfo; InternalHistogram() {} // for serialization - InternalHistogram(String name, List buckets, InternalOrder order, long minDocCount, EmptyBucketInfo emptyBucketInfo, ValueFormatter formatter, boolean keyed) { + InternalHistogram(String name, List buckets, InternalOrder order, long minDocCount, + EmptyBucketInfo emptyBucketInfo, @Nullable ValueFormatter formatter, boolean keyed) { super(name); this.buckets = buckets; this.order = order; @@ -416,8 +442,8 @@ public class InternalHistogram extends Inter return reduced; } - protected B createBucket(long key, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { - return (B) new InternalHistogram.Bucket(key, docCount, aggregations); + protected B createBucket(long key, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { + return (B) new InternalHistogram.Bucket(key, docCount, formatter, aggregations); } @Override @@ -465,29 +491,9 @@ public class InternalHistogram extends Inter } else { builder.startArray(CommonFields.BUCKETS); } - for (B bucket : buckets) { - if (formatter != null) { - Text keyTxt = new StringText(formatter.format(bucket.key)); - if (keyed) { - builder.startObject(keyTxt.string()); - } else { - builder.startObject(); - } - builder.field(CommonFields.KEY_AS_STRING, keyTxt); - } else { - if (keyed) { - builder.startObject(String.valueOf(bucket.getKeyAsNumber())); - } else { - builder.startObject(); - } - } - builder.field(CommonFields.KEY, bucket.key); - builder.field(CommonFields.DOC_COUNT, bucket.docCount); - bucket.aggregations.toXContentInternal(builder, params); - builder.endObject(); + bucket.toXContent(builder, params, keyed, formatter); } - if (keyed) { builder.endObject(); } else { diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingParser.java index 44c7539375c..b37de4c743c 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/missing/MissingParser.java @@ -19,13 +19,10 @@ package org.elasticsearch.search.aggregations.bucket.missing; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -43,37 +40,22 @@ public class MissingParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.class); - - String field = null; + ValuesSourceParser vsParser = ValuesSourceParser.any(aggregationName, InternalMissing.TYPE, context) + .scriptable(false) + .build(); XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else { throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "]."); } } - if (field == null) { - return new MissingAggregator.Factory(aggregationName, config); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new MissingAggregator.Factory(aggregationName, config); - } - - config.fieldContext(new FieldContext(field, context.fieldData().getForField(mapper))); - return new MissingAggregator.Factory(aggregationName, config); + return new MissingAggregator.Factory(aggregationName, vsParser.config()); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java index b9859e6cf98..cea46cd8585 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/InternalRange.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.bucket.range; import com.google.common.collect.Lists; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.text.StringText; @@ -65,8 +66,8 @@ public class InternalRange extends InternalAggre InternalAggregations aggregations; private String key; - public Bucket(String key, double from, double to, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { - this.key = key != null ? key : key(from, to, formatter); + public Bucket(String key, double from, double to, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { + this.key = key != null ? key : generateKey(from, to, formatter); this.from = from; this.to = to; this.docCount = docCount; @@ -123,7 +124,7 @@ public class InternalRange extends InternalAggre return reduced; } - void toXContent(XContentBuilder builder, Params params, ValueFormatter formatter, boolean keyed) throws IOException { + void toXContent(XContentBuilder builder, Params params, @Nullable ValueFormatter formatter, boolean keyed) throws IOException { if (keyed) { builder.startObject(key); } else { @@ -147,11 +148,11 @@ public class InternalRange extends InternalAggre builder.endObject(); } - private static String key(double from, double to, ValueFormatter formatter) { + protected String generateKey(double from, double to, @Nullable ValueFormatter formatter) { StringBuilder sb = new StringBuilder(); - sb.append(Double.isInfinite(from) ? "*" : formatter != null ? formatter.format(from) : from); + sb.append(Double.isInfinite(from) ? "*" : formatter != null ? formatter.format(from) : ValueFormatter.RAW.format(from)); sb.append("-"); - sb.append(Double.isInfinite(to) ? "*" : formatter != null ? formatter.format(to) : to); + sb.append(Double.isInfinite(to) ? "*" : formatter != null ? formatter.format(to) : ValueFormatter.RAW.format(to)); return sb.toString(); } @@ -163,26 +164,25 @@ public class InternalRange extends InternalAggre return TYPE.name(); } - public R create(String name, List ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) { + public R create(String name, List ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) { return (R) new InternalRange<>(name, ranges, formatter, keyed, unmapped); } - public B createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { + public B createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { return (B) new Bucket(key, from, to, docCount, aggregations, formatter); } } private List ranges; private Map rangeMap; - private ValueFormatter formatter; + private @Nullable ValueFormatter formatter; private boolean keyed; - private boolean unmapped; public InternalRange() {} // for serialization - public InternalRange(String name, List ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) { + public InternalRange(String name, List ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) { super(name); this.ranges = ranges; this.formatter = formatter; diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregator.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregator.java index 3ba307220ea..d9c8af6c679 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregator.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeAggregator.java @@ -21,6 +21,7 @@ package org.elasticsearch.search.aggregations.bucket.range; import com.google.common.collect.Lists; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.util.InPlaceMergeSorter; +import org.elasticsearch.common.Nullable; import org.elasticsearch.index.fielddata.DoubleValues; import org.elasticsearch.search.aggregations.*; import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; @@ -28,8 +29,10 @@ import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; import org.elasticsearch.search.aggregations.support.format.ValueFormatter; import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; @@ -65,18 +68,19 @@ public class RangeAggregator extends BucketsAggregator { return "[" + from + " to " + to + ")"; } - public void process(ValueParser parser, AggregationContext aggregationContext) { + public void process(ValueParser parser, SearchContext context) { + assert parser != null; if (fromAsStr != null) { - from = parser != null ? parser.parseDouble(fromAsStr, aggregationContext.searchContext()) : Double.valueOf(fromAsStr); + from = parser.parseDouble(fromAsStr, context); } if (toAsStr != null) { - to = parser != null ? parser.parseDouble(toAsStr, aggregationContext.searchContext()) : Double.valueOf(toAsStr); + to = parser.parseDouble(toAsStr, context); } } } private final ValuesSource.Numeric valuesSource; - private final ValueFormatter formatter; + private final @Nullable ValueFormatter formatter; private final Range[] ranges; private final boolean keyed; private final InternalRange.Factory rangeFactory; @@ -87,8 +91,7 @@ public class RangeAggregator extends BucketsAggregator { public RangeAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, - ValueFormatter formatter, - ValueParser parser, + @Nullable ValueFormat format, InternalRange.Factory rangeFactory, List ranges, boolean keyed, @@ -98,12 +101,14 @@ public class RangeAggregator extends BucketsAggregator { super(name, BucketAggregationMode.MULTI_BUCKETS, factories, ranges.size() * (parent == null ? 1 : parent.estimatedBucketCount()), aggregationContext, parent); assert valuesSource != null; this.valuesSource = valuesSource; - this.formatter = formatter; + this.formatter = format != null ? format.formatter() : null; this.keyed = keyed; this.rangeFactory = rangeFactory; this.ranges = ranges.toArray(new Range[ranges.size()]); + + ValueParser parser = format != null ? format.parser() : ValueParser.RAW; for (int i = 0; i < this.ranges.length; i++) { - this.ranges[i].process(parser, context); + this.ranges[i].process(parser, context.searchContext()); } sortRanges(this.ranges); @@ -193,8 +198,8 @@ public class RangeAggregator extends BucketsAggregator { for (int i = 0; i < ranges.length; i++) { Range range = ranges[i]; final long bucketOrd = subBucketOrdinal(owningBucketOrdinal, i); - org.elasticsearch.search.aggregations.bucket.range.Range.Bucket bucket = rangeFactory.createBucket( - range.key, range.from, range.to, bucketDocCount(bucketOrd),bucketAggregations(bucketOrd), formatter); + org.elasticsearch.search.aggregations.bucket.range.Range.Bucket bucket = + rangeFactory.createBucket(range.key, range.from, range.to, bucketDocCount(bucketOrd),bucketAggregations(bucketOrd), formatter); buckets.add(bucket); } // value source can be null in the case of unmapped fields @@ -207,8 +212,8 @@ public class RangeAggregator extends BucketsAggregator { List buckets = Lists.newArrayListWithCapacity(ranges.length); for (int i = 0; i < ranges.length; i++) { Range range = ranges[i]; - org.elasticsearch.search.aggregations.bucket.range.Range.Bucket bucket = rangeFactory.createBucket( - range.key, range.from, range.to, 0, subAggs, formatter); + org.elasticsearch.search.aggregations.bucket.range.Range.Bucket bucket = + rangeFactory.createBucket(range.key, range.from, range.to, 0, subAggs, formatter); buckets.add(bucket); } // value source can be null in the case of unmapped fields @@ -242,33 +247,30 @@ public class RangeAggregator extends BucketsAggregator { private final boolean keyed; private final InternalRange.Factory factory; private final ValueFormatter formatter; - private final ValueParser parser; public Unmapped(String name, List ranges, boolean keyed, - ValueFormatter formatter, - ValueParser parser, - AggregationContext aggregationContext, + ValueFormat format, + AggregationContext context, Aggregator parent, InternalRange.Factory factory) { - super(name, aggregationContext, parent); + super(name, context, parent); this.ranges = ranges; + ValueParser parser = format != null ? format.parser() : ValueParser.RAW; for (Range range : this.ranges) { - range.process(parser, context); + range.process(parser, context.searchContext()); } this.keyed = keyed; - this.formatter = formatter; - this.parser = parser; + this.formatter = format != null ? format.formatter() : null; this.factory = factory; } @Override public InternalAggregation buildEmptyAggregation() { InternalAggregations subAggs = buildEmptySubAggregations(); - List buckets = - new ArrayList<>(ranges.size()); + List buckets = new ArrayList<>(ranges.size()); for (RangeAggregator.Range range : ranges) { buckets.add(factory.createBucket(range.key, range.from, range.to, 0, subAggs, formatter)); } @@ -282,8 +284,8 @@ public class RangeAggregator extends BucketsAggregator { private final List ranges; private final boolean keyed; - public Factory(String name, ValuesSourceConfig valueSourceConfig, ValueFormatter formatter, ValueParser parser, InternalRange.Factory rangeFactory, List ranges, boolean keyed) { - super(name, rangeFactory.type(), valueSourceConfig, formatter, parser); + public Factory(String name, ValuesSourceConfig valueSourceConfig, InternalRange.Factory rangeFactory, List ranges, boolean keyed) { + super(name, rangeFactory.type(), valueSourceConfig); this.rangeFactory = rangeFactory; this.ranges = ranges; this.keyed = keyed; @@ -291,12 +293,12 @@ public class RangeAggregator extends BucketsAggregator { @Override protected Aggregator createUnmapped(AggregationContext aggregationContext, Aggregator parent) { - return new Unmapped(name, ranges, keyed, formatter, parser, aggregationContext, parent, rangeFactory); + return new Unmapped(name, ranges, keyed, config.format(), aggregationContext, parent, rangeFactory); } @Override protected Aggregator create(ValuesSource.Numeric valuesSource, long expectedBucketsCount, AggregationContext aggregationContext, Aggregator parent) { - return new RangeAggregator(name, factories, valuesSource, formatter, parser, rangeFactory, ranges, keyed, aggregationContext, parent); + return new RangeAggregator(name, factories, valuesSource, config.format(), rangeFactory, ranges, keyed, aggregationContext, parent); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeParser.java index 73b8332fa9d..55edd4a41fb 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/RangeParser.java @@ -19,22 +19,16 @@ package org.elasticsearch.search.aggregations.bucket.range; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * @@ -49,34 +43,21 @@ public class RangeParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - - String field = null; List ranges = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; boolean keyed = false; - boolean assumeSorted = false; - String format = null; + + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalRange.TYPE, context) + .requiresSortedValues(true) + .formattable(true) + .build(); XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else if ("format".equals(currentFieldName)) { - format = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.START_ARRAY) { if ("ranges".equals(currentFieldName)) { ranges = new ArrayList<>(); @@ -111,17 +92,9 @@ public class RangeParser implements Aggregator.Parser { } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } } else if (token == XContentParser.Token.VALUE_BOOLEAN) { if ("keyed".equals(currentFieldName)) { keyed = parser.booleanValue(); - } else if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } @@ -134,30 +107,6 @@ public class RangeParser implements Aggregator.Parser { throw new SearchParseException(context, "Missing [ranges] in ranges aggregator [" + aggregationName + "]"); } - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeSorted) { - // we need values to be sorted and unique for efficiency - config.ensureSorted(true); - } - - if (field == null) { - return new RangeAggregator.Factory(aggregationName, config, null, null, InternalRange.FACTORY, ranges, keyed); - } - - ValueFormatter valueFormatter = format == null ? ValueFormatter.RAW : new ValueFormatter.Number.Pattern(format); - ValueParser valueParser = format == null ? ValueParser.RAW : new ValueParser.Number.Pattern(format); - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new RangeAggregator.Factory(aggregationName, config, valueFormatter, valueParser, InternalRange.FACTORY, ranges, keyed); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return new RangeAggregator.Factory(aggregationName, config, valueFormatter, valueParser, InternalRange.FACTORY, ranges, keyed); + return new RangeAggregator.Factory(aggregationName, vsParser.config(), InternalRange.FACTORY, ranges, keyed); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeParser.java index 6d3ce8d5a87..31a9c5d1472 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/DateRangeParser.java @@ -19,25 +19,18 @@ package org.elasticsearch.search.aggregations.bucket.range.date; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.core.DateFieldMapper; import org.elasticsearch.search.SearchParseException; -import org.elasticsearch.search.aggregations.AggregationExecutionException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator; +import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * @@ -52,34 +45,22 @@ public class DateRangeParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalDateRange.TYPE, context) + .targetValueType(ValueType.DATE) + .requiresSortedValues(true) + .formattable(true) + .build(); - String field = null; List ranges = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; boolean keyed = false; - String format = null; - boolean assumeSorted = false; XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else if ("format".equals(currentFieldName)) { - format = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.START_ARRAY) { if ("ranges".equals(currentFieldName)) { ranges = new ArrayList<>(); @@ -116,17 +97,9 @@ public class DateRangeParser implements Aggregator.Parser { ranges.add(new RangeAggregator.Range(key, from, fromAsStr, to, toAsStr)); } } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } } else if (token == XContentParser.Token.VALUE_BOOLEAN) { if ("keyed".equals(currentFieldName)) { keyed = parser.booleanValue(); - } else if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } @@ -139,38 +112,6 @@ public class DateRangeParser implements Aggregator.Parser { throw new SearchParseException(context, "Missing [ranges] in ranges aggregator [" + aggregationName + "]"); } - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeSorted) { - // we need values to be sorted and unique for efficiency - config.ensureSorted(true); - } - - ValueFormatter valueFormatter = format != null ? new ValueFormatter.DateTime(format) : ValueFormatter.DateTime.DEFAULT; - ValueParser valueParser = ValueParser.DateMath.DEFAULT; - - if (field == null) { - return new RangeAggregator.Factory(aggregationName, config, valueFormatter, valueParser, InternalDateRange.FACTORY, ranges, keyed); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new RangeAggregator.Factory(aggregationName, config, valueFormatter, valueParser, InternalDateRange.FACTORY, ranges, keyed); - } - - if (!(mapper instanceof DateFieldMapper)) { - throw new AggregationExecutionException("date_range aggregation can only be applied to date fields which is not the case with field [" + field + "]"); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - if (format == null) { - valueFormatter = new ValueFormatter.DateTime(((DateFieldMapper) mapper).dateTimeFormatter()); - } - valueParser = new ValueParser.DateMath(((DateFieldMapper) mapper).dateMathParser()); - return new RangeAggregator.Factory(aggregationName, config, valueFormatter, valueParser, InternalDateRange.FACTORY, ranges, keyed); + return new RangeAggregator.Factory(aggregationName, vsParser.config(), InternalDateRange.FACTORY, ranges, keyed); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRange.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRange.java index 25e6829365c..622d250fc28 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRange.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/date/InternalDateRange.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.aggregations.bucket.range.date; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.search.aggregations.AggregationStreams; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -93,7 +94,7 @@ public class InternalDateRange extends InternalRange i InternalDateRange() {} // for serialization - InternalDateRange(String name, List ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) { + InternalDateRange(String name, List ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) { super(name, ranges, formatter, keyed, unmapped); } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceParser.java index f74e1d4012e..4276690655d 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/GeoDistanceParser.java @@ -18,12 +18,15 @@ */ package org.elasticsearch.search.aggregations.bucket.range.geodistance; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.geo.GeoDistance; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.*; -import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.fielddata.BytesValues; +import org.elasticsearch.index.fielddata.DoubleValues; +import org.elasticsearch.index.fielddata.GeoPointValues; +import org.elasticsearch.index.fielddata.LongValues; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; @@ -31,7 +34,6 @@ import org.elasticsearch.search.aggregations.bucket.range.InternalRange; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator.Unmapped; import org.elasticsearch.search.aggregations.support.*; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -43,6 +45,8 @@ import java.util.List; */ public class GeoDistanceParser implements Aggregator.Parser { + private static final ParseField ORIGIN_FIELD = new ParseField("origin", "center", "point", "por"); + @Override public String type() { return InternalGeoDistance.TYPE.name(); @@ -62,9 +66,13 @@ public class GeoDistanceParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - String field = null; + ValuesSourceParser vsParser = ValuesSourceParser.geoPoint(aggregationName, InternalGeoDistance.TYPE, context) + .requiresSortedValues(true) + .build(); + + GeoPointParser geoPointParser = new GeoPointParser(aggregationName, InternalGeoDistance.TYPE, context, ORIGIN_FIELD); + List ranges = null; - GeoPoint origin = null; DistanceUnit unit = DistanceUnit.DEFAULT; GeoDistance distanceType = GeoDistance.DEFAULT; boolean keyed = false; @@ -74,16 +82,15 @@ public class GeoDistanceParser implements Aggregator.Parser { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; + } else if (geoPointParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("unit".equals(currentFieldName)) { + if ("unit".equals(currentFieldName)) { unit = DistanceUnit.fromString(parser.text()); } else if ("distance_type".equals(currentFieldName) || "distanceType".equals(currentFieldName)) { distanceType = GeoDistance.fromString(parser.text()); - } else if ("point".equals(currentFieldName) || "origin".equals(currentFieldName) || "center".equals(currentFieldName)) { - origin = new GeoPoint(); - origin.resetFromString(parser.text()); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } @@ -124,44 +131,7 @@ public class GeoDistanceParser implements Aggregator.Parser { } ranges.add(new RangeAggregator.Range(key(key, from, to), from, fromAsStr, to, toAsStr)); } - } else if ("point".equals(currentFieldName) || "origin".equals(currentFieldName) || "center".equals(currentFieldName)) { - double lat = Double.NaN; - double lon = Double.NaN; - while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { - if (Double.isNaN(lon)) { - lon = parser.doubleValue(); - } else if (Double.isNaN(lat)) { - lat = parser.doubleValue(); - } else { - throw new SearchParseException(context, "malformed [origin] geo point array in geo_distance aggregator [" + aggregationName + "]. " + - "a geo point array must be of the form [lon, lat]"); - } - } - origin = new GeoPoint(lat, lon); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("point".equals(currentFieldName) || "origin".equals(currentFieldName) || "center".equals(currentFieldName)) { - double lat = Double.NaN; - double lon = Double.NaN; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_NUMBER) { - if ("lat".equals(currentFieldName)) { - lat = parser.doubleValue(); - } else if ("lon".equals(currentFieldName)) { - lon = parser.doubleValue(); - } - } - } - if (Double.isNaN(lat) || Double.isNaN(lon)) { - throw new SearchParseException(context, "malformed [origin] geo point object. either [lat] or [lon] (or both) are " + - "missing in geo_distance aggregator [" + aggregationName + "]"); - } - origin = new GeoPoint(lat, lon); - } else { + } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } } else { @@ -173,25 +143,12 @@ public class GeoDistanceParser implements Aggregator.Parser { throw new SearchParseException(context, "Missing [ranges] in geo_distance aggregator [" + aggregationName + "]"); } + GeoPoint origin = geoPointParser.geoPoint(); if (origin == null) { throw new SearchParseException(context, "Missing [origin] in geo_distance aggregator [" + aggregationName + "]"); } - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.GeoPoint.class); - - if (field == null) { - return new GeoDistanceFactory(aggregationName, config, InternalGeoDistance.FACTORY, origin, unit, distanceType, ranges, keyed); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new GeoDistanceFactory(aggregationName, config, InternalGeoDistance.FACTORY, origin, unit, distanceType, ranges, keyed); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return new GeoDistanceFactory(aggregationName, config, InternalGeoDistance.FACTORY, origin, unit, distanceType, ranges, keyed); + return new GeoDistanceFactory(aggregationName, vsParser.config(), InternalGeoDistance.FACTORY, origin, unit, distanceType, ranges, keyed); } private static class GeoDistanceFactory extends ValuesSourceAggregatorFactory { @@ -217,7 +174,7 @@ public class GeoDistanceParser implements Aggregator.Parser { @Override protected Aggregator createUnmapped(AggregationContext aggregationContext, Aggregator parent) { - return new Unmapped(name, ranges, keyed, null, null, aggregationContext, parent, rangeFactory); + return new Unmapped(name, ranges, keyed, null, aggregationContext, parent, rangeFactory); } @Override @@ -228,7 +185,7 @@ public class GeoDistanceParser implements Aggregator.Parser { // we need to ensure uniqueness distanceSource = new ValuesSource.Numeric.SortedAndUnique(distanceSource); } - return new RangeAggregator(name, factories, distanceSource, null, null, rangeFactory, ranges, keyed, aggregationContext, parent); + return new RangeAggregator(name, factories, distanceSource, null, rangeFactory, ranges, keyed, aggregationContext, parent); } private static class DistanceValues extends DoubleValues { diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistance.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistance.java index 63ce76f82fa..102486d26a1 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistance.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/geodistance/InternalGeoDistance.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.aggregations.bucket.range.geodistance; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.search.aggregations.AggregationStreams; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -70,19 +71,19 @@ public class InternalGeoDistance extends InternalRange ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) { + public InternalGeoDistance create(String name, List ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) { return new InternalGeoDistance(name, ranges, formatter, keyed, unmapped); } @Override - public Bucket createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { + public Bucket createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { return new Bucket(key, from, to, docCount, aggregations, formatter); } } InternalGeoDistance() {} // for serialization - public InternalGeoDistance(String name, List ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) { + public InternalGeoDistance(String name, List ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) { super(name, ranges, formatter, keyed, unmapped); } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/InternalIPv4Range.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/InternalIPv4Range.java index ad41a90319b..bd0141e291f 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/InternalIPv4Range.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/InternalIPv4Range.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.aggregations.bucket.range.ipv4; +import org.elasticsearch.common.inject.internal.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.search.aggregations.AggregationStreams; import org.elasticsearch.search.aggregations.InternalAggregation; @@ -54,12 +55,12 @@ public class InternalIPv4Range extends InternalRange i public static class Bucket extends InternalRange.Bucket implements IPv4Range.Bucket { - public Bucket(String key, double from, double to, long docCount, List aggregations, ValueFormatter formatter) { - super(key, from, to, docCount, new InternalAggregations(aggregations), formatter); + public Bucket(String key, double from, double to, long docCount, List aggregations) { + super(key, from, to, docCount, new InternalAggregations(aggregations), ValueFormatter.IPv4); } - public Bucket(String key, double from, double to, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { - super(key, from, to, docCount, aggregations, formatter); + public Bucket(String key, double from, double to, long docCount, InternalAggregations aggregations) { + super(key, from, to, docCount, aggregations, ValueFormatter.IPv4); } @Override @@ -83,13 +84,13 @@ public class InternalIPv4Range extends InternalRange i } @Override - public InternalIPv4Range create(String name, List ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) { + public InternalIPv4Range create(String name, List ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) { return new InternalIPv4Range(name, ranges, keyed, unmapped); } @Override - public Bucket createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { - return new Bucket(key, from, to, docCount, aggregations, formatter); + public Bucket createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) { + return new Bucket(key, from, to, docCount, aggregations); } } @@ -105,8 +106,8 @@ public class InternalIPv4Range extends InternalRange i } @Override - protected Bucket createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, ValueFormatter formatter) { - return new Bucket(key, from, to, docCount, aggregations, formatter); + protected Bucket createBucket(String key, double from, double to, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter ) { + return new Bucket(key, from, to, docCount, aggregations); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/IpRangeParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/IpRangeParser.java index f476227eecd..d411d17d12e 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/IpRangeParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/range/ipv4/IpRangeParser.java @@ -19,25 +19,18 @@ package org.elasticsearch.search.aggregations.bucket.range.ipv4; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.ip.IpFieldMapper; import org.elasticsearch.search.SearchParseException; -import org.elasticsearch.search.aggregations.AggregationExecutionException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.range.RangeAggregator; +import org.elasticsearch.search.aggregations.support.ValueType; import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Map; /** * @@ -52,31 +45,22 @@ public class IpRangeParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalIPv4Range.TYPE, context) + .targetValueType(ValueType.IP) + .requiresSortedValues(true) + .formattable(false) + .build(); - String field = null; List ranges = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; boolean keyed = false; - boolean assumeSorted = false; XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.START_ARRAY) { if ("ranges".equals(currentFieldName)) { ranges = new ArrayList<>(); @@ -118,17 +102,9 @@ public class IpRangeParser implements Aggregator.Parser { } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } } else if (token == XContentParser.Token.VALUE_BOOLEAN) { if ("keyed".equals(currentFieldName)) { keyed = parser.booleanValue(); - } else if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } @@ -141,32 +117,7 @@ public class IpRangeParser implements Aggregator.Parser { throw new SearchParseException(context, "Missing [ranges] in ranges aggregator [" + aggregationName + "]"); } - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeSorted) { - // we need values to be sorted and unique for efficiency - config.ensureSorted(true); - } - - if (field == null) { - return new RangeAggregator.Factory(aggregationName, config, ValueFormatter.IPv4, ValueParser.IPv4, InternalIPv4Range.FACTORY, ranges, keyed); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new RangeAggregator.Factory(aggregationName, config, ValueFormatter.IPv4, ValueParser.IPv4, InternalIPv4Range.FACTORY, ranges, keyed); - } - - if (!(mapper instanceof IpFieldMapper)) { - throw new AggregationExecutionException("ip_range aggregation can only be applied to ip fields which is not the case with field [" + field + "]"); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return new RangeAggregator.Factory(aggregationName, config, ValueFormatter.IPv4, ValueParser.IPv4, InternalIPv4Range.FACTORY, ranges, keyed); + return new RangeAggregator.Factory(aggregationName, vsParser.config(), InternalIPv4Range.FACTORY, ranges, keyed); } private static void parseMaskRange(String cidr, RangeAggregator.Range range, String aggregationName, SearchContext ctx) { diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java index 151ad5d96d4..371ea4e1639 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTerms.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.aggregations.bucket.significant; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.text.StringText; @@ -84,15 +85,15 @@ public class SignificantLongTerms extends InternalSignificantTerms { } - private ValueFormatter valueFormatter; + private ValueFormatter formatter; SignificantLongTerms() {} // for serialization - public SignificantLongTerms(long subsetSize, long supersetSize, String name, ValueFormatter valueFormatter, - int requiredSize, long minDocCount, Collection buckets) { + public SignificantLongTerms(long subsetSize, long supersetSize, String name, @Nullable ValueFormatter formatter, + int requiredSize, long minDocCount, Collection buckets) { super(subsetSize, supersetSize, name, requiredSize, minDocCount, buckets); - this.valueFormatter = valueFormatter; + this.formatter = formatter; } @Override @@ -103,7 +104,7 @@ public class SignificantLongTerms extends InternalSignificantTerms { @Override public void readFrom(StreamInput in) throws IOException { this.name = in.readString(); - this.valueFormatter = ValueFormatterStreams.readOptional(in); + this.formatter = ValueFormatterStreams.readOptional(in); this.requiredSize = readSize(in); this.minDocCount = in.readVLong(); this.subsetSize = in.readVLong(); @@ -124,7 +125,7 @@ public class SignificantLongTerms extends InternalSignificantTerms { @Override public void writeTo(StreamOutput out) throws IOException { out.writeString(name); - ValueFormatterStreams.writeOptional(valueFormatter, out); + ValueFormatterStreams.writeOptional(formatter, out); writeSize(requiredSize, out); out.writeVLong(minDocCount); out.writeVLong(subsetSize); @@ -146,8 +147,8 @@ public class SignificantLongTerms extends InternalSignificantTerms { for (InternalSignificantTerms.Bucket bucket : buckets) { builder.startObject(); builder.field(CommonFields.KEY, ((Bucket) bucket).term); - if (valueFormatter != null) { - builder.field(CommonFields.KEY_AS_STRING, valueFormatter.format(((Bucket) bucket).term)); + if (formatter != null) { + builder.field(CommonFields.KEY_AS_STRING, formatter.format(((Bucket) bucket).term)); } builder.field(CommonFields.DOC_COUNT, bucket.getDocCount()); builder.field("score", bucket.score); diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsAggregator.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsAggregator.java index a1c915368f0..8fed8059a00 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsAggregator.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantLongTermsAggregator.java @@ -19,13 +19,14 @@ package org.elasticsearch.search.aggregations.bucket.significant; import org.apache.lucene.index.IndexReader; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactories; import org.elasticsearch.search.aggregations.bucket.terms.LongTermsAggregator; import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; import org.elasticsearch.search.internal.ContextIndexSearcher; import java.io.IOException; @@ -37,11 +38,11 @@ import java.util.Collections; */ public class SignificantLongTermsAggregator extends LongTermsAggregator { - public SignificantLongTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, ValueFormatter formatter, + public SignificantLongTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, @Nullable ValueFormat format, long estimatedBucketCount, int requiredSize, int shardSize, long minDocCount, AggregationContext aggregationContext, Aggregator parent, SignificantTermsAggregatorFactory termsAggFactory) { - super(name, factories, valuesSource, formatter, estimatedBucketCount, null, requiredSize, shardSize, minDocCount, aggregationContext, parent); + super(name, factories, valuesSource, format, estimatedBucketCount, null, requiredSize, shardSize, minDocCount, aggregationContext, parent); this.termsAggFactory = termsAggFactory; } @@ -95,8 +96,7 @@ public class SignificantLongTermsAggregator extends LongTermsAggregator { bucket.aggregations = bucketAggregations(bucket.bucketOrd); list[i] = bucket; } - return new SignificantLongTerms(subsetSize, supersetSize, name, formatter, requiredSize, minDocCount, - Arrays.asList(list)); + return new SignificantLongTerms(subsetSize, supersetSize, name, formatter, requiredSize, minDocCount, Arrays.asList(list)); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregatorFactory.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregatorFactory.java index 7e3d7e6ce59..387cfc0c4d2 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregatorFactory.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregatorFactory.java @@ -28,15 +28,16 @@ import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lucene.index.FilterableTermsEnum; import org.elasticsearch.common.lucene.index.FreqTermsEnum; import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.search.aggregations.*; +import org.elasticsearch.search.aggregations.AggregationExecutionException; +import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.Aggregator.BucketAggregationMode; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.NonCollectingAggregator; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -60,9 +61,11 @@ public class SignificantTermsAggregatorFactory extends ValuesSourceAggregatorFac private int numberOfAggregatorsCreated = 0; private Filter filter; - public SignificantTermsAggregatorFactory(String name, ValuesSourceConfig valueSourceConfig, ValueFormatter formatter, ValueParser parser, - int requiredSize, int shardSize, long minDocCount, IncludeExclude includeExclude, String executionHint, Filter filter) { - super(name, SignificantStringTerms.TYPE.name(), valueSourceConfig, formatter, parser); + public SignificantTermsAggregatorFactory(String name, ValuesSourceConfig valueSourceConfig, int requiredSize, + int shardSize, long minDocCount, IncludeExclude includeExclude, + String executionHint, Filter filter) { + + super(name, SignificantStringTerms.TYPE.name(), valueSourceConfig); this.requiredSize = requiredSize; this.shardSize = shardSize; this.minDocCount = minDocCount; @@ -150,7 +153,7 @@ public class SignificantTermsAggregatorFactory extends ValuesSourceAggregatorFac if (((ValuesSource.Numeric) valuesSource).isFloatingPoint()) { throw new UnsupportedOperationException("No support for examining floating point numerics"); } - return new SignificantLongTermsAggregator(name, factories, (ValuesSource.Numeric) valuesSource, formatter, estimatedBucketCount, requiredSize, shardSize, minDocCount, aggregationContext, parent, this); + return new SignificantLongTermsAggregator(name, factories, (ValuesSource.Numeric) valuesSource, config.format(), estimatedBucketCount, requiredSize, shardSize, minDocCount, aggregationContext, parent, this); } throw new AggregationExecutionException("sigfnificant_terms aggregation cannot be applied to field [" + config.fieldContext().field() + diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsParser.java index de3688c9e31..d8f2ef80e58 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsParser.java @@ -19,27 +19,16 @@ package org.elasticsearch.search.aggregations.bucket.significant; import org.apache.lucene.search.Filter; -import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexNumericFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.core.DateFieldMapper; -import org.elasticsearch.index.mapper.ip.IpFieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.BucketUtils; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.regex.Pattern; /** * @@ -60,33 +49,32 @@ public class SignificantTermsParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - String field = null; + ValuesSourceParser vsParser = ValuesSourceParser.any(aggregationName, SignificantStringTerms.TYPE, context) + .scriptable(false) + .formattable(true) + .requiresSortedValues(true) + .requiresUniqueValues(true) + .build(); + + IncludeExclude.Parser incExcParser = new IncludeExclude.Parser(aggregationName, SignificantStringTerms.TYPE, context); + Filter filter = null; int requiredSize = DEFAULT_REQUIRED_SIZE; int shardSize = DEFAULT_SHARD_SIZE; - String format = null; - String include = null; - int includeFlags = 0; // 0 means no flags - String exclude = null; - int excludeFlags = 0; // 0 means no flags - String executionHint = null; long minDocCount = DEFAULT_MIN_DOC_COUNT; + String executionHint = null; XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; + } else if (incExcParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("format".equals(currentFieldName)) { - format = parser.text(); - } else if ("include".equals(currentFieldName)) { - include = parser.text(); - } else if ("exclude".equals(currentFieldName)) { - exclude = parser.text(); - } else if ("execution_hint".equals(currentFieldName) || "executionHint".equals(currentFieldName)) { + if ("execution_hint".equals(currentFieldName) || "executionHint".equals(currentFieldName)) { executionHint = parser.text(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); @@ -112,43 +100,8 @@ public class SignificantTermsParser implements Aggregator.Parser { // filters defined by end users and parsed below are not. // if ("background_context".equals(currentFieldName)) { // filter = context.queryParserService().parseInnerFilter(parser).filter(); -// } else +// } - if ("include".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("pattern".equals(currentFieldName)) { - include = parser.text(); - } else if ("flags".equals(currentFieldName)) { - includeFlags = Regex.flagsFromString(parser.text()); - } - } else if (token == XContentParser.Token.VALUE_NUMBER) { - if ("flags".equals(currentFieldName)) { - includeFlags = parser.intValue(); - } - } - } - } else if ("exclude".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("pattern".equals(currentFieldName)) { - exclude = parser.text(); - } else if ("flags".equals(currentFieldName)) { - excludeFlags = Regex.flagsFromString(parser.text()); - } - } else if (token == XContentParser.Token.VALUE_NUMBER) { - if ("flags".equals(currentFieldName)) { - excludeFlags = parser.intValue(); - } - } - } - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } } else { throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "]."); } @@ -171,55 +124,8 @@ public class SignificantTermsParser implements Aggregator.Parser { shardSize = requiredSize; } - IncludeExclude includeExclude = null; - if (include != null || exclude != null) { - Pattern includePattern = include != null ? Pattern.compile(include, includeFlags) : null; - Pattern excludePattern = exclude != null ? Pattern.compile(exclude, excludeFlags) : null; - includeExclude = new IncludeExclude(includePattern, excludePattern); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - config.unmapped(true); - return new SignificantTermsAggregatorFactory(aggregationName, config, null, null, requiredSize, shardSize, minDocCount, includeExclude, executionHint, filter); - } - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - - ValuesSourceConfig config; - ValueFormatter valueFormatter = null; - ValueParser valueParser = null; - if (mapper instanceof DateFieldMapper) { - DateFieldMapper dateMapper = (DateFieldMapper) mapper; - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - valueFormatter = format == null ? - new ValueFormatter.DateTime(dateMapper.dateTimeFormatter()) : - new ValueFormatter.DateTime(format); - valueParser = new ValueParser.DateMath(dateMapper.dateMathParser()); - - } else if (mapper instanceof IpFieldMapper) { - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - valueFormatter = ValueFormatter.IPv4; - valueParser = ValueParser.IPv4; - - } else if (indexFieldData instanceof IndexNumericFieldData) { - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - if (format != null) { - valueFormatter = new ValueFormatter.Number.Pattern(format); - } - - } else { - config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - // TODO: it will make sense to set false instead here if the aggregator factory uses - // ordinals instead of hash tables - config.needsHashes(true); - } - - config.fieldContext(new FieldContext(field, indexFieldData)); - // We need values to be unique to be able to run terms aggs efficiently - config.ensureUnique(true); - - return new SignificantTermsAggregatorFactory(aggregationName, config, valueFormatter, valueParser, requiredSize, shardSize, minDocCount, includeExclude, executionHint, filter); + IncludeExclude includeExclude = incExcParser.includeExclude(); + return new SignificantTermsAggregatorFactory(aggregationName, vsParser.config(), requiredSize, shardSize, minDocCount, includeExclude, executionHint, filter); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java index 7943007e4e7..d81e8bba91e 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTerms.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.aggregations.bucket.terms; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.text.StringText; @@ -88,17 +89,13 @@ public class DoubleTerms extends InternalTerms { } - private ValueFormatter valueFormatter; + private @Nullable ValueFormatter formatter; DoubleTerms() {} // for serialization - public DoubleTerms(String name, InternalOrder order, int requiredSize, long minDocCount, Collection buckets) { - this(name, order, null, requiredSize, minDocCount, buckets); - } - - public DoubleTerms(String name, InternalOrder order, ValueFormatter valueFormatter, int requiredSize, long minDocCount, Collection buckets) { + public DoubleTerms(String name, InternalOrder order, @Nullable ValueFormatter formatter, int requiredSize, long minDocCount, Collection buckets) { super(name, order, requiredSize, minDocCount, buckets); - this.valueFormatter = valueFormatter; + this.formatter = formatter; } @Override @@ -166,7 +163,7 @@ public class DoubleTerms extends InternalTerms { public void readFrom(StreamInput in) throws IOException { this.name = in.readString(); this.order = InternalOrder.Streams.readOrder(in); - this.valueFormatter = ValueFormatterStreams.readOptional(in); + this.formatter = ValueFormatterStreams.readOptional(in); this.requiredSize = readSize(in); this.minDocCount = in.readVLong(); int size = in.readVInt(); @@ -182,7 +179,7 @@ public class DoubleTerms extends InternalTerms { public void writeTo(StreamOutput out) throws IOException { out.writeString(name); InternalOrder.Streams.writeOrder(order, out); - ValueFormatterStreams.writeOptional(valueFormatter, out); + ValueFormatterStreams.writeOptional(formatter, out); writeSize(requiredSize, out); out.writeVLong(minDocCount); out.writeVInt(buckets.size()); @@ -200,8 +197,8 @@ public class DoubleTerms extends InternalTerms { for (InternalTerms.Bucket bucket : buckets) { builder.startObject(); builder.field(CommonFields.KEY, ((Bucket) bucket).term); - if (valueFormatter != null) { - builder.field(CommonFields.KEY_AS_STRING, valueFormatter.format(((Bucket) bucket).term)); + if (formatter != null) { + builder.field(CommonFields.KEY_AS_STRING, formatter.format(((Bucket) bucket).term)); } builder.field(CommonFields.DOC_COUNT, bucket.getDocCount()); ((InternalAggregations) bucket.getAggregations()).toXContentInternal(builder, params); diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java index 25c83762422..c9a44ea24e2 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/DoubleTermsAggregator.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.bucket.terms; import org.apache.lucene.index.AtomicReaderContext; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.util.LongHash; import org.elasticsearch.index.fielddata.DoubleValues; @@ -28,6 +29,7 @@ import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; import org.elasticsearch.search.aggregations.bucket.terms.support.BucketPriorityQueue; import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSource; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; import org.elasticsearch.search.aggregations.support.format.ValueFormatter; import java.io.IOException; @@ -48,11 +50,11 @@ public class DoubleTermsAggregator extends BucketsAggregator { private final LongHash bucketOrds; private DoubleValues values; - public DoubleTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, ValueFormatter formatter, long estimatedBucketCount, + public DoubleTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, @Nullable ValueFormat format, long estimatedBucketCount, InternalOrder order, int requiredSize, int shardSize, long minDocCount, AggregationContext aggregationContext, Aggregator parent) { super(name, BucketAggregationMode.PER_BUCKET, factories, estimatedBucketCount, aggregationContext, parent); this.valuesSource = valuesSource; - this.formatter = formatter; + this.formatter = format != null ? format.formatter() : null; this.order = InternalOrder.validate(order, this); this.requiredSize = requiredSize; this.shardSize = shardSize; diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java index b26d3558cc9..65e626dbea3 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTerms.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.search.aggregations.bucket.terms; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.text.StringText; @@ -88,13 +89,13 @@ public class LongTerms extends InternalTerms { } } - private ValueFormatter valueFormatter; + private @Nullable ValueFormatter formatter; LongTerms() {} // for serialization - public LongTerms(String name, InternalOrder order, ValueFormatter valueFormatter, int requiredSize, long minDocCount, Collection buckets) { + public LongTerms(String name, InternalOrder order, @Nullable ValueFormatter formatter, int requiredSize, long minDocCount, Collection buckets) { super(name, order, requiredSize, minDocCount, buckets); - this.valueFormatter = valueFormatter; + this.formatter = formatter; } @Override @@ -162,7 +163,7 @@ public class LongTerms extends InternalTerms { public void readFrom(StreamInput in) throws IOException { this.name = in.readString(); this.order = InternalOrder.Streams.readOrder(in); - this.valueFormatter = ValueFormatterStreams.readOptional(in); + this.formatter = ValueFormatterStreams.readOptional(in); this.requiredSize = readSize(in); this.minDocCount = in.readVLong(); int size = in.readVInt(); @@ -178,7 +179,7 @@ public class LongTerms extends InternalTerms { public void writeTo(StreamOutput out) throws IOException { out.writeString(name); InternalOrder.Streams.writeOrder(order, out); - ValueFormatterStreams.writeOptional(valueFormatter, out); + ValueFormatterStreams.writeOptional(formatter, out); writeSize(requiredSize, out); out.writeVLong(minDocCount); out.writeVInt(buckets.size()); @@ -196,8 +197,8 @@ public class LongTerms extends InternalTerms { for (InternalTerms.Bucket bucket : buckets) { builder.startObject(); builder.field(CommonFields.KEY, ((Bucket) bucket).term); - if (valueFormatter != null) { - builder.field(CommonFields.KEY_AS_STRING, valueFormatter.format(((Bucket) bucket).term)); + if (formatter != null) { + builder.field(CommonFields.KEY_AS_STRING, formatter.format(((Bucket) bucket).term)); } builder.field(CommonFields.DOC_COUNT, bucket.getDocCount()); ((InternalAggregations) bucket.getAggregations()).toXContentInternal(builder, params); diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java index 9e8d0b92533..cb1a2224293 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/LongTermsAggregator.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.bucket.terms; import org.apache.lucene.index.AtomicReaderContext; +import org.elasticsearch.common.Nullable; import org.elasticsearch.common.lease.Releasables; import org.elasticsearch.common.util.LongHash; import org.elasticsearch.index.fielddata.LongValues; @@ -29,6 +30,7 @@ import org.elasticsearch.search.aggregations.bucket.BucketsAggregator; import org.elasticsearch.search.aggregations.bucket.terms.support.BucketPriorityQueue; import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSource; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; import org.elasticsearch.search.aggregations.support.format.ValueFormatter; import java.io.IOException; @@ -45,15 +47,15 @@ public class LongTermsAggregator extends BucketsAggregator { protected final int shardSize; protected final long minDocCount; protected final ValuesSource.Numeric valuesSource; - protected final ValueFormatter formatter; + protected final @Nullable ValueFormatter formatter; protected final LongHash bucketOrds; private LongValues values; - public LongTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, ValueFormatter formatter, long estimatedBucketCount, + public LongTermsAggregator(String name, AggregatorFactories factories, ValuesSource.Numeric valuesSource, @Nullable ValueFormat format, long estimatedBucketCount, InternalOrder order, int requiredSize, int shardSize, long minDocCount, AggregationContext aggregationContext, Aggregator parent) { super(name, BucketAggregationMode.PER_BUCKET, factories, estimatedBucketCount, aggregationContext, parent); this.valuesSource = valuesSource; - this.formatter = formatter; + this.formatter = format != null ? format.formatter() : null; this.order = InternalOrder.validate(order, this); this.requiredSize = requiredSize; this.shardSize = shardSize; diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java index 3c7b9b27364..f1663fd153a 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/Terms.java @@ -21,7 +21,6 @@ package org.elasticsearch.search.aggregations.bucket.terms; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; -import org.elasticsearch.search.aggregations.support.ScriptValueType; import java.util.Collection; import java.util.Comparator; @@ -34,13 +33,13 @@ public interface Terms extends MultiBucketsAggregation { static enum ValueType { - STRING(ScriptValueType.STRING), - LONG(ScriptValueType.LONG), - DOUBLE(ScriptValueType.DOUBLE); + STRING(org.elasticsearch.search.aggregations.support.ValueType.STRING), + LONG(org.elasticsearch.search.aggregations.support.ValueType.LONG), + DOUBLE(org.elasticsearch.search.aggregations.support.ValueType.DOUBLE); - final ScriptValueType scriptValueType; + final org.elasticsearch.search.aggregations.support.ValueType scriptValueType; - private ValueType(ScriptValueType scriptValueType) { + private ValueType(org.elasticsearch.search.aggregations.support.ValueType scriptValueType) { this.scriptValueType = scriptValueType; } diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java index c0ea410cae0..9dd52435cbe 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java @@ -27,8 +27,6 @@ import org.elasticsearch.search.aggregations.support.AggregationContext; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; /** * @@ -140,10 +138,10 @@ public class TermsAggregatorFactory extends ValuesSourceAggregatorFactory { private final IncludeExclude includeExclude; private final String executionHint; - public TermsAggregatorFactory(String name, ValuesSourceConfig config, ValueFormatter formatter, ValueParser parser, - InternalOrder order, int requiredSize, int shardSize, long minDocCount, IncludeExclude includeExclude, String executionHint) { + public TermsAggregatorFactory(String name, ValuesSourceConfig config, InternalOrder order, int requiredSize, + int shardSize, long minDocCount, IncludeExclude includeExclude, String executionHint) { - super(name, StringTerms.TYPE.name(), config, formatter, parser); + super(name, StringTerms.TYPE.name(), config); this.order = order; this.requiredSize = requiredSize; this.shardSize = shardSize; @@ -225,9 +223,9 @@ public class TermsAggregatorFactory extends ValuesSourceAggregatorFactory { if (valuesSource instanceof ValuesSource.Numeric) { if (((ValuesSource.Numeric) valuesSource).isFloatingPoint()) { - return new DoubleTermsAggregator(name, factories, (ValuesSource.Numeric) valuesSource, formatter, estimatedBucketCount, order, requiredSize, shardSize, minDocCount, aggregationContext, parent); + return new DoubleTermsAggregator(name, factories, (ValuesSource.Numeric) valuesSource, config.format(), estimatedBucketCount, order, requiredSize, shardSize, minDocCount, aggregationContext, parent); } - return new LongTermsAggregator(name, factories, (ValuesSource.Numeric) valuesSource, formatter, estimatedBucketCount, order, requiredSize, shardSize, minDocCount, aggregationContext, parent); + return new LongTermsAggregator(name, factories, (ValuesSource.Numeric) valuesSource, config.format(), estimatedBucketCount, order, requiredSize, shardSize, minDocCount, aggregationContext, parent); } throw new AggregationExecutionException("terms aggregation cannot be applied to field [" + config.fieldContext().field() + diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsParser.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsParser.java index a7baae01fbe..be379ada8a2 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/TermsParser.java @@ -18,28 +18,15 @@ */ package org.elasticsearch.search.aggregations.bucket.terms; -import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.fielddata.IndexNumericFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.core.DateFieldMapper; -import org.elasticsearch.index.mapper.ip.IpFieldMapper; -import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.bucket.terms.support.IncludeExclude; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Map; -import java.util.regex.Pattern; /** * @@ -54,56 +41,37 @@ public class TermsParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - String field = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; - Terms.ValueType valueType = null; int requiredSize = 10; int shardSize = -1; String orderKey = "_count"; boolean orderAsc = false; - String format = null; - boolean assumeUnique = false; - String include = null; - int includeFlags = 0; // 0 means no flags - String exclude = null; - int excludeFlags = 0; // 0 means no flags + String executionHint = null; long minDocCount = 1; + ValuesSourceParser vsParser = ValuesSourceParser.any(aggregationName, StringTerms.TYPE, context) + .requiresSortedValues(true) + .requiresUniqueValues(true) + .formattable(true) + .build(); + + IncludeExclude.Parser incExcParser = new IncludeExclude.Parser(aggregationName, StringTerms.TYPE, context); XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; + } else if (incExcParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) { - valueType = Terms.ValueType.resolveType(parser.text()); - } else if ("format".equals(currentFieldName)) { - format = parser.text(); - } else if ("include".equals(currentFieldName)) { - include = parser.text(); - } else if ("exclude".equals(currentFieldName)) { - exclude = parser.text(); - } else if ("execution_hint".equals(currentFieldName) || "executionHint".equals(currentFieldName)) { + if ("execution_hint".equals(currentFieldName) || "executionHint".equals(currentFieldName)) { executionHint = parser.text(); } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } - } else if (token == XContentParser.Token.VALUE_BOOLEAN) { - if ("script_values_unique".equals(currentFieldName) || "scriptValuesUnique".equals(currentFieldName)) { - assumeUnique = parser.booleanValue(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } } else if (token == XContentParser.Token.VALUE_NUMBER) { if ("size".equals(currentFieldName)) { requiredSize = parser.intValue(); @@ -115,9 +83,7 @@ public class TermsParser implements Aggregator.Parser { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else if ("order".equals(currentFieldName)) { + if ("order".equals(currentFieldName)) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { orderKey = parser.currentName(); @@ -134,38 +100,6 @@ public class TermsParser implements Aggregator.Parser { throw new SearchParseException(context, "Unexpected token " + token + " for [order] in [" + aggregationName + "]."); } } - } else if ("include".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("pattern".equals(currentFieldName)) { - include = parser.text(); - } else if ("flags".equals(currentFieldName)) { - includeFlags = Regex.flagsFromString(parser.text()); - } - } else if (token == XContentParser.Token.VALUE_NUMBER) { - if ("flags".equals(currentFieldName)) { - includeFlags = parser.intValue(); - } - } - } - } else if ("exclude".equals(currentFieldName)) { - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("pattern".equals(currentFieldName)) { - exclude = parser.text(); - } else if ("flags".equals(currentFieldName)) { - excludeFlags = Regex.flagsFromString(parser.text()); - } - } else if (token == XContentParser.Token.VALUE_NUMBER) { - if ("flags".equals(currentFieldName)) { - excludeFlags = parser.intValue(); - } - } - } } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } @@ -187,101 +121,9 @@ public class TermsParser implements Aggregator.Parser { shardSize = requiredSize; } - IncludeExclude includeExclude = null; - if (include != null || exclude != null) { - Pattern includePattern = include != null ? Pattern.compile(include, includeFlags) : null; - Pattern excludePattern = exclude != null ? Pattern.compile(exclude, excludeFlags) : null; - includeExclude = new IncludeExclude(includePattern, excludePattern); - } - + IncludeExclude includeExclude = incExcParser.includeExclude(); InternalOrder order = resolveOrder(orderKey, orderAsc); - SearchScript searchScript = null; - if (script != null) { - searchScript = context.scriptService().search(context.lookup(), scriptLang, script, scriptParams); - } - - if (field == null) { - - Class valueSourceType = script == null ? - ValuesSource.class : // unknown, will inherit whatever is in the context - valueType != null ? valueType.scriptValueType.getValuesSourceType() : // the user explicitly defined a value type - ValuesSource.Bytes.class; // defaulting to bytes - - ValuesSourceConfig config = new ValuesSourceConfig(valueSourceType); - ValueFormatter valueFormatter = null; - ValueParser valueParser = null; - if (valueType != null) { - config.scriptValueType(valueType.scriptValueType); - if (valueType != Terms.ValueType.STRING && format != null) { - valueFormatter = new ValueFormatter.Number.Pattern(format); - valueParser = new ValueParser.Number.Pattern(format); - } - } - config.script(searchScript); - if (!assumeUnique) { - config.ensureUnique(true); - } - return new TermsAggregatorFactory(aggregationName, config, valueFormatter, valueParser, order, requiredSize, shardSize, minDocCount, includeExclude, executionHint); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - ValueFormatter valueFormatter = null; - ValueParser valueParser = null; - config.unmapped(true); - if (valueType != null) { - config.scriptValueType(valueType.scriptValueType); - if (valueType != Terms.ValueType.STRING && format != null) { - valueFormatter = new ValueFormatter.Number.Pattern(format); - valueParser = new ValueParser.Number.Pattern(format); - } - } - return new TermsAggregatorFactory(aggregationName, config, valueFormatter, valueParser, order, requiredSize, shardSize, minDocCount, includeExclude, executionHint); - } - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - - ValuesSourceConfig config; - ValueFormatter valueFormatter = null; - ValueParser valueParser = null; - - if (mapper instanceof DateFieldMapper) { - DateFieldMapper dateMapper = (DateFieldMapper) mapper; - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - valueFormatter = format == null ? - new ValueFormatter.DateTime(dateMapper.dateTimeFormatter()) : - new ValueFormatter.DateTime(format); - valueParser = new ValueParser.DateMath(dateMapper.dateMathParser()); - - } else if (mapper instanceof IpFieldMapper) { - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - valueFormatter = ValueFormatter.IPv4; - valueParser = ValueParser.IPv4; - - } else if (indexFieldData instanceof IndexNumericFieldData) { - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - if (format != null) { - valueFormatter = new ValueFormatter.Number.Pattern(format); - valueParser = new ValueParser.Number.Pattern(format); - } - - } else { - config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - // TODO: it will make sense to set false instead here if the aggregator factory uses - // ordinals instead of hash tables - config.needsHashes(true); - } - - config.script(searchScript); - - config.fieldContext(new FieldContext(field, indexFieldData)); - - // We need values to be unique to be able to run terms aggs efficiently - if (!assumeUnique) { - config.ensureUnique(true); - } - - return new TermsAggregatorFactory(aggregationName, config, valueFormatter, valueParser, order, requiredSize, shardSize, minDocCount, includeExclude, executionHint); + return new TermsAggregatorFactory(aggregationName, vsParser.config(), order, requiredSize, shardSize, minDocCount, includeExclude, executionHint); } static InternalOrder resolveOrder(String key, boolean asc) { diff --git a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java index 936efca6374..27c012c6a58 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java +++ b/src/main/java/org/elasticsearch/search/aggregations/bucket/terms/support/IncludeExclude.java @@ -21,7 +21,13 @@ package org.elasticsearch.search.aggregations.bucket.terms.support; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.CharsRef; import org.apache.lucene.util.UnicodeUtil; +import org.elasticsearch.common.regex.Regex; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.SearchParseException; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.internal.SearchContext; +import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -64,4 +70,86 @@ public class IncludeExclude { } return !exclude.reset(scratch).matches(); } + + public static class Parser { + + private final String aggName; + private final InternalAggregation.Type aggType; + private final SearchContext context; + + String include = null; + int includeFlags = 0; // 0 means no flags + String exclude = null; + int excludeFlags = 0; // 0 means no flags + + public Parser(String aggName, InternalAggregation.Type aggType, SearchContext context) { + this.aggName = aggName; + this.aggType = aggType; + this.context = context; + } + + public boolean token(String currentFieldName, XContentParser.Token token, XContentParser parser) throws IOException { + + if (token == XContentParser.Token.VALUE_STRING) { + if ("include".equals(currentFieldName)) { + include = parser.text(); + } else if ("exclude".equals(currentFieldName)) { + exclude = parser.text(); + } else { + return false; + } + return true; + } + + if (token == XContentParser.Token.START_OBJECT) { + if ("include".equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_STRING) { + if ("pattern".equals(currentFieldName)) { + include = parser.text(); + } else if ("flags".equals(currentFieldName)) { + includeFlags = Regex.flagsFromString(parser.text()); + } + } else if (token == XContentParser.Token.VALUE_NUMBER) { + if ("flags".equals(currentFieldName)) { + includeFlags = parser.intValue(); + } + } + } + } else if ("exclude".equals(currentFieldName)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_STRING) { + if ("pattern".equals(currentFieldName)) { + exclude = parser.text(); + } else if ("flags".equals(currentFieldName)) { + excludeFlags = Regex.flagsFromString(parser.text()); + } + } else if (token == XContentParser.Token.VALUE_NUMBER) { + if ("flags".equals(currentFieldName)) { + excludeFlags = parser.intValue(); + } + } + } + } else { + return false; + } + return true; + } + + return false; + } + + public IncludeExclude includeExclude() { + if (include == null && exclude == null) { + return null; + } + Pattern includePattern = include != null ? Pattern.compile(include, includeFlags) : null; + Pattern excludePattern = exclude != null ? Pattern.compile(exclude, excludeFlags) : null; + return new IncludeExclude(includePattern, excludePattern); + } + } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/NumericValuesSourceMetricsAggregatorParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/NumericValuesSourceMetricsAggregatorParser.java new file mode 100644 index 00000000000..44f76f3224a --- /dev/null +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/NumericValuesSourceMetricsAggregatorParser.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; + +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.SearchParseException; +import org.elasticsearch.search.aggregations.Aggregator; +import org.elasticsearch.search.aggregations.AggregatorFactory; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.support.ValuesSource; +import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; +import org.elasticsearch.search.internal.SearchContext; + +import java.io.IOException; + +/** + * + */ +public abstract class NumericValuesSourceMetricsAggregatorParser implements Aggregator.Parser { + + protected final InternalAggregation.Type aggType; + + protected NumericValuesSourceMetricsAggregatorParser(InternalAggregation.Type aggType) { + this.aggType = aggType; + } + + @Override + public String type() { + return aggType.name(); + } + + protected boolean requiresSortedValues() { + return false; + } + + @Override + public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { + + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, aggType, context) + .requiresSortedValues(requiresSortedValues()) + .build(); + + XContentParser.Token token; + String currentFieldName = null; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (!vsParser.token(currentFieldName, token, parser)) { + throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "]."); + } + } + + return createFactory(aggregationName, vsParser.config()); + } + + protected abstract AggregatorFactory createFactory(String aggregationName, ValuesSourceConfig config); +} diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/ValuesSourceMetricsAggregatorParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/ValuesSourceMetricsAggregatorParser.java deleted file mode 100644 index 0d1018d7e18..00000000000 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/ValuesSourceMetricsAggregatorParser.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.search.SearchParseException; -import org.elasticsearch.search.aggregations.Aggregator; -import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; -import org.elasticsearch.search.internal.SearchContext; - -import java.io.IOException; -import java.util.Map; - -/** - * - */ -public abstract class ValuesSourceMetricsAggregatorParser implements Aggregator.Parser { - - protected boolean requiresSortedValues() { - return false; - } - - @Override - public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - - String field = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; - boolean assumeSorted = false; - - XContentParser.Token token; - String currentFieldName = null; - while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.FIELD_NAME) { - currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.VALUE_BOOLEAN) { - if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else { - throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "]."); - } - } - - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeSorted && requiresSortedValues()) { - config.ensureSorted(true); - } - - if (field == null) { - return createFactory(aggregationName, config); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return createFactory(aggregationName, config); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return createFactory(aggregationName, config); - } - - protected abstract AggregatorFactory createFactory(String aggregationName, ValuesSourceConfig config); -} diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgParser.java index fcaa441c771..1c2b2be5345 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/avg/AvgParser.java @@ -19,18 +19,17 @@ package org.elasticsearch.search.aggregations.metrics.avg; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.metrics.ValuesSourceMetricsAggregatorParser; +import org.elasticsearch.search.aggregations.metrics.NumericValuesSourceMetricsAggregatorParser; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; /** * */ -public class AvgParser extends ValuesSourceMetricsAggregatorParser { +public class AvgParser extends NumericValuesSourceMetricsAggregatorParser { - @Override - public String type() { - return InternalAvg.TYPE.name(); + public AvgParser() { + super(InternalAvg.TYPE); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityParser.java index efd166a86c0..8b176b5ee25 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/cardinality/CardinalityParser.java @@ -21,20 +21,15 @@ package org.elasticsearch.search.aggregations.metrics.cardinality; import org.elasticsearch.common.ParseField; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.core.Murmur3FieldMapper; -import org.elasticsearch.index.mapper.core.NumberFieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Map; public class CardinalityParser implements Aggregator.Parser { @@ -48,34 +43,19 @@ public class CardinalityParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String name, XContentParser parser, SearchContext context) throws IOException { + + ValuesSourceParser vsParser = ValuesSourceParser.any(name, InternalCardinality.TYPE, context).build(); + long precisionThreshold = -1; Boolean rehash = null; - String field = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + name + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + name + "]: [" + currentFieldName + "]."); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.VALUE_BOOLEAN) { if ("rehash".equals(currentFieldName)) { rehash = parser.booleanValue(); @@ -93,41 +73,16 @@ public class CardinalityParser implements Aggregator.Parser { } } - ValuesSourceConfig config = null; + ValuesSourceConfig config = vsParser.config(); - if (script != null) { - config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (field != null) { - FieldMapper mapper = context.smartNameFieldMapper(field); - if (config == null) { - if (mapper instanceof NumberFieldMapper) { - config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); - } else { - config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - if (mapper == null) { - config.unmapped(true); - } - } - if (rehash == null && mapper instanceof Murmur3FieldMapper) { - rehash = false; - } - } - if (mapper != null) { - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - } - } else if (config == null) { - config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - } - - if (rehash == null) { + if (rehash == null && config.fieldContext() != null && config.fieldContext().mapper() instanceof Murmur3FieldMapper) { + rehash = false; + } else if (rehash == null) { rehash = true; } return new CardinalityAggregatorFactory(name, config, precisionThreshold, rehash); + } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxParser.java index 4c3c8d3587c..a5cdac5f638 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/max/MaxParser.java @@ -19,18 +19,17 @@ package org.elasticsearch.search.aggregations.metrics.max; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.metrics.ValuesSourceMetricsAggregatorParser; +import org.elasticsearch.search.aggregations.metrics.NumericValuesSourceMetricsAggregatorParser; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; /** * */ -public class MaxParser extends ValuesSourceMetricsAggregatorParser { +public class MaxParser extends NumericValuesSourceMetricsAggregatorParser { - @Override - public String type() { - return InternalMax.TYPE.name(); + public MaxParser() { + super(InternalMax.TYPE); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinParser.java index de55a029799..8f25127f736 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/min/MinParser.java @@ -19,18 +19,17 @@ package org.elasticsearch.search.aggregations.metrics.min; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.metrics.ValuesSourceMetricsAggregatorParser; +import org.elasticsearch.search.aggregations.metrics.NumericValuesSourceMetricsAggregatorParser; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; /** * */ -public class MinParser extends ValuesSourceMetricsAggregatorParser { +public class MinParser extends NumericValuesSourceMetricsAggregatorParser { - @Override - public String type() { - return InternalMin.TYPE.name(); + public MinParser() { + super(InternalMin.TYPE); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesParser.java index 7529dc9a859..1e3c21627c4 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentilesParser.java @@ -20,15 +20,12 @@ package org.elasticsearch.search.aggregations.metrics.percentiles; import com.carrotsearch.hppc.DoubleArrayList; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; import org.elasticsearch.search.aggregations.metrics.percentiles.tdigest.TDigest; import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; @@ -55,14 +52,11 @@ public class PercentilesParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); + ValuesSourceParser vsParser = ValuesSourceParser.numeric(aggregationName, InternalPercentiles.TYPE, context) + .requiresSortedValues(true) + .build(); - String field = null; - String script = null; - String scriptLang = null; double[] percents = DEFAULT_PERCENTS; - Map scriptParams = null; - boolean assumeSorted = false; boolean keyed = true; Map settings = null; @@ -71,19 +65,8 @@ public class PercentilesParser implements Aggregator.Parser { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else { - if (settings == null) { - settings = new HashMap<>(); - } - settings.put(currentFieldName, parser.text()); - } + } else if (vsParser.token(currentFieldName, token, parser)) { + continue; } else if (token == XContentParser.Token.START_ARRAY) { if ("percents".equals(currentFieldName)) { DoubleArrayList values = new DoubleArrayList(10); @@ -101,56 +84,22 @@ public class PercentilesParser implements Aggregator.Parser { } else { throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.VALUE_BOOLEAN) { - if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { - assumeSorted = parser.booleanValue(); - } if ("keyed".equals(currentFieldName)) { + } else if (token.isValue()) { + if (token == XContentParser.Token.VALUE_BOOLEAN && "keyed".equals(currentFieldName)) { keyed = parser.booleanValue(); } else { if (settings == null) { settings = new HashMap<>(); } - settings.put(currentFieldName, parser.booleanValue()); + settings.put(currentFieldName, parser.objectText()); } - } else if (token == XContentParser.Token.VALUE_NUMBER) { - if (settings == null) { - settings = new HashMap<>(); - } - settings.put(currentFieldName, parser.numberValue()); } else { throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "]."); } } PercentilesEstimator.Factory estimatorFactory = EstimatorType.TDIGEST.estimatorFactory(settings); - - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeSorted) { - config.ensureSorted(true); - } - - if (field == null) { - return new PercentilesAggregator.Factory(aggregationName, config, percents, estimatorFactory, keyed); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new PercentilesAggregator.Factory(aggregationName, config, percents, estimatorFactory, keyed); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return new PercentilesAggregator.Factory(aggregationName, config, percents, estimatorFactory, keyed); + return new PercentilesAggregator.Factory(aggregationName, vsParser.config(), percents, estimatorFactory, keyed); } /** diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsParser.java index 42722928f10..5ec9b2a59a7 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/StatsParser.java @@ -19,18 +19,17 @@ package org.elasticsearch.search.aggregations.metrics.stats; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.metrics.ValuesSourceMetricsAggregatorParser; +import org.elasticsearch.search.aggregations.metrics.NumericValuesSourceMetricsAggregatorParser; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; /** * */ -public class StatsParser extends ValuesSourceMetricsAggregatorParser { +public class StatsParser extends NumericValuesSourceMetricsAggregatorParser { - @Override - public String type() { - return InternalStats.TYPE.name(); + public StatsParser() { + super(InternalStats.TYPE); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsParser.java index 3b66fa5bc21..ee70c3d5b86 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/stats/extended/ExtendedStatsParser.java @@ -19,18 +19,17 @@ package org.elasticsearch.search.aggregations.metrics.stats.extended; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.metrics.ValuesSourceMetricsAggregatorParser; +import org.elasticsearch.search.aggregations.metrics.NumericValuesSourceMetricsAggregatorParser; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; /** * */ -public class ExtendedStatsParser extends ValuesSourceMetricsAggregatorParser { +public class ExtendedStatsParser extends NumericValuesSourceMetricsAggregatorParser { - @Override - public String type() { - return InternalExtendedStats.TYPE.name(); + public ExtendedStatsParser() { + super(InternalExtendedStats.TYPE); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumParser.java index c67d3ce9144..b43285a7bb5 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/sum/SumParser.java @@ -19,18 +19,17 @@ package org.elasticsearch.search.aggregations.metrics.sum; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.metrics.ValuesSourceMetricsAggregatorParser; +import org.elasticsearch.search.aggregations.metrics.NumericValuesSourceMetricsAggregatorParser; import org.elasticsearch.search.aggregations.support.ValuesSource; import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; /** * */ -public class SumParser extends ValuesSourceMetricsAggregatorParser { +public class SumParser extends NumericValuesSourceMetricsAggregatorParser { - @Override - public String type() { - return InternalSum.TYPE.name(); + public SumParser() { + super(InternalSum.TYPE); } @Override diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountAggregator.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountAggregator.java index bb5a8cd0bcb..a9d37fd293d 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountAggregator.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountAggregator.java @@ -40,13 +40,13 @@ import java.io.IOException; */ public class ValueCountAggregator extends MetricsAggregator.SingleValue { - private final ValuesSource.Bytes valuesSource; + private final ValuesSource valuesSource; private BytesValues values; // a count per bucket LongArray counts; - public ValueCountAggregator(String name, long expectedBucketsCount, ValuesSource.Bytes valuesSource, AggregationContext aggregationContext, Aggregator parent) { + public ValueCountAggregator(String name, long expectedBucketsCount, ValuesSource valuesSource, AggregationContext aggregationContext, Aggregator parent) { super(name, 0, aggregationContext, parent); this.valuesSource = valuesSource; if (valuesSource != null) { @@ -96,10 +96,10 @@ public class ValueCountAggregator extends MetricsAggregator.SingleValue { Releasables.release(counts); } - public static class Factory extends ValuesSourceAggregatorFactory.LeafOnly { + public static class Factory extends ValuesSourceAggregatorFactory.LeafOnly { - public Factory(String name, ValuesSourceConfig valuesSourceBuilder) { - super(name, InternalValueCount.TYPE.name(), valuesSourceBuilder); + public Factory(String name, ValuesSourceConfig config) { + super(name, InternalValueCount.TYPE.name(), config); } @Override @@ -108,7 +108,7 @@ public class ValueCountAggregator extends MetricsAggregator.SingleValue { } @Override - protected Aggregator create(ValuesSource.Bytes valuesSource, long expectedBucketsCount, AggregationContext aggregationContext, Aggregator parent) { + protected Aggregator create(ValuesSource valuesSource, long expectedBucketsCount, AggregationContext aggregationContext, Aggregator parent) { return new ValueCountAggregator(name, expectedBucketsCount, valuesSource, aggregationContext, parent); } diff --git a/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountParser.java b/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountParser.java index 60098c18fd2..e3ddfc51328 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/metrics/valuecount/ValueCountParser.java @@ -19,18 +19,13 @@ package org.elasticsearch.search.aggregations.metrics.valuecount; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.index.fielddata.IndexFieldData; -import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.aggregations.Aggregator; import org.elasticsearch.search.aggregations.AggregatorFactory; -import org.elasticsearch.search.aggregations.support.ValuesSource; -import org.elasticsearch.search.aggregations.support.FieldContext; -import org.elasticsearch.search.aggregations.support.ValuesSourceConfig; +import org.elasticsearch.search.aggregations.support.ValuesSourceParser; import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Map; /** * @@ -45,64 +40,20 @@ public class ValueCountParser implements Aggregator.Parser { @Override public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException { - ValuesSourceConfig config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); - - String field = null; - String script = null; - String scriptLang = null; - Map scriptParams = null; - boolean assumeUnique = false; + ValuesSourceParser vsParser = ValuesSourceParser.any(aggregationName, InternalValueCount.TYPE, context) + .requiresUniqueValues(true) + .build(); XContentParser.Token token; String currentFieldName = null; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); - } else if (token == XContentParser.Token.VALUE_STRING) { - if ("field".equals(currentFieldName)) { - field = parser.text(); - } else if ("script".equals(currentFieldName)) { - script = parser.text(); - } else if ("lang".equals(currentFieldName)) { - scriptLang = parser.text(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.VALUE_BOOLEAN) { - if ("script_values_unique".equals(currentFieldName) || "scriptValuesUnique".equals(currentFieldName)) { - assumeUnique = parser.booleanValue(); - } else { - throw new SearchParseException(context, "Unknown key for a " + token + " in [" + aggregationName + "]: [" + currentFieldName + "]."); - } - } else if (token == XContentParser.Token.START_OBJECT) { - if ("params".equals(currentFieldName)) { - scriptParams = parser.map(); - } - } else { + } else if (!vsParser.token(currentFieldName, token, parser)) { throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "]."); } } - if (script != null) { - config.script(context.scriptService().search(context.lookup(), scriptLang, script, scriptParams)); - } - - if (!assumeUnique) { - config.ensureUnique(true); - } - - if (field == null) { - return new ValueCountAggregator.Factory(aggregationName, config); - } - - FieldMapper mapper = context.smartNameFieldMapper(field); - if (mapper == null) { - config.unmapped(true); - return new ValueCountAggregator.Factory(aggregationName, config); - } - - IndexFieldData indexFieldData = context.fieldData().getForField(mapper); - config.fieldContext(new FieldContext(field, indexFieldData)); - return new ValueCountAggregator.Factory(aggregationName, config); + return new ValueCountAggregator.Factory(aggregationName, vsParser.config()); } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/FieldContext.java b/src/main/java/org/elasticsearch/search/aggregations/support/FieldContext.java index 9a84f4a69d6..169d87c40c6 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/support/FieldContext.java +++ b/src/main/java/org/elasticsearch/search/aggregations/support/FieldContext.java @@ -19,6 +19,7 @@ package org.elasticsearch.search.aggregations.support; import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.mapper.FieldMapper; /** * Used by all field data based aggregators. This determine the context of the field data the aggregators are operating @@ -28,6 +29,7 @@ public class FieldContext { private final String field; private final IndexFieldData indexFieldData; + private final FieldMapper mapper; /** * Constructs a field data context for the given field and its index field data @@ -35,9 +37,10 @@ public class FieldContext { * @param field The name of the field * @param indexFieldData The index field data of the field */ - public FieldContext(String field, IndexFieldData indexFieldData) { + public FieldContext(String field, IndexFieldData indexFieldData, FieldMapper mapper) { this.field = field; this.indexFieldData = indexFieldData; + this.mapper = mapper; } public String field() { @@ -51,4 +54,8 @@ public class FieldContext { return indexFieldData; } + public FieldMapper mapper() { + return mapper; + } + } diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/GeoPointParser.java b/src/main/java/org/elasticsearch/search/aggregations/support/GeoPointParser.java new file mode 100644 index 00000000000..35c381ec2a1 --- /dev/null +++ b/src/main/java/org/elasticsearch/search/aggregations/support/GeoPointParser.java @@ -0,0 +1,103 @@ +/* + * 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.support; + +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.SearchParseException; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.internal.SearchContext; + +import java.io.IOException; + +/** + * + */ +public class GeoPointParser { + + private final String aggName; + private final InternalAggregation.Type aggType; + private final SearchContext context; + private final ParseField field; + + GeoPoint point; + + public GeoPointParser(String aggName, InternalAggregation.Type aggType, SearchContext context, ParseField field) { + this.aggName = aggName; + this.aggType = aggType; + this.context = context; + this.field = field; + } + + public boolean token(String currentFieldName, XContentParser.Token token, XContentParser parser) throws IOException { + if (!field.match(currentFieldName)) { + return false; + } + if (token == XContentParser.Token.VALUE_STRING) { + point = new GeoPoint(); + point.resetFromString(parser.text()); + return true; + } + if (token == XContentParser.Token.START_ARRAY) { + double lat = Double.NaN; + double lon = Double.NaN; + while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { + if (Double.isNaN(lon)) { + lon = parser.doubleValue(); + } else if (Double.isNaN(lat)) { + lat = parser.doubleValue(); + } else { + throw new SearchParseException(context, "malformed [" + currentFieldName + "] geo point array in [" + + aggName + "] " + aggType + " aggregation. a geo point array must be of the form [lon, lat]"); + } + } + point = new GeoPoint(lat, lon); + return true; + } + if (token == XContentParser.Token.START_OBJECT) { + double lat = Double.NaN; + double lon = Double.NaN; + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token == XContentParser.Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == XContentParser.Token.VALUE_NUMBER) { + if ("lat".equals(currentFieldName)) { + lat = parser.doubleValue(); + } else if ("lon".equals(currentFieldName)) { + lon = parser.doubleValue(); + } + } + } + if (Double.isNaN(lat) || Double.isNaN(lon)) { + throw new SearchParseException(context, "malformed [" + currentFieldName + "] geo point object. either [lat] or [lon] (or both) are " + + "missing in [" + aggName + "] " + aggType + " aggregation"); + } + point = new GeoPoint(lat, lon); + return true; + } + return false; + } + + public GeoPoint geoPoint() { + return point; + } + +} diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/ScriptValueType.java b/src/main/java/org/elasticsearch/search/aggregations/support/ScriptValueType.java deleted file mode 100644 index 9e6082eaeb8..00000000000 --- a/src/main/java/org/elasticsearch/search/aggregations/support/ScriptValueType.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.support; - -/** - * - */ -public enum ScriptValueType { - - STRING(ValuesSource.Bytes.class), - LONG(ValuesSource.Numeric.class), - DOUBLE(ValuesSource.Numeric.class); - - final Class valuesSourceType; - - private ScriptValueType(Class valuesSourceType) { - this.valuesSourceType = valuesSourceType; - } - - public Class getValuesSourceType() { - return valuesSourceType; - } - - public boolean isNumeric() { - return this != STRING; - } - - public boolean isFloatingPoint() { - return this == DOUBLE; - } -} diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/ValueType.java b/src/main/java/org/elasticsearch/search/aggregations/support/ValueType.java new file mode 100644 index 00000000000..9d2ac2f4668 --- /dev/null +++ b/src/main/java/org/elasticsearch/search/aggregations/support/ValueType.java @@ -0,0 +1,151 @@ +/* + * 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.support; + +import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; +import org.elasticsearch.index.fielddata.IndexNumericFieldData; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; + +/** + * + */ +public enum ValueType { + + @Deprecated ANY("any", ValuesSource.class, IndexFieldData.class, null), + STRING("string", ValuesSource.Bytes.class, IndexFieldData.class, null), + LONG("byte|short|integer|long", ValuesSource.Numeric.class, IndexNumericFieldData.class, ValueFormat.RAW) { + @Override + public boolean isNumeric() { + return true; + } + }, + DOUBLE("float|double", ValuesSource.Numeric.class, IndexNumericFieldData.class, ValueFormat.RAW) { + @Override + public boolean isNumeric() { + return true; + } + + @Override + public boolean isFloatingPoint() { + return true; + } + }, + NUMBER("number", ValuesSource.Numeric.class, IndexNumericFieldData.class, ValueFormat.RAW) { + @Override + public boolean isNumeric() { + return true; + } + }, + DATE("date", ValuesSource.Numeric.class, IndexNumericFieldData.class, ValueFormat.DateTime.DEFAULT) { + @Override + public boolean isNumeric() { + return true; + } + }, + IP("ip", ValuesSource.Numeric.class, IndexNumericFieldData.class, ValueFormat.IPv4) { + @Override + public boolean isNumeric() { + return true; + } + }, + NUMERIC("numeric", ValuesSource.Numeric.class, IndexNumericFieldData.class, ValueFormat.RAW) { + @Override + public boolean isNumeric() { + return true; + } + }, + GEOPOINT("geo_point", ValuesSource.GeoPoint.class, IndexGeoPointFieldData.class, null) { + @Override + public boolean isGeoPoint() { + return true; + } + }; + + final String description; + final Class valuesSourceType; + final Class fieldDataType; + final ValueFormat defaultFormat; + + private ValueType(String description, Class valuesSourceType, Class fieldDataType, ValueFormat defaultFormat) { + this.description = description; + this.valuesSourceType = valuesSourceType; + this.fieldDataType = fieldDataType; + this.defaultFormat = defaultFormat; + } + + public String description() { + return description; + } + + public Class getValuesSourceType() { + return valuesSourceType; + } + + public boolean compatibleWith(IndexFieldData fieldData) { + return fieldDataType.isInstance(fieldData); + } + + public boolean isA(ValueType valueType) { + return valueType.valuesSourceType.isAssignableFrom(valuesSourceType) && + valueType.fieldDataType.isAssignableFrom(fieldDataType); + } + + public boolean isNotA(ValueType valueType) { + return !isA(valueType); + } + + public ValueFormat defaultFormat() { + return defaultFormat; + } + + public boolean isNumeric() { + return false; + } + + public boolean isFloatingPoint() { + return false; + } + + public boolean isGeoPoint() { + return false; + } + + public static ValueType resolveForScript(String type) { + switch (type) { + case "string": return STRING; + case "double": + case "float": return DOUBLE; + case "long": + case "integer": + case "short": + case "byte": return LONG; + case "date": return DATE; + case "ip": return IP; + default: + return null; + } + } + + @Override + public String toString() { + return description; + } +} diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSource.java b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSource.java index c07979a1623..c9be5521cad 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSource.java +++ b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSource.java @@ -545,13 +545,13 @@ public abstract class ValuesSource { } public static class Script extends Numeric { - private final ScriptValueType scriptValueType; + private final ValueType scriptValueType; private final ScriptDoubleValues doubleValues; private final ScriptLongValues longValues; private final ScriptBytesValues bytesValues; - public Script(SearchScript script, ScriptValueType scriptValueType) { + public Script(SearchScript script, ValueType scriptValueType) { this.scriptValueType = scriptValueType; longValues = new ScriptLongValues(script); doubleValues = new ScriptDoubleValues(script); diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregatorFactory.java b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregatorFactory.java index 81b098ffa2d..13cac61ea60 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregatorFactory.java +++ b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceAggregatorFactory.java @@ -19,8 +19,7 @@ package org.elasticsearch.search.aggregations.support; import org.elasticsearch.search.aggregations.*; -import org.elasticsearch.search.aggregations.support.format.ValueFormatter; -import org.elasticsearch.search.aggregations.support.format.ValueParser; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; /** * @@ -33,10 +32,6 @@ public abstract class ValuesSourceAggregatorFactory ext super(name, type, valuesSourceConfig); } - protected LeafOnly(String name, String type, ValuesSourceConfig valuesSourceConfig, ValueFormatter formatter, ValueParser parser) { - super(name, type, valuesSourceConfig, formatter, parser); - } - @Override public AggregatorFactory subFactories(AggregatorFactories subFactories) { throw new AggregationInitializationException("Aggregator [" + name + "] of type [" + type + "] cannot accept sub-aggregations"); @@ -44,18 +39,10 @@ public abstract class ValuesSourceAggregatorFactory ext } protected ValuesSourceConfig config; - protected ValueFormatter formatter; - protected ValueParser parser; protected ValuesSourceAggregatorFactory(String name, String type, ValuesSourceConfig config) { - this(name, type, config, null, null); - } - - protected ValuesSourceAggregatorFactory(String name, String type, ValuesSourceConfig config, ValueFormatter formatter, ValueParser parser) { super(name, type); this.config = config; - this.formatter = formatter; - this.parser = parser; } @Override @@ -85,9 +72,13 @@ public abstract class ValuesSourceAggregatorFactory ext config = ((ValuesSourceAggregatorFactory) parent).config; if (config != null && config.valid()) { if (requiredValuesSourceType == null || requiredValuesSourceType.isAssignableFrom(config.valueSourceType)) { + ValueFormat format = config.format; this.config = config; - this.formatter = ((ValuesSourceAggregatorFactory) parent).formatter; - this.parser = ((ValuesSourceAggregatorFactory) parent).parser; + // if the user explicitly defined a format pattern, we'll do our best to keep it even when we inherit the + // value source form one of the ancestor aggregations + if (this.config.formatPattern != null && format != null && format instanceof ValueFormat.Patternable) { + this.config.format = ((ValueFormat.Patternable) format).create(this.config.formatPattern); + } return; } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java index ac9eaf0508c..1ae409bce2d 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java +++ b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java @@ -19,6 +19,9 @@ package org.elasticsearch.search.aggregations.support; import org.elasticsearch.script.SearchScript; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; +import org.elasticsearch.search.aggregations.support.format.ValueFormatter; +import org.elasticsearch.search.aggregations.support.format.ValueParser; /** * @@ -28,11 +31,13 @@ public class ValuesSourceConfig { final Class valueSourceType; FieldContext fieldContext; SearchScript script; - ScriptValueType scriptValueType; + ValueType scriptValueType; boolean unmapped = false; boolean needsHashes = false; boolean ensureUnique = false; boolean ensureSorted = false; + String formatPattern; + ValueFormat format; public ValuesSourceConfig(Class valueSourceType) { this.valueSourceType = valueSourceType; @@ -46,6 +51,10 @@ public class ValuesSourceConfig { return fieldContext; } + public SearchScript script() { + return script; + } + public boolean unmapped() { return unmapped; } @@ -64,15 +73,6 @@ public class ValuesSourceConfig { return this; } - public ValuesSourceConfig scriptValueType(ScriptValueType scriptValueType) { - this.scriptValueType = scriptValueType; - return this; - } - - public ScriptValueType scriptValueType() { - return scriptValueType; - } - public ValuesSourceConfig unmapped(boolean unmapped) { this.unmapped = unmapped; return this; @@ -83,13 +83,15 @@ public class ValuesSourceConfig { return this; } - public ValuesSourceConfig ensureUnique(boolean unique) { - this.ensureUnique = unique; - return this; + public ValueFormat format() { + return format; } - public ValuesSourceConfig ensureSorted(boolean sorted) { - this.ensureSorted = sorted; - return this; + public ValueFormatter formatter() { + return format != null ? format.formatter() : null; + } + + public ValueParser parser() { + return format != null ? format.parser() : null; } } diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParser.java b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParser.java new file mode 100644 index 00000000000..e5575864bd0 --- /dev/null +++ b/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceParser.java @@ -0,0 +1,266 @@ +/* + * 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.support; + +import org.elasticsearch.common.Nullable; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; +import org.elasticsearch.index.fielddata.IndexNumericFieldData; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.mapper.core.DateFieldMapper; +import org.elasticsearch.index.mapper.core.NumberFieldMapper; +import org.elasticsearch.index.mapper.ip.IpFieldMapper; +import org.elasticsearch.script.SearchScript; +import org.elasticsearch.search.SearchParseException; +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.support.format.ValueFormat; +import org.elasticsearch.search.internal.SearchContext; + +import java.io.IOException; +import java.util.Map; + +/** + * + */ +public class ValuesSourceParser { + + public static Builder any(String aggName, InternalAggregation.Type aggType, SearchContext context) { + return new Builder<>(aggName, aggType, context, ValuesSource.class); + } + + public static Builder numeric(String aggName, InternalAggregation.Type aggType, SearchContext context) { + return new Builder<>(aggName, aggType, context, ValuesSource.Numeric.class).targetValueType(ValueType.NUMERIC); + } + + public static Builder bytes(String aggName, InternalAggregation.Type aggType, SearchContext context) { + return new Builder<>(aggName, aggType, context, ValuesSource.Bytes.class).targetValueType(ValueType.STRING); + } + + public static Builder geoPoint(String aggName, InternalAggregation.Type aggType, SearchContext context) { + return new Builder<>(aggName, aggType, context, ValuesSource.GeoPoint.class).targetValueType(ValueType.GEOPOINT).scriptable(false); + } + + private static class Input { + String field = null; + String script = null; + String lang = null; + Map params = null; + ValueType valueType = null; + boolean assumeUnique = false; + boolean assumeSorted = false; + String format = null; + } + + private final String aggName; + private final InternalAggregation.Type aggType; + private final SearchContext context; + private final Class valuesSourceType; + + private boolean scriptable = true; + private boolean formattable = false; + private ValueType targetValueType = null; + private boolean requiresSortedValues = false; + private boolean requiresUniqueValues = false; + + private Input input = new Input(); + + private ValuesSourceParser(String aggName, InternalAggregation.Type aggType, SearchContext context, Class valuesSourceType) { + this.aggName = aggName; + this.aggType = aggType; + this.context = context; + this.valuesSourceType = valuesSourceType; + } + + public boolean token(String currentFieldName, XContentParser.Token token, XContentParser parser) throws IOException { + if (token == XContentParser.Token.VALUE_STRING) { + if ("field".equals(currentFieldName)) { + input.field = parser.text(); + } else if (formattable && "format".equals(currentFieldName)) { + input.format = parser.text(); + } else if (scriptable) { + if ("script".equals(currentFieldName)) { + input.script = parser.text(); + } else if ("lang".equals(currentFieldName)) { + input.lang = parser.text(); + } else if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) { + input.valueType = ValueType.resolveForScript(parser.text()); + if (targetValueType != null && input.valueType.isNotA(targetValueType)) { + throw new SearchParseException(context, aggType.name() + " aggregation [" + aggName + + "] was configured with an incompatible value type [" + input.valueType + "]. [" + aggType + + "] aggregation can only work on value of type [" + targetValueType + "]"); + } + } else { + return false; + } + return true; + } else { + return false; + } + return true; + } + if (scriptable && token == XContentParser.Token.VALUE_BOOLEAN) { + if ("script_values_unique".equals(currentFieldName) || "scriptValuesUnique".equals(currentFieldName)) { + input.assumeUnique = parser.booleanValue(); + } else if ("script_values_sorted".equals(currentFieldName) || "scriptValuesSorted".equals(currentFieldName)) { + input.assumeSorted = parser.booleanValue(); + } else { + return false; + } + return true; + } + if (scriptable && token == XContentParser.Token.START_OBJECT) { + if ("params".equals(currentFieldName)) { + input.params = parser.map(); + return true; + } + return false; + } + + return false; + } + + public ValuesSourceConfig config() { + + ValueType valueType = input.valueType != null ? input.valueType : targetValueType; + + if (input.field == null) { + if (input.script == null) { + return new ValuesSourceConfig(ValuesSource.class); + } + Class valuesSourceType = valueType != null ? (Class) valueType.getValuesSourceType() : this.valuesSourceType; + if (valuesSourceType == null || valuesSourceType == ValuesSource.class) { + // the specific value source type is undefined, but for scripts, we need to have a specific value source + // type to know how to handle the script values, so we fallback on Bytes + valuesSourceType = ValuesSource.Bytes.class; + } + ValuesSourceConfig config = new ValuesSourceConfig(valuesSourceType); + config.format = resolveFormat(input.format, valueType); + config.script = createScript(); + config.scriptValueType = valueType; + config.ensureUnique = requiresUniqueValues && !input.assumeUnique; + config.ensureSorted = requiresSortedValues && !input.assumeSorted; + return config; + } + + FieldMapper mapper = context.smartNameFieldMapper(input.field); + if (mapper == null) { + Class valuesSourceType = valueType != null ? (Class) valueType.getValuesSourceType() : this.valuesSourceType; + ValuesSourceConfig config = new ValuesSourceConfig<>(valuesSourceType); + config.format = resolveFormat(input.format, valueType); + config.unmapped = true; + if (valueType != null) { + // todo do we really need this for unmapped? + config.scriptValueType = valueType; + } + return config; + } + + IndexFieldData indexFieldData = context.fieldData().getForField(mapper); + + ValuesSourceConfig config; + if (valuesSourceType == ValuesSource.class) { + if (indexFieldData instanceof IndexNumericFieldData) { + config = new ValuesSourceConfig<>(ValuesSource.Numeric.class); + } else if (indexFieldData instanceof IndexGeoPointFieldData) { + config = new ValuesSourceConfig<>(ValuesSource.GeoPoint.class); + } else { + config = new ValuesSourceConfig<>(ValuesSource.Bytes.class); + } + } else { + config = new ValuesSourceConfig(valuesSourceType); + } + + config.fieldContext = new FieldContext(input.field, indexFieldData, mapper); + config.script = createScript(); + if (config.script != null) { + config.ensureUnique = requiresUniqueValues && !input.assumeUnique; + config.ensureSorted = requiresSortedValues && !input.assumeSorted; + } + config.format = resolveFormat(input.format, mapper); + return config; + } + + private SearchScript createScript() { + return input.script == null ? null : context.scriptService().search(context.lookup(), input.lang, input.script, input.params); + } + + private static ValueFormat resolveFormat(@Nullable String format, @Nullable ValueType valueType) { + if (valueType == null) { + return null; // we can't figure it out + } + ValueFormat valueFormat = valueType.defaultFormat; + if (valueFormat != null && valueFormat instanceof ValueFormat.Patternable && format != null) { + return ((ValueFormat.Patternable) valueFormat).create(format); + } + return valueFormat; + } + + private static ValueFormat resolveFormat(@Nullable String format, FieldMapper mapper) { + if (mapper instanceof DateFieldMapper) { + return format != null ? ValueFormat.DateTime.format(format) : ValueFormat.DateTime.mapper((DateFieldMapper) mapper); + } + if (mapper instanceof IpFieldMapper) { + return ValueFormat.IPv4; + } + if (mapper instanceof NumberFieldMapper) { + return format != null ? ValueFormat.Number.format(format) : ValueFormat.RAW; + } + return null; + } + + public static class Builder { + + private final ValuesSourceParser parser; + + private Builder(String aggName, InternalAggregation.Type aggType, SearchContext context, Class valuesSourceType) { + parser = new ValuesSourceParser<>(aggName, aggType, context, valuesSourceType); + } + + public Builder scriptable(boolean scriptable) { + parser.scriptable = scriptable; + return this; + } + + public Builder formattable(boolean formattable) { + parser.formattable = formattable; + return this; + } + + public Builder targetValueType(ValueType valueType) { + parser.targetValueType = valueType; + return this; + } + + public Builder requiresSortedValues(boolean requiresSortedValues) { + parser.requiresSortedValues = requiresSortedValues; + return this; + } + + public Builder requiresUniqueValues(boolean requiresUniqueValues) { + parser.requiresUniqueValues = requiresUniqueValues; + return this; + } + + public ValuesSourceParser build() { + return parser; + } + } +} diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormat.java b/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormat.java new file mode 100644 index 00000000000..54c69ec914a --- /dev/null +++ b/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormat.java @@ -0,0 +1,103 @@ +/* + * 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.support.format; + +import org.elasticsearch.index.mapper.core.DateFieldMapper; + +/** + * + */ +public class ValueFormat { + + public static final ValueFormat RAW = new ValueFormat(ValueFormatter.RAW, ValueParser.RAW); + public static final ValueFormat IPv4 = new ValueFormat(ValueFormatter.IPv4, ValueParser.IPv4); + + private final ValueFormatter formatter; + private final ValueParser parser; + + public ValueFormat(ValueFormatter formatter, ValueParser parser) { + assert formatter != null && parser != null; + this.formatter = formatter; + this.parser = parser; + } + + public ValueFormatter formatter() { + return formatter; + } + + public ValueParser parser() { + return parser; + } + + public abstract static class Patternable> extends ValueFormat { + + private final String pattern; + + public Patternable(String pattern, ValueFormatter formatter, ValueParser parser) { + super(formatter, parser); + this.pattern = pattern; + } + + public String pattern() { + return pattern; + } + + public abstract VF create(String pattern); + } + + public static class DateTime extends Patternable { + + public static final DateTime DEFAULT = new DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER.format(), ValueFormatter.DateTime.DEFAULT, ValueParser.DateMath.DEFAULT); + + public static DateTime format(String format) { + return new DateTime(format, new ValueFormatter.DateTime(format), new ValueParser.DateMath(format, DateFieldMapper.Defaults.TIME_UNIT)); + } + + public static DateTime mapper(DateFieldMapper mapper) { + return new DateTime(mapper.dateTimeFormatter().format(), ValueFormatter.DateTime.mapper(mapper), ValueParser.DateMath.mapper(mapper)); + } + + public DateTime(String pattern, ValueFormatter formatter, ValueParser parser) { + super(pattern, formatter, parser); + } + + @Override + public DateTime create(String pattern) { + return format(pattern); + } + } + + public static class Number extends Patternable { + + public static Number format(String format) { + return new Number(format, new ValueFormatter.Number.Pattern(format), new ValueParser.Number.Pattern(format)); + } + + public Number(String pattern, ValueFormatter formatter, ValueParser parser) { + super(pattern, formatter, parser); + } + + @Override + public Number create(String pattern) { + return format(pattern); + } + } + +} diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormatter.java b/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormatter.java index a5738a6da47..565d0328f45 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormatter.java +++ b/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueFormatter.java @@ -102,6 +102,10 @@ public interface ValueFormatter extends Streamable { public static final ValueFormatter DEFAULT = new ValueFormatter.DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER); + public static DateTime mapper(DateFieldMapper mapper) { + return new DateTime(mapper.dateTimeFormatter()); + } + static final byte ID = 2; FormatDateTimeFormatter formatter; diff --git a/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueParser.java b/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueParser.java index f60eb79fe83..94646e5cd69 100644 --- a/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueParser.java +++ b/src/main/java/org/elasticsearch/search/aggregations/support/format/ValueParser.java @@ -51,6 +51,8 @@ public interface ValueParser { */ static class DateTime implements ValueParser { + public static final DateTime DEFAULT = new DateTime(DateFieldMapper.Defaults.DATE_TIME_FORMATTER); + private FormatDateTimeFormatter formatter; public DateTime(String format) { @@ -98,6 +100,10 @@ public interface ValueParser { public double parseDouble(String value, SearchContext searchContext) { return parseLong(value, searchContext); } + + public static DateMath mapper(DateFieldMapper mapper) { + return new DateMath(new DateMathParser(mapper.dateTimeFormatter(), DateFieldMapper.Defaults.TIME_UNIT)); + } } /** @@ -170,4 +176,5 @@ public interface ValueParser { } } } + } diff --git a/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java b/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java index 391bfd3e3a7..8e28216f076 100644 --- a/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java +++ b/src/test/java/org/elasticsearch/search/aggregations/bucket/DateHistogramTests.java @@ -1166,7 +1166,8 @@ public class DateHistogramTests extends ElasticsearchIntegrationTest { .field("date") .interval(DateHistogram.Interval.days(interval)) .minDocCount(0) - .extendedBounds(boundsMin, boundsMax) + // when explicitly specifying a format, the extended bounds should be defined by the same format + .extendedBounds(format(boundsMin, pattern), format(boundsMax, pattern)) .format(pattern)) .execute().actionGet(); diff --git a/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java b/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java index f807bd94aa0..23d35bbf349 100644 --- a/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java +++ b/src/test/java/org/elasticsearch/search/aggregations/bucket/SignificantTermsTests.java @@ -197,7 +197,5 @@ public class SignificantTermsTests extends ElasticsearchIntegrationTest { assertEquals(3, kellyTerm.getSubsetDf()); assertEquals(4, kellyTerm.getSupersetDf()); } - - }