Skip to content

Commit 380648a

Browse files
committed
Merge pull request #12280 from polyfractal/bugfix/movavg_validation
Aggregations: Add better validation of moving_avg model settings
2 parents a8391fc + 702f884 commit 380648a

File tree

9 files changed

+81
-10
lines changed

9 files changed

+81
-10
lines changed

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/MovAvgBuilder.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.elasticsearch.search.aggregations.pipeline.movavg.models.MovAvgModelBuilder;
2626

2727
import java.io.IOException;
28+
import java.util.Map;
2829

2930
/**
3031
* A builder to create MovingAvg pipeline aggregations
@@ -37,6 +38,7 @@ public class MovAvgBuilder extends PipelineAggregatorBuilder<MovAvgBuilder> {
3738
private Integer window;
3839
private Integer predict;
3940
private Boolean minimize;
41+
private Map<String, Object> settings;
4042

4143
public MovAvgBuilder(String name) {
4244
super(name, MovAvgPipelineAggregator.TYPE.name());
@@ -107,6 +109,18 @@ public MovAvgBuilder minimize(boolean minimize) {
107109
return this;
108110
}
109111

112+
/**
113+
* The hash of settings that should be provided to the model when it is
114+
* instantiated
115+
*
116+
* @param settings
117+
* @return
118+
*/
119+
public MovAvgBuilder settings(Map<String, Object> settings) {
120+
this.settings = settings;
121+
return this;
122+
}
123+
110124

111125
@Override
112126
protected XContentBuilder internalXContent(XContentBuilder builder, Params params) throws IOException {
@@ -128,6 +142,9 @@ protected XContentBuilder internalXContent(XContentBuilder builder, Params param
128142
if (minimize != null) {
129143
builder.field(MovAvgParser.MINIMIZE.getPreferredName(), minimize);
130144
}
145+
if (settings != null) {
146+
builder.field(MovAvgParser.SETTINGS.getPreferredName(), settings);
147+
}
131148
return builder;
132149
}
133150

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/EwmaModel.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,11 @@ public String getName() {
120120
}
121121

122122
@Override
123-
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException {
123+
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
124+
ParseFieldMatcher parseFieldMatcher) throws ParseException {
124125

125126
double alpha = parseDoubleParam(settings, "alpha", 0.3);
127+
checkUnrecognizedParams(settings);
126128
return new EwmaModel(alpha);
127129
}
128130

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/HoltLinearModel.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,12 @@ public String getName() {
180180
}
181181

182182
@Override
183-
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException {
183+
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
184+
ParseFieldMatcher parseFieldMatcher) throws ParseException {
184185

185186
double alpha = parseDoubleParam(settings, "alpha", 0.3);
186187
double beta = parseDoubleParam(settings, "beta", 0.1);
188+
checkUnrecognizedParams(settings);
187189
return new HoltLinearModel(alpha, beta);
188190
}
189191
}

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/HoltWintersModel.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,8 @@ public String getName() {
356356
}
357357

