|
@@ -20,9 +20,7 @@
|
|
|
package org.elasticsearch.index.mapper;
|
|
|
|
|
|
import org.apache.lucene.document.Field;
|
|
|
-import org.apache.lucene.document.FieldType;
|
|
|
import org.apache.lucene.index.DocValues;
|
|
|
-import org.apache.lucene.index.IndexOptions;
|
|
|
import org.apache.lucene.index.LeafReaderContext;
|
|
|
import org.apache.lucene.index.NumericDocValues;
|
|
|
import org.apache.lucene.index.SortedNumericDocValues;
|
|
@@ -34,13 +32,12 @@ import org.apache.lucene.search.TermQuery;
|
|
|
import org.apache.lucene.util.BytesRef;
|
|
|
import org.elasticsearch.common.Explicit;
|
|
|
import org.elasticsearch.common.settings.Setting;
|
|
|
-import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
|
+import org.elasticsearch.common.settings.Settings;
|
|
|
import org.elasticsearch.common.xcontent.XContentParser;
|
|
|
import org.elasticsearch.common.xcontent.XContentParser.Token;
|
|
|
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
|
|
import org.elasticsearch.index.fielddata.FieldData;
|
|
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
|
|
-import org.elasticsearch.index.fielddata.IndexFieldDataCache;
|
|
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
|
|
import org.elasticsearch.index.fielddata.LeafNumericFieldData;
|
|
|
import org.elasticsearch.index.fielddata.NumericDoubleValues;
|
|
@@ -48,9 +45,7 @@ import org.elasticsearch.index.fielddata.ScriptDocValues;
|
|
|
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
|
|
|
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
|
|
import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
|
|
|
-import org.elasticsearch.index.mapper.NumberFieldMapper.Defaults;
|
|
|
import org.elasticsearch.index.query.QueryShardContext;
|
|
|
-import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
|
|
import org.elasticsearch.search.DocValueFormat;
|
|
|
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
|
|
|
|
|
@@ -59,124 +54,82 @@ import java.math.BigDecimal;
|
|
|
import java.time.ZoneId;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
-import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
|
|
|
/** A {@link FieldMapper} for scaled floats. Values are internally multiplied
|
|
|
* by a scaling factor and rounded to the closest long. */
|
|
|
-public class ScaledFloatFieldMapper extends FieldMapper {
|
|
|
+public class ScaledFloatFieldMapper extends ParametrizedFieldMapper {
|
|
|
|
|
|
public static final String CONTENT_TYPE = "scaled_float";
|
|
|
+
|
|
|
// use the same default as numbers
|
|
|
private static final Setting<Boolean> COERCE_SETTING = NumberFieldMapper.COERCE_SETTING;
|
|
|
- private static final FieldType FIELD_TYPE = new FieldType();
|
|
|
- static {
|
|
|
- FIELD_TYPE.setIndexOptions(IndexOptions.DOCS);
|
|
|
+
|
|
|
+ private static ScaledFloatFieldMapper toType(FieldMapper in) {
|
|
|
+ return (ScaledFloatFieldMapper) in;
|
|
|
}
|
|
|
|
|
|
- public static class Builder extends FieldMapper.Builder<Builder> {
|
|
|
+ public static class Builder extends ParametrizedFieldMapper.Builder {
|
|
|
|
|
|
- private boolean scalingFactorSet = false;
|
|
|
- private double scalingFactor;
|
|
|
- private Boolean ignoreMalformed;
|
|
|
- private Boolean coerce;
|
|
|
- private Double nullValue;
|
|
|
+ private final Parameter<Boolean> indexed = Parameter.indexParam(m -> toType(m).indexed, true);
|
|
|
+ private final Parameter<Boolean> hasDocValues = Parameter.docValuesParam(m -> toType(m).hasDocValues, true);
|
|
|
+ private final Parameter<Boolean> stored = Parameter.storeParam(m -> toType(m).stored, false);
|
|
|
|
|
|
- public Builder(String name) {
|
|
|
- super(name, FIELD_TYPE);
|
|
|
- builder = this;
|
|
|
- }
|
|
|
+ private final Parameter<Explicit<Boolean>> ignoreMalformed;
|
|
|
+ private final Parameter<Explicit<Boolean>> coerce;
|
|
|
|
|
|
- public Builder ignoreMalformed(boolean ignoreMalformed) {
|
|
|
- this.ignoreMalformed = ignoreMalformed;
|
|
|
- return builder;
|
|
|
- }
|
|
|
+ private final Parameter<Double> scalingFactor = new Parameter<>("scaling_factor", false, () -> null,
|
|
|
+ (n, c, o) -> XContentMapValues.nodeDoubleValue(o), m -> toType(m).scalingFactor)
|
|
|
+ .setValidator(v -> {
|
|
|
+ if (v == null) {
|
|
|
+ throw new IllegalArgumentException("Field [scaling_factor] is required");
|
|
|
+ }
|
|
|
+ if (Double.isFinite(v) == false || v <= 0) {
|
|
|
+ throw new IllegalArgumentException("[scaling_factor] must be a positive number, got [" + v + "]");
|
|
|
+ }
|
|
|
+ });
|
|
|
+ private final Parameter<Double> nullValue = new Parameter<>("null_value", false, () -> null,
|
|
|
+ (n, c, o) -> XContentMapValues.nodeDoubleValue(o), m -> toType(m).nullValue);
|
|
|
|
|
|
- @Override
|
|
|
- public Builder indexOptions(IndexOptions indexOptions) {
|
|
|
- throw new MapperParsingException(
|
|
|
- "index_options not allowed in field [" + name + "] of type [" + CONTENT_TYPE + "]");
|
|
|
- }
|
|
|
+ private final Parameter<Map<String, String>> meta = Parameter.metaParam();
|
|
|
|
|
|
- protected Explicit<Boolean> ignoreMalformed(BuilderContext context) {
|
|
|
- if (ignoreMalformed != null) {
|
|
|
- return new Explicit<>(ignoreMalformed, true);
|
|
|
- }
|
|
|
- if (context.indexSettings() != null) {
|
|
|
- return new Explicit<>(IGNORE_MALFORMED_SETTING.get(context.indexSettings()), false);
|
|
|
- }
|
|
|
- return Defaults.IGNORE_MALFORMED;
|
|
|
+ public Builder(String name, Settings settings) {
|
|
|
+ this(name, IGNORE_MALFORMED_SETTING.get(settings), COERCE_SETTING.get(settings));
|
|
|
}
|
|
|
|
|
|
- public Builder coerce(boolean coerce) {
|
|
|
- this.coerce = coerce;
|
|
|
- return builder;
|
|
|
+ public Builder(String name, boolean ignoreMalformedByDefault, boolean coerceByDefault) {
|
|
|
+ super(name);
|
|
|
+ this.ignoreMalformed
|
|
|
+ = Parameter.explicitBoolParam("ignore_malformed", true, m -> toType(m).ignoreMalformed, ignoreMalformedByDefault);
|
|
|
+ this.coerce
|
|
|
+ = Parameter.explicitBoolParam("coerce", true, m -> toType(m).coerce, coerceByDefault);
|
|
|
}
|
|
|
|
|
|
- public Builder scalingFactor(double scalingFactor) {
|
|
|
- this.scalingFactor = scalingFactor;
|
|
|
- scalingFactorSet = true;
|
|
|
+ Builder scalingFactor(double scalingFactor) {
|
|
|
+ this.scalingFactor.setValue(scalingFactor);
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- public Builder nullValue(Double nullValue) {
|
|
|
- this.nullValue = nullValue;
|
|
|
+ Builder nullValue(double nullValue) {
|
|
|
+ this.nullValue.setValue(nullValue);
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- protected Explicit<Boolean> coerce(BuilderContext context) {
|
|
|
- if (coerce != null) {
|
|
|
- return new Explicit<>(coerce, true);
|
|
|
- }
|
|
|
- if (context.indexSettings() != null) {
|
|
|
- return new Explicit<>(COERCE_SETTING.get(context.indexSettings()), false);
|
|
|
- }
|
|
|
- return Defaults.COERCE;
|
|
|
+ @Override
|
|
|
+ protected List<Parameter<?>> getParameters() {
|
|
|
+ return List.of(indexed, hasDocValues, stored, ignoreMalformed, meta, scalingFactor, coerce, nullValue);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public ScaledFloatFieldMapper build(BuilderContext context) {
|
|
|
- if (scalingFactorSet == false) {
|
|
|
- throw new IllegalArgumentException("Field [" + name + "] misses required parameter [scaling_factor]");
|
|
|
- }
|
|
|
- ScaledFloatFieldType type = new ScaledFloatFieldType(buildFullName(context), indexed, hasDocValues, meta, scalingFactor);
|
|
|
- return new ScaledFloatFieldMapper(name, fieldType, type, ignoreMalformed(context),
|
|
|
- coerce(context), multiFieldsBuilder.build(this, context), copyTo, nullValue);
|
|
|
+ ScaledFloatFieldType type = new ScaledFloatFieldType(buildFullName(context), indexed.getValue(), hasDocValues.getValue(),
|
|
|
+ meta.getValue(), scalingFactor.getValue());
|
|
|
+ return new ScaledFloatFieldMapper(name, type, multiFieldsBuilder.build(this, context), copyTo.build(), this);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public static class TypeParser implements Mapper.TypeParser {
|
|
|
-
|
|
|
- @Override
|
|
|
- public Mapper.Builder<?> parse(String name, Map<String, Object> node,
|
|
|
- ParserContext parserContext) throws MapperParsingException {
|
|
|
- Builder builder = new Builder(name);
|
|
|
- TypeParsers.parseField(builder, name, node, parserContext);
|
|
|
- for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
|
|
|
- Map.Entry<String, Object> entry = iterator.next();
|
|
|
- String propName = entry.getKey();
|
|
|
- Object propNode = entry.getValue();
|
|
|
- if (propName.equals("null_value")) {
|
|
|
- if (propNode == null) {
|
|
|
- throw new MapperParsingException("Property [null_value] cannot be null.");
|
|
|
- }
|
|
|
- builder.nullValue(ScaledFloatFieldMapper.parse(propNode));
|
|
|
- iterator.remove();
|
|
|
- } else if (propName.equals("ignore_malformed")) {
|
|
|
- builder.ignoreMalformed(XContentMapValues.nodeBooleanValue(propNode, name + ".ignore_malformed"));
|
|
|
- iterator.remove();
|
|
|
- } else if (propName.equals("coerce")) {
|
|
|
- builder.coerce(XContentMapValues.nodeBooleanValue(propNode, name + ".coerce"));
|
|
|
- iterator.remove();
|
|
|
- } else if (propName.equals("scaling_factor")) {
|
|
|
- builder.scalingFactor(ScaledFloatFieldMapper.parse(propNode));
|
|
|
- iterator.remove();
|
|
|
- }
|
|
|
- }
|
|
|
- return builder;
|
|
|
- }
|
|
|
- }
|
|
|
+ public static final TypeParser PARSER = new TypeParser((n, c) -> new Builder(n, c.getSettings()));
|
|
|
|
|
|
public static final class ScaledFloatFieldType extends SimpleMappedFieldType {
|
|
|
|
|
@@ -264,19 +217,12 @@ public class ScaledFloatFieldMapper extends FieldMapper {
|
|
|
@Override
|
|
|
public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) {
|
|
|
failIfNoDocValues();
|
|
|
- return new IndexFieldData.Builder() {
|
|
|
- @Override
|
|
|
- public IndexFieldData<?> build(
|
|
|
- IndexFieldDataCache cache,
|
|
|
- CircuitBreakerService breakerService,
|
|
|
- MapperService mapperService
|
|
|
- ) {
|
|
|
- final IndexNumericFieldData scaledValues = new SortedNumericIndexFieldData.Builder(
|
|
|
- name(),
|
|
|
- IndexNumericFieldData.NumericType.LONG
|
|
|
- ).build(cache, breakerService, mapperService);
|
|
|
- return new ScaledFloatIndexFieldData(scaledValues, scalingFactor);
|
|
|
- }
|
|
|
+ return (cache, breakerService, mapperService) -> {
|
|
|
+ final IndexNumericFieldData scaledValues = new SortedNumericIndexFieldData.Builder(
|
|
|
+ name(),
|
|
|
+ IndexNumericFieldData.NumericType.LONG
|
|
|
+ ).build(cache, breakerService, mapperService);
|
|
|
+ return new ScaledFloatIndexFieldData(scaledValues, scalingFactor);
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -315,30 +261,33 @@ public class ScaledFloatFieldMapper extends FieldMapper {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private Explicit<Boolean> ignoreMalformed;
|
|
|
-
|
|
|
- private Explicit<Boolean> coerce;
|
|
|
-
|
|
|
+ private final Explicit<Boolean> ignoreMalformed;
|
|
|
+ private final Explicit<Boolean> coerce;
|
|
|
+ private final boolean indexed;
|
|
|
+ private final boolean hasDocValues;
|
|
|
+ private final boolean stored;
|
|
|
private final Double nullValue;
|
|
|
private final double scalingFactor;
|
|
|
|
|
|
+ private final boolean ignoreMalformedByDefault;
|
|
|
+ private final boolean coerceByDefault;
|
|
|
+
|
|
|
private ScaledFloatFieldMapper(
|
|
|
String simpleName,
|
|
|
- FieldType fieldType,
|
|
|
ScaledFloatFieldType mappedFieldType,
|
|
|
- Explicit<Boolean> ignoreMalformed,
|
|
|
- Explicit<Boolean> coerce,
|
|
|
MultiFields multiFields,
|
|
|
CopyTo copyTo,
|
|
|
- Double nullValue) {
|
|
|
- super(simpleName, fieldType, mappedFieldType, multiFields, copyTo);
|
|
|
- this.scalingFactor = mappedFieldType.scalingFactor;
|
|
|
- this.nullValue = nullValue;
|
|
|
- if (Double.isFinite(scalingFactor) == false || scalingFactor <= 0) {
|
|
|
- throw new IllegalArgumentException("[scaling_factor] must be a positive number, got [" + scalingFactor + "]");
|
|
|
- }
|
|
|
- this.ignoreMalformed = ignoreMalformed;
|
|
|
- this.coerce = coerce;
|
|
|
+ Builder builder) {
|
|
|
+ super(simpleName, mappedFieldType, multiFields, copyTo);
|
|
|
+ this.indexed = builder.indexed.getValue();
|
|
|
+ this.hasDocValues = builder.hasDocValues.getValue();
|
|
|
+ this.stored = builder.stored.getValue();
|
|
|
+ this.scalingFactor = builder.scalingFactor.getValue();
|
|
|
+ this.nullValue = builder.nullValue.getValue();
|
|
|
+ this.ignoreMalformed = builder.ignoreMalformed.getValue();
|
|
|
+ this.coerce = builder.coerce.getValue();
|
|
|
+ this.ignoreMalformedByDefault = builder.ignoreMalformed.getDefaultValue().value();
|
|
|
+ this.coerceByDefault = builder.coerce.getDefaultValue().value();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -351,6 +300,11 @@ public class ScaledFloatFieldMapper extends FieldMapper {
|
|
|
return CONTENT_TYPE;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public ParametrizedFieldMapper.Builder getMergeBuilder() {
|
|
|
+ return new Builder(simpleName(), ignoreMalformedByDefault, coerceByDefault).init(this);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
protected ScaledFloatFieldMapper clone() {
|
|
|
return (ScaledFloatFieldMapper) super.clone();
|
|
@@ -406,49 +360,15 @@ public class ScaledFloatFieldMapper extends FieldMapper {
|
|
|
}
|
|
|
long scaledValue = Math.round(doubleValue * scalingFactor);
|
|
|
|
|
|
- boolean indexed = fieldType().isSearchable();
|
|
|
- boolean docValued = fieldType().hasDocValues();
|
|
|
- boolean stored = fieldType.stored();
|
|
|
- List<Field> fields = NumberFieldMapper.NumberType.LONG.createFields(fieldType().name(), scaledValue, indexed, docValued, stored);
|
|
|
+ List<Field> fields
|
|
|
+ = NumberFieldMapper.NumberType.LONG.createFields(fieldType().name(), scaledValue, indexed, hasDocValues, stored);
|
|
|
context.doc().addAll(fields);
|
|
|
|
|
|
- if (docValued == false && (indexed || stored)) {
|
|
|
+ if (hasDocValues == false && (indexed || stored)) {
|
|
|
createFieldNamesField(context);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- protected void mergeOptions(FieldMapper other, List<String> conflicts) {
|
|
|
- ScaledFloatFieldMapper mergeWith = (ScaledFloatFieldMapper) other;
|
|
|
- ScaledFloatFieldType ft = (ScaledFloatFieldType) other.fieldType();
|
|
|
- if (fieldType().scalingFactor != ft.scalingFactor) {
|
|
|
- conflicts.add("mapper [" + name() + "] has different [scaling_factor] values");
|
|
|
- }
|
|
|
- if (mergeWith.ignoreMalformed.explicit()) {
|
|
|
- this.ignoreMalformed = mergeWith.ignoreMalformed;
|
|
|
- }
|
|
|
- if (mergeWith.coerce.explicit()) {
|
|
|
- this.coerce = mergeWith.coerce;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
|
|
- super.doXContentBody(builder, includeDefaults, params);
|
|
|
-
|
|
|
- builder.field("scaling_factor", scalingFactor);
|
|
|
-
|
|
|
- if (includeDefaults || ignoreMalformed.explicit()) {
|
|
|
- builder.field("ignore_malformed", ignoreMalformed.value());
|
|
|
- }
|
|
|
- if (includeDefaults || coerce.explicit()) {
|
|
|
- builder.field("coerce", coerce.value());
|
|
|
- }
|
|
|
- if (nullValue != null) {
|
|
|
- builder.field("null_value", nullValue);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
static Double parse(Object value) {
|
|
|
return objectToDouble(value);
|
|
|
}
|