Skip to content

Commit 3a1a364

Browse files
authored
Move unmapped type handling to QueryShardContext (#63407)
`MapperService` exposes an `unmappedFieldType` method that builds and caches anonoymous field types created when sorting on unmapped fields, based on the unmapped type being specified in the search request. Such method is only called through `QueryShardContext`, hence it can be moved out of `MapperService` and its logic can be simplified by removing the caching of unmapped field types.
1 parent 0fe9787 commit 3a1a364

File tree

6 files changed

+42
-43
lines changed

6 files changed

+42
-43
lines changed

server/src/main/java/org/elasticsearch/index/mapper/MapperService.java

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import org.elasticsearch.index.analysis.ReloadableCustomAnalyzer;
4747
import org.elasticsearch.index.analysis.TokenFilterFactory;
4848
import org.elasticsearch.index.analysis.TokenizerFactory;
49-
import org.elasticsearch.index.mapper.Mapper.BuilderContext;
5049
import org.elasticsearch.index.query.QueryShardContext;
5150
import org.elasticsearch.index.similarity.SimilarityService;
5251
import org.elasticsearch.indices.IndicesModule;
@@ -57,17 +56,13 @@
5756
import java.io.IOException;
5857
import java.util.ArrayList;
5958
import java.util.Collections;
60-
import java.util.HashMap;
6159
import java.util.List;
6260
import java.util.Map;
6361
import java.util.Set;
6462
import java.util.function.BooleanSupplier;
6563
import java.util.function.Function;
6664
import java.util.function.Supplier;
6765

68-
import static java.util.Collections.emptyMap;
69-
import static java.util.Collections.unmodifiableMap;
70-
7166
public class MapperService extends AbstractIndexComponent implements Closeable {
7267

7368
/**
@@ -118,8 +113,6 @@ public enum MergeReason {
118113
private final MapperAnalyzerWrapper searchAnalyzer;
119114
private final MapperAnalyzerWrapper searchQuoteAnalyzer;
120115

121-
private volatile Map<String, MappedFieldType> unmappedFieldTypes = emptyMap();
122-
123116
final MapperRegistry mapperRegistry;
124117

125118
private final BooleanSupplier idFieldDataEnabled;
@@ -428,30 +421,6 @@ public ObjectMapper getObjectMapper(String name) {
428421
return this.mapper == null ? null : this.mapper.objectMappers().get(name);
429422
}
430423

431-
/**
432-
* Given a type (eg. long, string, ...), return an anonymous field mapper that can be used for search operations.
433-
*/
434-
public MappedFieldType unmappedFieldType(String type) {
435-
MappedFieldType fieldType = unmappedFieldTypes.get(type);
436-
if (fieldType == null) {
437-
final Mapper.TypeParser.ParserContext parserContext = documentMapperParser().parserContext();
438-
Mapper.TypeParser typeParser = parserContext.typeParser(type);
439-
if (typeParser == null) {
440-
throw new IllegalArgumentException("No mapper found for type [" + type + "]");
441-
}
442-
final Mapper.Builder<?> builder = typeParser.parse("__anonymous_" + type, emptyMap(), parserContext);
443-
final BuilderContext builderContext = new BuilderContext(indexSettings.getSettings(), new ContentPath(1));
444-
fieldType = ((FieldMapper)builder.build(builderContext)).fieldType();
445-
446-
// There is no need to synchronize writes here. In the case of concurrent access, we could just
447-
// compute some mappers several times, which is not a big deal
448-
Map<String, MappedFieldType> newUnmappedFieldTypes = new HashMap<>(unmappedFieldTypes);
449-
newUnmappedFieldTypes.put(type, fieldType);
450-
unmappedFieldTypes = unmodifiableMap(newUnmappedFieldTypes);
451-
}
452-
return fieldType;
453-
}
454-
455424
public Analyzer indexAnalyzer() {
456425
return this.indexAnalyzer;
457426
}
@@ -544,5 +513,4 @@ public synchronized List<String> reloadSearchAnalyzers(AnalysisRegistry registry
544513
}
545514
return reloadedAnalyzers;
546515
}
547-
548516
}

