Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.index.mapper;

/**
* No-op Default of RootObjectMapperNamespaceValidator used in non-serverless Elasticsearch envs.
*/
public class DefaultRootObjectMapperNamespaceValidator implements RootObjectMapperNamespaceValidator {
@Override
public void validateNamespace(ObjectMapper.Subobjects subobjects, Mapper mapper) {}

// MP FIXME remove
@Override
public String toString() {
return "I'm the DefaultRootObjectMapperNamespaceValidator{}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,36 @@
*/
public final class MapperRegistry {

// there is one per node of MapperRegistery -. MapperSErvice can get the reserved namesapce from here

private final Map<String, Mapper.TypeParser> mapperParsers;
private final Map<String, RuntimeField.Parser> runtimeFieldParsers;
private final Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers;
private final Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers7x;
private final Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers6x;
private final Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers5x;
private final Function<String, FieldPredicate> fieldFilter;
private final RootObjectMapperNamespaceValidator namespaceValidator;

// MP TODO: remove this no-op RootObjectMapperNamespaceValidator once we know how all this is going to work
public MapperRegistry(
Map<String, Mapper.TypeParser> mapperParsers,
Map<String, RuntimeField.Parser> runtimeFieldParsers,
Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers,
Function<String, FieldPredicate> fieldFilter
) {
this(mapperParsers, runtimeFieldParsers, metadataMapperParsers, fieldFilter, new RootObjectMapperNamespaceValidator() {
@Override
public void validateNamespace(ObjectMapper.Subobjects subobjects, Mapper mapper) {}
});
}

public MapperRegistry(
Map<String, Mapper.TypeParser> mapperParsers,
Map<String, RuntimeField.Parser> runtimeFieldParsers,
Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers,
Function<String, FieldPredicate> fieldFilter,
RootObjectMapperNamespaceValidator namespaceValidator // MP TODO: new SPI for serverless
) {
this.mapperParsers = Collections.unmodifiableMap(new LinkedHashMap<>(mapperParsers));
this.runtimeFieldParsers = runtimeFieldParsers;
Expand All @@ -50,6 +67,7 @@ public MapperRegistry(
metadata5x.put(LegacyTypeFieldMapper.NAME, LegacyTypeFieldMapper.PARSER);
this.metadataMapperParsers5x = metadata5x;
this.fieldFilter = fieldFilter;
this.namespaceValidator = namespaceValidator;
}

/**
Expand All @@ -68,6 +86,10 @@ public Mapper.TypeParser getMapperParser(String type, IndexVersion indexVersionC
}
}

public RootObjectMapperNamespaceValidator getNamespaceValidator() {
return namespaceValidator;
}

public Map<String, RuntimeField.Parser> getRuntimeFieldParsers() {
return runtimeFieldParsers;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ public MapperService(
this.indexVersionCreated = indexSettings.getIndexVersionCreated();
this.indexAnalyzers = indexAnalyzers;
this.mapperRegistry = mapperRegistry;
// MP TODO: Huzzah! I can inject the namespace validator into the MappingParserContext here !!!
this.mappingParserContextSupplier = () -> new MappingParserContext(
similarityService::getSimilarity,
type -> mapperRegistry.getMapperParser(type, indexVersionCreated),
Expand All @@ -245,7 +246,8 @@ public MapperService(
indexAnalyzers,
indexSettings,
idFieldMapper,
bitSetProducer
bitSetProducer,
mapperRegistry.getNamespaceValidator()
);
this.documentParser = new DocumentParser(parserConfiguration, this.mappingParserContextSupplier.get());
Map<String, MetadataFieldMapper.TypeParser> metadataMapperParsers = mapperRegistry.getMetadataMapperParsers(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class MappingParserContext {
private final IdFieldMapper idFieldMapper;
private final Function<Query, BitSetProducer> bitSetProducer;
private final long mappingObjectDepthLimit;
private final RootObjectMapperNamespaceValidator namespaceValidator;
private long mappingObjectDepth = 0;

public MappingParserContext(
Expand All @@ -55,7 +56,8 @@ public MappingParserContext(
IndexAnalyzers indexAnalyzers,
IndexSettings indexSettings,
IdFieldMapper idFieldMapper,
Function<Query, BitSetProducer> bitSetProducer
Function<Query, BitSetProducer> bitSetProducer,
RootObjectMapperNamespaceValidator namespaceValidator
) {
this.similarityLookupService = similarityLookupService;
this.typeParsers = typeParsers;
Expand All @@ -69,6 +71,41 @@ public MappingParserContext(
this.idFieldMapper = idFieldMapper;
this.mappingObjectDepthLimit = indexSettings.getMappingDepthLimit();
this.bitSetProducer = bitSetProducer;
this.namespaceValidator = namespaceValidator;
}

// MP TODO: only used by tests, so remove this after tests are updated?
public MappingParserContext(
Function<String, SimilarityProvider> similarityLookupService,
Function<String, Mapper.TypeParser> typeParsers,
Function<String, RuntimeField.Parser> runtimeFieldParsers,
IndexVersion indexVersionCreated,
Supplier<TransportVersion> clusterTransportVersion,
Supplier<SearchExecutionContext> searchExecutionContextSupplier,
ScriptCompiler scriptCompiler,
IndexAnalyzers indexAnalyzers,
IndexSettings indexSettings,
IdFieldMapper idFieldMapper,
Function<Query, BitSetProducer> bitSetProducer
) {
this(
similarityLookupService,
typeParsers,
runtimeFieldParsers,
indexVersionCreated,
clusterTransportVersion,
searchExecutionContextSupplier,
scriptCompiler,
indexAnalyzers,
indexSettings,
idFieldMapper,
bitSetProducer,
null
);
}

public RootObjectMapperNamespaceValidator getNamespaceValidator() {
return namespaceValidator;
}

public IndexAnalyzers getIndexAnalyzers() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -547,12 +547,17 @@ public final Optional<SourceKeepMode> sourceKeepMode() {
}

@Override
public void validate(MappingLookup mappers) {
public final void validate(MappingLookup mappers) {
for (Mapper mapper : this.mappers.values()) {
mapper.validate(mappers);
validateSubField(mapper, mappers);
}
}

protected void validateSubField(Mapper mapper, MappingLookup mappers) {
mapper.validate(mappers);
}

protected MapperMergeContext createChildContext(MapperMergeContext mapperMergeContext, String name) {
return mapperMergeContext.createChildContext(name, dynamic);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,17 @@ public static class Builder extends ObjectMapper.Builder {
protected final Map<String, RuntimeField> runtimeFields = new HashMap<>();
protected Explicit<Boolean> dateDetection = Defaults.DATE_DETECTION;
protected Explicit<Boolean> numericDetection = Defaults.NUMERIC_DETECTION;
protected RootObjectMapperNamespaceValidator namespaceValidator;

public Builder(String name, Optional<Subobjects> subobjects) {
super(name, subobjects);
}

public Builder addNamespaceValidator(RootObjectMapperNamespaceValidator namespaceValidator) {
this.namespaceValidator = namespaceValidator;
return this;
}

public Builder dynamicDateTimeFormatter(Collection<DateFormatter> dateTimeFormatters) {
this.dynamicDateTimeFormatters = new Explicit<>(dateTimeFormatters.toArray(new DateFormatter[0]), true);
return this;
Expand Down Expand Up @@ -120,7 +126,8 @@ public RootObjectMapper build(MapperBuilderContext context) {
dynamicDateTimeFormatters,
dynamicTemplates,
dateDetection,
numericDetection
numericDetection,
namespaceValidator
);
}
}
Expand All @@ -130,6 +137,7 @@ public RootObjectMapper build(MapperBuilderContext context) {
private final Explicit<Boolean> numericDetection;
private final Explicit<DynamicTemplate[]> dynamicTemplates;
private final Map<String, RuntimeField> runtimeFields;
private final RootObjectMapperNamespaceValidator namespaceValidator;

RootObjectMapper(
String name,
Expand All @@ -142,14 +150,16 @@ public RootObjectMapper build(MapperBuilderContext context) {
Explicit<DateFormatter[]> dynamicDateTimeFormatters,
Explicit<DynamicTemplate[]> dynamicTemplates,
Explicit<Boolean> dateDetection,
Explicit<Boolean> numericDetection
Explicit<Boolean> numericDetection,
RootObjectMapperNamespaceValidator namespaceValidator
) {
super(name, name, enabled, subobjects, sourceKeepMode, dynamic, mappers);
this.runtimeFields = runtimeFields;
this.dynamicTemplates = dynamicTemplates;
this.dynamicDateTimeFormatters = dynamicDateTimeFormatters;
this.dateDetection = dateDetection;
this.numericDetection = numericDetection;
this.namespaceValidator = namespaceValidator;
if (sourceKeepMode.orElse(SourceKeepMode.NONE) == SourceKeepMode.ALL) {
throw new MapperParsingException(
"root object can't be configured with [" + Mapper.SYNTHETIC_SOURCE_KEEP_PARAM + ":" + SourceKeepMode.ALL + "]"
Expand Down Expand Up @@ -178,7 +188,8 @@ RootObjectMapper withoutMappers() {
dynamicDateTimeFormatters,
dynamicTemplates,
dateDetection,
numericDetection
numericDetection,
namespaceValidator
);
}

Expand Down Expand Up @@ -294,7 +305,8 @@ public RootObjectMapper merge(Mapper mergeWith, MapperMergeContext parentMergeCo
dynamicDateTimeFormatters,
dynamicTemplates,
dateDetection,
numericDetection
numericDetection,
namespaceValidator
);
}

Expand Down Expand Up @@ -447,10 +459,12 @@ protected boolean isRoot() {
return true;
}

// MP TODO: called from MappingParser.parse
public static RootObjectMapper.Builder parse(String name, Map<String, Object> node, MappingParserContext parserContext)
throws MapperParsingException {
Optional<Subobjects> subobjects = parseSubobjects(node);
RootObjectMapper.Builder builder = new Builder(name, subobjects);
builder.addNamespaceValidator(parserContext.getNamespaceValidator());
Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
Expand Down Expand Up @@ -540,4 +554,12 @@ private static boolean processField(
public int getTotalFieldsCount() {
return super.getTotalFieldsCount() - 1 + runtimeFields.size();
}

@Override
protected void validateSubField(Mapper mapper, MappingLookup mappers) {
if (namespaceValidator != null) {
namespaceValidator.validateNamespace(subobjects(), mapper);
}
super.validateSubField(mapper, mappers);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.index.mapper;

/**
* TODO: DOCUMENT ME
*/
public interface RootObjectMapperNamespaceValidator {

// I'm not sure if there are any use cases for this beyond mine
void validateNamespace(ObjectMapper.Subobjects subobjects, Mapper mapper);
}
13 changes: 11 additions & 2 deletions server/src/main/java/org/elasticsearch/indices/IndicesModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import org.elasticsearch.index.mapper.ObjectMapper;
import org.elasticsearch.index.mapper.PassThroughObjectMapper;
import org.elasticsearch.index.mapper.RangeType;
import org.elasticsearch.index.mapper.RootObjectMapperNamespaceValidator;
import org.elasticsearch.index.mapper.RoutingFieldMapper;
import org.elasticsearch.index.mapper.RuntimeField;
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
Expand Down Expand Up @@ -94,15 +95,23 @@
public class IndicesModule extends AbstractModule {
private final MapperRegistry mapperRegistry;

public IndicesModule(List<MapperPlugin> mapperPlugins) {
public IndicesModule(List<MapperPlugin> mapperPlugins, RootObjectMapperNamespaceValidator namespaceValidator) {
// MP TODO: happily, this is the only place tha the MapperRegistry is created
this.mapperRegistry = new MapperRegistry(
getMappers(mapperPlugins),
getRuntimeFields(mapperPlugins),
getMetadataMappers(mapperPlugins),
getFieldFilter(mapperPlugins)
getFieldFilter(mapperPlugins),
namespaceValidator
);
}

// MP TODO: in prod code: only called from one place: NodeConstruction.construct :yay: [ but called from 26 test/benchmark places :-( ]
// MP TODO: so remove this once all tests have been updated
public IndicesModule(List<MapperPlugin> mapperPlugins) {
this(mapperPlugins, null);
}

public static List<NamedWriteableRegistry.Entry> getNamedWriteables() {
return Arrays.asList(
new NamedWriteableRegistry.Entry(Condition.class, MinAgeCondition.NAME, MinAgeCondition::new),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@
import org.elasticsearch.index.SlowLogFieldProvider;
import org.elasticsearch.index.SlowLogFields;
import org.elasticsearch.index.analysis.AnalysisRegistry;
import org.elasticsearch.index.mapper.DefaultRootObjectMapperNamespaceValidator;
import org.elasticsearch.index.mapper.MapperMetrics;
import org.elasticsearch.index.mapper.RootObjectMapperNamespaceValidator;
import org.elasticsearch.index.mapper.SourceFieldMetrics;
import org.elasticsearch.index.search.stats.ShardSearchPhaseAPMMetrics;
import org.elasticsearch.index.shard.SearchOperationListener;
Expand Down Expand Up @@ -289,6 +291,8 @@ static NodeConstruction prepareConstruction(
constructor.createClientAndRegistries(settingsModule.getSettings(), threadPool, searchModule);
DocumentParsingProvider documentParsingProvider = constructor.getDocumentParsingProvider();

// MP TODO: can we create a NamespaceValidatorProvider and load it here? Then add that to construct.construct below

ScriptService scriptService = constructor.createScriptService(settingsModule, threadPool, serviceProvider);

constructor.createUpdateHelper(scriptService);
Expand Down Expand Up @@ -678,12 +682,22 @@ private void construct(
telemetryProvider.getTracer()
);

// MP TODO: this is where/how multi-project loads the ProjectResolverFactory SPI
// serverless deployments plug-in the multi-project resolver factory
ProjectResolver projectResolver = pluginsService.loadSingletonServiceProvider(
ProjectResolverFactory.class,
() -> ProjectResolverFactory.DEFAULT
).create();
modules.bindToInstance(ProjectResolver.class, projectResolver);

// MP TODO: this is where cross-project needs to load the RootObjectMapperNamespaceValidator SPI
RootObjectMapperNamespaceValidator namespaceValidator = pluginsService.loadSingletonServiceProvider(
RootObjectMapperNamespaceValidator.class,
() -> new DefaultRootObjectMapperNamespaceValidator()
);
modules.bindToInstance(RootObjectMapperNamespaceValidator.class, namespaceValidator);
logger.warn("XXX namespaceValidator loaded: " + namespaceValidator); // MP FIXME remove

ClusterService clusterService = createClusterService(settingsModule, threadPool, taskManager);
clusterService.addStateApplier(scriptService);

Expand Down Expand Up @@ -772,7 +786,10 @@ private void construct(
)::onNewInfo
);

IndicesModule indicesModule = new IndicesModule(pluginsService.filterPlugins(MapperPlugin.class).toList());
// MP TODO: this is the sole place that the IndicesModule is created
// MP TODO: so the question now is - where to load the RootObjectMapperNamespaceValidator SPI from?
// MP TODO: here and pass in *or* in the IndicesModule ctor?
IndicesModule indicesModule = new IndicesModule(pluginsService.filterPlugins(MapperPlugin.class).toList(), namespaceValidator);
modules.add(indicesModule);

modules.add(new GatewayModule());
Expand Down