88
99package org .elasticsearch .index .mapper ;
1010
11+ import org .elasticsearch .Version ;
12+ import org .elasticsearch .common .logging .DeprecationCategory ;
13+ import org .elasticsearch .common .logging .DeprecationLogger ;
14+ import org .elasticsearch .common .xcontent .ToXContent ;
1115import org .elasticsearch .common .xcontent .ToXContentFragment ;
1216import org .elasticsearch .common .xcontent .XContentBuilder ;
17+ import org .elasticsearch .index .mapper .FieldMapper .Parameter ;
1318
1419import java .io .IOException ;
1520import java .util .Collection ;
@@ -58,51 +63,69 @@ default XContentBuilder toXContent(XContentBuilder builder, Params params) throw
5863 */
5964 Collection <MappedFieldType > asMappedFieldTypes ();
6065
61- /**
62- * For runtime fields the {@link RuntimeField.Parser} returns directly the {@link MappedFieldType}.
63- * Internally we still create a {@link RuntimeField.Builder} so we reuse the {@link FieldMapper.Parameter} infrastructure,
64- * but {@link RuntimeField.Builder#init(FieldMapper)} and {@link RuntimeField.Builder#build(ContentPath)} are never called as
65- * {@link RuntimeField.Parser#parse(String, Map, Mapper.TypeParser.ParserContext)} calls
66- * {@link RuntimeField.Builder#parse(String, Mapper.TypeParser.ParserContext, Map)} and returns the corresponding
67- * {@link MappedFieldType}.
68- */
69- abstract class Builder extends FieldMapper .Builder {
70- final FieldMapper .Parameter <Map <String , String >> meta = FieldMapper .Parameter .metaParam ();
66+ abstract class Builder implements ToXContent {
67+ final String name ;
68+ final Parameter <Map <String , String >> meta = Parameter .metaParam ();
69+
70+ private static final DeprecationLogger deprecationLogger = DeprecationLogger .getLogger (RuntimeField .class );
7171
7272 protected Builder (String name ) {
73- super ( name ) ;
73+ this . name = name ;
7474 }
7575
7676 public Map <String , String > meta () {
7777 return meta .getValue ();
7878 }
7979
80- @ Override
81- protected List <FieldMapper .Parameter <?>> getParameters () {
80+ protected List <Parameter <?>> getParameters () {
8281 return Collections .singletonList (meta );
8382 }
8483
85- @ Override
86- public FieldMapper .Builder init (FieldMapper initializer ) {
87- throw new UnsupportedOperationException ();
88- }
84+ protected abstract RuntimeField createRuntimeField (Mapper .TypeParser .ParserContext parserContext );
8985
9086 @ Override
91- public final FieldMapper build (ContentPath context ) {
92- throw new UnsupportedOperationException ();
87+ public final XContentBuilder toXContent (XContentBuilder builder , Params params ) throws IOException {
88+ boolean includeDefaults = params .paramAsBoolean ("include_defaults" , false );
89+ for (Parameter <?> parameter : getParameters ()) {
90+ parameter .toXContent (builder , includeDefaults );
91+ }
92+ return builder ;
9393 }
9494
95- protected abstract RuntimeField createRuntimeField (Mapper .TypeParser .ParserContext parserContext );
96-
97- private void validate () {
98- ContentPath contentPath = parentPath (name ());
99- FieldMapper .MultiFields multiFields = multiFieldsBuilder .build (this , contentPath );
100- if (multiFields .iterator ().hasNext ()) {
101- throw new IllegalArgumentException ("runtime field [" + name + "] does not support [fields]" );
95+ public final void parse (String name , Mapper .TypeParser .ParserContext parserContext , Map <String , Object > fieldNode ) {
96+ Map <String , Parameter <?>> paramsMap = new HashMap <>();
97+ for (Parameter <?> param : getParameters ()) {
98+ paramsMap .put (param .name , param );
10299 }
103- FieldMapper .CopyTo copyTo = this .copyTo .build ();
104- if (copyTo .copyToFields ().isEmpty () == false ) {
105- throw new IllegalArgumentException ("runtime field [" + name + "] does not support [copy_to]" );
100+ String type = (String ) fieldNode .remove ("type" );
101+ for (Iterator <Map .Entry <String , Object >> iterator = fieldNode .entrySet ().iterator (); iterator .hasNext ();) {
102+ Map .Entry <String , Object > entry = iterator .next ();
103+ final String propName = entry .getKey ();
104+ final Object propNode = entry .getValue ();
105+ Parameter <?> parameter = paramsMap .get (propName );
106+ if (parameter == null ) {
107+ if (parserContext .isFromDynamicTemplate () && parserContext .indexVersionCreated ().before (Version .V_8_0_0 )) {
108+ // The parameter is unknown, but this mapping is from a dynamic template.
109+ // Until 7.x it was possible to use unknown parameters there, so for bwc we need to ignore it
110+ deprecationLogger .deprecate (DeprecationCategory .API , propName ,
111+ "Parameter [{}] is used in a dynamic template mapping and has no effect on type [{}]. "
112+ + "Usage will result in an error in future major versions and should be removed." ,
113+ propName ,
114+ type
115+ );
116+ iterator .remove ();
117+ continue ;
118+ }
119+ throw new MapperParsingException (
120+ "unknown parameter [" + propName + "] on runtime field [" + name + "] of type [" + type + "]"
121+ );
122+ }
123+ if (propNode == null && parameter .canAcceptNull () == false ) {
124+ throw new MapperParsingException ("[" + propName + "] on runtime field [" + name
125+ + "] of type [" + type + "] must not have a [null] value" );
126+ }
127+ parameter .parse (name , parserContext , propNode );
128+ iterator .remove ();
106129 }
107130 }
108131 }
@@ -123,7 +146,6 @@ RuntimeField parse(String name, Map<String, Object> node, Mapper.TypeParser.Pars
123146
124147 RuntimeField .Builder builder = builderFunction .apply (name );
125148 builder .parse (name , parserContext , node );
126- builder .validate ();
127149 return builder .createRuntimeField (parserContext );
128150 }
129151 }
0 commit comments