server/src/main/java/org/elasticsearch/index/query/QueryShardContext.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
4545
import org.elasticsearch.index.fielddata.IndexFieldData;
4646
import org.elasticsearch.index.mapper.ContentPath;
47+
import org.elasticsearch.index.mapper.FieldMapper;
4748
import org.elasticsearch.index.mapper.MappedFieldType;
4849
import org.elasticsearch.index.mapper.Mapper;
4950
import org.elasticsearch.index.mapper.MapperService;
@@ -61,6 +62,7 @@
6162
import org.elasticsearch.transport.RemoteClusterAware;
6263

6364
import java.io.IOException;
65+
import java.util.Collections;
6466
import java.util.HashMap;
6567
import java.util.List;
6668
import java.util.Map;
@@ -276,6 +278,25 @@ public Analyzer getSearchQuoteAnalyzer(MappedFieldType fieldType) {
276278
return mapperService.searchQuoteAnalyzer();
277279
}
278280

281+
/**
282+
* Given a type (eg. long, string, ...), returns an anonymous field type that can be used for search operations.
283+
* Generally used to handle unmapped fields in the context of sorting.
284+
*/
285+
public MappedFieldType buildAnonymousFieldType(String type) {
286+
final Mapper.TypeParser.ParserContext parserContext = mapperService.documentMapperParser().parserContext();
287+
Mapper.TypeParser typeParser = parserContext.typeParser(type);
288+
if (typeParser == null) {
289+
throw new IllegalArgumentException("No mapper found for type [" + type + "]");
290+
}
291+
final Mapper.Builder<?> builder = typeParser.parse("__anonymous_" + type, Collections.emptyMap(), parserContext);
292+
final Mapper.BuilderContext builderContext = new Mapper.BuilderContext(indexSettings.getSettings(), new ContentPath(1));
293+
Mapper mapper = builder.build(builderContext);
294+
if (mapper instanceof FieldMapper) {
295+
return ((FieldMapper)mapper).fieldType();
296+
}
297+
throw new IllegalArgumentException("Mapper for type [" + type + "] must be a leaf field");
298+
}
299+
279300
public IndexAnalyzers getIndexAnalyzers() {
280301
return mapperService.getIndexAnalyzers();
281302
}

server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ private MappedFieldType resolveUnmappedType(QueryShardContext context) {
449449
if (unmappedType == null) {
450450
throw new QueryShardException(context, "No mapping found for [" + fieldName + "] in order to sort on");
451451
}
452-
return context.getMapperService().unmappedFieldType(unmappedType);
452+
return context.buildAnonymousFieldType(unmappedType);
453453
}
454454

