7373import java .util .Collection ;
7474import java .util .Collections ;
7575import java .util .Comparator ;
76+ import java .util .HashMap ;
7677import java .util .List ;
7778import java .util .Locale ;
7879import java .util .Map ;
9899import static org .elasticsearch .cluster .metadata .MetadataCreateIndexService .buildIndexMetadata ;
99100import static org .elasticsearch .cluster .metadata .MetadataCreateIndexService .clusterStateCreateIndex ;
100101import static org .elasticsearch .cluster .metadata .MetadataCreateIndexService .getIndexNumberOfRoutingShards ;
101- import static org .elasticsearch .cluster .metadata .MetadataCreateIndexService .parseMappings ;
102+ import static org .elasticsearch .cluster .metadata .MetadataCreateIndexService .parseV1Mappings ;
102103import static org .elasticsearch .cluster .metadata .MetadataCreateIndexService .resolveAndValidateAliases ;
103104import static org .elasticsearch .cluster .shards .ClusterShardLimitIT .ShardCounts .forDataNodeCount ;
104105import static org .elasticsearch .index .IndexSettings .INDEX_SOFT_DELETES_SETTING ;
@@ -622,7 +623,7 @@ public void testParseMappingsAppliesDataFromTemplateAndRequest() throws Exceptio
622623 });
623624 request .mappings (createMapping ("mapping_from_request" , "text" ).string ());
624625
625- Map <String , Object > parsedMappings = MetadataCreateIndexService .parseMappings (request .mappings (),
626+ Map <String , Object > parsedMappings = MetadataCreateIndexService .parseV1Mappings (request .mappings (),
626627 List .of (templateMetadata .getMappings ()), NamedXContentRegistry .EMPTY );
627628
628629 assertThat (parsedMappings , hasKey ("_doc" ));
@@ -678,7 +679,7 @@ public void testRequestDataHavePriorityOverTemplateData() throws Exception {
678679 request .aliases (Set .of (new Alias ("alias" ).searchRouting ("fromRequest" )));
679680 request .settings (Settings .builder ().put ("key1" , "requestValue" ).build ());
680681
681- Map <String , Object > parsedMappings = MetadataCreateIndexService .parseMappings (request .mappings (),
682+ Map <String , Object > parsedMappings = MetadataCreateIndexService .parseV1Mappings (request .mappings (),
682683 List .of (templateMetadata .mappings ()), xContentRegistry ());
683684 List <AliasMetadata > resolvedAliases = resolveAndValidateAliases (request .index (), request .aliases (),
684685 MetadataIndexTemplateService .resolveAliases (List .of (templateMetadata )),
@@ -848,7 +849,7 @@ public void testParseMappingsWithTypedTemplateAndTypelessIndexMapping() throws E
848849 }
849850 });
850851
851- Map <String , Object > mappings = parseMappings ("{\" _doc\" :{}}" , List .of (templateMetadata .mappings ()), xContentRegistry ());
852+ Map <String , Object > mappings = parseV1Mappings ("{\" _doc\" :{}}" , List .of (templateMetadata .mappings ()), xContentRegistry ());
852853 assertThat (mappings , Matchers .hasKey (MapperService .SINGLE_MAPPING_NAME ));
853854 }
854855
@@ -861,7 +862,7 @@ public void testParseMappingsWithTypedTemplate() throws Exception {
861862 ExceptionsHelper .reThrowIfNotNull (e );
862863 }
863864 });
864- Map <String , Object > mappings = parseMappings ("" , List .of (templateMetadata .mappings ()), xContentRegistry ());
865+ Map <String , Object > mappings = parseV1Mappings ("" , List .of (templateMetadata .mappings ()), xContentRegistry ());
865866 assertThat (mappings , Matchers .hasKey (MapperService .SINGLE_MAPPING_NAME ));
866867 }
867868
@@ -873,7 +874,7 @@ public void testParseMappingsWithTypelessTemplate() throws Exception {
873874 ExceptionsHelper .reThrowIfNotNull (e );
874875 }
875876 });
876- Map <String , Object > mappings = parseMappings ("" , List .of (templateMetadata .mappings ()), xContentRegistry ());
877+ Map <String , Object > mappings = parseV1Mappings ("" , List .of (templateMetadata .mappings ()), xContentRegistry ());
877878 assertThat (mappings , Matchers .hasKey (MapperService .SINGLE_MAPPING_NAME ));
878879 }
879880
@@ -983,6 +984,69 @@ public void testDeprecateTranslogRetentionSettings() {
983984 + "and [index.translog.retention.size] are deprecated and effectively ignored. They will be removed in a future version." );
984985 }
985986
987+ @ SuppressWarnings ("unchecked" )
988+ public void testMappingsMergingIsSmart () throws Exception {
989+ Template ctt1 = new Template (null ,
990+ new CompressedXContent ("{\" _doc\" :{\" _source\" :{\" enabled\" : false},\" _meta\" :{\" ct1\" :{\" ver\" : \" text\" }}," +
991+ "\" properties\" :{\" foo\" :{\" type\" :\" text\" ,\" ignore_above\" :7,\" analyzer\" :\" english\" }}}}" ), null );
992+ Template ctt2 = new Template (null ,
993+ new CompressedXContent ("{\" _doc\" :{\" _meta\" :{\" ct1\" :{\" ver\" : \" keyword\" },\" ct2\" :\" potato\" }," +
994+ "\" properties\" :{\" foo\" :{\" type\" :\" keyword\" ,\" ignore_above\" :13}}}}" ), null );
995+
996+ ComponentTemplate ct1 = new ComponentTemplate (ctt1 , null , null );
997+ ComponentTemplate ct2 = new ComponentTemplate (ctt2 , null , null );
998+
999+ boolean shouldBeText = randomBoolean ();
1000+ List <String > composedOf = shouldBeText ? Arrays .asList ("ct2" , "ct1" ) : Arrays .asList ("ct1" , "ct2" );
1001+ logger .info ("--> the {} analyzer should win ({})" , shouldBeText ? "text" : "keyword" , composedOf );
1002+ IndexTemplateV2 template = new IndexTemplateV2 (Collections .singletonList ("index" ), null , composedOf , null , null , null );
1003+
1004+ ClusterState state = ClusterState .builder (ClusterState .EMPTY_STATE )
1005+ .metadata (Metadata .builder (Metadata .EMPTY_METADATA )
1006+ .put ("ct1" , ct1 )
1007+ .put ("ct2" , ct2 )
1008+ .put ("index-template" , template )
1009+ .build ())
1010+ .build ();
1011+
1012+ Map <String , Object > resolved =
1013+ MetadataCreateIndexService .resolveV2Mappings ("{\" _doc\" :{\" _meta\" :{\" ct2\" :\" eggplant\" }," +
1014+ "\" properties\" :{\" bar\" :{\" type\" :\" text\" }}}}" , state ,
1015+ "index-template" , new NamedXContentRegistry (Collections .emptyList ()));
1016+
1017+ assertThat ("expected exactly one type but was: " + resolved , resolved .size (), equalTo (1 ));
1018+ Map <String , Object > innerResolved = (Map <String , Object >) resolved .get (MapperService .SINGLE_MAPPING_NAME );
1019+ assertThat ("was: " + innerResolved , innerResolved .size (), equalTo (3 ));
1020+
1021+ Map <String , Object > nonProperties = new HashMap <>(innerResolved );
1022+ nonProperties .remove ("properties" );
1023+ Map <String , Object > expectedNonProperties = new HashMap <>();
1024+ expectedNonProperties .put ("_source" , Collections .singletonMap ("enabled" , false ));
1025+ Map <String , Object > meta = new HashMap <>();
1026+ meta .put ("ct2" , "eggplant" );
1027+ if (shouldBeText ) {
1028+ meta .put ("ct1" , Collections .singletonMap ("ver" , "text" ));
1029+ } else {
1030+ meta .put ("ct1" , Collections .singletonMap ("ver" , "keyword" ));
1031+ }
1032+ expectedNonProperties .put ("_meta" , meta );
1033+ assertThat (nonProperties , equalTo (expectedNonProperties ));
1034+
1035+ Map <String , Object > innerInnerResolved = (Map <String , Object >) innerResolved .get ("properties" );
1036+ assertThat (innerInnerResolved .size (), equalTo (2 ));
1037+ assertThat (innerInnerResolved .get ("bar" ), equalTo (Collections .singletonMap ("type" , "text" )));
1038+ Map <String , Object > fooMappings = new HashMap <>();
1039+ if (shouldBeText ) {
1040+ fooMappings .put ("type" , "text" );
1041+ fooMappings .put ("ignore_above" , 7 );
1042+ fooMappings .put ("analyzer" , "english" );
1043+ } else {
1044+ fooMappings .put ("type" , "keyword" );
1045+ fooMappings .put ("ignore_above" , 13 );
1046+ }
1047+ assertThat (innerInnerResolved .get ("foo" ), equalTo (fooMappings ));
1048+ }
1049+
9861050 private IndexTemplateMetadata addMatchingTemplate (Consumer <IndexTemplateMetadata .Builder > configurator ) {
9871051 IndexTemplateMetadata .Builder builder = templateMetadataBuilder ("template1" , "te*" );
9881052 configurator .accept (builder );
0 commit comments