diff --git a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java index 3d86f71077eee..b9939efc4e650 100644 --- a/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java +++ b/modules/aggs-matrix-stats/src/main/java/org/elasticsearch/search/aggregations/support/ArrayValuesSourceAggregationBuilder.java @@ -195,8 +195,8 @@ protected final ArrayValuesSourceAggregatorFactory doBuild(QueryShardContext que protected Map resolveConfig(QueryShardContext queryShardContext) { HashMap configs = new HashMap<>(); for (String field : fields) { - ValuesSourceConfig config = ValuesSourceConfig.resolve(queryShardContext, userValueTypeHint, field, null, missingMap.get(field), - null, format, CoreValuesSourceType.BYTES, ""); + ValuesSourceConfig config = ValuesSourceConfig.resolveUnregistered(queryShardContext, userValueTypeHint, field, null, + missingMap.get(field), null, format, CoreValuesSourceType.BYTES); configs.put(field, config); } return configs; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java index 7df525fb737d9..86acc021cda04 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/composite/CompositeValuesSourceBuilder.java @@ -277,8 +277,8 @@ protected abstract CompositeValuesSourceConfig innerBuild(QueryShardContext quer ValuesSourceConfig config) throws IOException; public final CompositeValuesSourceConfig build(QueryShardContext queryShardContext) throws IOException { - ValuesSourceConfig config = ValuesSourceConfig.resolve(queryShardContext, - valueType, field, script, null, timeZone(), format, CoreValuesSourceType.BYTES, name()); + ValuesSourceConfig config = ValuesSourceConfig.resolveUnregistered(queryShardContext, + valueType, field, script, null, timeZone(), format, CoreValuesSourceType.BYTES); return innerBuild(queryShardContext, config); } diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java index d622ff79f804c..1c36db7296472 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/MultiValuesSourceAggregationBuilder.java @@ -170,9 +170,8 @@ protected final MultiValuesSourceAggregatorFactory doBuild(QueryShardContext que Builder subFactoriesBuilder) throws IOException { Map configs = new HashMap<>(fields.size()); fields.forEach((key, value) -> { - ValuesSourceConfig config = ValuesSourceConfig.resolve(queryShardContext, userValueTypeHint, - value.getFieldName(), value.getScript(), value.getMissing(), value.getTimeZone(), format, defaultValueSourceType(), - getType()); + ValuesSourceConfig config = ValuesSourceConfig.resolveUnregistered(queryShardContext, userValueTypeHint, + value.getFieldName(), value.getScript(), value.getMissing(), value.getTimeZone(), format, defaultValueSourceType()); configs.put(key, config); }); DocValueFormat docValueFormat = resolveFormat(format, userValueTypeHint, defaultValueSourceType()); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java index fa1cd558f8d09..96bdda5dfb830 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java @@ -20,7 +20,11 @@ import org.elasticsearch.common.Nullable; import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.IndexGeoPointFieldData; +import org.elasticsearch.index.fielddata.IndexHistogramFieldData; +import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.RangeFieldMapper; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.script.AggregationScript; import org.elasticsearch.script.Script; @@ -61,6 +65,50 @@ public static ValuesSourceConfig resolve(QueryShardContext context, String format, ValuesSourceType defaultValueSourceType, String aggregationName) { + + return internalResolve(context, userValueTypeHint, field, script, missing, timeZone, format, defaultValueSourceType, + aggregationName, ValuesSourceConfig::getMappingFromRegistry); + } + + /** + * AKA legacy resolve. This method should be called by aggregations not supported by the {@link ValuesSourceRegistry}, to use the + * pre-registry logic to decide on the {@link ValuesSourceType}. New aggregations which extend from + * {@link ValuesSourceAggregationBuilder} should not use this method, preferring {@link ValuesSourceConfig#resolve} instead. + * + * @param context - the query context + * @param userValueTypeHint - User specified value type; used for missing values and scripts + * @param field - The field being aggregated over. At least one of field and script must not be null + * @param script - The script the user specified. At least one of field and script must not be null + * @param missing - A user specified value to apply when the field is missing. Should be of type userValueTypeHint + * @param timeZone - Used to generate a format for dates + * @param format - The format string to apply to this field. Confusingly, this is used for input parsing as well as output formatting + * See https://github.com/elastic/elasticsearch/issues/47469 + * @param defaultValueSourceType - per-aggregation {@link ValuesSource} of last resort. + * @return - An initialized {@link ValuesSourceConfig} that will yield the appropriate {@link ValuesSourceType} + */ + public static ValuesSourceConfig resolveUnregistered(QueryShardContext context, + ValueType userValueTypeHint, + String field, + Script script, + Object missing, + ZoneId timeZone, + String format, + ValuesSourceType defaultValueSourceType) { + return internalResolve(context, userValueTypeHint, field, script, missing, timeZone, format, defaultValueSourceType, null, + ValuesSourceConfig::getLegacyMapping); + } + + private static ValuesSourceConfig internalResolve(QueryShardContext context, + ValueType userValueTypeHint, + String field, + Script script, + Object missing, + ZoneId timeZone, + String format, + ValuesSourceType defaultValueSourceType, + String aggregationName, + FieldResolver fieldResolver + ) { ValuesSourceConfig config; MappedFieldType fieldType = null; ValuesSourceType valuesSourceType; @@ -105,10 +153,8 @@ public static ValuesSourceConfig resolve(QueryShardContext context, scriptValueType = userValueTypeHint; } } else { - IndexFieldData indexFieldData = context.getForField(fieldType); - valuesSourceType = context.getValuesSourceRegistry().getValuesSourceType(fieldType, aggregationName, indexFieldData, - userValueTypeHint, script, defaultValueSourceType); - + valuesSourceType = fieldResolver.getValuesSourceType(context, fieldType, aggregationName, userValueTypeHint, + defaultValueSourceType); aggregationScript = createScript(script, context); } } @@ -119,6 +165,53 @@ public static ValuesSourceConfig resolve(QueryShardContext context, return config; } + @FunctionalInterface + private interface FieldResolver { + ValuesSourceType getValuesSourceType( + QueryShardContext context, + MappedFieldType fieldType, + String aggregationName, + ValueType userValueTypeHint, + ValuesSourceType defaultValuesSourceType); + + } + + private static ValuesSourceType getMappingFromRegistry( + QueryShardContext context, + MappedFieldType fieldType, + String aggregationName, + ValueType userValueTypeHint, + ValuesSourceType defaultValuesSourceType) { + IndexFieldData indexFieldData = context.getForField(fieldType); + return context.getValuesSourceRegistry().getValuesSourceType(fieldType, aggregationName, indexFieldData, + userValueTypeHint, defaultValuesSourceType); + } + + private static ValuesSourceType getLegacyMapping( + QueryShardContext context, + MappedFieldType fieldType, + String aggregationName, + ValueType userValueTypeHint, + ValuesSourceType defaultValuesSourceType) { + IndexFieldData indexFieldData = context.getForField(fieldType); + if (indexFieldData instanceof IndexNumericFieldData) { + return CoreValuesSourceType.NUMERIC; + } else if (indexFieldData instanceof IndexGeoPointFieldData) { + return CoreValuesSourceType.GEOPOINT; + } else if (fieldType instanceof RangeFieldMapper.RangeFieldType) { + return CoreValuesSourceType.RANGE; + } else if (indexFieldData instanceof IndexHistogramFieldData) { + return CoreValuesSourceType.HISTOGRAM; + } else { + if (userValueTypeHint == null) { + return defaultValuesSourceType; + } else { + return userValueTypeHint.getValuesSourceType(); + } + } + + } + private static AggregationScript.LeafFactory createScript(Script script, QueryShardContext context) { if (script == null) { return null; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java index e60ed6789c1e6..91b86fb50aca8 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java @@ -25,7 +25,6 @@ import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.RangeFieldMapper; import org.elasticsearch.index.query.QueryShardContext; -import org.elasticsearch.script.Script; import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.aggregations.AggregationExecutionException; @@ -141,7 +140,7 @@ public AggregatorSupplier getAggregator(ValuesSourceType valuesSourceType, Strin public ValuesSourceType getValuesSourceType(MappedFieldType fieldType, String aggregationName, // TODO: the following arguments are only needed for the legacy case IndexFieldData indexFieldData, - ValueType valueType, Script script, + ValueType valueType, ValuesSourceType defaultValuesSourceType) { if (aggregationName != null && aggregatorRegistry.containsKey(aggregationName)) { // This will throw if the field doesn't support values sources, although really we probably threw much earlier in that case