Upgrade dynamic templates that use a dynamic type. #17254
Now that string has been splitted into text and keyword, we use text as a dynamic type when encountering string fields in a json document. However this does not play well with existing templates that look like ``` { "mapping": { "index": "not_analyzed", "type": "{dynamic_type}" }, "match": "*" } ``` Since we want existing templates to keep working as much as possible in 5.0, this commit adds a hack to dynamic templates so that elasticsearch will create a keyword field if the `index` property is set and is either `no` or `not_analyzed`, similarly to what was done in #16991. While this will make upgrades easier, we still need to figure out a way to allow users to create keyword fields when using dynamic types.
This commit is contained in:
parent
e50eeeaffb
commit
252ae5f15a
|
@ -161,7 +161,29 @@ public class DynamicTemplate implements ToXContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String mappingType(String dynamicType) {
|
public String mappingType(String dynamicType) {
|
||||||
return mapping.containsKey("type") ? mapping.get("type").toString().replace("{dynamic_type}", dynamicType).replace("{dynamicType}", dynamicType) : dynamicType;
|
String type;
|
||||||
|
if (mapping.containsKey("type")) {
|
||||||
|
type = mapping.get("type").toString();
|
||||||
|
type = type.replace("{dynamic_type}", dynamicType);
|
||||||
|
type = type.replace("{dynamicType}", dynamicType);
|
||||||
|
} else {
|
||||||
|
type = dynamicType;
|
||||||
|
}
|
||||||
|
if (type.equals(mapping.get("type")) == false // either the type was not set, or we updated it through replacements
|
||||||
|
&& "text".equals(type)) { // and the result is "text"
|
||||||
|
// now that string has been splitted into text and keyword, we use text for
|
||||||
|
// dynamic mappings. However before it used to be possible to index as a keyword
|
||||||
|
// by setting index=not_analyzed, so for now we will use a keyword field rather
|
||||||
|
// than a text field if index=not_analyzed and the field type was not specified
|
||||||
|
// explicitly
|
||||||
|
// TODO: remove this in 6.0
|
||||||
|
// TODO: how to do it in the future?
|
||||||
|
final Object index = mapping.get("index");
|
||||||
|
if ("not_analyzed".equals(index) || "no".equals(index)) {
|
||||||
|
type = "keyword";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean patternMatch(String pattern, String str) {
|
private boolean patternMatch(String pattern, String str) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
import org.apache.lucene.index.IndexOptions;
|
import org.apache.lucene.index.IndexOptions;
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -33,6 +34,8 @@ import org.elasticsearch.index.mapper.DocumentMapper;
|
||||||
import org.elasticsearch.index.mapper.DocumentMapperParser;
|
import org.elasticsearch.index.mapper.DocumentMapperParser;
|
||||||
import org.elasticsearch.index.mapper.FieldMapper;
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
import org.elasticsearch.index.mapper.core.TextFieldMapper.TextFieldType;
|
import org.elasticsearch.index.mapper.core.TextFieldMapper.TextFieldType;
|
||||||
|
import org.elasticsearch.index.mapper.Mapper;
|
||||||
|
import org.elasticsearch.index.mapper.ParsedDocument;
|
||||||
import org.elasticsearch.plugins.Plugin;
|
import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.test.ESSingleNodeTestCase;
|
import org.elasticsearch.test.ESSingleNodeTestCase;
|
||||||
import org.elasticsearch.test.InternalSettingsPlugin;
|
import org.elasticsearch.test.InternalSettingsPlugin;
|
||||||
|
@ -239,4 +242,96 @@ public class StringMappingUpgradeTests extends ESSingleNodeTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testUpgradeTemplateWithDynamicType() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startArray("dynamic_templates")
|
||||||
|
.startObject()
|
||||||
|
.startObject("my_template")
|
||||||
|
.field("match_mapping_type", "string")
|
||||||
|
.startObject("mapping")
|
||||||
|
.field("store", true)
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
BytesReference source = XContentFactory.jsonBuilder().startObject().field("foo", "bar").endObject().bytes();
|
||||||
|
ParsedDocument doc = mapper.parse("test", "type", "id", source);
|
||||||
|
Mapper fooMapper = doc.dynamicMappingsUpdate().root().getMapper("foo");
|
||||||
|
assertThat(fooMapper, instanceOf(TextFieldMapper.class));
|
||||||
|
assertTrue(((TextFieldMapper) fooMapper).fieldType().stored());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeTemplateWithDynamicType2() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startArray("dynamic_templates")
|
||||||
|
.startObject()
|
||||||
|
.startObject("my_template")
|
||||||
|
.field("match_mapping_type", "string")
|
||||||
|
.startObject("mapping")
|
||||||
|
.field("type", "{dynamic_type}")
|
||||||
|
.field("store", true)
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
BytesReference source = XContentFactory.jsonBuilder().startObject().field("foo", "bar").endObject().bytes();
|
||||||
|
ParsedDocument doc = mapper.parse("test", "type", "id", source);
|
||||||
|
Mapper fooMapper = doc.dynamicMappingsUpdate().root().getMapper("foo");
|
||||||
|
assertThat(fooMapper, instanceOf(TextFieldMapper.class));
|
||||||
|
assertTrue(((TextFieldMapper) fooMapper).fieldType().stored());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeTemplateWithDynamicTypeKeyword() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startArray("dynamic_templates")
|
||||||
|
.startObject()
|
||||||
|
.startObject("my_template")
|
||||||
|
.field("match_mapping_type", "string")
|
||||||
|
.startObject("mapping")
|
||||||
|
.field("index", "not_analyzed")
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
BytesReference source = XContentFactory.jsonBuilder().startObject().field("foo", "bar").endObject().bytes();
|
||||||
|
ParsedDocument doc = mapper.parse("test", "type", "id", source);
|
||||||
|
Mapper fooMapper = doc.dynamicMappingsUpdate().root().getMapper("foo");
|
||||||
|
assertThat(fooMapper, instanceOf(KeywordFieldMapper.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testUpgradeTemplateWithDynamicTypeKeyword2() throws IOException {
|
||||||
|
IndexService indexService = createIndex("test");
|
||||||
|
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
|
||||||
|
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||||
|
.startArray("dynamic_templates")
|
||||||
|
.startObject()
|
||||||
|
.startObject("my_template")
|
||||||
|
.field("match_mapping_type", "string")
|
||||||
|
.startObject("mapping")
|
||||||
|
.field("type", "{dynamic_type}")
|
||||||
|
.field("index", "not_analyzed")
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endObject()
|
||||||
|
.endArray()
|
||||||
|
.endObject().endObject().string();
|
||||||
|
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||||
|
BytesReference source = XContentFactory.jsonBuilder().startObject().field("foo", "bar").endObject().bytes();
|
||||||
|
ParsedDocument doc = mapper.parse("test", "type", "id", source);
|
||||||
|
Mapper fooMapper = doc.dynamicMappingsUpdate().root().getMapper("foo");
|
||||||
|
assertThat(fooMapper, instanceOf(KeywordFieldMapper.class));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue