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
This commit is contained in:
uboness 2014-04-03 12:00:35 +02:00
parent c6caeea887
commit c9b0b04f55
57 changed files with 1199 additions and 1290 deletions

View File

@ -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);
}

View File

@ -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();

View File

@ -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 {

View File

@ -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<ValuesSource.GeoPoint> 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<ValuesSource.GeoPoint> valueSourceConfig,
int precision, int requiredSize, int shardSize) {
super(name, InternalGeoHashGrid.TYPE.name(), valueSourceConfig);
public GeoGridFactory(String name, ValuesSourceConfig<ValuesSource.GeoPoint> config, int precision, int requiredSize, int shardSize) {
super(name, InternalGeoHashGrid.TYPE.name(), config);
this.precision = precision;
this.requiredSize = requiredSize;
this.shardSize = shardSize;

View File

@ -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<ValuesSource.Numeric> 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<String, Object> 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) {

View File

@ -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 " +

View File

@ -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<ValuesSource.Numeric> config, ValueFormatter formatter, ValueParser parser,
public Factory(String name, ValuesSourceConfig<ValuesSource.Numeric> 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);
}
}

View File

@ -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<ValuesSource.Numeric> 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<String, Object> 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);
}

View File

@ -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<InternalDateHistogr
static class Bucket extends InternalHistogram.Bucket implements DateHistogram.Bucket {
private final ValueFormatter formatter;
Bucket(long key, long docCount, InternalAggregations aggregations, ValueFormatter formatter) {
super(key, docCount, aggregations);
this.formatter = formatter;
Bucket(long key, long docCount, InternalAggregations aggregations, @Nullable ValueFormatter formatter) {
super(key, docCount, formatter, aggregations);
}
@Override
@ -88,12 +85,12 @@ public class InternalDateHistogram extends InternalHistogram<InternalDateHistogr
@Override
public InternalDateHistogram create(String name, List<InternalDateHistogram.Bucket> 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<InternalDateHistogr
InternalDateHistogram() {} // for serialization
InternalDateHistogram(String name, List<InternalDateHistogram.Bucket> 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);
}

View File

@ -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<B extends InternalHistogram.Bucket> 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<B extends InternalHistogram.Bucket> 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<B extends InternalHistogram.Bucket> extends Inter
}
public InternalHistogram<B> create(String name, List<B> 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<B extends InternalHistogram.Bucket> extends Inter
protected List<B> buckets;
private LongObjectOpenHashMap<B> 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<B> buckets, InternalOrder order, long minDocCount, EmptyBucketInfo emptyBucketInfo, ValueFormatter formatter, boolean keyed) {
InternalHistogram(String name, List<B> 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<B extends InternalHistogram.Bucket> 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<B extends InternalHistogram.Bucket> 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 {

View File

@ -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<ValuesSource> 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());
}
}

View File

@ -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<B extends InternalRange.Bucket> 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<B extends InternalRange.Bucket> 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<B extends InternalRange.Bucket> 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<B extends InternalRange.Bucket> extends InternalAggre
return TYPE.name();
}
public R create(String name, List<B> ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) {
public R create(String name, List<B> 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<B> ranges;
private Map<String, B> rangeMap;
private ValueFormatter formatter;
private @Nullable ValueFormatter formatter;
private boolean keyed;
private boolean unmapped;
public InternalRange() {} // for serialization
public InternalRange(String name, List<B> ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) {
public InternalRange(String name, List<B> ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) {
super(name);
this.ranges = ranges;
this.formatter = formatter;

View File

@ -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<Range> 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<org.elasticsearch.search.aggregations.bucket.range.Range.Bucket> 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<RangeAggregator.Range> 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<org.elasticsearch.search.aggregations.bucket.range.Range.Bucket> buckets =
new ArrayList<>(ranges.size());
List<org.elasticsearch.search.aggregations.bucket.range.Range.Bucket> 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<Range> ranges;
private final boolean keyed;
public Factory(String name, ValuesSourceConfig<ValuesSource.Numeric> valueSourceConfig, ValueFormatter formatter, ValueParser parser, InternalRange.Factory rangeFactory, List<Range> ranges, boolean keyed) {
super(name, rangeFactory.type(), valueSourceConfig, formatter, parser);
public Factory(String name, ValuesSourceConfig<ValuesSource.Numeric> valueSourceConfig, InternalRange.Factory rangeFactory, List<Range> 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);
}
}

View File

@ -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<ValuesSource.Numeric> config = new ValuesSourceConfig<>(ValuesSource.Numeric.class);
String field = null;
List<RangeAggregator.Range> ranges = null;
String script = null;
String scriptLang = null;
Map<String, Object> scriptParams = null;
boolean keyed = false;
boolean assumeSorted = false;
String format = null;
ValuesSourceParser<ValuesSource.Numeric> 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);
}
}

