|  | @@ -29,6 +29,7 @@ import org.elasticsearch.common.Strings;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.bytes.BytesReference;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.lucene.Lucene;
 | 
	
		
			
				|  |  |  import org.elasticsearch.common.unit.Fuzziness;
 | 
	
		
			
				|  |  | +import org.elasticsearch.core.CheckedConsumer;
 | 
	
		
			
				|  |  |  import org.elasticsearch.index.IndexSettings;
 | 
	
		
			
				|  |  |  import org.elasticsearch.index.analysis.AnalyzerScope;
 | 
	
		
			
				|  |  |  import org.elasticsearch.index.analysis.IndexAnalyzers;
 | 
	
	
		
			
				|  | @@ -38,6 +39,9 @@ import org.elasticsearch.index.codec.PerFieldMapperCodec;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xcontent.ToXContent;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xcontent.XContentBuilder;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xcontent.XContentFactory;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xcontent.XContentLocation;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xcontent.XContentParser;
 | 
	
		
			
				|  |  | +import org.elasticsearch.xcontent.XContentParserConfiguration;
 | 
	
		
			
				|  |  |  import org.elasticsearch.xcontent.json.JsonXContent;
 | 
	
		
			
				|  |  |  import org.hamcrest.FeatureMatcher;
 | 
	
		
			
				|  |  |  import org.hamcrest.Matcher;
 | 
	
	
		
			
				|  | @@ -45,7 +49,11 @@ import org.hamcrest.Matchers;
 | 
	
		
			
				|  |  |  import org.hamcrest.core.CombinableMatcher;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import java.io.IOException;
 | 
	
		
			
				|  |  | +import java.util.ArrayList;
 | 
	
		
			
				|  |  | +import java.util.Collections;
 | 
	
		
			
				|  |  | +import java.util.List;
 | 
	
		
			
				|  |  |  import java.util.Map;
 | 
	
		
			
				|  |  | +import java.util.Set;
 | 
	
		
			
				|  |  |  import java.util.function.Function;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import static org.elasticsearch.xcontent.XContentFactory.jsonBuilder;
 | 
	
	
		
			
				|  | @@ -135,7 +143,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testDefaultConfiguration() throws IOException {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
	
		
			
				|  | @@ -158,7 +165,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testCompletionAnalyzerSettings() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "completion");
 | 
	
		
			
				|  |  |              b.field("analyzer", "simple");
 | 
	
	
		
			
				|  | @@ -192,7 +198,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @SuppressWarnings("unchecked")
 | 
	
		
			
				|  |  |      public void testTypeParsing() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "completion");
 | 
	
		
			
				|  |  |              b.field("analyzer", "simple");
 | 
	
	
		
			
				|  | @@ -218,7 +223,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingMinimal() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -228,7 +232,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingFailure() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          MapperParsingException e = expectThrows(
 | 
	
	
		
			
				|  | @@ -239,7 +242,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testKeywordWithSubCompletionAndContext() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "keyword");
 | 
	
		
			
				|  |  |              b.startObject("fields");
 | 
	
	
		
			
				|  | @@ -284,7 +286,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testCompletionWithContextAndSubCompletion() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "completion");
 | 
	
		
			
				|  |  |              b.startArray("contexts");
 | 
	
	
		
			
				|  | @@ -366,8 +367,7 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public void testKeywordWithSubCompletionAndStringInsert() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    public void testGeoHashWithSubCompletionAndStringInsert() throws Exception {
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "geo_point");
 | 
	
		
			
				|  |  |              b.startObject("fields");
 | 
	
	
		
			
				|  | @@ -386,13 +386,19 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |          // unable to assert about geofield content, covered in a REST test
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public void testCompletionTypeWithSubCompletionFieldAndStringInsert() throws Exception {
 | 
	
		
			
				|  |  | +    public void testCompletionTypeWithSubfieldsAndStringInsert() throws Exception {
 | 
	
		
			
				|  |  | +        List<CheckedConsumer<XContentBuilder, IOException>> builders = new ArrayList<>();
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("analyzed1").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("analyzed2").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("subsuggest1").field("type", "completion").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("subsuggest2").field("type", "completion").endObject());
 | 
	
		
			
				|  |  | +        Collections.shuffle(builders, random());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "completion");
 | 
	
		
			
				|  |  |              b.startObject("fields");
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                b.startObject("subsuggest").field("type", "completion").endObject();
 | 
	
		
			
				|  |  | +            for (CheckedConsumer<XContentBuilder, IOException> builder : builders) {
 | 
	
		
			
				|  |  | +                builder.accept(b);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              b.endObject();
 | 
	
		
			
				|  |  |          }));
 | 
	
	
		
			
				|  | @@ -401,42 +407,80 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          LuceneDocument indexableFields = parsedDocument.rootDoc();
 | 
	
		
			
				|  |  |          assertThat(indexableFields.getFields("field"), arrayContainingInAnyOrder(suggestField("suggestion")));
 | 
	
		
			
				|  |  | -        assertThat(indexableFields.getFields("field.subsuggest"), arrayContainingInAnyOrder(suggestField("suggestion")));
 | 
	
		
			
				|  |  | +        assertThat(indexableFields.getFields("field.subsuggest1"), arrayContainingInAnyOrder(suggestField("suggestion")));
 | 
	
		
			
				|  |  | +        assertThat(indexableFields.getFields("field.subsuggest2"), arrayContainingInAnyOrder(suggestField("suggestion")));
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.analyzed1"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(keywordField("suggestion"), sortedSetDocValuesField("suggestion"))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.analyzed2"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(keywordField("suggestion"), sortedSetDocValuesField("suggestion"))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public void testCompletionTypeWithSubCompletionFieldAndObjectInsert() throws Exception {
 | 
	
		
			
				|  |  | +    public void testCompletionTypeWithSubfieldsAndArrayInsert() throws Exception {
 | 
	
		
			
				|  |  | +        List<CheckedConsumer<XContentBuilder, IOException>> builders = new ArrayList<>();
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("analyzed1").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("analyzed2").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("subcompletion1").field("type", "completion").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("subcompletion2").field("type", "completion").endObject());
 | 
	
		
			
				|  |  | +        Collections.shuffle(builders, random());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "completion");
 | 
	
		
			
				|  |  |              b.startObject("fields");
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                b.startObject("analyzed").field("type", "completion").endObject();
 | 
	
		
			
				|  |  | +            for (CheckedConsumer<XContentBuilder, IOException> builder : builders) {
 | 
	
		
			
				|  |  | +                builder.accept(b);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              b.endObject();
 | 
	
		
			
				|  |  |          }));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        ParsedDocument parsedDocument = defaultMapper.parse(source(b -> {
 | 
	
		
			
				|  |  | -            b.startObject("field");
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                b.array("input", "New York", "NY");
 | 
	
		
			
				|  |  | -                b.field("weight", 34);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            b.endObject();
 | 
	
		
			
				|  |  | -        }));
 | 
	
		
			
				|  |  | +        ParsedDocument parsedDocument = defaultMapper.parse(source(b -> b.array("field", "New York", "NY")));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          LuceneDocument indexableFields = parsedDocument.rootDoc();
 | 
	
		
			
				|  |  |          assertThat(indexableFields.getFields("field"), arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY")));
 | 
	
		
			
				|  |  | -        assertThat(indexableFields.getFields("field.analyzed"), arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY")));
 | 
	
		
			
				|  |  | -        // unable to assert about weight, covered in a REST test
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.subcompletion1"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY"))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.subcompletion2"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY"))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.analyzed1"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(
 | 
	
		
			
				|  |  | +                keywordField("New York"),
 | 
	
		
			
				|  |  | +                sortedSetDocValuesField("New York"),
 | 
	
		
			
				|  |  | +                keywordField("NY"),
 | 
	
		
			
				|  |  | +                sortedSetDocValuesField("NY")
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.analyzed2"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(
 | 
	
		
			
				|  |  | +                keywordField("New York"),
 | 
	
		
			
				|  |  | +                sortedSetDocValuesField("New York"),
 | 
	
		
			
				|  |  | +                keywordField("NY"),
 | 
	
		
			
				|  |  | +                sortedSetDocValuesField("NY")
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public void testCompletionTypeWithSubKeywordFieldAndObjectInsert() throws Exception {
 | 
	
		
			
				|  |  | +    public void testCompletionTypeWithSubfieldsAndObjectInsert() throws Exception {
 | 
	
		
			
				|  |  | +        List<CheckedConsumer<XContentBuilder, IOException>> builders = new ArrayList<>();
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("analyzed1").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("analyzed2").field("type", "keyword").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("subcompletion1").field("type", "completion").endObject());
 | 
	
		
			
				|  |  | +        builders.add(b -> b.startObject("subcompletion2").field("type", "completion").endObject());
 | 
	
		
			
				|  |  | +        Collections.shuffle(builders, random());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  |              b.field("type", "completion");
 | 
	
		
			
				|  |  |              b.startObject("fields");
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                b.startObject("analyzed").field("type", "keyword").endObject();
 | 
	
		
			
				|  |  | +            for (CheckedConsumer<XContentBuilder, IOException> builder : builders) {
 | 
	
		
			
				|  |  | +                builder.accept(b);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              b.endObject();
 | 
	
		
			
				|  |  |          }));
 | 
	
	
		
			
				|  | @@ -453,7 +497,15 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |          LuceneDocument indexableFields = parsedDocument.rootDoc();
 | 
	
		
			
				|  |  |          assertThat(indexableFields.getFields("field"), arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY")));
 | 
	
		
			
				|  |  |          assertThat(
 | 
	
		
			
				|  |  | -            indexableFields.getFields("field.analyzed"),
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.subcompletion1"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY"))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.subcompletion2"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(suggestField("New York"), suggestField("NY"))
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        assertThat(
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.analyzed1"),
 | 
	
		
			
				|  |  |              arrayContainingInAnyOrder(
 | 
	
		
			
				|  |  |                  keywordField("New York"),
 | 
	
		
			
				|  |  |                  sortedSetDocValuesField("New York"),
 | 
	
	
		
			
				|  | @@ -461,32 +513,19 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |                  sortedSetDocValuesField("NY")
 | 
	
		
			
				|  |  |              )
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  | -        // unable to assert about weight, covered in a REST test
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    public void testCompletionTypeWithSubKeywordFieldAndStringInsert() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(b -> {
 | 
	
		
			
				|  |  | -            b.field("type", "completion");
 | 
	
		
			
				|  |  | -            b.startObject("fields");
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                b.startObject("analyzed").field("type", "keyword").endObject();
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            b.endObject();
 | 
	
		
			
				|  |  | -        }));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        ParsedDocument parsedDocument = defaultMapper.parse(source(b -> b.field("field", "suggestion")));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        LuceneDocument indexableFields = parsedDocument.rootDoc();
 | 
	
		
			
				|  |  | -        assertThat(indexableFields.getFields("field"), arrayContainingInAnyOrder(suggestField("suggestion")));
 | 
	
		
			
				|  |  |          assertThat(
 | 
	
		
			
				|  |  | -            indexableFields.getFields("field.analyzed"),
 | 
	
		
			
				|  |  | -            arrayContainingInAnyOrder(keywordField("suggestion"), sortedSetDocValuesField("suggestion"))
 | 
	
		
			
				|  |  | +            indexableFields.getFields("field.analyzed2"),
 | 
	
		
			
				|  |  | +            arrayContainingInAnyOrder(
 | 
	
		
			
				|  |  | +                keywordField("New York"),
 | 
	
		
			
				|  |  | +                sortedSetDocValuesField("New York"),
 | 
	
		
			
				|  |  | +                keywordField("NY"),
 | 
	
		
			
				|  |  | +                sortedSetDocValuesField("NY")
 | 
	
		
			
				|  |  | +            )
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  | +        // unable to assert about weight, covered in a REST test
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingMultiValued() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -497,7 +536,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingWithWeight() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -515,7 +553,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingMultiValueWithWeight() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -536,7 +573,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingWithGeoFieldAlias() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          MapperService mapperService = createMapperService(mapping(b -> {
 | 
	
		
			
				|  |  |              b.startObject("completion");
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -574,7 +610,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingFull() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -596,7 +631,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testParsingMixed() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          Mapper fieldMapper = defaultMapper.mappers().getMapper("field");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -640,7 +674,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testNonContextEnabledParsingWithContexts() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          MapperParsingException e = expectThrows(MapperParsingException.class, () -> defaultMapper.parse(source(b -> {
 | 
	
		
			
				|  |  |              b.startObject("field");
 | 
	
	
		
			
				|  | @@ -656,7 +689,6 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      public void testFieldValueValidation() throws Exception {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          DocumentMapper defaultMapper = createDocumentMapper(fieldMapping(this::minimalMapping));
 | 
	
		
			
				|  |  |          CharsRefBuilder charsRefBuilder = new CharsRefBuilder();
 | 
	
		
			
				|  |  |          charsRefBuilder.append("sugg");
 | 
	
	
		
			
				|  | @@ -790,6 +822,96 @@ public class CompletionFieldMapperTests extends MapperTestCase {
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private static CompletionFieldMapper.CompletionInputMetadata randomCompletionMetadata() {
 | 
	
		
			
				|  |  | +        Map<String, Set<String>> contexts = randomBoolean()
 | 
	
		
			
				|  |  | +            ? Collections.emptyMap()
 | 
	
		
			
				|  |  | +            : Collections.singletonMap("filter", Collections.singleton("value"));
 | 
	
		
			
				|  |  | +        return new CompletionFieldMapper.CompletionInputMetadata("text", contexts, 10);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static XContentParser documentParser(CompletionFieldMapper.CompletionInputMetadata metadata) throws IOException {
 | 
	
		
			
				|  |  | +        XContentBuilder docBuilder = JsonXContent.contentBuilder();
 | 
	
		
			
				|  |  | +        if (randomBoolean()) {
 | 
	
		
			
				|  |  | +            docBuilder.prettyPrint();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        docBuilder.startObject();
 | 
	
		
			
				|  |  | +        docBuilder.field("field");
 | 
	
		
			
				|  |  | +        docBuilder.map(metadata.toMap());
 | 
	
		
			
				|  |  | +        docBuilder.endObject();
 | 
	
		
			
				|  |  | +        String document = Strings.toString(docBuilder);
 | 
	
		
			
				|  |  | +        XContentParser docParser = JsonXContent.jsonXContent.createParser(XContentParserConfiguration.EMPTY, document);
 | 
	
		
			
				|  |  | +        docParser.nextToken();
 | 
	
		
			
				|  |  | +        docParser.nextToken();
 | 
	
		
			
				|  |  | +        assertEquals(XContentParser.Token.START_OBJECT, docParser.nextToken());
 | 
	
		
			
				|  |  | +        return docParser;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testMultiFieldParserSimpleValue() throws IOException {
 | 
	
		
			
				|  |  | +        CompletionFieldMapper.CompletionInputMetadata metadata = randomCompletionMetadata();
 | 
	
		
			
				|  |  | +        XContentParser documentParser = documentParser(metadata);
 | 
	
		
			
				|  |  | +        XContentParser multiFieldParser = new CompletionFieldMapper.MultiFieldParser(
 | 
	
		
			
				|  |  | +            metadata,
 | 
	
		
			
				|  |  | +            documentParser.currentName(),
 | 
	
		
			
				|  |  | +            documentParser.getTokenLocation()
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        // we don't check currentToken here because it returns START_OBJECT that is inconsistent with returning a value
 | 
	
		
			
				|  |  | +        assertEquals("text", multiFieldParser.textOrNull());
 | 
	
		
			
				|  |  | +        assertEquals(documentParser.getTokenLocation(), multiFieldParser.getTokenLocation());
 | 
	
		
			
				|  |  | +        assertEquals(documentParser.currentName(), multiFieldParser.currentName());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testMultiFieldParserCompletionSubfield() throws IOException {
 | 
	
		
			
				|  |  | +        CompletionFieldMapper.CompletionInputMetadata metadata = randomCompletionMetadata();
 | 
	
		
			
				|  |  | +        XContentParser documentParser = documentParser(metadata);
 | 
	
		
			
				|  |  | +        // compare the object structure with the original metadata, this implicitly verifies that the xcontent read is valid
 | 
	
		
			
				|  |  | +        XContentBuilder multiFieldBuilder = JsonXContent.contentBuilder()
 | 
	
		
			
				|  |  | +            .copyCurrentStructure(
 | 
	
		
			
				|  |  | +                new CompletionFieldMapper.MultiFieldParser(metadata, documentParser.currentName(), documentParser.getTokenLocation())
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +        XContentBuilder metadataBuilder = JsonXContent.contentBuilder().map(metadata.toMap());
 | 
	
		
			
				|  |  | +        String jsonMetadata = Strings.toString(metadataBuilder);
 | 
	
		
			
				|  |  | +        assertEquals(jsonMetadata, Strings.toString(multiFieldBuilder));
 | 
	
		
			
				|  |  | +        // advance token by token and verify currentName as well as getTokenLocation
 | 
	
		
			
				|  |  | +        XContentParser multiFieldParser = new CompletionFieldMapper.MultiFieldParser(
 | 
	
		
			
				|  |  | +            metadata,
 | 
	
		
			
				|  |  | +            documentParser.currentName(),
 | 
	
		
			
				|  |  | +            documentParser.getTokenLocation()
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        XContentParser expectedParser = JsonXContent.jsonXContent.createParser(XContentParserConfiguration.EMPTY, jsonMetadata);
 | 
	
		
			
				|  |  | +        assertEquals(expectedParser.nextToken(), multiFieldParser.currentToken());
 | 
	
		
			
				|  |  | +        XContentLocation expectedTokenLocation = documentParser.getTokenLocation();
 | 
	
		
			
				|  |  | +        while (expectedParser.nextToken() != null) {
 | 
	
		
			
				|  |  | +            XContentParser.Token token = multiFieldParser.nextToken();
 | 
	
		
			
				|  |  | +            assertEquals(expectedParser.currentToken(), token);
 | 
	
		
			
				|  |  | +            assertEquals(expectedParser.currentToken(), multiFieldParser.currentToken());
 | 
	
		
			
				|  |  | +            assertEquals(expectedTokenLocation, multiFieldParser.getTokenLocation());
 | 
	
		
			
				|  |  | +            assertEquals(documentParser.nextToken(), multiFieldParser.currentToken());
 | 
	
		
			
				|  |  | +            assertEquals(documentParser.currentName(), multiFieldParser.currentName());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        assertNull(multiFieldParser.nextToken());
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public void testMultiFieldParserMixedSubfields() throws IOException {
 | 
	
		
			
				|  |  | +        CompletionFieldMapper.CompletionInputMetadata metadata = randomCompletionMetadata();
 | 
	
		
			
				|  |  | +        XContentParser documentParser = documentParser(metadata);
 | 
	
		
			
				|  |  | +        // simulate 10 sub-fields which may either read simple values or the full object structure
 | 
	
		
			
				|  |  | +        for (int i = 0; i < 10; i++) {
 | 
	
		
			
				|  |  | +            XContentParser multiFieldParser = new CompletionFieldMapper.MultiFieldParser(
 | 
	
		
			
				|  |  | +                metadata,
 | 
	
		
			
				|  |  | +                documentParser.currentName(),
 | 
	
		
			
				|  |  | +                documentParser.getTokenLocation()
 | 
	
		
			
				|  |  | +            );
 | 
	
		
			
				|  |  | +            if (randomBoolean()) {
 | 
	
		
			
				|  |  | +                assertEquals("text", multiFieldParser.textOrNull());
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                XContentBuilder multiFieldBuilder = JsonXContent.contentBuilder().copyCurrentStructure(multiFieldParser);
 | 
	
		
			
				|  |  | +                XContentBuilder metadataBuilder = JsonXContent.contentBuilder().map(metadata.toMap());
 | 
	
		
			
				|  |  | +                String jsonMetadata = Strings.toString(metadataBuilder);
 | 
	
		
			
				|  |  | +                assertEquals(jsonMetadata, Strings.toString(multiFieldBuilder));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private Matcher<IndexableField> suggestField(String value) {
 | 
	
		
			
				|  |  |          return Matchers.allOf(hasProperty(IndexableField::stringValue, equalTo(value)), Matchers.instanceOf(SuggestField.class));
 | 
	
		
			
				|  |  |      }
 |