-
Notifications
You must be signed in to change notification settings - Fork 25.7k
Split RuntimeFieldType from corresponding MappedFieldType #70695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,180 +8,53 @@ | |
|
|
||
| package org.elasticsearch.index.mapper; | ||
|
|
||
| import org.apache.lucene.analysis.TokenStream; | ||
| import org.apache.lucene.search.MultiTermQuery; | ||
| import org.apache.lucene.search.Query; | ||
| import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; | ||
| import org.apache.lucene.search.spans.SpanQuery; | ||
| import org.elasticsearch.ElasticsearchException; | ||
| import org.elasticsearch.common.CheckedBiConsumer; | ||
| import org.elasticsearch.common.geo.ShapeRelation; | ||
| import org.elasticsearch.common.time.DateMathParser; | ||
| import org.elasticsearch.common.unit.Fuzziness; | ||
| import org.elasticsearch.common.xcontent.ToXContentFragment; | ||
| import org.elasticsearch.common.xcontent.XContentBuilder; | ||
| import org.elasticsearch.index.query.SearchExecutionContext; | ||
|
|
||
| import java.io.IOException; | ||
| import java.time.ZoneId; | ||
| import java.util.Collections; | ||
| import java.util.HashMap; | ||
| import java.util.Iterator; | ||
| import java.util.List; | ||
| import java.util.Locale; | ||
| import java.util.Map; | ||
| import java.util.function.BiFunction; | ||
|
|
||
| import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES; | ||
|
|
||
| /** | ||
| * Base implementation for a runtime field that can be defined as part of the runtime section of the index mappings | ||
| * Definition of a runtime field that can be defined as part of the runtime section of the index mappings | ||
| */ | ||
| public abstract class RuntimeFieldType extends MappedFieldType implements ToXContentFragment { | ||
|
|
||
| private final CheckedBiConsumer<XContentBuilder, Boolean, IOException> toXContent; | ||
|
|
||
| protected RuntimeFieldType(String name, RuntimeFieldType.Builder builder) { | ||
| this(name, builder.meta(), builder::toXContent); | ||
| } | ||
|
|
||
| protected RuntimeFieldType(String name, Map<String, String> meta, CheckedBiConsumer<XContentBuilder, Boolean, IOException> toXContent) { | ||
| super(name, false, false, false, TextSearchInfo.SIMPLE_MATCH_WITHOUT_TERMS, meta); | ||
| this.toXContent = toXContent; | ||
| } | ||
| public interface RuntimeFieldType extends ToXContentFragment { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @romseygeek looks familiar? :) I did take inspiration from your previous proposal around aliases as runtime fields.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would propose renaming this to |
||
|
|
||
| @Override | ||
| public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
| default XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { | ||
| builder.startObject(name()); | ||
| builder.field("type", typeName()); | ||
| boolean includeDefaults = params.paramAsBoolean("include_defaults", false); | ||
| doXContentBody(builder, includeDefaults); | ||
| doXContentBody(builder, params); | ||
| builder.endObject(); | ||
| return builder; | ||
| } | ||
|
|
||
| /** | ||
| * Prints out the parameters that subclasses expose | ||
| */ | ||
| final void doXContentBody(XContentBuilder builder, boolean includeDefaults) throws IOException { | ||
| toXContent.accept(builder, includeDefaults); | ||
| } | ||
|
|
||
| @Override | ||
| public final boolean isSearchable() { | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public final boolean isAggregatable() { | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public final Query rangeQuery( | ||
| Object lowerTerm, | ||
| Object upperTerm, | ||
| boolean includeLower, | ||
| boolean includeUpper, | ||
| ShapeRelation relation, | ||
| ZoneId timeZone, | ||
| DateMathParser parser, | ||
| SearchExecutionContext context | ||
| ) { | ||
| if (relation == ShapeRelation.DISJOINT) { | ||
| String message = "Runtime field [%s] of type [%s] does not support DISJOINT ranges"; | ||
| throw new IllegalArgumentException(String.format(Locale.ROOT, message, name(), typeName())); | ||
| } | ||
| return rangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, parser, context); | ||
| } | ||
|
|
||
| protected abstract Query rangeQuery( | ||
| Object lowerTerm, | ||
| Object upperTerm, | ||
| boolean includeLower, | ||
| boolean includeUpper, | ||
| ZoneId timeZone, | ||
| DateMathParser parser, | ||
| SearchExecutionContext context | ||
| ); | ||
|
|
||
| @Override | ||
| public Query fuzzyQuery( | ||
| Object value, | ||
| Fuzziness fuzziness, | ||
| int prefixLength, | ||
| int maxExpansions, | ||
| boolean transpositions, | ||
| SearchExecutionContext context | ||
| ) { | ||
| throw new IllegalArgumentException(unsupported("fuzzy", "keyword and text")); | ||
| } | ||
|
|
||
| @Override | ||
| public Query prefixQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, SearchExecutionContext context) { | ||
| throw new IllegalArgumentException(unsupported("prefix", "keyword, text and wildcard")); | ||
| } | ||
|
|
||
| @Override | ||
| public Query wildcardQuery(String value, MultiTermQuery.RewriteMethod method, boolean caseInsensitive, SearchExecutionContext context) { | ||
| throw new IllegalArgumentException(unsupported("wildcard", "keyword, text and wildcard")); | ||
| } | ||
| void doXContentBody(XContentBuilder builder, Params params) throws IOException; | ||
|
|
||
| @Override | ||
| public Query regexpQuery( | ||
| String value, | ||
| int syntaxFlags, | ||
| int matchFlags, | ||
| int maxDeterminizedStates, | ||
| MultiTermQuery.RewriteMethod method, | ||
| SearchExecutionContext context | ||
| ) { | ||
| throw new IllegalArgumentException(unsupported("regexp", "keyword and text")); | ||
| } | ||
|
|
||
| @Override | ||
| public Query phraseQuery(TokenStream stream, int slop, boolean enablePositionIncrements) { | ||
| throw new IllegalArgumentException(unsupported("phrase", "text")); | ||
| } | ||
|
|
||
| @Override | ||
| public Query multiPhraseQuery(TokenStream stream, int slop, boolean enablePositionIncrements) { | ||
| throw new IllegalArgumentException(unsupported("phrase", "text")); | ||
| } | ||
|
|
||
| @Override | ||
| public Query phrasePrefixQuery(TokenStream stream, int slop, int maxExpansions) { | ||
| throw new IllegalArgumentException(unsupported("phrase prefix", "text")); | ||
| } | ||
|
|
||
| @Override | ||
| public SpanQuery spanPrefixQuery(String value, SpanMultiTermQueryWrapper.SpanRewriteMethod method, SearchExecutionContext context) { | ||
| throw new IllegalArgumentException(unsupported("span prefix", "text")); | ||
| } | ||
|
|
||
| private String unsupported(String query, String supported) { | ||
| return String.format( | ||
| Locale.ROOT, | ||
| "Can only use %s queries on %s fields - not on [%s] which is a runtime field of type [%s]", | ||
| query, | ||
| supported, | ||
| name(), | ||
| typeName() | ||
| ); | ||
| } | ||
| /** | ||
| * Exposes the name of the runtime field | ||
| * @return name of the field | ||
| */ | ||
| String name(); | ||
|
|
||
| protected final void checkAllowExpensiveQueries(SearchExecutionContext context) { | ||
| if (context.allowExpensiveQueries() == false) { | ||
| throw new ElasticsearchException( | ||
| "queries cannot be executed against runtime fields while [" + ALLOW_EXPENSIVE_QUERIES.getKey() + "] is set to [false]." | ||
| ); | ||
| } | ||
| } | ||
| /** | ||
| * Exposes the type of the runtime field | ||
| * @return type of the field | ||
| */ | ||
| String typeName(); | ||
|
|
||
| @Override | ||
| public final ValueFetcher valueFetcher(SearchExecutionContext context, String format) { | ||
| return new DocValueFetcher(docValueFormat(format, null), context.getForField(this)); | ||
| } | ||
| /** | ||
| * Exposes the {@link MappedFieldType} backing this runtime field, used to execute queries, run aggs etc. | ||
| * @return the {@link MappedFieldType} backing this runtime field | ||
| */ | ||
| MappedFieldType asMappedFieldType(); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I envision this returning a Collection once we introduce the object runtime field |
||
|
|
||
| /** | ||
| * For runtime fields the {@link RuntimeFieldType.Parser} returns directly the {@link MappedFieldType}. | ||
|
|
@@ -191,7 +64,7 @@ public final ValueFetcher valueFetcher(SearchExecutionContext context, String fo | |
| * {@link RuntimeFieldType.Builder#parse(String, Mapper.TypeParser.ParserContext, Map)} and returns the corresponding | ||
| * {@link MappedFieldType}. | ||
| */ | ||
| public abstract static class Builder extends FieldMapper.Builder { | ||
| abstract class Builder extends FieldMapper.Builder { | ||
| final FieldMapper.Parameter<Map<String, String>> meta = FieldMapper.Parameter.metaParam(); | ||
|
|
||
| protected Builder(String name) { | ||
|
|
@@ -236,7 +109,7 @@ private void validate() { | |
| * Parser for a runtime field. Creates the appropriate {@link RuntimeFieldType} for a runtime field, | ||
| * as defined in the runtime section of the index mappings. | ||
| */ | ||
| public static final class Parser { | ||
| final class Parser { | ||
| private final BiFunction<String, Mapper.TypeParser.ParserContext, RuntimeFieldType.Builder> builderFunction; | ||
|
|
||
| public Parser(BiFunction<String, Mapper.TypeParser.ParserContext, RuntimeFieldType.Builder> builderFunction) { | ||
|
|
@@ -261,9 +134,9 @@ RuntimeFieldType parse(String name, Map<String, Object> node, Mapper.TypeParser. | |
| * translated to the removal of such runtime field | ||
| * @return the parsed runtime fields | ||
| */ | ||
| public static Map<String, RuntimeFieldType> parseRuntimeFields(Map<String, Object> node, | ||
| Mapper.TypeParser.ParserContext parserContext, | ||
| boolean supportsRemoval) { | ||
| static Map<String, RuntimeFieldType> parseRuntimeFields(Map<String, Object> node, | ||
| Mapper.TypeParser.ParserContext parserContext, | ||
| boolean supportsRemoval) { | ||
| Map<String, RuntimeFieldType> runtimeFields = new HashMap<>(); | ||
| Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); | ||
| while (iterator.hasNext()) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -106,7 +106,7 @@ public class SearchExecutionContext extends QueryRewriteContext { | |
| private boolean mapUnmappedFieldAsString; | ||
| private NestedScope nestedScope; | ||
| private final ValuesSourceRegistry valuesSourceRegistry; | ||
| private final Map<String, RuntimeFieldType> runtimeMappings; | ||
| private final Map<String, MappedFieldType> runtimeMappings; | ||
|
|
||
| /** | ||
| * Build a {@linkplain SearchExecutionContext}. | ||
|
|
@@ -199,7 +199,7 @@ private SearchExecutionContext(int shardId, | |
| Index fullyQualifiedIndex, | ||
| BooleanSupplier allowExpensiveQueries, | ||
| ValuesSourceRegistry valuesSourceRegistry, | ||
| Map<String, RuntimeFieldType> runtimeMappings) { | ||
| Map<String, MappedFieldType> runtimeMappings) { | ||
| super(xContentRegistry, namedWriteableRegistry, client, nowInMillis); | ||
| this.shardId = shardId; | ||
| this.shardRequestIndex = shardRequestIndex; | ||
|
|
@@ -608,11 +608,18 @@ public Index getFullyQualifiedIndex() { | |
| return fullyQualifiedIndex; | ||
| } | ||
|
|
||
| private static Map<String, RuntimeFieldType> parseRuntimeMappings(Map<String, Object> runtimeMappings, MapperService mapperService) { | ||
| private static Map<String, MappedFieldType> parseRuntimeMappings(Map<String, Object> runtimeMappings, MapperService mapperService) { | ||
| if (runtimeMappings.isEmpty()) { | ||
| return Collections.emptyMap(); | ||
| } | ||
| return RuntimeFieldType.parseRuntimeFields(new HashMap<>(runtimeMappings), mapperService.parserContext(), false); | ||
| Map<String, RuntimeFieldType> runtimeFields = RuntimeFieldType.parseRuntimeFields(new HashMap<>(runtimeMappings), | ||
| mapperService.parserContext(), false); | ||
| Map<String, MappedFieldType> runtimeFieldTypes = new HashMap<>(); | ||
| for (RuntimeFieldType runtimeFieldType : runtimeFields.values()) { | ||
| MappedFieldType fieldType = runtimeFieldType.asMappedFieldType(); | ||
| runtimeFieldTypes.put(fieldType.name(), fieldType); | ||
| } | ||
| return Collections.unmodifiableMap(runtimeFieldTypes); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking that maybe we should make FieldTypeLookup public and use it in SearchExecutionContext instead of reproducing its behavour with the runtime fields defined in the search request |
||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not extremely important, at the same time I got frustrated that we were carrying around a CheckedBiConsumer while we have a commonly used interface for this sort of things. This change allows carrying a ToXContent instance instead in the different script field types