View File

@ -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<ValuesSource.Numeric> config = new ValuesSourceConfig<>(ValuesSource.Numeric.class);
ValuesSourceParser<ValuesSource.Numeric> vsParser = ValuesSourceParser.numeric(aggregationName, InternalDateRange.TYPE, context)
.targetValueType(ValueType.DATE)
.requiresSortedValues(true)
.formattable(true)
.build();
String field = null;
List<RangeAggregator.Range> ranges = null;
String script = null;
String scriptLang = null;
Map<String, Object> 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);
}
}

View File

@ -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<InternalDateRange.Bucket> i
InternalDateRange() {} // for serialization
InternalDateRange(String name, List<InternalDateRange.Bucket> ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) {
InternalDateRange(String name, List<InternalDateRange.Bucket> ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) {
super(name, ranges, formatter, keyed, unmapped);
}

View File

@ -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<ValuesSource.GeoPoint> vsParser = ValuesSourceParser.geoPoint(aggregationName, InternalGeoDistance.TYPE, context)
.requiresSortedValues(true)
.build();
GeoPointParser geoPointParser = new GeoPointParser(aggregationName, InternalGeoDistance.TYPE, context, ORIGIN_FIELD);
List<RangeAggregator.Range> 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<ValuesSource.GeoPoint> 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<ValuesSource.GeoPoint> {
@ -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 {

View File

@ -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<InternalGeoDistance.Bucke
}
@Override
public InternalGeoDistance create(String name, List<Bucket> ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) {
public InternalGeoDistance create(String name, List<Bucket> 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<Bucket> ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) {
public InternalGeoDistance(String name, List<Bucket> ranges, @Nullable ValueFormatter formatter, boolean keyed, boolean unmapped) {
super(name, ranges, formatter, keyed, unmapped);
}

View File

@ -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<InternalIPv4Range.Bucket> i
public static class Bucket extends InternalRange.Bucket implements IPv4Range.Bucket {
public Bucket(String key, double from, double to, long docCount, List<InternalAggregation> aggregations, ValueFormatter formatter) {
super(key, from, to, docCount, new InternalAggregations(aggregations), formatter);
public Bucket(String key, double from, double to, long docCount, List<InternalAggregation> 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<InternalIPv4Range.Bucket> i
}
@Override
public InternalIPv4Range create(String name, List<Bucket> ranges, ValueFormatter formatter, boolean keyed, boolean unmapped) {
public InternalIPv4Range create(String name, List<Bucket> 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<InternalIPv4Range.Bucket> 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);
}
}

View File

@ -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<ValuesSource.Numeric> config = new ValuesSourceConfig<>(ValuesSource.Numeric.class);
ValuesSourceParser<ValuesSource.Numeric> vsParser = ValuesSourceParser.numeric(aggregationName, InternalIPv4Range.TYPE, context)
.targetValueType(ValueType.IP)
.requiresSortedValues(true)
.formattable(false)
.build();
String field = null;
List<RangeAggregator.Range> ranges = null;
String script = null;
String scriptLang = null;
Map<String, Object> 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) {

View File

@ -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<InternalSignificantTerms.Bucket> buckets) {
public SignificantLongTerms(long subsetSize, long supersetSize, String name, @Nullable ValueFormatter formatter,
int requiredSize, long minDocCount, Collection<InternalSignificantTerms.Bucket> 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);

View File

@ -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

View File

@ -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() +

View File

@ -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);
}
}

View File

@ -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<InternalTerms.Bucket> buckets) {
this(name, order, null, requiredSize, minDocCount, buckets);
}
public DoubleTerms(String name, InternalOrder order, ValueFormatter valueFormatter, int requiredSize, long minDocCount, Collection<InternalTerms.Bucket> buckets) {
public DoubleTerms(String name, InternalOrder order, @Nullable ValueFormatter formatter, int requiredSize, long minDocCount, Collection<InternalTerms.Bucket> 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);

View File

@ -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;

View File

@ -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<InternalTerms.Bucket> buckets) {
public LongTerms(String name, InternalOrder order, @Nullable ValueFormatter formatter, int requiredSize, long minDocCount, Collection<InternalTerms.Bucket> 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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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() +

View File

@ -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<String, Object> 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<? extends ValuesSource> 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) {

View File

@ -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);
}
}
}

