Merge pull request #12280 from polyfractal/bugfix/movavg_validation

Aggregations: Add better validation of moving_avg model settings
This commit is contained in:
Zachary Tong 2015-07-16 11:44:32 -04:00
commit 380648ab86
9 changed files with 81 additions and 10 deletions

View File

@ -25,6 +25,7 @@ import org.elasticsearch.search.aggregations.pipeline.PipelineAggregatorBuilder;
import org.elasticsearch.search.aggregations.pipeline.movavg.models.MovAvgModelBuilder; import org.elasticsearch.search.aggregations.pipeline.movavg.models.MovAvgModelBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/** /**
* A builder to create MovingAvg pipeline aggregations * A builder to create MovingAvg pipeline aggregations
@ -37,6 +38,7 @@ public class MovAvgBuilder extends PipelineAggregatorBuilder<MovAvgBuilder> {
private Integer window; private Integer window;
private Integer predict; private Integer predict;
private Boolean minimize; private Boolean minimize;
private Map<String, Object> settings;
public MovAvgBuilder(String name) { public MovAvgBuilder(String name) {
super(name, MovAvgPipelineAggregator.TYPE.name()); super(name, MovAvgPipelineAggregator.TYPE.name());
@ -107,6 +109,18 @@ public class MovAvgBuilder extends PipelineAggregatorBuilder<MovAvgBuilder> {
return this; return this;
} }
/**
* The hash of settings that should be provided to the model when it is
* instantiated
*
* @param settings
* @return
*/
public MovAvgBuilder settings(Map<String, Object> settings) {
this.settings = settings;
return this;
}
@Override @Override
protected XContentBuilder internalXContent(XContentBuilder builder, Params params) throws IOException { protected XContentBuilder internalXContent(XContentBuilder builder, Params params) throws IOException {
@ -128,6 +142,9 @@ public class MovAvgBuilder extends PipelineAggregatorBuilder<MovAvgBuilder> {
if (minimize != null) { if (minimize != null) {
builder.field(MovAvgParser.MINIMIZE.getPreferredName(), minimize); builder.field(MovAvgParser.MINIMIZE.getPreferredName(), minimize);
} }
if (settings != null) {
builder.field(MovAvgParser.SETTINGS.getPreferredName(), settings);
}
return builder; return builder;
} }

View File

@ -120,9 +120,11 @@ public class EwmaModel extends MovAvgModel {
} }
@Override @Override
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException { public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
ParseFieldMatcher parseFieldMatcher) throws ParseException {
double alpha = parseDoubleParam(settings, "alpha", 0.3); double alpha = parseDoubleParam(settings, "alpha", 0.3);
checkUnrecognizedParams(settings);
return new EwmaModel(alpha); return new EwmaModel(alpha);
} }

View File

@ -180,10 +180,12 @@ public class HoltLinearModel extends MovAvgModel {
} }
@Override @Override
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException { public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
ParseFieldMatcher parseFieldMatcher) throws ParseException {
double alpha = parseDoubleParam(settings, "alpha", 0.3); double alpha = parseDoubleParam(settings, "alpha", 0.3);
double beta = parseDoubleParam(settings, "beta", 0.1); double beta = parseDoubleParam(settings, "beta", 0.1);
checkUnrecognizedParams(settings);
return new HoltLinearModel(alpha, beta); return new HoltLinearModel(alpha, beta);
} }
} }

View File

@ -356,7 +356,8 @@ public class HoltWintersModel extends MovAvgModel {
} }
@Override @Override
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException { public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
ParseFieldMatcher parseFieldMatcher) throws ParseException {
double alpha = parseDoubleParam(settings, "alpha", 0.3); double alpha = parseDoubleParam(settings, "alpha", 0.3);
double beta = parseDoubleParam(settings, "beta", 0.1); double beta = parseDoubleParam(settings, "beta", 0.1);
@ -376,6 +377,7 @@ public class HoltWintersModel extends MovAvgModel {
if (value != null) { if (value != null) {
if (value instanceof String) { if (value instanceof String) {
seasonalityType = SeasonalityType.parse((String)value, parseFieldMatcher); seasonalityType = SeasonalityType.parse((String)value, parseFieldMatcher);
settings.remove("type");
} else { } else {
throw new ParseException("Parameter [type] must be a String, type `" throw new ParseException("Parameter [type] must be a String, type `"
+ value.getClass().getSimpleName() + "` provided instead", 0); + value.getClass().getSimpleName() + "` provided instead", 0);
@ -385,6 +387,7 @@ public class HoltWintersModel extends MovAvgModel {
boolean pad = parseBoolParam(settings, "pad", seasonalityType.equals(SeasonalityType.MULTIPLICATIVE)); boolean pad = parseBoolParam(settings, "pad", seasonalityType.equals(SeasonalityType.MULTIPLICATIVE));
checkUnrecognizedParams(settings);
return new HoltWintersModel(alpha, beta, gamma, period, seasonalityType, pad); return new HoltWintersModel(alpha, beta, gamma, period, seasonalityType, pad);
} }
} }

View File

@ -107,7 +107,9 @@ public class LinearModel extends MovAvgModel {
} }
@Override @Override
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException { public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
ParseFieldMatcher parseFieldMatcher) throws ParseException {
checkUnrecognizedParams(settings);
return new LinearModel(); return new LinearModel();
} }
} }