455455
private MultiValueMode localSortMode() {

server/src/main/java/org/elasticsearch/search/sort/GeoDistanceSortBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.elasticsearch.index.query.QueryShardContext;
5858
import org.elasticsearch.search.DocValueFormat;
5959
import org.elasticsearch.search.MultiValueMode;
60+
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
6061

6162
import java.io.IOException;
6263
import java.util.ArrayList;
@@ -569,7 +570,7 @@ private IndexGeoPointFieldData fieldData(QueryShardContext context) {
569570
MappedFieldType fieldType = context.getFieldType(fieldName);
570571
if (fieldType == null) {
571572
if (ignoreUnmapped) {
572-
fieldType = context.getMapperService().unmappedFieldType("geo_point");
573+
return new LatLonPointIndexFieldData(fieldName, CoreValuesSourceType.GEOPOINT);
573574
} else {
574575
throw new IllegalArgumentException("failed to find mapper for [" + fieldName + "] for geo distance based sort");
575576
}

server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@
3636
import org.elasticsearch.index.analysis.NamedAnalyzer;
3737
import org.elasticsearch.index.analysis.ReloadableCustomAnalyzer;
3838
import org.elasticsearch.index.analysis.TokenFilterFactory;
39-
import org.elasticsearch.index.mapper.KeywordFieldMapper.KeywordFieldType;
4039
import org.elasticsearch.index.mapper.MapperService.MergeReason;
41-
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
4240
import org.elasticsearch.indices.analysis.AnalysisModule.AnalysisProvider;
4341
import org.elasticsearch.plugins.AnalysisPlugin;
4442
import org.elasticsearch.plugins.Plugin;
@@ -52,7 +50,6 @@
5250
import java.util.Map;
5351

5452
import static org.hamcrest.CoreMatchers.containsString;
55-
import static org.hamcrest.Matchers.instanceOf;
5653
import static org.hamcrest.Matchers.notNullValue;
5754
import static org.hamcrest.Matchers.nullValue;
5855

@@ -126,12 +123,6 @@ public void testMappingDepthExceedsLimit() throws Throwable {
126123
assertThat(e.getMessage(), containsString("Limit of mapping depth [1] has been exceeded"));
127124
}
128125

129-
public void testUnmappedFieldType() {
130-
MapperService mapperService = createIndex("index").mapperService();
131-
assertThat(mapperService.unmappedFieldType("keyword"), instanceOf(KeywordFieldType.class));
132-
assertThat(mapperService.unmappedFieldType("long"), instanceOf(NumberFieldType.class));
133-
}
134-
135126
public void testPartitionedConstraints() {
136127
// partitioned index must have routing
137128
IllegalArgumentException noRoutingException = expectThrows(IllegalArgumentException.class, () -> {

server/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,15 @@
5050
import org.elasticsearch.index.fielddata.LeafFieldData;
5151
import org.elasticsearch.index.fielddata.ScriptDocValues;
5252
import org.elasticsearch.index.fielddata.plain.AbstractLeafOrdinalsFieldData;
53+
import org.elasticsearch.index.mapper.DocumentMapperParser;
5354
import org.elasticsearch.index.mapper.IndexFieldMapper;
55+
import org.elasticsearch.index.mapper.KeywordFieldMapper;
5456
import org.elasticsearch.index.mapper.MappedFieldType;
57+
import org.elasticsearch.index.mapper.Mapper;
5558
import org.elasticsearch.index.mapper.MapperService;
59+
import org.elasticsearch.index.mapper.NumberFieldMapper;
5660
import org.elasticsearch.index.mapper.TextFieldMapper;
61+
import org.elasticsearch.indices.IndicesModule;
5762
import org.elasticsearch.search.lookup.LeafDocLookup;
5863
import org.elasticsearch.search.lookup.LeafSearchLookup;
5964
import org.elasticsearch.search.lookup.SearchLookup;
@@ -63,6 +68,7 @@
6368
import java.util.ArrayList;
6469
import java.util.Collections;
6570
import java.util.List;
71+
import java.util.Map;
6672
import java.util.function.BiFunction;
6773
import java.util.function.Supplier;
6874

@@ -102,6 +108,12 @@ public void testFailIfFieldMappingNotFound() {
102108
assertThat(result.name(), equalTo("name"));
103109
}
104110

111+
public void testBuildAnonymousFieldType() {
112+
QueryShardContext context = createQueryShardContext("uuid", null);
113+
assertThat(context.buildAnonymousFieldType("keyword"), instanceOf(KeywordFieldMapper.KeywordFieldType.class));
114+
assertThat(context.buildAnonymousFieldType("long"), instanceOf(NumberFieldMapper.NumberFieldType.class));
115+
}
116+
105117
public void testToQueryFails() {
106118
QueryShardContext context = createQueryShardContext(IndexMetadata.INDEX_UUID_NA_VALUE, null);
107119
Exception exc = expectThrows(Exception.class,
@@ -300,6 +312,12 @@ private static QueryShardContext createQueryShardContext(String indexUuid, Strin
300312
when(mapperService.getIndexSettings()).thenReturn(indexSettings);
301313
when(mapperService.index()).thenReturn(indexMetadata.getIndex());
302314
when(mapperService.getIndexAnalyzers()).thenReturn(indexAnalyzers);
315+
DocumentMapperParser documentMapperParser = mock(DocumentMapperParser.class);
316+
Map<String, Mapper.TypeParser> typeParserMap = IndicesModule.getMappers(Collections.emptyList());
317+
Mapper.TypeParser.ParserContext parserContext = new Mapper.TypeParser.ParserContext(name -> null, mapperService,
318+
typeParserMap::get, Version.CURRENT, () -> null, null, null);
319+
when(documentMapperParser.parserContext()).thenReturn(parserContext);
320+
when(mapperService.documentMapperParser()).thenReturn(documentMapperParser);
303321
if (runtimeDocValues != null) {
304322
when(mapperService.fieldType(any())).thenAnswer(fieldTypeInv -> {
305323
String fieldName = (String)fieldTypeInv.getArguments()[0];

0 commit comments

Comments
 (0)