View File

@ -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<S extends MetricsAggregation> 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<ValuesSource.Numeric> 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<ValuesSource.Numeric> config);
}

View File

@ -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<S extends MetricsAggregation> implements Aggregator.Parser {
protected boolean requiresSortedValues() {
return false;
}
@Override
public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException {
ValuesSourceConfig<ValuesSource.Numeric> config = new ValuesSourceConfig<>(ValuesSource.Numeric.class);
String field = null;
String script = null;
String scriptLang = null;
Map<String, Object> 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<ValuesSource.Numeric> config);
}

View File

@ -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<InternalAvg> {
public class AvgParser extends NumericValuesSourceMetricsAggregatorParser<InternalAvg> {
@Override
public String type() {
return InternalAvg.TYPE.name();
public AvgParser() {
super(InternalAvg.TYPE);
}
@Override

View File

@ -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<String, Object> 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);
}
}

View File

@ -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<InternalMax> {
public class MaxParser extends NumericValuesSourceMetricsAggregatorParser<InternalMax> {
@Override
public String type() {
return InternalMax.TYPE.name();
public MaxParser() {
super(InternalMax.TYPE);
}
@Override

View File

@ -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<InternalMin> {
public class MinParser extends NumericValuesSourceMetricsAggregatorParser<InternalMin> {
@Override
public String type() {
return InternalMin.TYPE.name();
public MinParser() {
super(InternalMin.TYPE);
}
@Override

View File

@ -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<ValuesSource.Numeric> config = new ValuesSourceConfig<>(ValuesSource.Numeric.class);
ValuesSourceParser<ValuesSource.Numeric> vsParser = ValuesSourceParser.numeric(aggregationName, InternalPercentiles.TYPE, context)
.requiresSortedValues(true)
.build();
String field = null;
String script = null;
String scriptLang = null;
double[] percents = DEFAULT_PERCENTS;
Map<String, Object> scriptParams = null;
boolean assumeSorted = false;
boolean keyed = true;
Map<String, Object> 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);
}
/**

View File

@ -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<InternalStats> {
public class StatsParser extends NumericValuesSourceMetricsAggregatorParser<InternalStats> {
@Override
public String type() {
return InternalStats.TYPE.name();
public StatsParser() {
super(InternalStats.TYPE);
}
@Override

View File

@ -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<InternalExtendedStats> {
public class ExtendedStatsParser extends NumericValuesSourceMetricsAggregatorParser<InternalExtendedStats> {
@Override
public String type() {
return InternalExtendedStats.TYPE.name();
public ExtendedStatsParser() {
super(InternalExtendedStats.TYPE);
}
@Override

View File

@ -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<InternalSum> {
public class SumParser extends NumericValuesSourceMetricsAggregatorParser<InternalSum> {
@Override
public String type() {
return InternalSum.TYPE.name();
public SumParser() {
super(InternalSum.TYPE);
}
@Override

View File

@ -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<ValuesSource.Bytes> {
public static class Factory<VS extends ValuesSource> extends ValuesSourceAggregatorFactory.LeafOnly<VS> {
public Factory(String name, ValuesSourceConfig<ValuesSource.Bytes> valuesSourceBuilder) {
super(name, InternalValueCount.TYPE.name(), valuesSourceBuilder);
public Factory(String name, ValuesSourceConfig<VS> 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);
}

View File

@ -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<ValuesSource.Bytes> config = new ValuesSourceConfig<>(ValuesSource.Bytes.class);
String field = null;
String script = null;
String scriptLang = null;
Map<String, Object> 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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<? extends ValuesSource> valuesSourceType;
private ScriptValueType(Class<? extends ValuesSource> valuesSourceType) {
this.valuesSourceType = valuesSourceType;
}
public Class<? extends ValuesSource> getValuesSourceType() {
return valuesSourceType;
}
public boolean isNumeric() {
return this != STRING;
}
public boolean isFloatingPoint() {
return this == DOUBLE;
}
}

View File

@ -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<? extends ValuesSource> valuesSourceType;
final Class<? extends IndexFieldData> fieldDataType;
final ValueFormat defaultFormat;
private ValueType(String description, Class<? extends ValuesSource> valuesSourceType, Class<? extends IndexFieldData> fieldDataType, ValueFormat defaultFormat) {
this.description = description;
this.valuesSourceType = valuesSourceType;
this.fieldDataType = fieldDataType;
this.defaultFormat = defaultFormat;
}
public String description() {
return description;
}
public Class<? extends ValuesSource> 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;
}
}

View File

@ -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);

View File

@ -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<VS extends ValuesSource> ext
super(name, type, valuesSourceConfig);
}
protected LeafOnly(String name, String type, ValuesSourceConfig<VS> 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<VS extends ValuesSource> ext
}
protected ValuesSourceConfig<VS> config;
protected ValueFormatter formatter;
protected ValueParser parser;
protected ValuesSourceAggregatorFactory(String name, String type, ValuesSourceConfig<VS> config) {
this(name, type, config, null, null);
}
protected ValuesSourceAggregatorFactory(String name, String type, ValuesSourceConfig<VS> 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<VS extends ValuesSource> 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;
}
}

View File

@ -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<VS extends ValuesSource> {
final Class<VS> 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<VS> valueSourceType) {
this.valueSourceType = valueSourceType;
@ -46,6 +51,10 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
return fieldContext;
}
public SearchScript script() {
return script;
}
public boolean unmapped() {
return unmapped;
}
@ -64,15 +73,6 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
return this;
}
public ValuesSourceConfig<VS> scriptValueType(ScriptValueType scriptValueType) {
this.scriptValueType = scriptValueType;
return this;
}
public ScriptValueType scriptValueType() {
return scriptValueType;
}
public ValuesSourceConfig<VS> unmapped(boolean unmapped) {
this.unmapped = unmapped;
return this;
@ -83,13 +83,15 @@ public class ValuesSourceConfig<VS extends ValuesSource> {
return this;
}
public ValuesSourceConfig<VS> ensureUnique(boolean unique) {
this.ensureUnique = unique;
return this;
public ValueFormat format() {
return format;
}
public ValuesSourceConfig<VS> 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;
}
}

View File

@ -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<VS extends ValuesSource> {
public static Builder any(String aggName, InternalAggregation.Type aggType, SearchContext context) {
return new Builder<>(aggName, aggType, context, ValuesSource.class);
}
public static Builder<ValuesSource.Numeric> numeric(String aggName, InternalAggregation.Type aggType, SearchContext context) {
return new Builder<>(aggName, aggType, context, ValuesSource.Numeric.class).targetValueType(ValueType.NUMERIC);
}
public static Builder<ValuesSource.Bytes> bytes(String aggName, InternalAggregation.Type aggType, SearchContext context) {
return new Builder<>(aggName, aggType, context, ValuesSource.Bytes.class).targetValueType(ValueType.STRING);
}
public static Builder<ValuesSource.GeoPoint> 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<String, Object> 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<VS> 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<VS> 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<VS> 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<VS>) 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<VS> config = new ValuesSourceConfig<VS>(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<VS> valuesSourceType = valueType != null ? (Class<VS>) valueType.getValuesSourceType() : this.valuesSourceType;
ValuesSourceConfig<VS> 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<VS extends ValuesSource> {
private final ValuesSourceParser<VS> parser;
private Builder(String aggName, InternalAggregation.Type aggType, SearchContext context, Class<VS> valuesSourceType) {
parser = new ValuesSourceParser<>(aggName, aggType, context, valuesSourceType);
}
public Builder<VS> scriptable(boolean scriptable) {
parser.scriptable = scriptable;
return this;
}
public Builder<VS> formattable(boolean formattable) {
parser.formattable = formattable;
return this;
}
public Builder<VS> targetValueType(ValueType valueType) {
parser.targetValueType = valueType;
return this;
}
public Builder<VS> requiresSortedValues(boolean requiresSortedValues) {
parser.requiresSortedValues = requiresSortedValues;
return this;
}
public Builder<VS> requiresUniqueValues(boolean requiresUniqueValues) {
parser.requiresUniqueValues = requiresUniqueValues;
return this;
}
public ValuesSourceParser<VS> build() {
return parser;
}
}
}

View File

@ -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<VF extends Patternable<VF>> 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<DateTime> {
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<Number> {
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);
}
}
}

View File

@ -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;

View File

@ -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 {
}
}
}
}

View File

@ -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();

View File

@ -197,7 +197,5 @@ public class SignificantTermsTests extends ElasticsearchIntegrationTest {
assertEquals(3, kellyTerm.getSubsetDf());
assertEquals(4, kellyTerm.getSupersetDf());
}
}