View File

@ -155,7 +155,8 @@ public abstract class MovAvgModel {
* @param parseFieldMatcher Matcher for field names * @param parseFieldMatcher Matcher for field names
* @return A fully built moving average model * @return A fully built moving average model
*/ */
public abstract MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException; public abstract MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName,
int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException;
/** /**
@ -180,6 +181,7 @@ public abstract class MovAvgModel {
} else if (value instanceof Number) { } else if (value instanceof Number) {
double v = ((Number) value).doubleValue(); double v = ((Number) value).doubleValue();
if (v >= 0 && v <= 1) { if (v >= 0 && v <= 1) {
settings.remove(name);
return v; return v;
} }
@ -211,6 +213,7 @@ public abstract class MovAvgModel {
if (value == null) { if (value == null) {
return defaultValue; return defaultValue;
} else if (value instanceof Number) { } else if (value instanceof Number) {
settings.remove(name);
return ((Number) value).intValue(); return ((Number) value).intValue();
} }
@ -238,12 +241,19 @@ public abstract class MovAvgModel {
if (value == null) { if (value == null) {
return defaultValue; return defaultValue;
} else if (value instanceof Boolean) { } else if (value instanceof Boolean) {
settings.remove(name);
return (Boolean)value; return (Boolean)value;
} }
throw new ParseException("Parameter [" + name + "] must be a boolean, type `" throw new ParseException("Parameter [" + name + "] must be a boolean, type `"
+ value.getClass().getSimpleName() + "` provided instead", 0); + value.getClass().getSimpleName() + "` provided instead", 0);
} }
protected void checkUnrecognizedParams(@Nullable Map<String, Object> settings) throws ParseException {
if (settings != null && settings.size() > 0) {
throw new ParseException("Unrecognized parameter(s): [" + String.join(", ", settings.keySet()) + "]", 0);
}
}
} }
} }

View File

@ -60,7 +60,7 @@ public class SimpleModel extends MovAvgModel {
protected <T extends Number> double[] doPredict(Collection<T> values, int numPredictions) { protected <T extends Number> double[] doPredict(Collection<T> values, int numPredictions) {
double[] predictions = new double[numPredictions]; double[] predictions = new double[numPredictions];
// EWMA just emits the same final prediction repeatedly. // Simple just emits the same final prediction repeatedly.
Arrays.fill(predictions, next(values)); Arrays.fill(predictions, next(values));
return predictions; return predictions;
@ -100,7 +100,9 @@ public class SimpleModel extends MovAvgModel {
} }
@Override @Override
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException { public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
ParseFieldMatcher parseFieldMatcher) throws ParseException {
checkUnrecognizedParams(settings);
return new SimpleModel(); return new SimpleModel();
} }
} }

View File

@ -25,7 +25,6 @@ import com.google.common.collect.EvictingQueue;
import org.elasticsearch.action.index.IndexRequestBuilder; import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchPhaseExecutionException; import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram; import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram;
import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram.Bucket; import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram.Bucket;
@ -1265,6 +1264,42 @@ public class MovAvgTests extends ElasticsearchIntegrationTest {
fail("Model [" + builder.toString() + "] can be minimized, but an exception was thrown"); fail("Model [" + builder.toString() + "] can be minimized, but an exception was thrown");
} }
} }
}
@Test
public void testUnrecognizedParams() {
MovAvgModelBuilder[] builders = new MovAvgModelBuilder[]{
new SimpleModel.SimpleModelBuilder(),
new LinearModel.LinearModelBuilder(),
new EwmaModel.EWMAModelBuilder(),
new HoltLinearModel.HoltLinearModelBuilder(),
new HoltWintersModel.HoltWintersModelBuilder()
};
Map<String, Object> badSettings = new HashMap<>(1);
badSettings.put("abc", 1.2);
for (MovAvgModelBuilder builder : builders) {
try {
SearchResponse response = client()
.prepareSearch("idx").setTypes("type")
.addAggregation(
histogram("histo").field(INTERVAL_FIELD).interval(interval)
.extendedBounds(0L, (long) (interval * (numBuckets - 1)))
.subAggregation(metric)
.subAggregation(movingAvg("movavg_counts")
.window(10)
.modelBuilder(builder)
.gapPolicy(gapPolicy)
.settings(badSettings)
.setBucketsPaths("_count"))
).execute().actionGet();
} catch (SearchPhaseExecutionException e) {
// All good
}
}
} }

View File

@ -611,8 +611,6 @@ public class MovAvgUnitTests extends ElasticsearchTestCase {
for (MovAvgModel.AbstractModelParser parser : parsers) { for (MovAvgModel.AbstractModelParser parser : parsers) {
for (Object v : values) { for (Object v : values) {
settings.put("alpha", v); settings.put("alpha", v);
settings.put("beta", v);
settings.put("gamma", v);
try { try {
parser.parse(settings, "pipeline", 10, ParseFieldMatcher.STRICT); parser.parse(settings, "pipeline", 10, ParseFieldMatcher.STRICT);