Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add time_series_metric parameter #76766

Merged
merged 17 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public class ScriptScoreBenchmark {
private final ScriptModule scriptModule = new ScriptModule(Settings.EMPTY, pluginsService.filterPlugins(ScriptPlugin.class));

private final Map<String, MappedFieldType> fieldTypes = Map.ofEntries(
Map.entry("n", new NumberFieldType("n", NumberType.LONG, false, false, true, true, null, Map.of(), null, false))
Map.entry("n", new NumberFieldType("n", NumberType.LONG, false, false, true, true, null, Map.of(), null, false, null))
);
private final IndexFieldDataCache fieldDataCache = new IndexFieldDataCache.None();
private final CircuitBreakerService breakerService = new NoneCircuitBreakerService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,27 @@ public TokenCountFieldMapper build(MapperBuilderContext context) {

static class TokenCountFieldType extends NumberFieldMapper.NumberFieldType {

TokenCountFieldType(String name, boolean isSearchable, boolean isStored,
boolean hasDocValues, Number nullValue, Map<String, String> meta) {
super(name, NumberFieldMapper.NumberType.INTEGER, isSearchable, isStored, hasDocValues, false, nullValue, meta, null, false);
TokenCountFieldType(
String name,
boolean isSearchable,
boolean isStored,
boolean hasDocValues,
Number nullValue,
Map<String, String> meta
) {
super(
name,
NumberFieldMapper.NumberType.INTEGER,
isSearchable,
isStored,
hasDocValues,
false,
nullValue,
meta,
null,
false,
null
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -905,6 +906,67 @@ public static Parameter<String> restrictedStringParam(String name, boolean updat
});
}

/**
* Defines a parameter that takes any of the values of an enumeration.
*
* @param name the parameter name
* @param updateable whether the parameter can be changed by a mapping update
* @param initializer a function that reads the parameter value from an existing mapper
* @param defaultValue the default value, to be used if the parameter is undefined in a mapping
* @param enumClass the enumeration class the parameter takes values from
*/
public static <T extends Enum<T>> Parameter<T> enumParam(
String name,
boolean updateable,
Function<FieldMapper, T> initializer,
T defaultValue,
Class<T> enumClass
) {
Set<T> acceptedValues = EnumSet.allOf(enumClass);
return restrictedEnumParam(name, updateable, initializer, defaultValue, enumClass, acceptedValues);
}

/**
* Defines a parameter that takes one of a restricted set of values from an enumeration.
*
* @param name the parameter name
* @param updateable whether the parameter can be changed by a mapping update
* @param initializer a function that reads the parameter value from an existing mapper
* @param defaultValue the default value, to be used if the parameter is undefined in a mapping
* @param enumClass the enumeration class the parameter takes values from
* @param values the set of values that the parameter can take
*/
public static <T extends Enum<T>> Parameter<T> restrictedEnumParam(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@romseygeek can you please have a look at this and let me know what you think?

I thought this could be re-usable

String name,
boolean updateable,
Function<FieldMapper, T> initializer,
T defaultValue,
Class<T> enumClass,
Set<T> values
) {
csoulios marked this conversation as resolved.
Show resolved Hide resolved
assert values.size() > 0;
return new Parameter<T>(name, updateable, () -> defaultValue, (n, c, o) -> {
if (o == null) {
return defaultValue;
}
try {
@SuppressWarnings("unchecked")
T enumValue = Enum.valueOf(enumClass, (String) o);
return enumValue;
} catch (IllegalArgumentException e) {
throw new MapperParsingException(
"Unknown value [" + o + "] for field [" + name + "] - accepted values are " + values
);
}
}, initializer).addValidator(v -> {
if (v != null && values.contains(v) == false) {
throw new MapperParsingException(
"Unknown value [" + v + "] for field [" + name + "] - accepted values are " + values
);
}
});
}

/**
* Defines a parameter that takes an analyzer name
* @param name the parameter name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
import org.elasticsearch.index.mapper.TimeSeriesParams.MetricType;
import org.elasticsearch.index.query.SearchExecutionContext;
import org.elasticsearch.script.DoubleFieldScript;
import org.elasticsearch.script.LongFieldScript;
Expand Down Expand Up @@ -83,8 +84,18 @@ public static class Builder extends FieldMapper.Builder {

private final Parameter<Script> script = Parameter.scriptParam(m -> toType(m).script);
private final Parameter<String> onScriptError = Parameter.onScriptErrorParam(m -> toType(m).onScriptError, script);

/**
* Parameter that marks this field as a time series dimension.
*/
private final Parameter<Boolean> dimension;

/**
* Parameter that marks this field as a time series metric defining its time series metric type.
* For the numeric fields gauge and counter metric types are
* supported
*/
private final Parameter<MetricType> metric;

private final Parameter<Map<String, String>> meta = Parameter.metaParam();

Expand Down Expand Up @@ -126,6 +137,15 @@ public Builder(String name, NumberType type, ScriptCompiler compiler, boolean ig
}
});

this.metric = TimeSeriesParams.metricParam(m -> toType(m).metricType, MetricType.gauge, MetricType.counter)
.addValidator(v -> {
if (v != null && hasDocValues.getValue() == false) {
throw new IllegalArgumentException(
"Field [" + TimeSeriesParams.TIME_SERIES_METRIC_PARAM + "] requires that [" + hasDocValues.name + "] is true"
);
}
});

this.script.precludesParameters(ignoreMalformed, coerce, nullValue);
addScriptValidation(script, indexed, hasDocValues);
}
Expand All @@ -152,9 +172,26 @@ public Builder dimension(boolean dimension) {
return this;
}

public Builder metric(MetricType metric) {
this.metric.setValue(metric);
return this;
}

@Override
protected List<Parameter<?>> getParameters() {
return List.of(indexed, hasDocValues, stored, ignoreMalformed, coerce, nullValue, script, onScriptError, meta, dimension);
return List.of(
indexed,
hasDocValues,
stored,
ignoreMalformed,
coerce,
nullValue,
script,
onScriptError,
meta,
dimension,
metric
);
}

@Override
Expand Down Expand Up @@ -967,26 +1004,29 @@ public static class NumberFieldType extends SimpleMappedFieldType {
private final Number nullValue;
private final FieldValues<Number> scriptValues;
private final boolean isDimension;
private final MetricType metricType;

public NumberFieldType(String name, NumberType type, boolean isSearchable, boolean isStored,
boolean hasDocValues, boolean coerce, Number nullValue, Map<String, String> meta,
FieldValues<Number> script, boolean isDimension) {
FieldValues<Number> script, boolean isDimension, MetricType metricType) {
super(name, isSearchable, isStored, hasDocValues, TextSearchInfo.SIMPLE_MATCH_WITHOUT_TERMS, meta);
this.type = Objects.requireNonNull(type);
this.coerce = coerce;
this.nullValue = nullValue;
this.scriptValues = script;
this.isDimension = isDimension;
this.metricType = metricType;
}

NumberFieldType(String name, Builder builder) {
this(name, builder.type, builder.indexed.getValue(), builder.stored.getValue(), builder.hasDocValues.getValue(),
builder.coerce.getValue().value(), builder.nullValue.getValue(), builder.meta.getValue(),
builder.scriptValues(), builder.dimension.getValue());
builder.scriptValues(), builder.dimension.getValue(),
builder.metric.getValue());
}

public NumberFieldType(String name, NumberType type) {
this(name, type, true, false, true, true, null, Collections.emptyMap(), null, false);
this(name, type, true, false, true, true, null, Collections.emptyMap(), null, false, null);
}

@Override
Expand Down Expand Up @@ -1082,6 +1122,14 @@ public CollapseType collapseType() {
public boolean isDimension() {
return isDimension;
}

/**
* If field is a time series metric field, returns its metric type
* @return the metric type or null
*/
public MetricType getMetricType() {
return metricType;
}
}

private final NumberType type;
Expand All @@ -1098,6 +1146,7 @@ public boolean isDimension() {
private final boolean dimension;
private final ScriptCompiler scriptCompiler;
private final Script script;
private final TimeSeriesParams.MetricType metricType;

private NumberFieldMapper(
String simpleName,
Expand All @@ -1119,6 +1168,7 @@ private NumberFieldMapper(
this.dimension = builder.dimension.getValue();
this.scriptCompiler = builder.scriptCompiler;
this.script = builder.script.getValue();
this.metricType = builder.metric.getValue();
}

boolean coerce() {
Expand Down Expand Up @@ -1206,6 +1256,8 @@ protected void indexScriptValues(SearchLookup searchLookup, LeafReaderContext re
@Override
public FieldMapper.Builder getMergeBuilder() {
return new Builder(simpleName(), type, scriptCompiler, ignoreMalformedByDefault, coerceByDefault)
.dimension(dimension).init(this);
.dimension(dimension)
.metric(metricType)
.init(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

package org.elasticsearch.index.mapper;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.function.Function;

/**
* Utility functions for time series related mapper parameters
*/
public final class TimeSeriesParams {

public static final String TIME_SERIES_METRIC_PARAM = "time_series_metric";

private TimeSeriesParams() {}

public enum MetricType {
gauge,
counter,
histogram,
summary
csoulios marked this conversation as resolved.
Show resolved Hide resolved
}

public static FieldMapper.Parameter<MetricType> metricParam(Function<FieldMapper, MetricType> initializer, MetricType... values) {
assert values.length > 0;
EnumSet<MetricType> acceptedValues = EnumSet.noneOf(MetricType.class);
acceptedValues.addAll(Arrays.asList(values));
return FieldMapper.Parameter.restrictedEnumParam(
TIME_SERIES_METRIC_PARAM,
false,
initializer,
null,
MetricType.class,
acceptedValues
).acceptsNull();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -311,13 +311,13 @@ private void doTestRequireDocValues(MappedFieldType ft) {
public void testRequireDocValuesOnLongs() {
doTestRequireDocValues(new NumberFieldMapper.NumberFieldType("field", LONG));
doTestRequireDocValues(new NumberFieldMapper.NumberFieldType("field", LONG,
true, false, false, false, null, Collections.emptyMap(), null, false));
true, false, false, false, null, Collections.emptyMap(), null, false, null));
}

public void testRequireDocValuesOnDoubles() {
doTestRequireDocValues(new NumberFieldMapper.NumberFieldType("field", NumberType.DOUBLE));
doTestRequireDocValues(new NumberFieldMapper.NumberFieldType("field", NumberType.DOUBLE,
true, false, false, false, null, Collections.emptyMap(), null, false));
true, false, false, false, null, Collections.emptyMap(), null, false, null));
}

public void testRequireDocValuesOnBools() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,47 @@ public void testDimension() throws IOException {
assertThat(e.getCause().getMessage(), containsString("Parameter [dimension] cannot be set"));
}

public void testMetricType() throws IOException {
// Test default setting
MapperService mapperService = createMapperService(fieldMapping(b -> minimalMapping(b)));
NumberFieldMapper.NumberFieldType ft = (NumberFieldMapper.NumberFieldType) mapperService.fieldType("field");
assertNull(ft.getMetricType());

assertMetricType("gauge", NumberFieldMapper.NumberFieldType::getMetricType);
assertMetricType("counter", NumberFieldMapper.NumberFieldType::getMetricType);

{
// Test invalid metric type for this field type
Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_metric", "histogram");
})));
assertThat(
e.getCause().getMessage(),
containsString("Unknown value [histogram] for field [time_series_metric] - accepted values are [gauge, counter]")
);
}
{
// Test invalid metric type for this field type
Exception e = expectThrows(MapperParsingException.class, () -> createMapperService(fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_metric", "unknown");
})));
assertThat(
e.getCause().getMessage(),
containsString("Unknown value [unknown] for field [time_series_metric] - accepted values are [gauge, counter]")
);
}
}

public void testMetricAndDocvalues() {
Exception e = expectThrows(MapperParsingException.class, () -> createDocumentMapper(fieldMapping(b -> {
minimalMapping(b);
b.field("time_series_metric", "counter").field("doc_values", false);
})));
assertThat(e.getCause().getMessage(), containsString("Field [time_series_metric] requires that [doc_values] is true"));
}

@Override
protected final Object generateRandomInputValue(MappedFieldType ft) {
Number n = randomNumber();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public void testLongTermQueryWithDecimalPart() {
}

private static MappedFieldType unsearchable() {
return new NumberFieldType("field", NumberType.LONG, false, false, true, true, null, Collections.emptyMap(), null, false);
return new NumberFieldType("field", NumberType.LONG, false, false, true, true, null, Collections.emptyMap(), null, false, null);
}

public void testTermQuery() {
Expand Down
Loading