Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,73 @@ dummy-zero_count_only | "{""scale"":2,""sum"":0.0,""min"":0.0,""max"":0.0,""
dummy-zero_threshold_only | "{""scale"":0,""sum"":0.0,""zero"":{""threshold"":2.0E-5}}"
;



allAggsGrouped
required_capability: exponential_histogram_minmax_support

FROM exp_histo_sample
| EVAL instance = CASE(STARTS_WITH(instance, "dummy"), "dummy-grouped", instance)
| STATS min = MIN(responseTime), max = MAX(responseTime), p75 = PERCENTILE(responseTime,75) BY instance
| EVAL p75 = ROUND(p75, 7) // rounding to avoid floating point precision issues
| KEEP instance, min, max, p75
| SORT instance
;

instance:keyword | min:double | max:double | p75:double
dummy-grouped | -100.0 | 50.0 | 8.3457089
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-1 | 2.17E-4 | 3.190723 | 0.0016068
instance-2 | 2.2E-4 | 2.744054 | 0.0016068
;



allAggsInlineGrouped
required_capability: exponential_histogram_minmax_support

FROM exp_histo_sample
| INLINE STATS min = MIN(responseTime), max = MAX(responseTime), p75 = PERCENTILE(responseTime,75) BY instance
| EVAL p75 = ROUND(p75, 7) // rounding to avoid floating point precision issues
| KEEP instance, min, max, p75
| SORT instance
| Limit 15
;

instance:keyword | min:double | max:double | p75:double
dummy-empty | null | null | null
dummy-full | -100.0 | 50.0 | 10.6666667
dummy-negative_only | -50.0 | -1.0 | -12.8729318
dummy-no_zero_bucket | -100.0 | 50.0 | 10.6666667
dummy-positive_only | 1.0 | 50.0 | 34.7656715
dummy-zero_count_only | 0.0 | 0.0 | 0.0
dummy-zero_threshold_only | null | null | null
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
instance-0 | 2.4E-4 | 6.786232 | 0.2608237
;



allAggsOnEmptyHistogram
required_capability: exponential_histogram_minmax_support

FROM exp_histo_sample | WHERE instance == "dummy-empty"
| STATS min = MIN(responseTime), max = MAX(responseTime), p75 = PERCENTILE(responseTime,75)
| KEEP min, max, p75
;

min:double | max:double | p75:double
NULL | NULL | NULL
;



ungroupedPercentiles
required_capability: exponential_histogram_percentiles_support

Expand All @@ -27,6 +94,8 @@ p0:double | p50:double | p99:double | p100:double
2.17E-4 | 0.0016965 | 0.9472324 | 6.786232
;



groupedPercentiles
required_capability: exponential_histogram_percentiles_support

Expand All @@ -42,3 +111,62 @@ instance-0 | 2.4E-4 | 0.0211404 | 1.0432946 | 6.786232
instance-1 | 2.17E-4 | 6.469E-4 | 0.1422151 | 3.190723
instance-2 | 2.2E-4 | 6.469E-4 | 0.0857672 | 2.7059714542564097
;



percentileOnEmptyHistogram
required_capability: exponential_histogram_percentiles_support

FROM exp_histo_sample | WHERE instance == "dummy-empty"
| STATS p50 = PERCENTILE(responseTime,50)
| KEEP p50
;

p50:double
NULL
;



ungroupedMinMax
required_capability: exponential_histogram_minmax_support

FROM exp_histo_sample | WHERE NOT STARTS_WITH(instance, "dummy")
| STATS min = MIN(responseTime), max = MAX(responseTime)
| KEEP min, max
;

min:double | max:double
2.17E-4 | 6.786232
;



groupedMinMax
required_capability: exponential_histogram_minmax_support

FROM exp_histo_sample | WHERE NOT STARTS_WITH(instance, "dummy")
| STATS min = MIN(responseTime), max = MAX(responseTime) BY instance
| KEEP instance, min, max
| SORT instance
;

instance:keyword | min:double | max:double
instance-0 | 2.4E-4 | 6.786232
instance-1 | 2.17E-4 | 3.190723
instance-2 | 2.2E-4 | 2.744054
;



minMaxOnEmptyHistogram
required_capability: exponential_histogram_minmax_support

FROM exp_histo_sample | WHERE instance == "dummy-empty"
| STATS min = MIN(responseTime), max = MAX(responseTime)
| KEEP min, max
;

