This commit makes DateFieldMapper extend ParametrizedFieldMapper, declaring its parameters explicitly. As well as changes to DateFieldMapper itself, there are some changes to dynamic mapping code to ensure that dynamically detected date formats are passed through to new date mapper builders.
This commit is contained in:
parent
301d61a98e
commit
b29d368b52
|
@ -57,8 +57,8 @@ public class BinaryFieldMapper extends ParametrizedFieldMapper {
|
|||
|
||||
public static class Builder extends ParametrizedFieldMapper.Builder {
|
||||
|
||||
private final Parameter<Boolean> stored = Parameter.boolParam("store", false, m -> toType(m).stored, false);
|
||||
private final Parameter<Boolean> hasDocValues = Parameter.boolParam("doc_values", false, m -> toType(m).hasDocValues, false);
|
||||
private final Parameter<Boolean> stored = Parameter.storeParam(m -> toType(m).stored, false);
|
||||
private final Parameter<Boolean> hasDocValues = Parameter.docValuesParam(m -> toType(m).hasDocValues, false);
|
||||
private final Parameter<Map<String, String>> meta = Parameter.metaParam();
|
||||
|
||||
public Builder(String name) {
|
||||
|
|
|
@ -75,14 +75,14 @@ public class BooleanFieldMapper extends ParametrizedFieldMapper {
|
|||
|
||||
public static class Builder extends ParametrizedFieldMapper.Builder {
|
||||
|
||||
private final Parameter<Boolean> docValues = Parameter.boolParam("doc_values", false, m -> toType(m).hasDocValues, true);
|
||||
private final Parameter<Boolean> indexed = Parameter.boolParam("index", false, m -> toType(m).indexed, true);
|
||||
private final Parameter<Boolean> stored = Parameter.boolParam("store", false, m -> toType(m).stored, false);
|
||||
private final Parameter<Boolean> docValues = Parameter.docValuesParam(m -> toType(m).hasDocValues, true);
|
||||
private final Parameter<Boolean> indexed = Parameter.indexParam(m -> toType(m).indexed, true);
|
||||
private final Parameter<Boolean> stored = Parameter.storeParam(m -> toType(m).stored, false);
|
||||
|
||||
private final Parameter<Boolean> nullValue = new Parameter<>("null_value", false, () -> null,
|
||||
(n, c, o) -> XContentMapValues.nodeBooleanValue(o), m -> toType(m).nullValue);
|
||||
|
||||
private final Parameter<Float> boost = Parameter.floatParam("boost", true, m -> m.fieldType().boost(), 1.0f);
|
||||
private final Parameter<Float> boost = Parameter.boostParam();
|
||||
private final Parameter<Map<String, String>> meta = Parameter.metaParam();
|
||||
|
||||
public Builder(String name) {
|
||||
|
|
|
@ -19,11 +19,9 @@
|
|||
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.document.SortedNumericDocValuesField;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.PointValues;
|
||||
import org.apache.lucene.index.Term;
|
||||
|
@ -35,7 +33,7 @@ import org.apache.lucene.search.Query;
|
|||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.Explicit;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.geo.ShapeRelation;
|
||||
import org.elasticsearch.common.joda.Joda;
|
||||
|
@ -44,8 +42,6 @@ import org.elasticsearch.common.time.DateFormatters;
|
|||
import org.elasticsearch.common.time.DateMathParser;
|
||||
import org.elasticsearch.common.time.DateUtils;
|
||||
import org.elasticsearch.common.util.LocaleUtils;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.support.XContentMapValues;
|
||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
|
||||
import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
|
||||
|
@ -59,36 +55,23 @@ import java.time.DateTimeException;
|
|||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
import static org.elasticsearch.common.time.DateUtils.toLong;
|
||||
|
||||
/** A {@link FieldMapper} for dates. */
|
||||
public final class DateFieldMapper extends FieldMapper {
|
||||
public final class DateFieldMapper extends ParametrizedFieldMapper {
|
||||
|
||||
public static final String CONTENT_TYPE = "date";
|
||||
public static final String DATE_NANOS_CONTENT_TYPE = "date_nanos";
|
||||
public static final DateFormatter DEFAULT_DATE_TIME_FORMATTER = DateFormatter.forPattern("strict_date_optional_time||epoch_millis");
|
||||
|
||||
public static class Defaults {
|
||||
public static final Explicit<Boolean> IGNORE_MALFORMED = new Explicit<>(false, false);
|
||||
public static final FieldType FIELD_TYPE = new FieldType();
|
||||
static {
|
||||
FIELD_TYPE.setTokenized(true);
|
||||
FIELD_TYPE.setStored(false);
|
||||
FIELD_TYPE.setStoreTermVectors(false);
|
||||
FIELD_TYPE.setOmitNorms(false);
|
||||
FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
|
||||
}
|
||||
}
|
||||
|
||||
public enum Resolution {
|
||||
MILLISECONDS(CONTENT_TYPE, NumericType.DATE) {
|
||||
@Override
|
||||
|
@ -180,84 +163,67 @@ public final class DateFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
public static class Builder extends FieldMapper.Builder<Builder> {
|
||||
private static DateFieldMapper toType(FieldMapper in) {
|
||||
return (DateFieldMapper) in;
|
||||
}
|
||||
|
||||
private Boolean ignoreMalformed;
|
||||
private Explicit<String> format = new Explicit<>(DEFAULT_DATE_TIME_FORMATTER.pattern(), false);
|
||||
private Locale locale;
|
||||
private Resolution resolution = Resolution.MILLISECONDS;
|
||||
String nullValue;
|
||||
public static class Builder extends ParametrizedFieldMapper.Builder {
|
||||
|
||||
public Builder(String name) {
|
||||
super(name, Defaults.FIELD_TYPE);
|
||||
builder = this;
|
||||
locale = Locale.ROOT;
|
||||
}
|
||||
private final Parameter<Boolean> index = Parameter.indexParam(m -> toType(m).indexed, true);
|
||||
private final Parameter<Boolean> docValues = Parameter.docValuesParam(m -> toType(m).hasDocValues, true);
|
||||
private final Parameter<Boolean> store = Parameter.storeParam(m -> toType(m).store, false);
|
||||
|
||||
public Builder ignoreMalformed(boolean ignoreMalformed) {
|
||||
this.ignoreMalformed = ignoreMalformed;
|
||||
return builder;
|
||||
}
|
||||
private final Parameter<Float> boost = Parameter.boostParam();
|
||||
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;
|
||||
}
|
||||
private final Parameter<String> format
|
||||
= Parameter.stringParam("format", false, m -> toType(m).format, DEFAULT_DATE_TIME_FORMATTER.pattern());
|
||||
private final Parameter<Locale> locale = new Parameter<>("locale", false, () -> Locale.ROOT,
|
||||
(n, c, o) -> LocaleUtils.parse(o.toString()), m -> toType(m).locale);
|
||||
|
||||
public Builder locale(Locale locale) {
|
||||
this.locale = locale;
|
||||
return this;
|
||||
}
|
||||
private final Parameter<String> nullValue
|
||||
= Parameter.stringParam("null_value", false, m -> toType(m).nullValueAsString, null);
|
||||
private final Parameter<Boolean> ignoreMalformed;
|
||||
|
||||
public Builder nullValue(String nullValue) {
|
||||
this.nullValue = nullValue;
|
||||
return this;
|
||||
}
|
||||
private final Resolution resolution;
|
||||
private final Version indexCreatedVersion;
|
||||
|
||||
public Locale locale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public String format() {
|
||||
return format.value();
|
||||
}
|
||||
|
||||
public Builder format(String format) {
|
||||
this.format = new Explicit<>(format, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withResolution(Resolution resolution) {
|
||||
public Builder(String name, Version indexCreatedVersion, Resolution resolution,
|
||||
DateFormatter dateFormatter, boolean ignoreMalformedByDefault) {
|
||||
super(name);
|
||||
this.resolution = resolution;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isFormatterSet() {
|
||||
return format.explicit();
|
||||
this.indexCreatedVersion = indexCreatedVersion;
|
||||
this.ignoreMalformed
|
||||
= Parameter.boolParam("ignore_malformed", true, m -> toType(m).ignoreMalformed, ignoreMalformedByDefault);
|
||||
if (dateFormatter != null) {
|
||||
this.format.setValue(dateFormatter.pattern());
|
||||
this.locale.setValue(dateFormatter.locale());
|
||||
}
|
||||
}
|
||||
|
||||
protected DateFieldType setupFieldType(BuilderContext context) {
|
||||
String pattern = this.format.value();
|
||||
DateFormatter formatter;
|
||||
if (Joda.isJodaPattern(context.indexCreatedVersion(), pattern)) {
|
||||
formatter = Joda.forPattern(pattern).withLocale(locale);
|
||||
if (Joda.isJodaPattern(indexCreatedVersion, format.getValue())) {
|
||||
formatter = Joda.forPattern(format.getValue()).withLocale(locale.getValue());
|
||||
} else {
|
||||
formatter = DateFormatter.forPattern(pattern).withLocale(locale);
|
||||
formatter = DateFormatter.forPattern(format.getValue()).withLocale(locale.getValue());
|
||||
}
|
||||
return new DateFieldType(buildFullName(context), indexed, hasDocValues, formatter, resolution, meta);
|
||||
return new DateFieldType(buildFullName(context), index.getValue(), docValues.getValue(),
|
||||
formatter, resolution, meta.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Parameter<?>> getParameters() {
|
||||
return Arrays.asList(index, docValues, store, format, locale, nullValue, ignoreMalformed, boost, meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateFieldMapper build(BuilderContext context) {
|
||||
DateFieldType ft = setupFieldType(context);
|
||||
Long nullTimestamp = nullValue == null ? null : ft.dateTimeFormatter.parseMillis(nullValue);
|
||||
return new DateFieldMapper(name, fieldType, ft, ignoreMalformed(context), nullTimestamp, nullValue,
|
||||
multiFieldsBuilder.build(this, context), copyTo);
|
||||
ft.setBoost(boost.getValue());
|
||||
Long nullTimestamp = nullValue.getValue() == null ? null : ft.dateTimeFormatter.parseMillis(nullValue.getValue());
|
||||
return new DateFieldMapper(name, ft, multiFieldsBuilder.build(this, context),
|
||||
copyTo.build(), nullTimestamp, resolution, indexCreatedVersion, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,32 +237,10 @@ public final class DateFieldMapper extends FieldMapper {
|
|||
|
||||
@Override
|
||||
public Mapper.Builder<?> parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
|
||||
Builder builder = new Builder(name);
|
||||
builder.withResolution(resolution);
|
||||
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(propNode.toString());
|
||||
iterator.remove();
|
||||
} else if (propName.equals("ignore_malformed")) {
|
||||
builder.ignoreMalformed(XContentMapValues.nodeBooleanValue(propNode, name + ".ignore_malformed"));
|
||||
iterator.remove();
|
||||
} else if (propName.equals("locale")) {
|
||||
builder.locale(LocaleUtils.parse(propNode.toString()));
|
||||
iterator.remove();
|
||||
} else if (propName.equals("format")) {
|
||||
builder.format(propNode.toString());
|
||||
iterator.remove();
|
||||
} else if (TypeParsers.parseMultiField(builder::addMultiField, name, parserContext, propName, propNode)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
boolean ignoreMalformedByDefault = IGNORE_MALFORMED_SETTING.get(parserContext.getSettings());
|
||||
Builder builder = new Builder(name, parserContext.indexVersionCreated(), resolution,
|
||||
parserContext.getDateFormatter(), ignoreMalformedByDefault);
|
||||
builder.parse(name, parserContext, node);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
@ -318,6 +262,14 @@ public final class DateFieldMapper extends FieldMapper {
|
|||
this(name, true, true, DEFAULT_DATE_TIME_FORMATTER, Resolution.MILLISECONDS, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public DateFieldType(String name, DateFormatter dateFormatter) {
|
||||
this(name, true, true, dateFormatter, Resolution.MILLISECONDS, Collections.emptyMap());
|
||||
}
|
||||
|
||||
public DateFieldType(String name, Resolution resolution) {
|
||||
this(name, true, true, DEFAULT_DATE_TIME_FORMATTER, resolution, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String typeName() {
|
||||
return resolution.type();
|
||||
|
@ -509,22 +461,45 @@ public final class DateFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
private Explicit<Boolean> ignoreMalformed;
|
||||
private final boolean store;
|
||||
private final boolean indexed;
|
||||
private final boolean hasDocValues;
|
||||
private final Locale locale;
|
||||
private final String format;
|
||||
private final boolean ignoreMalformed;
|
||||
private final Long nullValue;
|
||||
private final String nullValueAsString;
|
||||
private final Resolution resolution;
|
||||
private final Version indexCreatedVersion;
|
||||
|
||||
private final boolean ignoreMalformedByDefault;
|
||||
|
||||
private DateFieldMapper(
|
||||
String simpleName,
|
||||
FieldType fieldType,
|
||||
MappedFieldType mappedFieldType,
|
||||
Explicit<Boolean> ignoreMalformed,
|
||||
Long nullValue, String nullValueAsString,
|
||||
MultiFields multiFields,
|
||||
CopyTo copyTo) {
|
||||
super(simpleName, fieldType, mappedFieldType, multiFields, copyTo);
|
||||
this.ignoreMalformed = ignoreMalformed;
|
||||
CopyTo copyTo,
|
||||
Long nullValue,
|
||||
Resolution resolution,
|
||||
Version indexCreatedVersion,
|
||||
Builder builder) {
|
||||
super(simpleName, mappedFieldType, multiFields, copyTo);
|
||||
this.store = builder.store.getValue();
|
||||
this.indexed = builder.index.getValue();
|
||||
this.hasDocValues = builder.docValues.getValue();
|
||||
this.locale = builder.locale.getValue();
|
||||
this.format = builder.format.getValue();
|
||||
this.ignoreMalformed = builder.ignoreMalformed.getValue();
|
||||
this.nullValueAsString = builder.nullValue.getValue();
|
||||
this.nullValue = nullValue;
|
||||
this.nullValueAsString = nullValueAsString;
|
||||
this.resolution = resolution;
|
||||
this.indexCreatedVersion = indexCreatedVersion;
|
||||
this.ignoreMalformedByDefault = builder.ignoreMalformed.getDefaultValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParametrizedFieldMapper.Builder getMergeBuilder() {
|
||||
return new Builder(simpleName(), indexCreatedVersion, resolution, null, ignoreMalformedByDefault).init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -566,7 +541,7 @@ public final class DateFieldMapper extends FieldMapper {
|
|||
try {
|
||||
timestamp = fieldType().parse(dateAsString);
|
||||
} catch (IllegalArgumentException | ElasticsearchParseException | DateTimeException e) {
|
||||
if (ignoreMalformed.value()) {
|
||||
if (ignoreMalformed) {
|
||||
context.addIgnoredField(mappedFieldType.name());
|
||||
return;
|
||||
} else {
|
||||
|
@ -575,60 +550,21 @@ public final class DateFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
if (mappedFieldType.isSearchable()) {
|
||||
if (indexed) {
|
||||
context.doc().add(new LongPoint(fieldType().name(), timestamp));
|
||||
}
|
||||
if (fieldType().hasDocValues()) {
|
||||
if (hasDocValues) {
|
||||
context.doc().add(new SortedNumericDocValuesField(fieldType().name(), timestamp));
|
||||
} else if (fieldType.stored() || mappedFieldType.isSearchable()) {
|
||||
} else if (store || indexed) {
|
||||
createFieldNamesField(context);
|
||||
}
|
||||
if (fieldType.stored()) {
|
||||
if (store) {
|
||||
context.doc().add(new StoredField(fieldType().name(), timestamp));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mergeOptions(FieldMapper other, List<String> conflicts) {
|
||||
final DateFieldMapper d = (DateFieldMapper) other;
|
||||
if (Objects.equals(fieldType().dateTimeFormatter.pattern(), d.fieldType().dateTimeFormatter.pattern()) == false) {
|
||||
conflicts.add("mapper [" + name() + "] has different [format] values");
|
||||
}
|
||||
if (Objects.equals(fieldType().dateTimeFormatter.locale(), d.fieldType().dateTimeFormatter.locale()) == false) {
|
||||
conflicts.add("mapper [" + name() + "] has different [locale] values");
|
||||
}
|
||||
if (Objects.equals(fieldType().resolution.type(), d.fieldType().resolution.type()) == false) {
|
||||
conflicts.add("mapper [" + name() + "] cannot change between milliseconds and nanoseconds");
|
||||
}
|
||||
if (d.ignoreMalformed.explicit()) {
|
||||
this.ignoreMalformed = d.ignoreMalformed;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException {
|
||||
super.doXContentBody(builder, includeDefaults, params);
|
||||
|
||||
if (includeDefaults || ignoreMalformed.explicit()) {
|
||||
builder.field("ignore_malformed", ignoreMalformed.value());
|
||||
}
|
||||
|
||||
if (nullValue != null) {
|
||||
builder.field("null_value", nullValueAsString);
|
||||
}
|
||||
|
||||
if (includeDefaults
|
||||
|| fieldType().dateTimeFormatter().pattern().equals(DEFAULT_DATE_TIME_FORMATTER.pattern()) == false) {
|
||||
builder.field("format", fieldType().dateTimeFormatter().pattern());
|
||||
}
|
||||
|
||||
if (includeDefaults
|
||||
|| fieldType().dateTimeFormatter().locale().equals(DEFAULT_DATE_TIME_FORMATTER.locale()) == false) {
|
||||
builder.field("locale", fieldType().dateTimeFormatter().locale());
|
||||
}
|
||||
}
|
||||
|
||||
public Explicit<Boolean> getIgnoreMalformed() {
|
||||
public boolean getIgnoreMalformed() {
|
||||
return ignoreMalformed;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.Version;
|
|||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.compress.CompressedXContent;
|
||||
import org.elasticsearch.common.time.DateFormatter;
|
||||
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
|
@ -67,7 +68,12 @@ public class DocumentMapperParser {
|
|||
|
||||
public Mapper.TypeParser.ParserContext parserContext() {
|
||||
return new Mapper.TypeParser.ParserContext(similarityService::getSimilarity, mapperService,
|
||||
typeParsers::get, indexVersionCreated, queryShardContextSupplier);
|
||||
typeParsers::get, indexVersionCreated, queryShardContextSupplier, null);
|
||||
}
|
||||
|
||||
public Mapper.TypeParser.ParserContext parserContext(DateFormatter dateFormatter) {
|
||||
return new Mapper.TypeParser.ParserContext(similarityService::getSimilarity, mapperService,
|
||||
typeParsers::get, indexVersionCreated, queryShardContextSupplier, dateFormatter);
|
||||
}
|
||||
|
||||
public DocumentMapper parse(@Nullable String type, CompressedXContent source) throws MapperParsingException {
|
||||
|
|
|
@ -41,6 +41,8 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.index.mapper.FieldMapper.IGNORE_MALFORMED_SETTING;
|
||||
|
||||
/** A parser for documents, given mappings from a DocumentMapper */
|
||||
final class DocumentParser {
|
||||
|
||||
|
@ -644,14 +646,6 @@ final class DocumentParser {
|
|||
return new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.FLOAT);
|
||||
}
|
||||
|
||||
private static Mapper.Builder<?> newDateBuilder(String name, DateFormatter dateTimeFormatter, Version indexCreated) {
|
||||
DateFieldMapper.Builder builder = new DateFieldMapper.Builder(name);
|
||||
if (dateTimeFormatter != null) {
|
||||
builder.format(dateTimeFormatter.pattern()).locale(dateTimeFormatter.locale());
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static Mapper.Builder<?> createBuilderFromDynamicValue(final ParseContext context,
|
||||
XContentParser.Token token,
|
||||
String currentFieldName) throws IOException {
|
||||
|
@ -697,17 +691,16 @@ final class DocumentParser {
|
|||
// failure to parse this, continue
|
||||
continue;
|
||||
}
|
||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, XContentFieldType.DATE);
|
||||
Mapper.Builder builder
|
||||
= context.root().findTemplateBuilder(context, currentFieldName, dateTimeFormatter);
|
||||
if (builder == null) {
|
||||
builder = newDateBuilder(currentFieldName, dateTimeFormatter, context.indexSettings().getIndexVersionCreated());
|
||||
}
|
||||
if (builder instanceof DateFieldMapper.Builder) {
|
||||
DateFieldMapper.Builder dateBuilder = (DateFieldMapper.Builder) builder;
|
||||
if (dateBuilder.isFormatterSet() == false) {
|
||||
dateBuilder.format(dateTimeFormatter.pattern()).locale(dateTimeFormatter.locale());
|
||||
}
|
||||
boolean ignoreMalformed = IGNORE_MALFORMED_SETTING.get(context.indexSettings().getSettings());
|
||||
Version indexCreatedVersion = context.indexSettings().getIndexVersionCreated();
|
||||
builder = new DateFieldMapper.Builder(currentFieldName, indexCreatedVersion,
|
||||
DateFieldMapper.Resolution.MILLISECONDS, dateTimeFormatter, ignoreMalformed);
|
||||
}
|
||||
return builder;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.index.mapper;
|
|||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.time.DateFormatter;
|
||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||
import org.elasticsearch.index.analysis.IndexAnalyzers;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
|
@ -88,20 +89,28 @@ public abstract class Mapper implements ToXContentFragment, Iterable<Mapper> {
|
|||
|
||||
private final Supplier<QueryShardContext> queryShardContextSupplier;
|
||||
|
||||
private final DateFormatter dateFormatter;
|
||||
|
||||
public ParserContext(Function<String, SimilarityProvider> similarityLookupService,
|
||||
MapperService mapperService, Function<String, TypeParser> typeParsers,
|
||||
Version indexVersionCreated, Supplier<QueryShardContext> queryShardContextSupplier) {
|
||||
Version indexVersionCreated, Supplier<QueryShardContext> queryShardContextSupplier,
|
||||
DateFormatter dateFormatter) {
|
||||
this.similarityLookupService = similarityLookupService;
|
||||
this.mapperService = mapperService;
|
||||
this.typeParsers = typeParsers;
|
||||
this.indexVersionCreated = indexVersionCreated;
|
||||
this.queryShardContextSupplier = queryShardContextSupplier;
|
||||
this.dateFormatter = dateFormatter;
|
||||
}
|
||||
|
||||
public IndexAnalyzers getIndexAnalyzers() {
|
||||
return mapperService.getIndexAnalyzers();
|
||||
}
|
||||
|
||||
public Settings getSettings() {
|
||||
return mapperService.getIndexSettings().getSettings();
|
||||
}
|
||||
|
||||
public SimilarityProvider getSimilarity(String name) {
|
||||
return similarityLookupService.apply(name);
|
||||
}
|
||||
|
@ -122,6 +131,15 @@ public abstract class Mapper implements ToXContentFragment, Iterable<Mapper> {
|
|||
return queryShardContextSupplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an optional default date format for date fields that do not have an explicit format set
|
||||
*
|
||||
* If {@code null}, then date fields will default to {@link DateFieldMapper#DEFAULT_DATE_TIME_FORMATTER}.
|
||||
*/
|
||||
public DateFormatter getDateFormatter() {
|
||||
return dateFormatter;
|
||||
}
|
||||
|
||||
public boolean isWithinMultiField() { return false; }
|
||||
|
||||
protected Function<String, TypeParser> typeParsers() { return typeParsers; }
|
||||
|
@ -135,7 +153,7 @@ public abstract class Mapper implements ToXContentFragment, Iterable<Mapper> {
|
|||
static class MultiFieldParserContext extends ParserContext {
|
||||
MultiFieldParserContext(ParserContext in) {
|
||||
super(in.similarityLookupService(), in.mapperService(), in.typeParsers(),
|
||||
in.indexVersionCreated(), in.queryShardContextSupplier());
|
||||
in.indexVersionCreated(), in.queryShardContextSupplier(), in.getDateFormatter());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -168,6 +168,13 @@ public abstract class ParametrizedFieldMapper extends FieldMapper {
|
|||
return isSet ? value : defaultValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value of the parameter
|
||||
*/
|
||||
public T getDefaultValue() {
|
||||
return defaultValue.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current value of the parameter
|
||||
*/
|
||||
|
@ -317,6 +324,22 @@ public abstract class ParametrizedFieldMapper extends FieldMapper {
|
|||
(n, c, o) -> TypeParsers.parseMeta(n, o), m -> m.fieldType().meta());
|
||||
}
|
||||
|
||||
public static Parameter<Boolean> indexParam(Function<FieldMapper, Boolean> initializer, boolean defaultValue) {
|
||||
return Parameter.boolParam("index", false, initializer, defaultValue);
|
||||
}
|
||||
|
||||
public static Parameter<Boolean> storeParam(Function<FieldMapper, Boolean> initializer, boolean defaultValue) {
|
||||
return Parameter.boolParam("store", false, initializer, defaultValue);
|
||||
}
|
||||
|
||||
public static Parameter<Boolean> docValuesParam(Function<FieldMapper, Boolean> initializer, boolean defaultValue) {
|
||||
return Parameter.boolParam("doc_values", false, initializer, defaultValue);
|
||||
}
|
||||
|
||||
public static Parameter<Float> boostParam() {
|
||||
return Parameter.floatParam("boost", true, m -> m.fieldType().boost(), 1.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Conflicts {
|
||||
|
|
|
@ -252,22 +252,28 @@ public class RootObjectMapper extends ObjectMapper {
|
|||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Mapper.Builder findTemplateBuilder(ParseContext context, String name, XContentFieldType matchType) {
|
||||
return findTemplateBuilder(context, name, matchType.defaultMappingType(), matchType);
|
||||
return findTemplateBuilder(context, name, matchType, null);
|
||||
}
|
||||
|
||||
public Mapper.Builder findTemplateBuilder(ParseContext context, String name, DateFormatter dateFormatter) {
|
||||
return findTemplateBuilder(context, name, XContentFieldType.DATE, dateFormatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a template. Returns {@code null} if no template could be found.
|
||||
* @param name the field name
|
||||
* @param dynamicType the field type to give the field if the template does not define one
|
||||
* @param matchType the type of the field in the json document or null if unknown
|
||||
* @param dateFormat a dateformatter to use if the type is a date, null if not a date or is using the default format
|
||||
* @return a mapper builder, or null if there is no template for such a field
|
||||
*/
|
||||
public Mapper.Builder findTemplateBuilder(ParseContext context, String name, String dynamicType, XContentFieldType matchType) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Mapper.Builder findTemplateBuilder(ParseContext context, String name, XContentFieldType matchType, DateFormatter dateFormat) {
|
||||
DynamicTemplate dynamicTemplate = findTemplate(context.path(), name, matchType);
|
||||
if (dynamicTemplate == null) {
|
||||
return null;
|
||||
}
|
||||
Mapper.TypeParser.ParserContext parserContext = context.docMapperParser().parserContext();
|
||||
String dynamicType = matchType.defaultMappingType();
|
||||
Mapper.TypeParser.ParserContext parserContext = context.docMapperParser().parserContext(dateFormat);
|
||||
String mappingType = dynamicTemplate.mappingType(dynamicType);
|
||||
Mapper.TypeParser typeParser = parserContext.typeParser(mappingType);
|
||||
if (typeParser == null) {
|
||||
|
|
|
@ -549,11 +549,10 @@ public class MetadataRolloverServiceTests extends ESTestCase {
|
|||
|
||||
ThreadPool testThreadPool = new TestThreadPool(getTestName());
|
||||
try {
|
||||
Settings settings = Settings.builder()
|
||||
.put("index.version.created", Version.CURRENT)
|
||||
.build();
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(settings, new ContentPath(0));
|
||||
DateFieldMapper dateFieldMapper = new DateFieldMapper.Builder("@timestamp").build(builderContext);
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(Settings.EMPTY, new ContentPath(0));
|
||||
DateFieldMapper dateFieldMapper
|
||||
= new DateFieldMapper.Builder("@timestamp", Version.CURRENT, DateFieldMapper.Resolution.MILLISECONDS, null, false)
|
||||
.build(builderContext);
|
||||
MetadataFieldMapper mockedTimestampField = mock(MetadataFieldMapper.class);
|
||||
when(mockedTimestampField.name()).thenReturn("_data_stream_timestamp");
|
||||
MappedFieldType mockedTimestampFieldType = mock(MappedFieldType.class);
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.elasticsearch.index.IndexService;
|
|||
import org.elasticsearch.index.mapper.MapperService.MergeReason;
|
||||
import org.elasticsearch.index.termvectors.TermVectorsService;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||
import org.elasticsearch.test.InternalSettingsPlugin;
|
||||
import org.junit.Before;
|
||||
|
||||
|
@ -41,13 +42,11 @@ import java.time.ZoneOffset;
|
|||
import java.time.ZonedDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
public class DateFieldMapperTests extends FieldMapperTestCase<DateFieldMapper.Builder> {
|
||||
public class DateFieldMapperTests extends ESSingleNodeTestCase {
|
||||
|
||||
IndexService indexService;
|
||||
DocumentMapperParser parser;
|
||||
|
@ -56,18 +55,6 @@ public class DateFieldMapperTests extends FieldMapperTestCase<DateFieldMapper.Bu
|
|||
public void setup() {
|
||||
indexService = createIndex("test");
|
||||
parser = indexService.mapperService().documentMapperParser();
|
||||
addModifier("format", false, (a, b) -> {
|
||||
a.format("basic_week_date");
|
||||
});
|
||||
addModifier("locale", false, (a, b) -> {
|
||||
a.locale(Locale.CANADA);
|
||||
b.locale(Locale.JAPAN);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> unsupportedProperties() {
|
||||
return org.elasticsearch.common.collect.Set.of("analyzer", "similarity");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,11 +62,6 @@ public class DateFieldMapperTests extends FieldMapperTestCase<DateFieldMapper.Bu
|
|||
return pluginList(InternalSettingsPlugin.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DateFieldMapper.Builder newBuilder() {
|
||||
return new DateFieldMapper.Builder("date");
|
||||
}
|
||||
|
||||
public void testDefaults() throws Exception {
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.startObject("properties").startObject("field").field("type", "date").endObject().endObject()
|
||||
|
@ -332,7 +314,7 @@ public class DateFieldMapperTests extends FieldMapperTestCase<DateFieldMapper.Bu
|
|||
.endObject().endObject());
|
||||
|
||||
Exception e = expectThrows(MapperParsingException.class, () -> parser.parse("type", new CompressedXContent(mapping)));
|
||||
assertEquals("[format] must not have a [null] value", e.getMessage());
|
||||
assertEquals("[format] on mapper [field] of type [date] must not have a [null] value", e.getMessage());
|
||||
}
|
||||
|
||||
public void testEmptyName() throws IOException {
|
||||
|
@ -401,7 +383,7 @@ public class DateFieldMapperTests extends FieldMapperTestCase<DateFieldMapper.Bu
|
|||
Exception e = expectThrows(IllegalArgumentException.class,
|
||||
() -> indexService.mapperService().merge("movie", new CompressedXContent(updateFormatMapping),
|
||||
MapperService.MergeReason.MAPPING_UPDATE));
|
||||
assertThat(e.getMessage(), containsString("[mapper [release_date] has different [format] values]"));
|
||||
assertThat(e.getMessage(), containsString("parameter [format] from [yyyy/MM/dd] to [epoch_millis]"));
|
||||
}
|
||||
|
||||
public void testMergeText() throws Exception {
|
||||
|
|
|
@ -201,7 +201,7 @@ public class ParametrizedMapperTests extends ESSingleNodeTestCase {
|
|||
return new BinaryFieldMapper.TypeParser();
|
||||
}
|
||||
return null;
|
||||
}, version, () -> null);
|
||||
}, version, () -> null, null);
|
||||
return (TestMapper) new TypeParser()
|
||||
.parse("field", XContentHelper.convertToMap(JsonXContent.jsonXContent, mapping, true), pc)
|
||||
.build(new Mapper.BuilderContext(Settings.EMPTY, new ContentPath(0)));
|
||||
|
|
|
@ -187,7 +187,7 @@ public class TypeParsersTests extends ESTestCase {
|
|||
|
||||
Mapper.TypeParser typeParser = new KeywordFieldMapper.TypeParser();
|
||||
Mapper.TypeParser.ParserContext parserContext = new Mapper.TypeParser.ParserContext(
|
||||
null, null, type -> typeParser, Version.CURRENT, null);
|
||||
null, null, type -> typeParser, Version.CURRENT, null, null);
|
||||
|
||||
TypeParsers.parseField(builder, "some-field", fieldNode, parserContext);
|
||||
assertWarnings("At least one multi-field, [sub-field], was " +
|
||||
|
@ -215,7 +215,7 @@ public class TypeParsersTests extends ESTestCase {
|
|||
|
||||
public void testParseMeta() {
|
||||
FieldMapper.Builder<?> builder = new KeywordFieldMapper.Builder("foo");
|
||||
Mapper.TypeParser.ParserContext parserContext = new Mapper.TypeParser.ParserContext(null, null, null, null, null);
|
||||
Mapper.TypeParser.ParserContext parserContext = new Mapper.TypeParser.ParserContext(null, null, null, null, null, null);
|
||||
|
||||
{
|
||||
Map<String, Object> mapping = new HashMap<>(Collections.singletonMap("meta", 3));
|
||||
|
|
|
@ -25,17 +25,13 @@ import org.apache.lucene.index.Term;
|
|||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
||||
import org.elasticsearch.common.breaker.CircuitBreaker;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.BigArrays;
|
||||
import org.elasticsearch.index.IndexService;
|
||||
import org.elasticsearch.index.engine.Engine;
|
||||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.DateFieldMapper;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
import org.elasticsearch.indices.breaker.CircuitBreakerService;
|
||||
|
@ -112,14 +108,8 @@ public class AggregatorBaseTests extends ESSingleNodeTestCase {
|
|||
boolean indexed,
|
||||
QueryShardContext context
|
||||
) {
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(
|
||||
Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT).build(),
|
||||
new ContentPath()
|
||||
);
|
||||
MappedFieldType ft = new DateFieldMapper.Builder(fieldName).index(indexed)
|
||||
.withResolution(resolution)
|
||||
.build(builderContext)
|
||||
.fieldType();
|
||||
MappedFieldType ft = new DateFieldMapper.DateFieldType(fieldName, indexed, true,
|
||||
DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER, resolution, Collections.emptyMap());
|
||||
return ValuesSourceConfig.resolveFieldOnly(ft, context);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,17 +52,16 @@ import org.elasticsearch.ElasticsearchParseException;
|
|||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.common.time.DateFormatter;
|
||||
import org.elasticsearch.common.time.DateFormatters;
|
||||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.IndexSettings;
|
||||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.DateFieldMapper;
|
||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||
import org.elasticsearch.index.mapper.GeoPointFieldMapper;
|
||||
import org.elasticsearch.index.mapper.IpFieldMapper;
|
||||
import org.elasticsearch.index.mapper.KeywordFieldMapper;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.NumberFieldMapper;
|
||||
import org.elasticsearch.search.aggregations.Aggregator;
|
||||
|
@ -118,14 +117,7 @@ public class CompositeAggregatorTests extends AggregatorTestCase {
|
|||
FIELD_TYPES[0] = new KeywordFieldMapper.KeywordFieldType("keyword");
|
||||
FIELD_TYPES[1] = new NumberFieldMapper.NumberFieldType("long", NumberFieldMapper.NumberType.LONG);
|
||||
FIELD_TYPES[2] = new NumberFieldMapper.NumberFieldType("double", NumberFieldMapper.NumberType.DOUBLE);
|
||||
|
||||
DateFieldMapper.Builder builder = new DateFieldMapper.Builder("date");
|
||||
builder.docValues(true);
|
||||
builder.format("yyyy-MM-dd||epoch_millis");
|
||||
DateFieldMapper fieldMapper =
|
||||
builder.build(new Mapper.BuilderContext(createIndexSettings().getSettings(), new ContentPath(0)));
|
||||
FIELD_TYPES[3] = fieldMapper.fieldType();
|
||||
|
||||
FIELD_TYPES[3] = new DateFieldMapper.DateFieldType("date", DateFormatter.forPattern("yyyy-MM-dd||epoch_millis"));
|
||||
FIELD_TYPES[4] = new NumberFieldMapper.NumberFieldType("price", NumberFieldMapper.NumberType.INTEGER);
|
||||
FIELD_TYPES[5] = new KeywordFieldMapper.KeywordFieldType("terms");
|
||||
FIELD_TYPES[6] = new IpFieldMapper.IpFieldType("ip");
|
||||
|
|
|
@ -872,7 +872,12 @@ public abstract class AggregatorTestCase extends ESTestCase {
|
|||
|
||||
private class MockParserContext extends Mapper.TypeParser.ParserContext {
|
||||
MockParserContext() {
|
||||
super(null, null, null, null, null);
|
||||
super(null, null, null, Version.CURRENT, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getSettings() {
|
||||
return Settings.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -898,10 +903,7 @@ public abstract class AggregatorTestCase extends ESTestCase {
|
|||
* Make a {@linkplain DateFieldMapper.DateFieldType} for a {@code date}.
|
||||
*/
|
||||
protected DateFieldMapper.DateFieldType dateField(String name, DateFieldMapper.Resolution resolution) {
|
||||
DateFieldMapper.Builder builder = new DateFieldMapper.Builder(name);
|
||||
builder.withResolution(resolution);
|
||||
Settings settings = Settings.builder().put("index.version.created", Version.CURRENT.id).build();
|
||||
return builder.build(new BuilderContext(settings, new ContentPath())).fieldType();
|
||||
return new DateFieldMapper.DateFieldType(name, resolution);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -167,7 +167,7 @@ public class DataStreamTimestampFieldMapper extends MetadataFieldMapper {
|
|||
"data stream timestamp field [" + path + "] has disallowed [null_value] attribute specified"
|
||||
);
|
||||
}
|
||||
if (dateFieldMapper.getIgnoreMalformed().explicit()) {
|
||||
if (dateFieldMapper.getIgnoreMalformed()) {
|
||||
throw new IllegalArgumentException(
|
||||
"data stream timestamp field [" + path + "] has disallowed [ignore_malformed] attribute specified"
|
||||
);
|
||||
|
|
|
@ -545,11 +545,9 @@ public class RollupIndexerIndexingTests extends AggregatorTestCase {
|
|||
*/
|
||||
private Map<String, MappedFieldType> createFieldTypes(RollupJobConfig job) {
|
||||
Map<String, MappedFieldType> fieldTypes = new HashMap<>();
|
||||
MappedFieldType fieldType = new DateFieldMapper.Builder(job.getGroupConfig().getDateHistogram().getField())
|
||||
.format(randomFrom("basic_date", "date_optional_time", "epoch_second"))
|
||||
.locale(Locale.ROOT)
|
||||
.build(new Mapper.BuilderContext(settings.getSettings(), new ContentPath(0)))
|
||||
.fieldType();
|
||||
DateFormatter formatter
|
||||
= DateFormatter.forPattern(randomFrom("basic_date", "date_optional_time", "epoch_second")).withLocale(Locale.ROOT);
|
||||
MappedFieldType fieldType = new DateFieldMapper.DateFieldType(job.getGroupConfig().getDateHistogram().getField(), formatter);
|
||||
fieldTypes.put(fieldType.name(), fieldType);
|
||||
|
||||
if (job.getGroupConfig().getHistogram() != null) {
|
||||
|
|
Loading…
Reference in New Issue