Skip to content

Commit

Permalink
Extend dynamic templates to make them define runtime fields (#66112)
Browse files Browse the repository at this point in the history
Runtime fields are defined as part of the runtime section in the mappings. Dynamic templates allow to specify mappings for fields that are getting automatically created. With this change, we allow users to create dynamic under the runtime section, and optionally define their mappings.

The following is an example of dynamic template that matches any incoming long field as runtime field, meaning they will all be evaluated at runtime. When a script is not specified, runtime fields are loaded from a field with the same name in _source.

```
{
  "mappings": {
    "dynamic_templates": [
      {
        "long_as_runtime": {
          "match_mapping_type": "long",
          "runtime": {

          }
        }
      }
    ]
  }
}
```

* fix test

* fix indentation

* missing break was causing needless deprecation warnings
  • Loading branch information
javanna authored Dec 10, 2020
1 parent cc7216e commit 3882653
Show file tree
Hide file tree
Showing 10 changed files with 747 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import java.io.IOException;
import java.time.format.DateTimeParseException;
import java.util.Map;

/**
* Encapsulates the logic for dynamically creating fields as part of document parsing.
Expand Down Expand Up @@ -144,7 +145,7 @@ Mapper createDynamicObjectMapper(ParseContext context, String name) {
* Note that objects are always mapped under properties.
*/
Mapper createObjectMapperFromTemplate(ParseContext context, String name) {
Mapper.Builder templateBuilder = findTemplateBuilder(context, name, DynamicTemplate.XContentFieldType.OBJECT, null);
Mapper.Builder templateBuilder = findTemplateBuilderForObject(context, name);
return templateBuilder == null ? null : templateBuilder.build(context.path());
}

Expand Down Expand Up @@ -176,38 +177,70 @@ private static void createDynamicField(ParseContext context,
DynamicTemplate.XContentFieldType matchType,
DateFormatter dateFormatter,
CheckedRunnable<IOException> dynamicFieldStrategy) throws IOException {
Mapper.Builder templateBuilder = findTemplateBuilder(context, name, matchType, dateFormatter);
if (templateBuilder == null) {
if (applyMatchingTemplate(context, name, matchType, dateFormatter) == false) {
dynamicFieldStrategy.run();
} else {
CONCRETE.createDynamicField(templateBuilder, context);
}
}

/**
* Find a template. Returns {@code null} if no template could be found.
* Find and apply a matching dynamic template. Returns {@code true} if a template could be found, {@code false} otherwise.
* @param context the parse context for this document
* @param name the current field name
* @param matchType the type of the field in the json document or null if unknown
* @param dateFormatter a date formatter 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
* @return true if a template was found and applied, false otherwise
*/
private static Mapper.Builder findTemplateBuilder(ParseContext context,
String name,
DynamicTemplate.XContentFieldType matchType,
DateFormatter dateFormatter) {
private static boolean applyMatchingTemplate(ParseContext context,
String name,
DynamicTemplate.XContentFieldType matchType,
DateFormatter dateFormatter) throws IOException {
DynamicTemplate dynamicTemplate = context.root().findTemplate(context.path(), name, matchType);
if (dynamicTemplate == null) {
return false;
}
String dynamicType = dynamicTemplate.isRuntimeMapping() ? matchType.defaultRuntimeMappingType() : matchType.defaultMappingType();

String mappingType = dynamicTemplate.mappingType(dynamicType);
Map<String, Object> mapping = dynamicTemplate.mappingForName(name, dynamicType);
if (dynamicTemplate.isRuntimeMapping()) {
Mapper.TypeParser.ParserContext parserContext = context.parserContext(dateFormatter);
RuntimeFieldType.Parser parser = parserContext.runtimeFieldTypeParser(mappingType);
String fullName = context.path().pathAsText(name);
if (parser == null) {
throw new MapperParsingException("failed to find type parsed [" + mappingType + "] for [" + fullName + "]");
}
RuntimeFieldType runtimeFieldType = parser.parse(fullName, mapping, parserContext);
Runtime.createDynamicField(runtimeFieldType, context);
} else {
Mapper.Builder builder = parseMapping(name, mappingType, mapping, dateFormatter, context);
CONCRETE.createDynamicField(builder, context);
}
return true;
}

private static Mapper.Builder findTemplateBuilderForObject(ParseContext context, String name) {
DynamicTemplate.XContentFieldType matchType = DynamicTemplate.XContentFieldType.OBJECT;
DynamicTemplate dynamicTemplate = context.root().findTemplate(context.path(), name, matchType);
if (dynamicTemplate == null) {
return null;
}
String dynamicType = matchType.defaultMappingType();
Mapper.TypeParser.ParserContext parserContext = context.parserContext(dateFormatter);
String mappingType = dynamicTemplate.mappingType(dynamicType);
Map<String, Object> mapping = dynamicTemplate.mappingForName(name, dynamicType);
return parseMapping(name, mappingType, mapping, null, context);
}

private static Mapper.Builder parseMapping(String name,
String mappingType,
Map<String, Object> mapping,
DateFormatter dateFormatter,
ParseContext context) {
Mapper.TypeParser.ParserContext parserContext = context.parserContext(dateFormatter);
Mapper.TypeParser typeParser = parserContext.typeParser(mappingType);
if (typeParser == null) {
throw new MapperParsingException("failed to find type parsed [" + mappingType + "] for [" + name + "]");
}
return typeParser.parse(name, dynamicTemplate.mappingForName(name, dynamicType), parserContext);
return typeParser.parse(name, mapping, parserContext);
}

/**
Expand Down Expand Up @@ -284,39 +317,43 @@ void newDynamicBinaryField(ParseContext context, String name) throws IOException
* @see Dynamic
*/
private static final class Runtime implements Strategy {
static void createDynamicField(RuntimeFieldType runtimeFieldType, ParseContext context) {
context.addDynamicRuntimeField(runtimeFieldType);
}

@Override
public void newDynamicStringField(ParseContext context, String name) {
String fullName = context.path().pathAsText(name);
RuntimeFieldType runtimeFieldType = context.getDynamicRuntimeFieldsBuilder().newDynamicStringField(fullName);
context.addDynamicRuntimeField(runtimeFieldType);
createDynamicField(runtimeFieldType, context);
}

@Override
public void newDynamicLongField(ParseContext context, String name) {
String fullName = context.path().pathAsText(name);
RuntimeFieldType runtimeFieldType = context.getDynamicRuntimeFieldsBuilder().newDynamicLongField(fullName);
context.addDynamicRuntimeField(runtimeFieldType);
createDynamicField(runtimeFieldType, context);
}

@Override
public void newDynamicDoubleField(ParseContext context, String name) {
String fullName = context.path().pathAsText(name);
RuntimeFieldType runtimeFieldType = context.getDynamicRuntimeFieldsBuilder().newDynamicDoubleField(fullName);
context.addDynamicRuntimeField(runtimeFieldType);
createDynamicField(runtimeFieldType, context);
}

@Override
public void newDynamicBooleanField(ParseContext context, String name) {
String fullName = context.path().pathAsText(name);
RuntimeFieldType runtimeFieldType = context.getDynamicRuntimeFieldsBuilder().newDynamicBooleanField(fullName);
context.addDynamicRuntimeField(runtimeFieldType);
createDynamicField(runtimeFieldType, context);
}

@Override
public void newDynamicDateField(ParseContext context, String name, DateFormatter dateFormatter) {
String fullName = context.path().pathAsText(name);
RuntimeFieldType runtimeFieldType = context.getDynamicRuntimeFieldsBuilder().newDynamicDateField(fullName, dateFormatter);
context.addDynamicRuntimeField(runtimeFieldType);
createDynamicField(runtimeFieldType, context);
}
}
}
Loading

0 comments on commit 3882653

Please sign in to comment.