min:double | max:double
NULL | NULL
;
Original file line number Diff line number Diff line change
Expand Up @@ -1562,6 +1562,16 @@ public enum Cap {
*/
EXPONENTIAL_HISTOGRAM_TOPN(EXPONENTIAL_HISTOGRAM_FEATURE_FLAG),

/**
* Support for exponential_histogram type in PERCENTILES aggregation.
*/
EXPONENTIAL_HISTOGRAM_PERCENTILES_SUPPORT(EXPONENTIAL_HISTOGRAM_FEATURE_FLAG),

/**
* Support for exponential_histogram type in MIN/MAX aggregation.
*/
EXPONENTIAL_HISTOGRAM_MINMAX_SUPPORT(EXPONENTIAL_HISTOGRAM_FEATURE_FLAG),

/**
* Create new block when filtering OrdinalBytesRefBlock
*/
Expand Down Expand Up @@ -1652,11 +1662,6 @@ public enum Cap {

FULL_TEXT_FUNCTIONS_ACCEPT_NULL_FIELD,

/**
* Support for exponential_histogram type in PERCENTILES aggregation.
*/
EXPONENTIAL_HISTOGRAM_PERCENTILES_SUPPORT(EXPONENTIAL_HISTOGRAM_FEATURE_FLAG),

/**
* Support for the temporary work to eventually allow FIRST to work with null and multi-value fields, among other things.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.compute.aggregation.MaxIpAggregatorFunctionSupplier;
import org.elasticsearch.compute.aggregation.MaxLongAggregatorFunctionSupplier;
import org.elasticsearch.compute.data.AggregateMetricDoubleBlockBuilder;
import org.elasticsearch.compute.data.ExponentialHistogramBlock;
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Literal;
Expand All @@ -30,6 +31,7 @@
import org.elasticsearch.xpack.esql.expression.function.FunctionType;
import org.elasticsearch.xpack.esql.expression.function.Param;
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.FromAggregateMetricDouble;
import org.elasticsearch.xpack.esql.expression.function.scalar.histogram.ExtractHistogramComponent;
import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMax;
import org.elasticsearch.xpack.esql.planner.ToAggregator;

Expand Down Expand Up @@ -59,7 +61,18 @@ public class Max extends AggregateFunction implements ToAggregator, SurrogateExp
);

@FunctionInfo(
returnType = { "boolean", "double", "integer", "long", "date", "date_nanos", "ip", "keyword", "unsigned_long", "version" },
returnType = {
"boolean",
"double",
"integer",
"long",
"date",
"date_nanos",
"ip",
"keyword",
"unsigned_long",
"version",
"exponential_histogram" },
description = "The maximum value of a field.",
type = FunctionType.AGGREGATE,
examples = {
Expand Down Expand Up @@ -88,7 +101,8 @@ public Max(
"keyword",
"text",
"unsigned_long",
"version" }
"version",
"exponential_histogram" }
) Expression field
) {
this(source, field, Literal.TRUE, NO_WINDOW);
Expand Down Expand Up @@ -126,7 +140,7 @@ public Max replaceChildren(List<Expression> newChildren) {
protected TypeResolution resolveType() {
return TypeResolutions.isType(
field(),
dt -> SUPPLIERS.containsKey(dt) || dt == DataType.AGGREGATE_METRIC_DOUBLE,
dt -> SUPPLIERS.containsKey(dt) || dt == DataType.AGGREGATE_METRIC_DOUBLE || dt == DataType.EXPONENTIAL_HISTOGRAM,
sourceText(),
DEFAULT,
"boolean",
Expand All @@ -135,13 +149,14 @@ protected TypeResolution resolveType() {
"string",
"version",
"aggregate_metric_double",
"exponential_histogram",
"numeric except counter types"
);
}

@Override
public DataType dataType() {
if (field().dataType() == DataType.AGGREGATE_METRIC_DOUBLE) {
if (field().dataType() == DataType.AGGREGATE_METRIC_DOUBLE || field().dataType() == DataType.EXPONENTIAL_HISTOGRAM) {
return DataType.DOUBLE;
}
return field().dataType().noText();
Expand All @@ -167,6 +182,14 @@ public Expression surrogate() {
window()
);
}
if (field().dataType() == DataType.EXPONENTIAL_HISTOGRAM) {
return new Max(
source(),
ExtractHistogramComponent.create(source(), field(), ExponentialHistogramBlock.Component.MAX),
filter(),
window()
);
}
return field().foldable() ? new MvMax(source(), field()) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.compute.aggregation.MinIpAggregatorFunctionSupplier;
import org.elasticsearch.compute.aggregation.MinLongAggregatorFunctionSupplier;
import org.elasticsearch.compute.data.AggregateMetricDoubleBlockBuilder;
import org.elasticsearch.compute.data.ExponentialHistogramBlock;
import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException;
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Literal;
Expand All @@ -30,6 +31,7 @@
import org.elasticsearch.xpack.esql.expression.function.FunctionType;
import org.elasticsearch.xpack.esql.expression.function.Param;
import org.elasticsearch.xpack.esql.expression.function.scalar.convert.FromAggregateMetricDouble;
import org.elasticsearch.xpack.esql.expression.function.scalar.histogram.ExtractHistogramComponent;
import org.elasticsearch.xpack.esql.expression.function.scalar.multivalue.MvMin;
import org.elasticsearch.xpack.esql.planner.ToAggregator;

Expand Down Expand Up @@ -88,7 +90,8 @@ public Min(
"keyword",
"text",
"unsigned_long",
"version" }
"version",
"exponential_histogram" }
) Expression field
) {
this(source, field, Literal.TRUE, NO_WINDOW);
Expand Down Expand Up @@ -126,7 +129,7 @@ public Min withFilter(Expression filter) {
protected TypeResolution resolveType() {
return TypeResolutions.isType(
field(),
dt -> SUPPLIERS.containsKey(dt) || dt == DataType.AGGREGATE_METRIC_DOUBLE,
dt -> SUPPLIERS.containsKey(dt) || dt == DataType.AGGREGATE_METRIC_DOUBLE || dt == DataType.EXPONENTIAL_HISTOGRAM,
sourceText(),
DEFAULT,
"boolean",
Expand All @@ -135,13 +138,14 @@ protected TypeResolution resolveType() {
"string",
"version",
"aggregate_metric_double",
"exponential_histogram",
"numeric except counter types"
);
}

@Override
public DataType dataType() {
if (field().dataType() == DataType.AGGREGATE_METRIC_DOUBLE) {
if (field().dataType() == DataType.AGGREGATE_METRIC_DOUBLE || field().dataType() == DataType.EXPONENTIAL_HISTOGRAM) {
return DataType.DOUBLE;
}
return field().dataType().noText();
Expand All @@ -167,6 +171,14 @@ public Expression surrogate() {
window()
);
}
if (field().dataType() == DataType.EXPONENTIAL_HISTOGRAM) {
return new Min(
source(),
ExtractHistogramComponent.create(source(), field(), ExponentialHistogramBlock.Component.MIN),
filter(),
window()
);
}
return field().foldable() ? new MvMin(source(), field()) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.elasticsearch.xpack.esql.core.expression.Expression;
import org.elasticsearch.xpack.esql.core.expression.Expressions;
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
import org.elasticsearch.xpack.esql.core.expression.Literal;
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
Expand All @@ -37,6 +38,7 @@
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable;
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType;
import static org.elasticsearch.xpack.esql.core.type.DataType.DOUBLE;
import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER;
import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;

/**
Expand Down Expand Up @@ -78,6 +80,10 @@ private ExtractHistogramComponent(StreamInput in) throws IOException {
this(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(Expression.class), in.readNamedWriteable(Expression.class));
}

public static Expression create(Source source, Expression field, ExponentialHistogramBlock.Component component) {
return new ExtractHistogramComponent(source, field, new Literal(source, component.ordinal(), INTEGER));
}

Expression field() {
return field;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1152,7 +1152,8 @@ public void testAggregateOnCounter() {
error("FROM test | STATS min(network.bytes_in)", tsdb),
equalTo(
"1:19: argument of [min(network.bytes_in)] must be"
+ " [boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types],"
+ " [boolean, date, ip, string, version, aggregate_metric_double,"
+ " exponential_histogram or numeric except counter types],"
+ " found value [network.bytes_in] type [counter_long]"
)
);
Expand All @@ -1161,7 +1162,8 @@ public void testAggregateOnCounter() {
error("FROM test | STATS max(network.bytes_in)", tsdb),
equalTo(
"1:19: argument of [max(network.bytes_in)] must be"
+ " [boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types],"
+ " [boolean, date, ip, string, version, aggregate_metric_double, exponential_histogram"
+ " or numeric except counter types],"
+ " found value [network.bytes_in] type [counter_long]"
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerP
false,
validPerPosition,
signature,
(v, p) -> "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types"
(v, p) -> "boolean, date, ip, string, version, aggregate_metric_double, "
+ "exponential_histogram or numeric except counter types"
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ protected Matcher<String> expectedTypeErrorMatcher(List<Set<DataType>> validPerP
false,
validPerPosition,
signature,
(v, p) -> "boolean, date, ip, string, version, aggregate_metric_double or numeric except counter types"
(v, p) -> "boolean, date, ip, string, version, aggregate_metric_double, "
+ "exponential_histogram or numeric except counter types"
)
);
}
Expand Down
Loading