|  | @@ -17,6 +17,7 @@ import org.elasticsearch.index.analysis.IndexAnalyzers;
 | 
	
		
			
				|  |  |  import org.elasticsearch.index.similarity.SimilarityService;
 | 
	
		
			
				|  |  |  import org.elasticsearch.indices.IndicesModule;
 | 
	
		
			
				|  |  |  import org.elasticsearch.script.ScriptService;
 | 
	
		
			
				|  |  | +import org.elasticsearch.test.VersionUtils;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xcontent.XContentBuilder;
 | 
	
		
			
				|  |  |  import org.hamcrest.CoreMatchers;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -29,8 +30,12 @@ import java.util.function.Supplier;
 | 
	
		
			
				|  |  |  public class MappingParserTests extends MapperServiceTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static MappingParser createMappingParser(Settings settings) {
 | 
	
		
			
				|  |  | +        return createMappingParser(settings, Version.CURRENT);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static MappingParser createMappingParser(Settings settings, Version version) {
 | 
	
		
			
				|  |  |          ScriptService scriptService = new ScriptService(settings, Collections.emptyMap(), Collections.emptyMap(), () -> 1L);
 | 
	
		
			
				|  |  | -        IndexSettings indexSettings = createIndexSettings(Version.CURRENT, settings);
 | 
	
		
			
				|  |  | +        IndexSettings indexSettings = createIndexSettings(version, settings);
 | 
	
		
			
				|  |  |          IndexAnalyzers indexAnalyzers = createIndexAnalyzers();
 | 
	
		
			
				|  |  |          SimilarityService similarityService = new SimilarityService(indexSettings, scriptService, Collections.emptyMap());
 | 
	
		
			
				|  |  |          MapperRegistry mapperRegistry = new IndicesModule(Collections.emptyList()).getMapperRegistry();
 | 
	
	
		
			
				|  | @@ -207,18 +212,22 @@ public class MappingParserTests extends MapperServiceTestCase {
 | 
	
		
			
				|  |  |          assertEquals("obj.source.geo.location", geoPointFieldMapper.mappedFieldType.name());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private static String randomFieldType() {
 | 
	
		
			
				|  |  | +        return randomBoolean() ? KeywordFieldMapper.CONTENT_TYPE : ObjectMapper.CONTENT_TYPE;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      public void testFieldStartingWithDot() throws Exception {
 | 
	
		
			
				|  |  | -        XContentBuilder builder = mapping(b -> b.startObject(".foo").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        XContentBuilder builder = mapping(b -> b.startObject(".foo").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |          IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  |              IllegalArgumentException.class,
 | 
	
		
			
				|  |  |              () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          // TODO isn't this error misleading?
 | 
	
		
			
				|  |  | -        assertEquals("name cannot be empty string", iae.getMessage());
 | 
	
		
			
				|  |  | +        assertEquals("field name cannot be an empty string", iae.getMessage());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testFieldEndingWithDot() throws Exception {
 | 
	
		
			
				|  |  | -        XContentBuilder builder = mapping(b -> b.startObject("foo.").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        XContentBuilder builder = mapping(b -> b.startObject("foo.").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |          Mapping mapping = createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  |          // TODO this needs fixing as part of addressing https://github.com/elastic/elasticsearch/issues/28948
 | 
	
		
			
				|  |  |          assertNotNull(mapping.getRoot().mappers.get("foo"));
 | 
	
	
		
			
				|  | @@ -226,17 +235,17 @@ public class MappingParserTests extends MapperServiceTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testFieldTrailingDots() throws Exception {
 | 
	
		
			
				|  |  | -        XContentBuilder builder = mapping(b -> b.startObject("top..foo").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        XContentBuilder builder = mapping(b -> b.startObject("top..foo").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |          IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  |              IllegalArgumentException.class,
 | 
	
		
			
				|  |  |              () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          // TODO isn't this error misleading?
 | 
	
		
			
				|  |  | -        assertEquals("name cannot be empty string", iae.getMessage());
 | 
	
		
			
				|  |  | +        assertEquals("field name cannot be an empty string", iae.getMessage());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testDottedFieldEndingWithDot() throws Exception {
 | 
	
		
			
				|  |  | -        XContentBuilder builder = mapping(b -> b.startObject("foo.bar.").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        XContentBuilder builder = mapping(b -> b.startObject("foo.bar.").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |          Mapping mapping = createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  |          // TODO this needs fixing as part of addressing https://github.com/elastic/elasticsearch/issues/28948
 | 
	
		
			
				|  |  |          assertNotNull(((ObjectMapper) mapping.getRoot().mappers.get("foo")).mappers.get("bar"));
 | 
	
	
		
			
				|  | @@ -244,53 +253,81 @@ public class MappingParserTests extends MapperServiceTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testFieldStartingAndEndingWithDot() throws Exception {
 | 
	
		
			
				|  |  | -        XContentBuilder builder = mapping(b -> b.startObject("foo..bar.").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        XContentBuilder builder = mapping(b -> b.startObject("foo..bar.").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |          IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  |              IllegalArgumentException.class,
 | 
	
		
			
				|  |  |              () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          // TODO isn't this error misleading?
 | 
	
		
			
				|  |  | -        assertEquals("name cannot be empty string", iae.getMessage());
 | 
	
		
			
				|  |  | +        assertEquals("field name cannot be an empty string", iae.getMessage());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testDottedFieldWithTrailingWhitespace() throws Exception {
 | 
	
		
			
				|  |  | -        XContentBuilder builder = mapping(b -> b.startObject("top. .foo").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | -        Mapping mapping = createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  | -        ObjectMapper top = (ObjectMapper) mapping.getRoot().mappers.get("top");
 | 
	
		
			
				|  |  | -        // TODO this needs fixing? This field name is not allowed in documents when subobjects are enabled.
 | 
	
		
			
				|  |  | -        ObjectMapper mapper = (ObjectMapper) top.getMapper(" ");
 | 
	
		
			
				|  |  | -        assertNotNull(mapper.getMapper("foo"));
 | 
	
		
			
				|  |  | +        XContentBuilder builder = mapping(b -> b.startObject("top. .foo").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  | +        IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  | +            IllegalArgumentException.class,
 | 
	
		
			
				|  |  | +            () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        // TODO isn't this error misleading?
 | 
	
		
			
				|  |  | +        assertEquals("field name cannot contain only whitespaces", iae.getMessage());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testEmptyFieldName() throws Exception {
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            XContentBuilder builder = mapping(b -> b.startObject("").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mapping(b -> b.startObject("").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |              IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  |                  IllegalArgumentException.class,
 | 
	
		
			
				|  |  |                  () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  | -            assertEquals("name cannot be empty string", iae.getMessage());
 | 
	
		
			
				|  |  | +            assertEquals("field name cannot be an empty string", iae.getMessage());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            XContentBuilder builder = mappingNoSubobjects(b -> b.startObject("").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mappingNoSubobjects(b -> b.startObject("").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  |              IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  |                  IllegalArgumentException.class,
 | 
	
		
			
				|  |  |                  () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  | -            assertEquals("name cannot be empty string", iae.getMessage());
 | 
	
		
			
				|  |  | +            assertEquals("field name cannot be an empty string", iae.getMessage());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testBlankFieldName() throws Exception {
 | 
	
		
			
				|  |  | -        // TODO this needs fixing? This field name is never allowed in documents hence such a field can never be indexed?
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            XContentBuilder builder = mapping(b -> b.startObject(" ").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | -            Mapping mapping = createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mapping(b -> b.startObject(" ").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  | +            IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  | +                IllegalArgumentException.class,
 | 
	
		
			
				|  |  | +                () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +            assertEquals("field name cannot contain only whitespaces", iae.getMessage());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mappingNoSubobjects(b -> b.startObject(" ").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +            IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  | +                IllegalArgumentException.class,
 | 
	
		
			
				|  |  | +                () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +            assertEquals("field name cannot contain only whitespaces", iae.getMessage());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testBlankFieldNameBefore8_6_0() throws Exception {
 | 
	
		
			
				|  |  | +        Version version = VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumIndexCompatibilityVersion(), Version.V_8_5_0);
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mapping(b -> b.startObject(" ").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  | +            MappingParser mappingParser = createMappingParser(Settings.EMPTY, version);
 | 
	
		
			
				|  |  | +            Mapping mapping = mappingParser.parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  |              assertNotNull(mapping.getRoot().getMapper(" "));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mapping(b -> b.startObject("top. .foo").field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  | +            MappingParser mappingParser = createMappingParser(Settings.EMPTY, version);
 | 
	
		
			
				|  |  | +            Mapping mapping = mappingParser.parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  | +            assertNotNull(((ObjectMapper) mapping.getRoot().getMapper("top")).getMapper(" "));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              XContentBuilder builder = mappingNoSubobjects(b -> b.startObject(" ").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | -            Mapping mapping = createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  | +            MappingParser mappingParser = createMappingParser(Settings.EMPTY, version);
 | 
	
		
			
				|  |  | +            Mapping mapping = mappingParser.parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  |              assertNotNull(mapping.getRoot().getMapper(" "));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -298,31 +335,19 @@ public class MappingParserTests extends MapperServiceTestCase {
 | 
	
		
			
				|  |  |      public void testFieldNameDotsOnly() throws Exception {
 | 
	
		
			
				|  |  |          String[] fieldNames = { ".", "..", "..." };
 | 
	
		
			
				|  |  |          for (String fieldName : fieldNames) {
 | 
	
		
			
				|  |  | -            XContentBuilder builder = mapping(b -> b.startObject(fieldName).field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | -            // TODO this should really throw a better error, relates to https://github.com/elastic/elasticsearch/issues/21862
 | 
	
		
			
				|  |  | -            expectThrows(
 | 
	
		
			
				|  |  | -                ArrayIndexOutOfBoundsException.class,
 | 
	
		
			
				|  |  | +            XContentBuilder builder = mapping(b -> b.startObject(fieldName).field("type", randomFieldType()).endObject());
 | 
	
		
			
				|  |  | +            IllegalArgumentException iae = expectThrows(
 | 
	
		
			
				|  |  | +                IllegalArgumentException.class,
 | 
	
		
			
				|  |  |                  () -> createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)))
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    public void testFieldNameDotsOnlySubobjectsFalse() throws Exception {
 | 
	
		
			
				|  |  | -        String[] fieldNames = { ".", "..", "..." };
 | 
	
		
			
				|  |  | -        for (String fieldName : fieldNames) {
 | 
	
		
			
				|  |  | -            XContentBuilder builder = mappingNoSubobjects(b -> b.startObject(fieldName).field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            createMappingParser(Settings.EMPTY).parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  | +            assertEquals("field name cannot contain only dots", iae.getMessage());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testDynamicFieldEdgeCaseNamesSubobjectsFalse() throws Exception {
 | 
	
		
			
				|  |  | -        // these combinations are not accepted by default, but they are when subobjects are disabled
 | 
	
		
			
				|  |  | -        String[] fieldNames = new String[] { ".foo", "foo.", "top..foo", "top.foo.", "top..foo.", "top. .foo" };
 | 
	
		
			
				|  |  |          MappingParser mappingParser = createMappingParser(Settings.EMPTY);
 | 
	
		
			
				|  |  | -        for (String fieldName : fieldNames) {
 | 
	
		
			
				|  |  | +        for (String fieldName : DocumentParserTests.VALID_FIELD_NAMES_NO_SUBOBJECTS) {
 | 
	
		
			
				|  |  |              XContentBuilder builder = mappingNoSubobjects(b -> b.startObject(fieldName).field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | -            // TODO this is not accepted in documents, shall we revert https://github.com/elastic/elasticsearch/pull/90950 ?
 | 
	
		
			
				|  |  |              assertNotNull(mappingParser.parse("_doc", new CompressedXContent(BytesReference.bytes(builder))));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -330,9 +355,8 @@ public class MappingParserTests extends MapperServiceTestCase {
 | 
	
		
			
				|  |  |      public void testDynamicFieldEdgeCaseNamesRuntimeSection() throws Exception {
 | 
	
		
			
				|  |  |          // TODO these combinations are not accepted by default, but they are in the runtime section, though they are not accepted when
 | 
	
		
			
				|  |  |          // parsing documents with subobjects enabled
 | 
	
		
			
				|  |  | -        String[] fieldNames = new String[] { ".foo", "foo.", "top..foo", "top.foo.", "top..foo.", "top. .foo" };
 | 
	
		
			
				|  |  |          MappingParser mappingParser = createMappingParser(Settings.EMPTY);
 | 
	
		
			
				|  |  | -        for (String fieldName : fieldNames) {
 | 
	
		
			
				|  |  | +        for (String fieldName : DocumentParserTests.VALID_FIELD_NAMES_NO_SUBOBJECTS) {
 | 
	
		
			
				|  |  |              XContentBuilder builder = runtimeMapping(b -> b.startObject(fieldName).field("type", "keyword").endObject());
 | 
	
		
			
				|  |  |              mappingParser.parse("_doc", new CompressedXContent(BytesReference.bytes(builder)));
 | 
	
		
			
				|  |  |          }
 |