358358
@Override
359-
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException {
359+
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
360+
ParseFieldMatcher parseFieldMatcher) throws ParseException {
360361

361362
double alpha = parseDoubleParam(settings, "alpha", 0.3);
362363
double beta = parseDoubleParam(settings, "beta", 0.1);
@@ -376,6 +377,7 @@ public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipeline
376377
if (value != null) {
377378
if (value instanceof String) {
378379
seasonalityType = SeasonalityType.parse((String)value, parseFieldMatcher);
380+
settings.remove("type");
379381
} else {
380382
throw new ParseException("Parameter [type] must be a String, type `"
381383
+ value.getClass().getSimpleName() + "` provided instead", 0);
@@ -385,6 +387,7 @@ public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipeline
385387

386388
boolean pad = parseBoolParam(settings, "pad", seasonalityType.equals(SeasonalityType.MULTIPLICATIVE));
387389

390+
checkUnrecognizedParams(settings);
388391
return new HoltWintersModel(alpha, beta, gamma, period, seasonalityType, pad);
389392
}
390393
}

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/LinearModel.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,9 @@ public String getName() {
107107
}
108108

109109
@Override
110-
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException {
110+
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
111+
ParseFieldMatcher parseFieldMatcher) throws ParseException {
112+
checkUnrecognizedParams(settings);
111113
return new LinearModel();
112114
}
113115
}

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/MovAvgModel.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ public abstract static class AbstractModelParser {
155155
* @param parseFieldMatcher Matcher for field names
156156
* @return A fully built moving average model
157157
*/
158-
public abstract MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException;
158+
public abstract MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName,
159+
int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException;
159160

160161

161162
/**
@@ -180,6 +181,7 @@ protected double parseDoubleParam(@Nullable Map<String, Object> settings, String
180181
} else if (value instanceof Number) {
181182
double v = ((Number) value).doubleValue();
182183
if (v >= 0 && v <= 1) {
184+
settings.remove(name);
183185
return v;
184186
}
185187

@@ -211,6 +213,7 @@ protected int parseIntegerParam(@Nullable Map<String, Object> settings, String n
211213
if (value == null) {
212214
return defaultValue;
213215
} else if (value instanceof Number) {
216+
settings.remove(name);
214217
return ((Number) value).intValue();
215218
}
216219

@@ -238,12 +241,19 @@ protected boolean parseBoolParam(@Nullable Map<String, Object> settings, String
238241
if (value == null) {
239242
return defaultValue;
240243
} else if (value instanceof Boolean) {
244+
settings.remove(name);
241245
return (Boolean)value;
242246
}
243247

244248
throw new ParseException("Parameter [" + name + "] must be a boolean, type `"
245249
+ value.getClass().getSimpleName() + "` provided instead", 0);
246250
}
251+
252+
protected void checkUnrecognizedParams(@Nullable Map<String, Object> settings) throws ParseException {
253+
if (settings != null && settings.size() > 0) {
254+
throw new ParseException("Unrecognized parameter(s): [" + String.join(", ", settings.keySet()) + "]", 0);
255+
}
256+
}
247257
}
248258

249259
}

core/src/main/java/org/elasticsearch/search/aggregations/pipeline/movavg/models/SimpleModel.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public MovAvgModel clone() {
6060
protected <T extends Number> double[] doPredict(Collection<T> values, int numPredictions) {
6161
double[] predictions = new double[numPredictions];
6262

63-
// EWMA just emits the same final prediction repeatedly.
63+
// Simple just emits the same final prediction repeatedly.
6464
Arrays.fill(predictions, next(values));
6565

6666
return predictions;
@@ -100,7 +100,9 @@ public String getName() {
100100
}
101101

102102
@Override
103-
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize, ParseFieldMatcher parseFieldMatcher) throws ParseException {
103+
public MovAvgModel parse(@Nullable Map<String, Object> settings, String pipelineName, int windowSize,
104+
ParseFieldMatcher parseFieldMatcher) throws ParseException {
105+
checkUnrecognizedParams(settings);
104106
return new SimpleModel();
105107
}
106108
}

core/src/test/java/org/elasticsearch/search/aggregations/pipeline/moving/avg/MovAvgTests.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import org.elasticsearch.action.index.IndexRequestBuilder;
2626
import org.elasticsearch.action.search.SearchPhaseExecutionException;
2727
import org.elasticsearch.action.search.SearchResponse;
28-
import org.elasticsearch.search.SearchParseException;
2928
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
3029
import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram;
3130
import org.elasticsearch.search.aggregations.bucket.histogram.InternalHistogram.Bucket;
@@ -1265,6 +1264,42 @@ public void checkIfTunableCanBeMinimized() {
12651264
fail("Model [" + builder.toString() + "] can be minimized, but an exception was thrown");
12661265
}
12671266
}
1267+
}
1268+
1269+
@Test
1270+
public void testUnrecognizedParams() {
1271+
1272+
MovAvgModelBuilder[] builders = new MovAvgModelBuilder[]{
1273+
new SimpleModel.SimpleModelBuilder(),
1274+
new LinearModel.LinearModelBuilder(),
1275+
new EwmaModel.EWMAModelBuilder(),
1276+
new HoltLinearModel.HoltLinearModelBuilder(),
1277+
new HoltWintersModel.HoltWintersModelBuilder()
1278+
};
1279+
Map<String, Object> badSettings = new HashMap<>(1);
1280+
badSettings.put("abc", 1.2);
1281+
1282+
for (MovAvgModelBuilder builder : builders) {
1283+
try {
1284+
SearchResponse response = client()
1285+
.prepareSearch("idx").setTypes("type")
1286+
.addAggregation(
1287+
histogram("histo").field(INTERVAL_FIELD).interval(interval)
1288+
.extendedBounds(0L, (long) (interval * (numBuckets - 1)))
1289+
.subAggregation(metric)
1290+
.subAggregation(movingAvg("movavg_counts")
1291+
.window(10)
1292+
.modelBuilder(builder)
1293+
.gapPolicy(gapPolicy)
1294+
.settings(badSettings)
1295+
.setBucketsPaths("_count"))
1296+
).execute().actionGet();
1297+
} catch (SearchPhaseExecutionException e) {
1298+
// All good
1299+
}
1300+
}
1301+
1302+
12681303

12691304

12701305
}

core/src/test/java/org/elasticsearch/search/aggregations/pipeline/moving/avg/MovAvgUnitTests.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -611,8 +611,6 @@ public void testNumericValidation() {
611611
for (MovAvgModel.AbstractModelParser parser : parsers) {
612612
for (Object v : values) {
613613
settings.put("alpha", v);
614-
settings.put("beta", v);
615-
settings.put("gamma", v);
616614

617615
try {
618616
parser.parse(settings, "pipeline", 10, ParseFieldMatcher.STRICT);

0 commit comments

Comments
 (0)