Preparing ValuesSourceAggregatorFactory/Parser for refactoring

This change adds AbstractValuesSourceParser which will be the new class used to create ValuesSourceAggregatorFactory objects. AbstractValuesSourceParser parses all the parameters required for ValuesSource and passes to the sub-class to parse any other (implementation specific) parameters. After parsing is complete it will call createFactory on the implementing class to create the AggregatorFactory object and then set the ValuesSource specific parameters before returning it.

ValuesSourceAggregatorFactory also now has setter methods so that it can be used as the 'builder' object in the future.
This commit is contained in:
Colin Goodheart-Smithe 2015-07-14 12:39:56 +01:00
parent 7a3f6fc1ba
commit 855c199f60
10 changed files with 273 additions and 63 deletions

View File

@ -189,10 +189,6 @@ public class DateHistogramParser implements Aggregator.Parser {
.timeZone(vsParser.input().timezone())
.offset(offset).build();
ValuesSourceConfig config = vsParser.config();
if (config.formatter()!=null) {
((DateTime) config.formatter()).setTimeZone(timeZone);
}
ValuesSourceParser.Input input = vsParser.input();
return new HistogramAggregator.DateHistogramFactory(aggregationName, input, rounding, order, keyed, minDocCount, extendedBounds,
new InternalDateHistogram.Factory());

View File

@ -38,7 +38,6 @@ import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory;
import org.elasticsearch.search.aggregations.support.ValuesSourceParser;
import org.elasticsearch.search.aggregations.support.format.ValueFormatter;
import org.elasticsearch.search.aggregations.support.format.ValueFormatter.DateTime;
import org.joda.time.DateTimeZone;
import java.io.IOException;
@ -206,16 +205,14 @@ public class HistogramAggregator extends BucketsAggregator {
private DateTimeZone timeZone;
public DateHistogramFactory(String name, ValuesSourceParser.Input<Numeric> input, Rounding rounding, InternalOrder order,
boolean keyed, long minDocCount, ExtendedBounds extendedBounds,
org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram.Factory<?> histogramFactory, DateTimeZone timeZone) {
boolean keyed, long minDocCount, ExtendedBounds extendedBounds, InternalHistogram.Factory<?> histogramFactory) {
super(name, input, rounding, order, keyed, minDocCount, extendedBounds, histogramFactory);
this.timeZone = timeZone;
this.timeZone = input.timezone();
}
@Override
protected Aggregator createUnmapped(AggregationContext aggregationContext, Aggregator parent,
List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) throws IOException {
setFormatterTimeZone();
return super.createUnmapped(aggregationContext, parent, pipelineAggregators, metaData);
}
@ -223,15 +220,8 @@ public class HistogramAggregator extends BucketsAggregator {
protected Aggregator doCreateInternal(Numeric valuesSource, AggregationContext aggregationContext, Aggregator parent,
boolean collectsFromSingleBucket, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData)
throws IOException {
setFormatterTimeZone();
return super
.doCreateInternal(valuesSource, aggregationContext, parent, collectsFromSingleBucket, pipelineAggregators, metaData);
}
private void setFormatterTimeZone() {
if (config.formatter() instanceof ValueFormatter.DateTime) {
((DateTime) config.formatter()).setTimeZone(timeZone);
}
}
}
}

View File

@ -19,8 +19,6 @@
package org.elasticsearch.search.aggregations.metrics.cardinality;
import org.elasticsearch.index.mapper.core.Murmur3FieldMapper;
import org.elasticsearch.search.aggregations.AggregationExecutionException;
import org.elasticsearch.search.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.bucket.SingleBucketAggregator;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
@ -49,19 +47,9 @@ final class CardinalityAggregatorFactory extends ValuesSourceAggregatorFactory.L
@Override
protected Aggregator createUnmapped(AggregationContext context, Aggregator parent, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData)
throws IOException {
resolveRehash();
return new CardinalityAggregator(name, null, precision(parent), config.formatter(), context, parent, pipelineAggregators, metaData);
}
private void resolveRehash() {
if (rehash == null && config.fieldContext() != null
&& config.fieldContext().fieldType() instanceof Murmur3FieldMapper.Murmur3FieldType) {
rehash = false;
} else if (rehash == null) {
rehash = true;
}
}
@Override
protected Aggregator doCreateInternal(ValuesSource valuesSource, AggregationContext context, Aggregator parent,
boolean collectsFromSingleBucket, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) throws IOException {

View File

@ -24,6 +24,7 @@ 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.support.ValuesSource;
import org.elasticsearch.search.aggregations.support.ValuesSourceParser;
import org.elasticsearch.search.internal.SearchContext;

View File

@ -59,7 +59,7 @@ public class PercentilesParser extends AbstractPercentilesParser {
if (method == PercentilesMethod.TDIGEST) {
return new TDigestPercentilesAggregator.Factory(aggregationName, valuesSourceInput, keys, compression, keyed);
} else if (method == PercentilesMethod.HDR) {
return new HDRPercentilesAggregator.Factory(aggregationName, valuesSourceConfig, keys, numberOfSignificantValueDigits, keyed);
return new HDRPercentilesAggregator.Factory(aggregationName, valuesSourceInput, keys, numberOfSignificantValueDigits, keyed);
} else {
throw new AssertionError();
}

View File

@ -26,7 +26,7 @@ import org.elasticsearch.search.aggregations.support.AggregationContext;
import org.elasticsearch.search.aggregations.support.ValuesSource;
import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory;
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
import org.elasticsearch.search.aggregations.support.ValuesSourceParser;
import org.elasticsearch.search.aggregations.support.format.ValueFormatter;
import java.io.IOException;
@ -80,9 +80,9 @@ public class HDRPercentileRanksAggregator extends AbstractHDRPercentilesAggregat
private final int numberOfSignificantValueDigits;
private final boolean keyed;
public Factory(String name, ValuesSourceConfig<ValuesSource.Numeric> valuesSourceConfig, double[] values,
public Factory(String name, ValuesSourceParser.Input<ValuesSource.Numeric> valuesSourceInput, double[] values,
int numberOfSignificantValueDigits, boolean keyed) {
super(name, InternalHDRPercentiles.TYPE.name(), valuesSourceConfig);
super(name, InternalHDRPercentiles.TYPE.name(), valuesSourceInput);
this.values = values;
this.numberOfSignificantValueDigits = numberOfSignificantValueDigits;
this.keyed = keyed;

View File

@ -27,7 +27,7 @@ import org.elasticsearch.search.aggregations.support.AggregationContext;
import org.elasticsearch.search.aggregations.support.ValuesSource;
import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory;
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
import org.elasticsearch.search.aggregations.support.ValuesSourceParser;
import org.elasticsearch.search.aggregations.support.format.ValueFormatter;
import java.io.IOException;
@ -82,9 +82,9 @@ public class HDRPercentilesAggregator extends AbstractHDRPercentilesAggregator {
private final int numberOfSignificantValueDigits;
private final boolean keyed;
public Factory(String name, ValuesSourceConfig<ValuesSource.Numeric> valuesSourceConfig, double[] percents,
public Factory(String name, ValuesSourceParser.Input<ValuesSource.Numeric> valuesSourceInput, double[] percents,
int numberOfSignificantValueDigits, boolean keyed) {
super(name, InternalTDigestPercentiles.TYPE.name(), valuesSourceConfig);
super(name, InternalTDigestPercentiles.TYPE.name(), valuesSourceInput);
this.percents = percents;
this.numberOfSignificantValueDigits = numberOfSignificantValueDigits;
this.keyed = keyed;

View File

@ -0,0 +1,163 @@
/*
* 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.xcontent.XContentParser;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.Script.ScriptField;
import org.elasticsearch.script.ScriptParameterParser;
import org.elasticsearch.script.ScriptParameterParser.ScriptParameterValue;
import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.AggregatorFactory;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException;
import java.util.Map;
import static com.google.common.collect.Maps.newHashMap;
/**
*
*/
public abstract class AbstractValuesSourceParser<VS extends ValuesSource> implements Aggregator.Parser {
public abstract static class AnyValuesSourceParser extends AbstractValuesSourceParser<ValuesSource> {
protected AnyValuesSourceParser(boolean scriptable, boolean formattable) {
super(scriptable, formattable, ValuesSource.class, null);
}
}
public abstract static class NumericValuesSourceParser extends AbstractValuesSourceParser<ValuesSource.Numeric> {
protected NumericValuesSourceParser(boolean scriptable, boolean formattable) {
super(scriptable, formattable, ValuesSource.Numeric.class, ValueType.NUMERIC);
}
}
public abstract static class BytesValuesSourceParser extends AbstractValuesSourceParser<ValuesSource.Bytes> {
protected BytesValuesSourceParser(boolean scriptable, boolean formattable) {
super(scriptable, formattable, ValuesSource.Bytes.class, ValueType.STRING);
}
}
public abstract static class GeoPointValuesSourceParser extends AbstractValuesSourceParser<ValuesSource.GeoPoint> {
protected GeoPointValuesSourceParser(boolean scriptable, boolean formattable) {
super(scriptable, formattable, ValuesSource.GeoPoint.class, ValueType.GEOPOINT);
}
}
private boolean scriptable = true;
private boolean formattable = false;
private Class<VS> valuesSourceType = null;
private ValueType targetValueType = null;
private ScriptParameterParser scriptParameterParser = new ScriptParameterParser();
private AbstractValuesSourceParser(boolean scriptable,
boolean formattable, Class<VS> valuesSourceType, ValueType targetValueType) {
this.valuesSourceType = valuesSourceType;
this.targetValueType = targetValueType;
this.scriptable = scriptable;
this.formattable = formattable;
}
@Override
public AggregatorFactory parse(String aggregationName, XContentParser parser, SearchContext context) throws IOException {
String field = null;
Script script = null;
@Deprecated
Map<String, Object> params = null; // TODO Remove in 3.0
ValueType valueType = null;
String format = null;
Object missing = 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 ("missing".equals(currentFieldName) && token.isValue()) {
missing = parser.objectText();
} else if (token == XContentParser.Token.VALUE_STRING) {
if ("field".equals(currentFieldName)) {
field = parser.text();
} else if (formattable && "format".equals(currentFieldName)) {
format = parser.text();
} else if (scriptable) {
if ("value_type".equals(currentFieldName) || "valueType".equals(currentFieldName)) {
valueType = ValueType.resolveForScript(parser.text());
if (targetValueType != null && valueType.isNotA(targetValueType)) {
throw new SearchParseException(context, type() + " aggregation [" + aggregationName
+ "] was configured with an incompatible value type [" + valueType + "]. [" + type()
+ "] aggregation can only work on value of type [" + targetValueType + "]",
parser.getTokenLocation());
}
} else if (!scriptParameterParser.token(currentFieldName, token, parser, context.parseFieldMatcher())) {
throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "].",
parser.getTokenLocation());
}
} else {
throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "].",
parser.getTokenLocation());
}
} else if (scriptable && token == XContentParser.Token.START_OBJECT) {
if (context.parseFieldMatcher().match(currentFieldName, ScriptField.SCRIPT)) {
script = Script.parse(parser, context.parseFieldMatcher());
} else if ("params".equals(currentFieldName)) {
params = parser.map();
} else {
throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "].",
parser.getTokenLocation());
}
} else if (!token(currentFieldName, token, parser)) {
throw new SearchParseException(context, "Unexpected token " + token + " in [" + aggregationName + "].",
parser.getTokenLocation());
}
}
if (script == null) { // Didn't find anything using the new API so
// try using the old one instead
ScriptParameterValue scriptValue = scriptParameterParser.getDefaultScriptParameterValue();
if (scriptValue != null) {
if (params == null) {
params = newHashMap();
}
script = new Script(scriptValue.script(), scriptValue.scriptType(), scriptParameterParser.lang(), params);
}
}
ValuesSourceAggregatorFactory<VS> factory = createFactory(aggregationName, this.valuesSourceType, this.targetValueType);
factory.field(field);
factory.script(script);
factory.valueType(valueType);
factory.format(format);
factory.missing(missing);
return factory;
}
protected abstract ValuesSourceAggregatorFactory<VS> createFactory(String aggregationName, Class<VS> valuesSourceType,
ValueType targetValueType);
protected abstract boolean token(String currentFieldName, XContentParser.Token token, XContentParser parser) throws IOException;
}

View File

@ -38,6 +38,7 @@ import org.elasticsearch.search.aggregations.AggregatorFactory;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
import org.elasticsearch.search.aggregations.support.format.ValueFormat;
import org.elasticsearch.search.internal.SearchContext;
import org.joda.time.DateTimeZone;
import java.io.IOException;
import java.util.List;
@ -54,23 +55,78 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
super(name, type, input);
}
protected LeafOnly(String name, String type, Class<VS> valuesSourceType, ValueType targetValueType) {
super(name, type, valuesSourceType, targetValueType);
}
@Override
public AggregatorFactory subFactories(AggregatorFactories subFactories) {
throw new AggregationInitializationException("Aggregator [" + name + "] of type [" + type + "] cannot accept sub-aggregations");
}
}
private final Class<VS> valuesSourceType;
private final ValueType targetValueType;
private String field = null;
private Script script = null;
private ValueType valueType = null;
private String format = null;
private Object missing = null;
protected ValuesSourceConfig<VS> config;
private ValuesSourceParser.Input<VS> input;
private DateTimeZone timeZone;
// NORELEASE remove this method when aggs refactoring complete
/**
* This constructor remains here until all subclasses have been moved to the
* new constructor. This also means moving from using
* {@link ValuesSourceParser} to using {@link AbstractValuesSourceParser}.
*/
@Deprecated
protected ValuesSourceAggregatorFactory(String name, String type, ValuesSourceParser.Input<VS> input) {
super(name, type);
this.input = input;
this.valuesSourceType = input.valuesSourceType;
this.targetValueType = input.targetValueType;
this.field = input.field;
this.script = input.script;
this.valueType = input.valueType;
this.format = input.format;
this.missing = input.missing;
this.timeZone = input.timezone;
}
protected ValuesSourceAggregatorFactory(String name, String type, Class<VS> valuesSourceType, ValueType targetValueType) {
super(name, type);
this.valuesSourceType = valuesSourceType;
this.targetValueType = targetValueType;
}
public void field(String field) {
this.field = field;
}
public void script(Script script) {
this.script = script;
}
public void valueType(ValueType valueType) {
this.valueType = valueType;
}
public void format(String format) {
this.format = format;
}
public void missing(Object missing) {
this.missing = missing;
}
public void timeZone(DateTimeZone timeZone) {
this.timeZone = timeZone;
}
@Override
public void doInit(AggregationContext context) {
this.config = config(input, context);
this.config = config(context);
if (config == null || !config.valid()) {
resolveValuesSourceConfigFromAncestors(name, this.parent, config.valueSourceType());
}
@ -91,17 +147,17 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
public void doValidate() {
}
public ValuesSourceConfig<VS> config(ValuesSourceParser.Input<VS> input, AggregationContext context) {
public ValuesSourceConfig<VS> config(AggregationContext context) {
ValueType valueType = input.valueType != null ? input.valueType : input.targetValueType;
ValueType valueType = this.valueType != null ? this.valueType : targetValueType;
if (input.field == null) {
if (input.script == null) {
if (field == null) {
if (script == null) {
ValuesSourceConfig<VS> config = new ValuesSourceConfig(ValuesSource.class);
config.format = resolveFormat(null, valueType);
return config;
}
Class valuesSourceType = valueType != null ? (Class<VS>) valueType.getValuesSourceType() : input.valuesSourceType;
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
@ -110,19 +166,19 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
valuesSourceType = ValuesSource.Bytes.class;
}
ValuesSourceConfig<VS> config = new ValuesSourceConfig<VS>(valuesSourceType);
config.missing = input.missing;
config.format = resolveFormat(input.format, valueType);
config.script = createScript(input.script, context.searchContext());
config.missing = missing;
config.format = resolveFormat(format, valueType);
config.script = createScript(script, context.searchContext());
config.scriptValueType = valueType;
return config;
}
MappedFieldType fieldType = context.searchContext().smartNameFieldTypeFromAnyType(input.field);
MappedFieldType fieldType = context.searchContext().smartNameFieldTypeFromAnyType(field);
if (fieldType == null) {
Class<VS> valuesSourceType = valueType != null ? (Class<VS>) valueType.getValuesSourceType() : input.valuesSourceType;
Class<VS> valuesSourceType = valueType != null ? (Class<VS>) valueType.getValuesSourceType() : this.valuesSourceType;
ValuesSourceConfig<VS> config = new ValuesSourceConfig<>(valuesSourceType);
config.missing = input.missing;
config.format = resolveFormat(input.format, valueType);
config.missing = missing;
config.format = resolveFormat(format, valueType);
config.unmapped = true;
if (valueType != null) {
// todo do we really need this for unmapped?
@ -134,7 +190,7 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
IndexFieldData<?> indexFieldData = context.searchContext().fieldData().getForField(fieldType);
ValuesSourceConfig config;
if (input.valuesSourceType == ValuesSource.class) {
if (valuesSourceType == ValuesSource.class) {
if (indexFieldData instanceof IndexNumericFieldData) {
config = new ValuesSourceConfig<>(ValuesSource.Numeric.class);
} else if (indexFieldData instanceof IndexGeoPointFieldData) {
@ -143,13 +199,13 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
config = new ValuesSourceConfig<>(ValuesSource.Bytes.class);
}
} else {
config = new ValuesSourceConfig(input.valuesSourceType);
config = new ValuesSourceConfig(valuesSourceType);
}
config.fieldContext = new FieldContext(input.field, indexFieldData, fieldType);
config.missing = input.missing;
config.script = createScript(input.script, context.searchContext());
config.format = resolveFormat(input.format, fieldType);
config.fieldContext = new FieldContext(field, indexFieldData, fieldType);
config.missing = missing;
config.script = createScript(script, context.searchContext());
config.format = resolveFormat(format, this.timeZone, fieldType);
return config;
}
@ -168,10 +224,10 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
return valueFormat;
}
private static ValueFormat resolveFormat(@Nullable String format, MappedFieldType fieldType) {
private static ValueFormat resolveFormat(@Nullable String format, @Nullable DateTimeZone timezone, MappedFieldType fieldType) {
if (fieldType instanceof DateFieldMapper.DateFieldType) {
return format != null ? ValueFormat.DateTime.format(format) : ValueFormat.DateTime
.mapper((DateFieldMapper.DateFieldType) fieldType);
return format != null ? ValueFormat.DateTime.format(format, timezone) : ValueFormat.DateTime.mapper(
(DateFieldMapper.DateFieldType) fieldType, timezone);
}
if (fieldType instanceof IpFieldMapper.IpFieldType) {
return ValueFormat.IPv4;
@ -201,7 +257,8 @@ public abstract class ValuesSourceAggregatorFactory<VS extends ValuesSource> ext
if (requiredValuesSourceType == null || requiredValuesSourceType.isAssignableFrom(config.valueSourceType)) {
ValueFormat format = config.format;
this.config = config;
// if the user explicitly defined a format pattern, we'll do our best to keep it even when we inherit the
// 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);

View File

@ -35,9 +35,12 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
// NORELEASE remove this class when aggs refactoring complete
/**
*
* @deprecated use {@link AbstractValuesSourceParser} instead. This class will
* be removed when aggs refactoring is complete.
*/
@Deprecated
public class ValuesSourceParser<VS extends ValuesSource> {
static final ParseField TIME_ZONE = new ParseField("time_zone");
@ -58,6 +61,12 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return new Builder<>(aggName, aggType, context, ValuesSource.GeoPoint.class).targetValueType(ValueType.GEOPOINT).scriptable(false);
}
// NORELEASE remove this class when aggs refactoring complete
/**
* @deprecated use {@link AbstractValuesSourceParser} instead. This class
* will be removed when aggs refactoring is complete.
*/
@Deprecated
public static class Input<VS> {
String field = null;
Script script = null;
@ -68,6 +77,7 @@ public class ValuesSourceParser<VS extends ValuesSource> {
Object missing = null;
Class<VS> valuesSourceType = null;
ValueType targetValueType = null;
DateTimeZone timezone = DateTimeZone.UTC;
public boolean valid() {
return field != null || script != null;
@ -77,7 +87,6 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return this.timezone;
}
}
}
private final String aggName;
private final InternalAggregation.Type aggType;
@ -163,6 +172,12 @@ public class ValuesSourceParser<VS extends ValuesSource> {
return input;
}
// NORELEASE remove this class when aggs refactoring complete
/**
* @deprecated use {@link AbstractValuesSourceParser} instead. This class
* will be removed when aggs refactoring is complete.
*/
@Deprecated
public static class Builder<VS extends ValuesSource> {
private final ValuesSourceParser<VS> parser;