org.elasticsearch.index.mapper.MetadataFieldMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch - Open Source, Distributed, RESTful Search Engine
/*
* 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 org.elasticsearch.common.Explicit;
import org.elasticsearch.common.logging.DeprecationCategory;
import org.elasticsearch.common.util.Maps;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.IndexVersion;
import org.elasticsearch.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
* A mapper for a builtin field containing metadata about a document.
*/
public abstract class MetadataFieldMapper extends FieldMapper {
public interface TypeParser {
MetadataFieldMapper.Builder parse(String name, Map node, MappingParserContext parserContext)
throws MapperParsingException;
/**
* Get the default {@link MetadataFieldMapper} to use, if nothing had to be parsed.
*
* @param parserContext context that may be useful to build the field like analyzers
*/
MetadataFieldMapper getDefault(MappingParserContext parserContext);
}
/**
* Declares an updateable boolean parameter for a metadata field
*
* We need to distinguish between explicit configuration and default value for metadata
* fields, because mapping updates will carry over the previous metadata values if a
* metadata field is not explicitly declared in the update. A standard boolean
* parameter explicitly configured with a default value will not be serialized (as
* we do not serialize default parameters for mapping updates), and as such will be
* ignored by the update merge. Instead, we use an {@link Explicit} object that
* will serialize its value if it has been configured, no matter what the value is.
*/
public static Parameter> updateableBoolParam(
String name,
Function> initializer,
boolean defaultValue
) {
return new Parameter<>(
name,
true,
defaultValue ? () -> Explicit.IMPLICIT_TRUE : () -> Explicit.IMPLICIT_FALSE,
(n, c, o) -> Explicit.explicitBoolean(XContentMapValues.nodeBooleanValue(o)),
initializer,
(b, n, v) -> b.field(n, v.value()),
v -> Boolean.toString(v.value())
);
}
/**
* A type parser for an unconfigurable metadata field.
*/
public static class FixedTypeParser implements TypeParser {
final Function mapperParser;
public FixedTypeParser(Function mapperParser) {
this.mapperParser = mapperParser;
}
@Override
public Builder parse(String name, Map node, MappingParserContext parserContext) throws MapperParsingException {
throw new MapperParsingException(name + " is not configurable");
}
@Override
public MetadataFieldMapper getDefault(MappingParserContext parserContext) {
return mapperParser.apply(parserContext);
}
}
public static class ConfigurableTypeParser implements TypeParser {
final Function defaultMapperParser;
final Function builderFunction;
public ConfigurableTypeParser(
Function defaultMapperParser,
Function builderFunction
) {
this.defaultMapperParser = defaultMapperParser;
this.builderFunction = builderFunction;
}
@Override
public Builder parse(String name, Map node, MappingParserContext parserContext) throws MapperParsingException {
Builder builder = builderFunction.apply(parserContext);
builder.parseMetadataField(name, parserContext, node);
return builder;
}
@Override
public MetadataFieldMapper getDefault(MappingParserContext parserContext) {
return defaultMapperParser.apply(parserContext);
}
}
public abstract static class Builder extends FieldMapper.Builder {
protected Builder(String name) {
super(name);
}
boolean isConfigured() {
for (Parameter> param : getParameters()) {
if (param.isConfigured()) {
return true;
}
}
return false;
}
@Override
public final MetadataFieldMapper build(MapperBuilderContext context) {
return build();
}
private static final Set UNSUPPORTED_PARAMETERS_8_6_0 = Set.of("type", "fields", "copy_to", "boost");
public final void parseMetadataField(String name, MappingParserContext parserContext, Map fieldNode) {
final Parameter>[] params = getParameters();
Map> paramsMap = Maps.newHashMapWithExpectedSize(params.length);
for (Parameter> param : params) {
paramsMap.put(param.name, param);
}
for (Iterator> iterator = fieldNode.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = iterator.next();
final String propName = entry.getKey();
final Object propNode = entry.getValue();
Parameter> parameter = paramsMap.get(propName);
if (parameter == null) {
if (UNSUPPORTED_PARAMETERS_8_6_0.contains(propName)) {
if (parserContext.indexVersionCreated().onOrAfter(IndexVersion.V_8_6_0)) {
// silently ignore type, and a few other parameters: sadly we've been doing this for a long time
deprecationLogger.warn(
DeprecationCategory.API,
propName,
"Parameter [{}] has no effect on metadata field [{}] and will be removed in future",
propName,
name
);
}
iterator.remove();
continue;
}
throw new MapperParsingException("unknown parameter [" + propName + "] on metadata field [" + name + "]");
}
parameter.parse(name, parserContext, propNode);
iterator.remove();
}
validate();
}
public abstract MetadataFieldMapper build();
}
protected MetadataFieldMapper(MappedFieldType mappedFieldType) {
super(mappedFieldType.name(), mappedFieldType, MultiFields.empty(), CopyTo.empty(), false, null);
}
@Override
public FieldMapper.Builder getMergeBuilder() {
return null; // by default, things can't be configured so we have no builder
}
@Override
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
MetadataFieldMapper.Builder mergeBuilder = (MetadataFieldMapper.Builder) getMergeBuilder();
if (mergeBuilder == null || mergeBuilder.isConfigured() == false) {
return builder;
}
builder.startObject(simpleName());
getMergeBuilder().toXContent(builder, params);
return builder.endObject();
}
@Override
protected void parseCreateField(DocumentParserContext context) throws IOException {
throw new DocumentParsingException(
context.parser().getTokenLocation(),
"Field [" + name() + "] is a metadata field and cannot be added inside a document. Use the index API request parameters."
);
}
/**
* Called before {@link FieldMapper#parse(DocumentParserContext)} on the {@link RootObjectMapper}.
*/
public void preParse(DocumentParserContext context) throws IOException {
// do nothing
}
/**
* Called after {@link FieldMapper#parse(DocumentParserContext)} on the {@link RootObjectMapper}.
*/
public void postParse(DocumentParserContext context) throws IOException {
// do nothing
}
@Override
public abstract SourceLoader.SyntheticFieldLoader syntheticFieldLoader();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy