Browse Source

[ES|QL] Add ToAggregateMetricDouble example (#125518)

Adds AggregateMetricDouble to the ES|QL CSV tests and examples of how to
use the ToAggregateMetricDouble function
Larisa Motova 6 months ago
parent
commit
10719831b5

+ 25 - 0
docs/reference/query-languages/esql/_snippets/functions/examples/to_aggregate_metric_double.md

@@ -0,0 +1,25 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Examples**
+
+```esql
+ROW x = 3892095203
+| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)
+```
+
+| x:long | agg_metric:aggregate_metric_double |
+| --- | --- |
+| 3892095203 | {"min":3892095203.0,"max":3892095203.0,"sum":3892095203.0,"value_count":1} |
+
+The expression also accepts multi-values
+
+```esql
+ROW x = [5032, 11111, 40814]
+| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)
+```
+
+| x:integer | agg_metric:aggregate_metric_double |
+| --- | --- |
+| [5032, 11111, 40814] | {"min":5032.0,"max":40814.0,"sum":56957.0,"value_count":3} |
+
+

+ 3 - 0
docs/reference/query-languages/esql/_snippets/functions/layout/to_aggregate_metric_double.md

@@ -21,3 +21,6 @@ product: COMING 9.1
 
 :::{include} ../types/to_aggregate_metric_double.md
 :::
+
+:::{include} ../examples/to_aggregate_metric_double.md
+:::

+ 1 - 1
docs/reference/query-languages/esql/_snippets/functions/parameters/to_aggregate_metric_double.md

@@ -3,5 +3,5 @@
 **Parameters**
 
 `number`
-:   Input value. The input can be a single-valued column or an expression.
+:   Input value. The input can be a single- or multi-valued column or an expression.
 

+ 4 - 0
docs/reference/query-languages/esql/kibana/definition/functions/to_aggregate_metric_double.json

@@ -4,6 +4,10 @@
   "name" : "to_aggregate_metric_double",
   "description" : "Encode a numeric to an aggregate_metric_double.",
   "signatures" : [ ],
+  "examples" : [
+    "ROW x = 3892095203\n| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)",
+    "ROW x = [5032, 11111, 40814]\n| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)"
+  ],
   "preview" : false,
   "snapshot_only" : false
 }

+ 4 - 0
docs/reference/query-languages/esql/kibana/docs/functions/to_aggregate_metric_double.md

@@ -5,3 +5,7 @@ This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../RE
 ### TO_AGGREGATE_METRIC_DOUBLE
 Encode a numeric to an aggregate_metric_double.
 
+```
+ROW x = 3892095203
+| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)
+```

+ 7 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java

@@ -10,6 +10,7 @@ package org.elasticsearch.xpack.esql;
 import org.apache.lucene.util.BytesRef;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.time.DateFormatter;
+import org.elasticsearch.compute.data.AggregateMetricDoubleBlockBuilder;
 import org.elasticsearch.compute.data.Page;
 import org.elasticsearch.logging.Logger;
 import org.elasticsearch.search.DocValueFormat;
@@ -40,6 +41,7 @@ import static org.elasticsearch.xpack.esql.core.util.DateUtils.UTC_DATE_TIME_FOR
 import static org.elasticsearch.xpack.esql.core.util.NumericUtils.unsignedLongAsNumber;
 import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.CARTESIAN;
 import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.GEO;
+import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.aggregateMetricDoubleLiteralToString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.junit.Assert.assertEquals;
@@ -409,6 +411,11 @@ public final class CsvAssert {
             case Type.VERSION -> // convert BytesRef-packed Version to String
                 rebuildExpected(expectedValue, BytesRef.class, x -> new Version((BytesRef) x).toString());
             case UNSIGNED_LONG -> rebuildExpected(expectedValue, Long.class, x -> unsignedLongAsNumber((long) x));
+            case AGGREGATE_METRIC_DOUBLE -> rebuildExpected(
+                expectedValue,
+                AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral.class,
+                x -> aggregateMetricDoubleLiteralToString((AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral) x)
+            );
             default -> expectedValue;
         };
     }

+ 14 - 1
x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvTestUtils.java

@@ -15,6 +15,7 @@ import org.elasticsearch.common.network.InetAddresses;
 import org.elasticsearch.common.time.DateFormatters;
 import org.elasticsearch.common.time.DateUtils;
 import org.elasticsearch.common.util.BigArrays;
+import org.elasticsearch.compute.data.AggregateMetricDoubleBlockBuilder;
 import org.elasticsearch.compute.data.Block;
 import org.elasticsearch.compute.data.BlockFactory;
 import org.elasticsearch.compute.data.BlockUtils;
@@ -62,6 +63,7 @@ import static org.elasticsearch.xpack.esql.core.util.DateUtils.UTC_DATE_TIME_FOR
 import static org.elasticsearch.xpack.esql.core.util.NumericUtils.asLongUnsigned;
 import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.CARTESIAN;
 import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.GEO;
+import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.stringToAggregateMetricDoubleLiteral;
 
 public final class CsvTestUtils {
     private static final int MAX_WIDTH = 80;
@@ -480,6 +482,10 @@ public final class CsvTestUtils {
         CARTESIAN_POINT(x -> x == null ? null : CARTESIAN.wktToWkb(x), BytesRef.class),
         GEO_SHAPE(x -> x == null ? null : GEO.wktToWkb(x), BytesRef.class),
         CARTESIAN_SHAPE(x -> x == null ? null : CARTESIAN.wktToWkb(x), BytesRef.class),
+        AGGREGATE_METRIC_DOUBLE(
+            x -> x == null ? null : stringToAggregateMetricDoubleLiteral(x),
+            AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral.class
+        ),
         UNSUPPORTED(Type::convertUnsupported, Void.class);
 
         private static Void convertUnsupported(String s) {
@@ -560,11 +566,18 @@ public final class CsvTestUtils {
                 case BYTES_REF -> bytesRefBlockType(actualType);
                 case BOOLEAN -> BOOLEAN;
                 case DOC -> throw new IllegalArgumentException("can't assert on doc blocks");
-                case COMPOSITE -> throw new IllegalArgumentException("can't assert on composite blocks");
+                case COMPOSITE -> compositeBlockType(actualType);
                 case UNKNOWN -> throw new IllegalArgumentException("Unknown block types cannot be handled");
             };
         }
 
+        private static Type compositeBlockType(Type actualType) {
+            return switch (actualType) {
+                case AGGREGATE_METRIC_DOUBLE -> actualType;
+                default -> throw new IllegalArgumentException("can't assert on composite blocks that aren't aggregate metric doubles");
+            };
+        }
+
         private static Type bytesRefBlockType(Type actualType) {
             return switch (actualType) {
                 case NULL -> NULL;

+ 28 - 0
x-pack/plugin/esql/qa/testFixtures/src/main/resources/convert.csv-spec

@@ -453,3 +453,31 @@ emp_no:integer  | birth_date:datetime
 10097           | 1952-02-27T00:00:00.000Z
 10100           | 1953-04-21T00:00:00.000Z
 ;
+
+convertToAggregateMetricDouble
+required_capability: aggregate_metric_double_convert_to
+//tag::toAggregateMetricDouble[]
+ROW x = 3892095203
+| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)
+//end::toAggregateMetricDouble[]
+;
+
+//tag::toAggregateMetricDouble-result[]
+x:long     | agg_metric:aggregate_metric_double
+3892095203 | {"min":3892095203.0,"max":3892095203.0,"sum":3892095203.0,"value_count":1}
+//end::toAggregateMetricDouble-result[]
+;
+
+convertToAggregateMetricDoubleMv
+required_capability: aggregate_metric_double_convert_to
+//tag::toAggregateMetricDoubleMv[]
+ROW x = [5032, 11111, 40814]
+| EVAL agg_metric = TO_AGGREGATE_METRIC_DOUBLE(x)
+//end::toAggregateMetricDoubleMv[]
+;
+
+//tag::toAggregateMetricDoubleMv-result[]
+x:integer            | agg_metric:aggregate_metric_double
+[5032, 11111, 40814] | {"min":5032.0,"max":40814.0,"sum":56957.0,"value_count":3}
+//end::toAggregateMetricDoubleMv-result[]
+;

+ 5 - 1
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToAggregateMetricDouble.java

@@ -30,6 +30,7 @@ import org.elasticsearch.xpack.esql.core.expression.Expression;
 import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
 import org.elasticsearch.xpack.esql.core.tree.Source;
 import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.expression.function.Example;
 import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo;
 import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle;
 import org.elasticsearch.xpack.esql.expression.function.FunctionInfo;
@@ -67,6 +68,9 @@ public class ToAggregateMetricDouble extends AbstractConvertFunction {
     @FunctionInfo(
         returnType = "aggregate_metric_double",
         description = "Encode a numeric to an aggregate_metric_double.",
+        examples = {
+            @Example(file = "convert", tag = "toAggregateMetricDouble"),
+            @Example(description = "The expression also accepts multi-values", file = "convert", tag = "toAggregateMetricDoubleMv") },
         appliesTo = { @FunctionAppliesTo(lifeCycle = FunctionAppliesToLifecycle.COMING, version = "9.1") }
     )
     public ToAggregateMetricDouble(
@@ -74,7 +78,7 @@ public class ToAggregateMetricDouble extends AbstractConvertFunction {
         @Param(
             name = "number",
             type = { "double", "long", "unsigned_long", "integer", "aggregate_metric_double" },
-            description = "Input value. The input can be a single-valued column or an expression."
+            description = "Input value. The input can be a single- or multi-valued column or an expression."
         ) Expression field
     ) {
         super(source, field);

+ 55 - 0
x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/type/EsqlDataTypeConverter.java

@@ -16,6 +16,7 @@ import org.elasticsearch.common.lucene.BytesRefs;
 import org.elasticsearch.common.time.DateFormatter;
 import org.elasticsearch.common.time.DateFormatters;
 import org.elasticsearch.common.time.DateUtils;
+import org.elasticsearch.compute.data.AggregateMetricDoubleBlockBuilder;
 import org.elasticsearch.compute.data.AggregateMetricDoubleBlockBuilder.Metric;
 import org.elasticsearch.compute.data.CompositeBlock;
 import org.elasticsearch.compute.data.DoubleBlock;
@@ -701,6 +702,60 @@ public class EsqlDataTypeConverter {
         }
     }
 
+    public static String aggregateMetricDoubleLiteralToString(AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral aggMetric) {
+        try (XContentBuilder builder = JsonXContent.contentBuilder()) {
+            builder.startObject();
+            if (aggMetric.min() != null) {
+                builder.field(Metric.MIN.getLabel(), aggMetric.min());
+            }
+            if (aggMetric.max() != null) {
+                builder.field(Metric.MAX.getLabel(), aggMetric.max());
+            }
+            if (aggMetric.sum() != null) {
+                builder.field(Metric.SUM.getLabel(), aggMetric.sum());
+            }
+            if (aggMetric.count() != null) {
+                builder.field(Metric.COUNT.getLabel(), aggMetric.count());
+            }
+            builder.endObject();
+            return Strings.toString(builder);
+        } catch (IOException e) {
+            throw new IllegalStateException("error rendering aggregate metric double", e);
+        }
+    }
+
+    public static AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral stringToAggregateMetricDoubleLiteral(String s) {
+        Double min = null;
+        Double max = null;
+        Double sum = null;
+        Integer count = null;
+        String[] values = s.substring(1, s.length() - 1).split(",");
+        for (String v : values) {
+            var pair = v.split(":");
+            String type = pair[0];
+            String number = pair[1];
+            switch (type) {
+                case "min":
+                    min = Double.parseDouble(number);
+                    break;
+                case "max":
+                    max = Double.parseDouble(number);
+                    break;
+                case "sum":
+                    sum = Double.parseDouble(number);
+                    break;
+                case "value_count":
+                    count = Integer.parseInt(number);
+                    break;
+                default:
+                    throw new IllegalArgumentException(
+                        "Received a metric that wasn't min, max, sum, or value_count: " + type + " with value: " + number
+                    );
+            }
+        }
+        return new AggregateMetricDoubleBlockBuilder.AggregateMetricDoubleLiteral(min, max, sum, count);
+    }
+
     public enum EsqlConverter implements Converter {
 
         STRING_TO_DATE_PERIOD(x -> EsqlDataTypeConverter.parseTemporalAmount(x, DataType.DATE_PERIOD)),