-
Notifications
You must be signed in to change notification settings - Fork 25k
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
RuntimeField.Builder should not extend FieldMapper.Builder #73840
Changes from all commits
467c26c
e9e1c85
a0abee4
82f5de8
8deac31
b22257e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,8 +8,13 @@ | |
|
||
package org.elasticsearch.index.mapper; | ||
|
||
import org.elasticsearch.Version; | ||
import org.elasticsearch.common.logging.DeprecationCategory; | ||
import org.elasticsearch.common.logging.DeprecationLogger; | ||
import org.elasticsearch.common.xcontent.ToXContent; | ||
import org.elasticsearch.common.xcontent.ToXContentFragment; | ||
import org.elasticsearch.common.xcontent.XContentBuilder; | ||
import org.elasticsearch.index.mapper.FieldMapper.Parameter; | ||
|
||
import java.io.IOException; | ||
import java.util.Collection; | ||
|
@@ -58,51 +63,69 @@ default XContentBuilder toXContent(XContentBuilder builder, Params params) throw | |
*/ | ||
Collection<MappedFieldType> asMappedFieldTypes(); | ||
|
||
/** | ||
* For runtime fields the {@link RuntimeField.Parser} returns directly the {@link MappedFieldType}. | ||
* Internally we still create a {@link RuntimeField.Builder} so we reuse the {@link FieldMapper.Parameter} infrastructure, | ||
* but {@link RuntimeField.Builder#init(FieldMapper)} and {@link RuntimeField.Builder#build(ContentPath)} are never called as | ||
* {@link RuntimeField.Parser#parse(String, Map, Mapper.TypeParser.ParserContext)} calls | ||
* {@link RuntimeField.Builder#parse(String, Mapper.TypeParser.ParserContext, Map)} and returns the corresponding | ||
* {@link MappedFieldType}. | ||
*/ | ||
abstract class Builder extends FieldMapper.Builder { | ||
final FieldMapper.Parameter<Map<String, String>> meta = FieldMapper.Parameter.metaParam(); | ||
abstract class Builder implements ToXContent { | ||
final String name; | ||
final Parameter<Map<String, String>> meta = Parameter.metaParam(); | ||
|
||
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(RuntimeField.class); | ||
|
||
protected Builder(String name) { | ||
super(name); | ||
this.name = name; | ||
} | ||
|
||
public Map<String, String> meta() { | ||
return meta.getValue(); | ||
} | ||
|
||
@Override | ||
protected List<FieldMapper.Parameter<?>> getParameters() { | ||
protected List<Parameter<?>> getParameters() { | ||
return Collections.singletonList(meta); | ||
} | ||
|
||
@Override | ||
public FieldMapper.Builder init(FieldMapper initializer) { | ||
throw new UnsupportedOperationException(); | ||
} | ||
protected abstract RuntimeField createRuntimeField(Mapper.TypeParser.ParserContext parserContext); | ||
|
||
@Override | ||
public final FieldMapper build(ContentPath context) { | ||
throw new UnsupportedOperationException(); | ||
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
boolean includeDefaults = params.paramAsBoolean("include_defaults", false); | ||
for (Parameter<?> parameter : getParameters()) { | ||
parameter.toXContent(builder, includeDefaults); | ||
} | ||
return builder; | ||
} | ||
|
||
protected abstract RuntimeField createRuntimeField(Mapper.TypeParser.ParserContext parserContext); | ||
|
||
private void validate() { | ||
ContentPath contentPath = parentPath(name()); | ||
FieldMapper.MultiFields multiFields = multiFieldsBuilder.build(this, contentPath); | ||
if (multiFields.iterator().hasNext()) { | ||
throw new IllegalArgumentException("runtime field [" + name + "] does not support [fields]"); | ||
public final void parse(String name, Mapper.TypeParser.ParserContext parserContext, Map<String, Object> fieldNode) { | ||
Map<String, Parameter<?>> paramsMap = new HashMap<>(); | ||
for (Parameter<?> param : getParameters()) { | ||
paramsMap.put(param.name, param); | ||
} | ||
FieldMapper.CopyTo copyTo = this.copyTo.build(); | ||
if (copyTo.copyToFields().isEmpty() == false) { | ||
throw new IllegalArgumentException("runtime field [" + name + "] does not support [copy_to]"); | ||
String type = (String) fieldNode.remove("type"); | ||
for (Iterator<Map.Entry<String, Object>> iterator = fieldNode.entrySet().iterator(); iterator.hasNext();) { | ||
Map.Entry<String, Object> entry = iterator.next(); | ||
final String propName = entry.getKey(); | ||
final Object propNode = entry.getValue(); | ||
Parameter<?> parameter = paramsMap.get(propName); | ||
if (parameter == null) { | ||
if (parserContext.isFromDynamicTemplate() && parserContext.indexVersionCreated().before(Version.V_8_0_0)) { | ||
// The parameter is unknown, but this mapping is from a dynamic template. | ||
// Until 7.x it was possible to use unknown parameters there, so for bwc we need to ignore it | ||
deprecationLogger.deprecate(DeprecationCategory.API, propName, | ||
"Parameter [{}] is used in a dynamic template mapping and has no effect on type [{}]. " | ||
+ "Usage will result in an error in future major versions and should be removed.", | ||
propName, | ||
type | ||
); | ||
iterator.remove(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this deserve a new test that targets runtime fields specifically? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've been writing tests for this and I think I've found a bug with the existing impl - we're supposed to issue a deprecation warning if dynamic templates contain mappings with bad parameters, but we actually throw an error because the template validation doesn't propagate its 'within a dynamic template' context. Will open a separate PR for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sounds good |
||
continue; | ||
} | ||
throw new MapperParsingException( | ||
"unknown parameter [" + propName + "] on runtime field [" + name + "] of type [" + type + "]" | ||
); | ||
} | ||
if (propNode == null && parameter.canAcceptNull() == false) { | ||
throw new MapperParsingException("[" + propName + "] on runtime field [" + name | ||
+ "] of type [" + type + "] must not have a [null] value"); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if it's worth factoring out this code that is common between the two implementations instead of duplicating it, though it would mean that the base class has to allow to be extended to introduce parsing of common fields etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the two methods are sufficiently different that it's not worth trying to share stuff here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are they? I did not realize that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have not deprecated anything on the runtime section so far, but I do wonder: should we handle deprecations like we do for field mappers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this makes sense only if we share the code between the two, supporting parameters deprecation while there isn't one deprecated parameter is overkill There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ++ we can copy the machinery from FieldMapper easily enough if we need to |
||
parameter.parse(name, parserContext, propNode); | ||
iterator.remove(); | ||
} | ||
} | ||
} | ||
|
@@ -123,7 +146,6 @@ RuntimeField parse(String name, Map<String, Object> node, Mapper.TypeParser.Pars | |
|
||
RuntimeField.Builder builder = builderFunction.apply(name); | ||
builder.parse(name, parserContext, node); | ||
builder.validate(); | ||
return builder.createRuntimeField(parserContext); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been wondering why the builder is only responsible to print out its parameters. Could we include RuntimeField#toXContent in this so that we don't need to wrap it? If we do so maybe the toXContent within AbstractScriptFieldType could take a RUntimeField.Builder argument instead of ToXCOntent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's see if we can do this as a follow-up, not important for merging