diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/description/clamp.md new file mode 100644 index 0000000000000..5d7d56931af88 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/description/clamp.md @@ -0,0 +1,6 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Description** + +Clamps the values of all samples to have a lower limit of min and an upper limit of max. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md new file mode 100644 index 0000000000000..d2cc12be612cf --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md @@ -0,0 +1,6 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Description** + +Returns clamps the values of all input samples clamped to have an upper limit of max. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md new file mode 100644 index 0000000000000..5fa0df5c1ce4f --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md @@ -0,0 +1,6 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Description** + +Returns clamps the values of all input samples clamped to have a lower limit of min. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md new file mode 100644 index 0000000000000..754111da85fc3 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md @@ -0,0 +1,20 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Example** + +```esql +TS k8s +| STATS bytes_in = sum(network.bytes_in), + // our network cards are rate-limited to 200bps so we clamp to that (just an example) + clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200)) + BY time_bucket = bucket(@timestamp,1minute) +``` + +| bytes_in:long | clamped_network_bytes_in:long | time_bucket:datetime | +| --- | --- | --- | +| 1849 | 600 | 2024-05-10T00:00:00.000Z | +| 2334 | 800 | 2024-05-10T00:01:00.000Z | +| 3487 | 1178 | 2024-05-10T00:02:00.000Z | +| 2941 | 861 | 2024-05-10T00:03:00.000Z | + + diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md new file mode 100644 index 0000000000000..d4d25c611aa85 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md @@ -0,0 +1,19 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Example** + +```esql +TS k8s +| EVAL full_clamped_cost = clamp(network.cost, 1, 20) +| KEEP full_clamped_cost, @timestamp +``` + +| full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime | +| --- | --- | --- | --- | +| 18.0 | 9.0 | 94.875 | 2024-05-10T00:09:00.000Z | +| 15.25 | 8.0 | 84.125 | 2024-05-10T00:08:00.000Z | +| 15.0 | 8.0 | 83.5 | 2024-05-10T00:15:00.000Z | +| 13.75 | 7.0 | 71.625 | 2024-05-10T00:22:00.000Z | +| 13.125 | 7.5 | 90.5 | 2024-05-10T00:18:00.000Z | + + diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md new file mode 100644 index 0000000000000..2d4edc73a8e96 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md @@ -0,0 +1,17 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Example** + +```esql +FROM k8s +| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute) +``` + +| full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime | +| --- | --- | --- | --- | +| 39.0 | 20.0 | 206.25 | 2024-05-10T00:09:00.000Z | +| 29.125 | 15.5 | 173.0 | 2024-05-10T00:18:00.000Z | +| 28.0 | 14.125 | 155.625 | 2024-05-10T00:17:00.000Z | +| 23.25 | 12.0 | 124.875 | 2024-05-10T00:08:00.000Z | + + diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md new file mode 100644 index 0000000000000..1e99c49038c2e --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md @@ -0,0 +1,23 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +## `CLAMP` [esql-clamp] + +**Syntax** + +:::{image} ../../../images/functions/clamp.svg +:alt: Embedded +:class: text-center +::: + + +:::{include} ../parameters/clamp.md +::: + +:::{include} ../description/clamp.md +::: + +:::{include} ../types/clamp.md +::: + +:::{include} ../examples/clamp.md +::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md new file mode 100644 index 0000000000000..05c6daafb4f83 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md @@ -0,0 +1,23 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +## `CLAMP_MAX` [esql-clamp_max] + +**Syntax** + +:::{image} ../../../images/functions/clamp_max.svg +:alt: Embedded +:class: text-center +::: + + +:::{include} ../parameters/clamp_max.md +::: + +:::{include} ../description/clamp_max.md +::: + +:::{include} ../types/clamp_max.md +::: + +:::{include} ../examples/clamp_max.md +::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md new file mode 100644 index 0000000000000..d592eab4faf8c --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md @@ -0,0 +1,23 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +## `CLAMP_MIN` [esql-clamp_min] + +**Syntax** + +:::{image} ../../../images/functions/clamp_min.svg +:alt: Embedded +:class: text-center +::: + + +:::{include} ../parameters/clamp_min.md +::: + +:::{include} ../description/clamp_min.md +::: + +:::{include} ../types/clamp_min.md +::: + +:::{include} ../examples/clamp_min.md +::: diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md new file mode 100644 index 0000000000000..68bfe9eeba491 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md @@ -0,0 +1,13 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Parameters** + +`field` +: Numeric expression. If `null`, the function returns `null`. + +`min` +: The min value to clamp data into. + +`max` +: The max value to clamp data into. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md new file mode 100644 index 0000000000000..9137ac5aee8db --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md @@ -0,0 +1,10 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Parameters** + +`field` +: field to clamp. + +`max` +: The max value to clamp data into. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md new file mode 100644 index 0000000000000..b878673b0a99a --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md @@ -0,0 +1,10 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Parameters** + +`field` +: field to clamp. + +`min` +: The min value to clamp data into. + diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/clamp.md b/docs/reference/query-languages/esql/_snippets/functions/types/clamp.md new file mode 100644 index 0000000000000..5b76a0978788b --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/types/clamp.md @@ -0,0 +1,16 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Supported types** + +| field | min | max | result | +| --- | --- | --- | --- | +| boolean | boolean | boolean | boolean | +| date | date | date | date | +| double | double | double | double | +| integer | integer | integer | integer | +| ip | ip | ip | ip | +| keyword | keyword | keyword | keyword | +| long | long | long | long | +| unsigned_long | unsigned_long | unsigned_long | unsigned_long | +| version | version | version | version | + diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md new file mode 100644 index 0000000000000..3a3af0e670f7b --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md @@ -0,0 +1,16 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Supported types** + +| field | max | result | +| --- | --- | --- | +| boolean | boolean | boolean | +| date | date | date | +| double | double | double | +| integer | integer | integer | +| ip | ip | ip | +| keyword | keyword | keyword | +| long | long | long | +| unsigned_long | unsigned_long | unsigned_long | +| version | version | version | + diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md new file mode 100644 index 0000000000000..0100d810fea52 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md @@ -0,0 +1,16 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Supported types** + +| field | min | result | +| --- | --- | --- | +| boolean | boolean | boolean | +| date | date | date | +| double | double | double | +| integer | integer | integer | +| ip | ip | ip | +| keyword | keyword | keyword | +| long | long | long | +| unsigned_long | unsigned_long | unsigned_long | +| version | version | version | + diff --git a/docs/reference/query-languages/esql/images/functions/clamp.svg b/docs/reference/query-languages/esql/images/functions/clamp.svg new file mode 100644 index 0000000000000..36cb2c79c0f63 --- /dev/null +++ b/docs/reference/query-languages/esql/images/functions/clamp.svg @@ -0,0 +1 @@ +CLAMP(field,min,max) \ No newline at end of file diff --git a/docs/reference/query-languages/esql/images/functions/clamp_max.svg b/docs/reference/query-languages/esql/images/functions/clamp_max.svg new file mode 100644 index 0000000000000..77db4f490a5ec --- /dev/null +++ b/docs/reference/query-languages/esql/images/functions/clamp_max.svg @@ -0,0 +1 @@ +CLAMP_MAX(field,max) \ No newline at end of file diff --git a/docs/reference/query-languages/esql/images/functions/clamp_min.svg b/docs/reference/query-languages/esql/images/functions/clamp_min.svg new file mode 100644 index 0000000000000..982dc156cc458 --- /dev/null +++ b/docs/reference/query-languages/esql/images/functions/clamp_min.svg @@ -0,0 +1 @@ +CLAMP_MIN(field,min) \ No newline at end of file diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/clamp.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp.json new file mode 100644 index 0000000000000..8650cb54878fe --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp.json @@ -0,0 +1,229 @@ +{ + "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.", + "type" : "scalar", + "name" : "clamp", + "description" : "Clamps the values of all samples to have a lower limit of min and an upper limit of max.", + "signatures" : [ + { + "params" : [ + { + "name" : "field", + "type" : "boolean", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "boolean", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "boolean", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "field", + "type" : "date", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "date", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "date", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "date" + }, + { + "params" : [ + { + "name" : "field", + "type" : "double", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "double", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "double", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "integer", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "integer", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "integer", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "integer" + }, + { + "params" : [ + { + "name" : "field", + "type" : "ip", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "ip", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "ip", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "ip" + }, + { + "params" : [ + { + "name" : "field", + "type" : "keyword", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "keyword", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "keyword", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "keyword" + }, + { + "params" : [ + { + "name" : "field", + "type" : "long", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "long", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "long", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "unsigned_long", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "unsigned_long", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "unsigned_long", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "unsigned_long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "version", + "optional" : false, + "description" : "Numeric expression. If `null`, the function returns `null`." + }, + { + "name" : "min", + "type" : "version", + "optional" : false, + "description" : "The min value to clamp data into." + }, + { + "name" : "max", + "type" : "version", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "version" + } + ], + "examples" : [ + "TS k8s\n| STATS bytes_in = sum(network.bytes_in),\n // our network cards are rate-limited to 200bps so we clamp to that (just an example)\n clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200))\n BY time_bucket = bucket(@timestamp,1minute)" + ], + "preview" : false, + "snapshot_only" : false +} diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json new file mode 100644 index 0000000000000..594cf22e0c931 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json @@ -0,0 +1,175 @@ +{ + "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.", + "type" : "scalar", + "name" : "clamp_max", + "description" : "Returns clamps the values of all input samples clamped to have an upper limit of max.", + "signatures" : [ + { + "params" : [ + { + "name" : "field", + "type" : "boolean", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "boolean", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "field", + "type" : "date", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "date", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "date" + }, + { + "params" : [ + { + "name" : "field", + "type" : "double", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "double", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "integer", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "integer", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "integer" + }, + { + "params" : [ + { + "name" : "field", + "type" : "ip", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "ip", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "ip" + }, + { + "params" : [ + { + "name" : "field", + "type" : "keyword", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "keyword", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "keyword" + }, + { + "params" : [ + { + "name" : "field", + "type" : "long", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "long", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "unsigned_long", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "unsigned_long", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "unsigned_long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "version", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "max", + "type" : "version", + "optional" : false, + "description" : "The max value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "version" + } + ], + "examples" : [ + "TS k8s\n| EVAL full_clamped_cost = clamp(network.cost, 1, 20)\n| KEEP full_clamped_cost, @timestamp" + ], + "preview" : false, + "snapshot_only" : false +} diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json new file mode 100644 index 0000000000000..5572b05f3a9bb --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json @@ -0,0 +1,175 @@ +{ + "comment" : "This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it.", + "type" : "scalar", + "name" : "clamp_min", + "description" : "Returns clamps the values of all input samples clamped to have a lower limit of min.", + "signatures" : [ + { + "params" : [ + { + "name" : "field", + "type" : "boolean", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "boolean", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "field", + "type" : "date", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "date", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "date" + }, + { + "params" : [ + { + "name" : "field", + "type" : "double", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "double", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "double" + }, + { + "params" : [ + { + "name" : "field", + "type" : "integer", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "integer", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "integer" + }, + { + "params" : [ + { + "name" : "field", + "type" : "ip", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "ip", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "ip" + }, + { + "params" : [ + { + "name" : "field", + "type" : "keyword", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "keyword", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "keyword" + }, + { + "params" : [ + { + "name" : "field", + "type" : "long", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "long", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "unsigned_long", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "unsigned_long", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "unsigned_long" + }, + { + "params" : [ + { + "name" : "field", + "type" : "version", + "optional" : false, + "description" : "field to clamp." + }, + { + "name" : "min", + "type" : "version", + "optional" : false, + "description" : "The min value to clamp data into." + } + ], + "variadic" : false, + "returnType" : "version" + } + ], + "examples" : [ + "FROM k8s\n| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute)" + ], + "preview" : false, + "snapshot_only" : false +} diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/clamp.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp.md new file mode 100644 index 0000000000000..b4e3300f8e3b9 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp.md @@ -0,0 +1,12 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +### CLAMP +Clamps the values of all samples to have a lower limit of min and an upper limit of max. + +```esql +TS k8s +| STATS bytes_in = sum(network.bytes_in), + // our network cards are rate-limited to 200bps so we clamp to that (just an example) + clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200)) + BY time_bucket = bucket(@timestamp,1minute) +``` diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md new file mode 100644 index 0000000000000..793a219d5a682 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md @@ -0,0 +1,10 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +### CLAMP MAX +Returns clamps the values of all input samples clamped to have an upper limit of max. + +```esql +TS k8s +| EVAL full_clamped_cost = clamp(network.cost, 1, 20) +| KEEP full_clamped_cost, @timestamp +``` diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md new file mode 100644 index 0000000000000..c267ec1aac292 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md @@ -0,0 +1,9 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +### CLAMP MIN +Returns clamps the values of all input samples clamped to have a lower limit of min. + +```esql +FROM k8s +| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute) +``` diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec new file mode 100644 index 0000000000000..ccfdffff2745d --- /dev/null +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec @@ -0,0 +1,100 @@ +clamp_of_double_no_grouping +required_capability: ts_command_v0 +required_capability: clamp_functions +// tag::clamp-min[] +FROM k8s +| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute) +// end::clamp-min[] +| SORT full_clamped_cost DESC, time_bucket DESC | LIMIT 10; + +// tag::clamp-min-result[] +full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime +39.0 | 20.0 | 206.25 | 2024-05-10T00:09:00.000Z +29.125 | 15.5 | 173.0 | 2024-05-10T00:18:00.000Z +28.0 | 14.125 | 155.625 | 2024-05-10T00:17:00.000Z +23.25 | 12.0 | 124.875 | 2024-05-10T00:08:00.000Z +// end::clamp-min-result[] +20.625 | 11.0 | 113.5 | 2024-05-10T00:15:00.000Z +19.0 | 9.625 | 105.375 | 2024-05-10T00:06:00.000Z +17.75 | 9.0 | 91.625 | 2024-05-10T00:22:00.000Z +17.625 | 9.0 | 91.25 | 2024-05-10T00:13:00.000Z +16.375 | 8.625 | 100.0 | 2024-05-10T00:20:00.000Z +16.0 | 8.125 | 91.25 | 2024-05-10T00:14:00.000Z + +; + +clamp_of_double_no_grouping +required_capability: ts_command_v0 +required_capability: clamp_functions +// tag::clamp-max[] +TS k8s +| STATS full_clamped_cost=sum(clamp(network.cost, 1, 2)), clamped_cost=sum(clamp_max(network.cost, 1)), clamped_min_cost=sum(clamp_min(network.cost, 10)) BY time_bucket = bucket(@timestamp,1minute) +// end::clamp-max[] +| SORT full_clamped_cost DESC, time_bucket DESC | LIMIT 10; + +// tag::clamp-max-result[] +full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_bucket:datetime +18.0 | 9.0 | 94.875 | 2024-05-10T00:09:00.000Z +15.25 | 8.0 | 84.125 | 2024-05-10T00:08:00.000Z +15.0 | 8.0 | 83.5 | 2024-05-10T00:15:00.000Z +13.75 | 7.0 | 71.625 | 2024-05-10T00:22:00.000Z +13.125 | 7.5 | 90.5 | 2024-05-10T00:18:00.000Z +// end::clamp-max-result[] +13.0 | 6.5 | 75.625 | 2024-05-10T00:17:00.000Z +12.0 | 5.875 | 70.0 | 2024-05-10T00:20:00.000Z +12.0 | 6.125 | 70.0 | 2024-05-10T00:14:00.000Z +12.0 | 6.0 | 61.25 | 2024-05-10T00:13:00.000Z +12.0 | 6.0 | 60.0 | 2024-05-10T00:02:00.000Z + +; + +clamp_of_long +required_capability: ts_command_v0 +required_capability: clamp_functions +// tag::clamp[] +TS k8s +| STATS bytes_in = sum(network.bytes_in), + // our network cards are rate-limited to 200bps so we clamp to that (just an example) + clamped_network_bytes_in = sum(clamp(network.bytes_in, 0, 200)) + BY time_bucket = bucket(@timestamp,1minute) +// end::clamp[] +| SORT time_bucket, bytes_in DESC | LIMIT 10; + +// tag::clamp-result[] +bytes_in:long | clamped_network_bytes_in:long | time_bucket:datetime +1849 | 600 | 2024-05-10T00:00:00.000Z +2334 | 800 | 2024-05-10T00:01:00.000Z +3487 | 1178 | 2024-05-10T00:02:00.000Z +2941 | 861 | 2024-05-10T00:03:00.000Z +// end::clamp-result[] +1557 | 403 | 2024-05-10T00:04:00.000Z +1144 | 434 | 2024-05-10T00:05:00.000Z +3594 | 1200 | 2024-05-10T00:06:00.000Z +1353 | 401 | 2024-05-10T00:07:00.000Z +3188 | 1295 | 2024-05-10T00:08:00.000Z +3850 | 1468 | 2024-05-10T00:09:00.000Z + +; + +clamp_without_stats_command +required_capability: clamp_functions +// tag::clamp[] +TS k8s +| EVAL full_clamped_cost = clamp(network.cost, 1, 20) +| KEEP full_clamped_cost, @timestamp +// end::clamp-max[] +| SORT full_clamped_cost DESC, @timestamp | LIMIT 10; + +// tag::clamp-max-result[] +full_clamped_cost:double | @timestamp:datetime +12.5 | 2024-05-10T00:09:52.000Z +12.375 | 2024-05-10T00:01:29.000Z +12.375 | 2024-05-10T00:06:42.000Z +12.375 | 2024-05-10T00:17:12.000Z +12.25 | 2024-05-10T00:19:48.000Z +12.125 | 2024-05-10T00:00:57.000Z +12.125 | 2024-05-10T00:03:26.000Z +12.125 | 2024-05-10T00:07:19.000Z +12.125 | 2024-05-10T00:17:39.000Z +12.0 | 2024-05-10T00:08:08.000Z +; diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java new file mode 100644 index 0000000000000..8a828c781e6db --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BooleanBlock; +import org.elasticsearch.compute.data.BooleanVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMax}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMaxBooleanEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxBooleanEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator max; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMaxBooleanEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator max, DriverContext driverContext) { + this.source = source; + this.field = field; + this.max = max; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (BooleanBlock fieldBlock = (BooleanBlock) field.eval(page)) { + try (BooleanBlock maxBlock = (BooleanBlock) max.eval(page)) { + BooleanVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + BooleanVector maxVector = maxBlock.asVector(); + if (maxVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + return eval(page.getPositionCount(), fieldVector, maxVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += max.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public BooleanBlock eval(int positionCount, BooleanBlock fieldBlock, BooleanBlock maxBlock) { + try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (maxBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + boolean field = fieldBlock.getBoolean(fieldBlock.getFirstValueIndex(p)); + boolean max = maxBlock.getBoolean(maxBlock.getFirstValueIndex(p)); + result.appendBoolean(ClampMax.process(field, max)); + } + return result.build(); + } + } + + public BooleanVector eval(int positionCount, BooleanVector fieldVector, BooleanVector maxVector) { + try(BooleanVector.FixedBuilder result = driverContext.blockFactory().newBooleanVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + boolean field = fieldVector.getBoolean(p); + boolean max = maxVector.getBoolean(p); + result.appendBoolean(p, ClampMax.process(field, max)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMaxBooleanEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, max); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory max; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory max) { + this.source = source; + this.field = field; + this.max = max; + } + + @Override + public ClampMaxBooleanEvaluator get(DriverContext context) { + return new ClampMaxBooleanEvaluator(source, field.get(context), max.get(context), context); + } + + @Override + public String toString() { + return "ClampMaxBooleanEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java new file mode 100644 index 0000000000000..38d47429edcc5 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java @@ -0,0 +1,167 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMax}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMaxBytesRefEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxBytesRefEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator max; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMaxBytesRefEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator max, DriverContext driverContext) { + this.source = source; + this.field = field; + this.max = max; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (BytesRefBlock fieldBlock = (BytesRefBlock) field.eval(page)) { + try (BytesRefBlock maxBlock = (BytesRefBlock) max.eval(page)) { + BytesRefVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + BytesRefVector maxVector = maxBlock.asVector(); + if (maxVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + return eval(page.getPositionCount(), fieldVector, maxVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += max.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public BytesRefBlock eval(int positionCount, BytesRefBlock fieldBlock, BytesRefBlock maxBlock) { + try(BytesRefBlock.Builder result = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) { + BytesRef fieldScratch = new BytesRef(); + BytesRef maxScratch = new BytesRef(); + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (maxBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + BytesRef field = fieldBlock.getBytesRef(fieldBlock.getFirstValueIndex(p), fieldScratch); + BytesRef max = maxBlock.getBytesRef(maxBlock.getFirstValueIndex(p), maxScratch); + result.appendBytesRef(ClampMax.process(field, max)); + } + return result.build(); + } + } + + public BytesRefVector eval(int positionCount, BytesRefVector fieldVector, + BytesRefVector maxVector) { + try(BytesRefVector.Builder result = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) { + BytesRef fieldScratch = new BytesRef(); + BytesRef maxScratch = new BytesRef(); + position: for (int p = 0; p < positionCount; p++) { + BytesRef field = fieldVector.getBytesRef(p, fieldScratch); + BytesRef max = maxVector.getBytesRef(p, maxScratch); + result.appendBytesRef(ClampMax.process(field, max)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMaxBytesRefEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, max); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory max; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory max) { + this.source = source; + this.field = field; + this.max = max; + } + + @Override + public ClampMaxBytesRefEvaluator get(DriverContext context) { + return new ClampMaxBytesRefEvaluator(source, field.get(context), max.get(context), context); + } + + @Override + public String toString() { + return "ClampMaxBytesRefEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java new file mode 100644 index 0000000000000..f931e15956179 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMax}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMaxDoubleEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxDoubleEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator max; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMaxDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator max, DriverContext driverContext) { + this.source = source; + this.field = field; + this.max = max; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + try (DoubleBlock maxBlock = (DoubleBlock) max.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + DoubleVector maxVector = maxBlock.asVector(); + if (maxVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + return eval(page.getPositionCount(), fieldVector, maxVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += max.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock, DoubleBlock maxBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (maxBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + double field = fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)); + double max = maxBlock.getDouble(maxBlock.getFirstValueIndex(p)); + result.appendDouble(ClampMax.process(field, max)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector, DoubleVector maxVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + double field = fieldVector.getDouble(p); + double max = maxVector.getDouble(p); + result.appendDouble(p, ClampMax.process(field, max)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMaxDoubleEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, max); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory max; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory max) { + this.source = source; + this.field = field; + this.max = max; + } + + @Override + public ClampMaxDoubleEvaluator get(DriverContext context) { + return new ClampMaxDoubleEvaluator(source, field.get(context), max.get(context), context); + } + + @Override + public String toString() { + return "ClampMaxDoubleEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java new file mode 100644 index 0000000000000..96624136cf39d --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMax}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMaxIntEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxIntEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator max; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMaxIntEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator max, DriverContext driverContext) { + this.source = source; + this.field = field; + this.max = max; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + try (IntBlock maxBlock = (IntBlock) max.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + IntVector maxVector = maxBlock.asVector(); + if (maxVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + return eval(page.getPositionCount(), fieldVector, maxVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += max.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock maxBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + if (maxBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (maxBlock.getValueCount(p) != 1) { + if (maxBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)); + int max = maxBlock.getInt(maxBlock.getFirstValueIndex(p)); + result.appendInt(ClampMax.process(field, max)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector, IntVector maxVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + int field = fieldVector.getInt(p); + int max = maxVector.getInt(p); + result.appendInt(p, ClampMax.process(field, max)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMaxIntEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, max); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory max; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory max) { + this.source = source; + this.field = field; + this.max = max; + } + + @Override + public ClampMaxIntEvaluator get(DriverContext context) { + return new ClampMaxIntEvaluator(source, field.get(context), max.get(context), context); + } + + @Override + public String toString() { + return "ClampMaxIntEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java new file mode 100644 index 0000000000000..7a60af399851d --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMax}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMaxIntegerEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxIntegerEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator max; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMaxIntegerEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator max, DriverContext driverContext) { + this.source = source; + this.field = field; + this.max = max; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + try (IntBlock maxBlock = (IntBlock) max.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + IntVector maxVector = maxBlock.asVector(); + if (maxVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + return eval(page.getPositionCount(), fieldVector, maxVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += max.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock maxBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (maxBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)); + int max = maxBlock.getInt(maxBlock.getFirstValueIndex(p)); + result.appendInt(ClampMax.process(field, max)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector, IntVector maxVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + int field = fieldVector.getInt(p); + int max = maxVector.getInt(p); + result.appendInt(p, ClampMax.process(field, max)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMaxIntegerEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, max); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory max; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory max) { + this.source = source; + this.field = field; + this.max = max; + } + + @Override + public ClampMaxIntegerEvaluator get(DriverContext context) { + return new ClampMaxIntegerEvaluator(source, field.get(context), max.get(context), context); + } + + @Override + public String toString() { + return "ClampMaxIntegerEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java new file mode 100644 index 0000000000000..21f5b65ef871e --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMax}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMaxLongEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMaxLongEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator max; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMaxLongEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator max, DriverContext driverContext) { + this.source = source; + this.field = field; + this.max = max; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + try (LongBlock maxBlock = (LongBlock) max.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + LongVector maxVector = maxBlock.asVector(); + if (maxVector == null) { + return eval(page.getPositionCount(), fieldBlock, maxBlock); + } + return eval(page.getPositionCount(), fieldVector, maxVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += max.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock, LongBlock maxBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (maxBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + long field = fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)); + long max = maxBlock.getLong(maxBlock.getFirstValueIndex(p)); + result.appendLong(ClampMax.process(field, max)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector, LongVector maxVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + long field = fieldVector.getLong(p); + long max = maxVector.getLong(p); + result.appendLong(p, ClampMax.process(field, max)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMaxLongEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, max); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory max; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory max) { + this.source = source; + this.field = field; + this.max = max; + } + + @Override + public ClampMaxLongEvaluator get(DriverContext context) { + return new ClampMaxLongEvaluator(source, field.get(context), max.get(context), context); + } + + @Override + public String toString() { + return "ClampMaxLongEvaluator[" + "field=" + field + ", max=" + max + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java new file mode 100644 index 0000000000000..ce4fc04004e9a --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BooleanBlock; +import org.elasticsearch.compute.data.BooleanVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMin}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMinBooleanEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinBooleanEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator min; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMinBooleanEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator min, DriverContext driverContext) { + this.source = source; + this.field = field; + this.min = min; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (BooleanBlock fieldBlock = (BooleanBlock) field.eval(page)) { + try (BooleanBlock minBlock = (BooleanBlock) min.eval(page)) { + BooleanVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + BooleanVector minVector = minBlock.asVector(); + if (minVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + return eval(page.getPositionCount(), fieldVector, minVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += min.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public BooleanBlock eval(int positionCount, BooleanBlock fieldBlock, BooleanBlock minBlock) { + try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (minBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + boolean field = fieldBlock.getBoolean(fieldBlock.getFirstValueIndex(p)); + boolean min = minBlock.getBoolean(minBlock.getFirstValueIndex(p)); + result.appendBoolean(ClampMin.process(field, min)); + } + return result.build(); + } + } + + public BooleanVector eval(int positionCount, BooleanVector fieldVector, BooleanVector minVector) { + try(BooleanVector.FixedBuilder result = driverContext.blockFactory().newBooleanVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + boolean field = fieldVector.getBoolean(p); + boolean min = minVector.getBoolean(p); + result.appendBoolean(p, ClampMin.process(field, min)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMinBooleanEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, min); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory min; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory min) { + this.source = source; + this.field = field; + this.min = min; + } + + @Override + public ClampMinBooleanEvaluator get(DriverContext context) { + return new ClampMinBooleanEvaluator(source, field.get(context), min.get(context), context); + } + + @Override + public String toString() { + return "ClampMinBooleanEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java new file mode 100644 index 0000000000000..474aedc4306f8 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java @@ -0,0 +1,167 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.BytesRefBlock; +import org.elasticsearch.compute.data.BytesRefVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMin}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMinBytesRefEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinBytesRefEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator min; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMinBytesRefEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator min, DriverContext driverContext) { + this.source = source; + this.field = field; + this.min = min; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (BytesRefBlock fieldBlock = (BytesRefBlock) field.eval(page)) { + try (BytesRefBlock minBlock = (BytesRefBlock) min.eval(page)) { + BytesRefVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + BytesRefVector minVector = minBlock.asVector(); + if (minVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + return eval(page.getPositionCount(), fieldVector, minVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += min.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public BytesRefBlock eval(int positionCount, BytesRefBlock fieldBlock, BytesRefBlock minBlock) { + try(BytesRefBlock.Builder result = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) { + BytesRef fieldScratch = new BytesRef(); + BytesRef minScratch = new BytesRef(); + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (minBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + BytesRef field = fieldBlock.getBytesRef(fieldBlock.getFirstValueIndex(p), fieldScratch); + BytesRef min = minBlock.getBytesRef(minBlock.getFirstValueIndex(p), minScratch); + result.appendBytesRef(ClampMin.process(field, min)); + } + return result.build(); + } + } + + public BytesRefVector eval(int positionCount, BytesRefVector fieldVector, + BytesRefVector minVector) { + try(BytesRefVector.Builder result = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) { + BytesRef fieldScratch = new BytesRef(); + BytesRef minScratch = new BytesRef(); + position: for (int p = 0; p < positionCount; p++) { + BytesRef field = fieldVector.getBytesRef(p, fieldScratch); + BytesRef min = minVector.getBytesRef(p, minScratch); + result.appendBytesRef(ClampMin.process(field, min)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMinBytesRefEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, min); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory min; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory min) { + this.source = source; + this.field = field; + this.min = min; + } + + @Override + public ClampMinBytesRefEvaluator get(DriverContext context) { + return new ClampMinBytesRefEvaluator(source, field.get(context), min.get(context), context); + } + + @Override + public String toString() { + return "ClampMinBytesRefEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java new file mode 100644 index 0000000000000..ea7a91d42e23f --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.DoubleBlock; +import org.elasticsearch.compute.data.DoubleVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMin}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMinDoubleEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinDoubleEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator min; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMinDoubleEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator min, DriverContext driverContext) { + this.source = source; + this.field = field; + this.min = min; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (DoubleBlock fieldBlock = (DoubleBlock) field.eval(page)) { + try (DoubleBlock minBlock = (DoubleBlock) min.eval(page)) { + DoubleVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + DoubleVector minVector = minBlock.asVector(); + if (minVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + return eval(page.getPositionCount(), fieldVector, minVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += min.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public DoubleBlock eval(int positionCount, DoubleBlock fieldBlock, DoubleBlock minBlock) { + try(DoubleBlock.Builder result = driverContext.blockFactory().newDoubleBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (minBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + double field = fieldBlock.getDouble(fieldBlock.getFirstValueIndex(p)); + double min = minBlock.getDouble(minBlock.getFirstValueIndex(p)); + result.appendDouble(ClampMin.process(field, min)); + } + return result.build(); + } + } + + public DoubleVector eval(int positionCount, DoubleVector fieldVector, DoubleVector minVector) { + try(DoubleVector.FixedBuilder result = driverContext.blockFactory().newDoubleVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + double field = fieldVector.getDouble(p); + double min = minVector.getDouble(p); + result.appendDouble(p, ClampMin.process(field, min)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMinDoubleEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, min); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory min; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory min) { + this.source = source; + this.field = field; + this.min = min; + } + + @Override + public ClampMinDoubleEvaluator get(DriverContext context) { + return new ClampMinDoubleEvaluator(source, field.get(context), min.get(context), context); + } + + @Override + public String toString() { + return "ClampMinDoubleEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java new file mode 100644 index 0000000000000..d650458e2d3d6 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMin}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMinIntEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinIntEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator min; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMinIntEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator min, DriverContext driverContext) { + this.source = source; + this.field = field; + this.min = min; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + try (IntBlock minBlock = (IntBlock) min.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + IntVector minVector = minBlock.asVector(); + if (minVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + return eval(page.getPositionCount(), fieldVector, minVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += min.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock minBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + if (fieldBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (fieldBlock.getValueCount(p) != 1) { + if (fieldBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + if (minBlock.isNull(p)) { + result.appendNull(); + continue position; + } + if (minBlock.getValueCount(p) != 1) { + if (minBlock.getValueCount(p) > 1) { + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + } + result.appendNull(); + continue position; + } + int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)); + int min = minBlock.getInt(minBlock.getFirstValueIndex(p)); + result.appendInt(ClampMin.process(field, min)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector, IntVector minVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + int field = fieldVector.getInt(p); + int min = minVector.getInt(p); + result.appendInt(p, ClampMin.process(field, min)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMinIntEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, min); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory min; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory min) { + this.source = source; + this.field = field; + this.min = min; + } + + @Override + public ClampMinIntEvaluator get(DriverContext context) { + return new ClampMinIntEvaluator(source, field.get(context), min.get(context), context); + } + + @Override + public String toString() { + return "ClampMinIntEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java new file mode 100644 index 0000000000000..a8ec2068bd571 --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.IntBlock; +import org.elasticsearch.compute.data.IntVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMin}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMinIntegerEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinIntegerEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator min; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMinIntegerEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator min, DriverContext driverContext) { + this.source = source; + this.field = field; + this.min = min; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (IntBlock fieldBlock = (IntBlock) field.eval(page)) { + try (IntBlock minBlock = (IntBlock) min.eval(page)) { + IntVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + IntVector minVector = minBlock.asVector(); + if (minVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + return eval(page.getPositionCount(), fieldVector, minVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += min.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public IntBlock eval(int positionCount, IntBlock fieldBlock, IntBlock minBlock) { + try(IntBlock.Builder result = driverContext.blockFactory().newIntBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (minBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + int field = fieldBlock.getInt(fieldBlock.getFirstValueIndex(p)); + int min = minBlock.getInt(minBlock.getFirstValueIndex(p)); + result.appendInt(ClampMin.process(field, min)); + } + return result.build(); + } + } + + public IntVector eval(int positionCount, IntVector fieldVector, IntVector minVector) { + try(IntVector.FixedBuilder result = driverContext.blockFactory().newIntVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + int field = fieldVector.getInt(p); + int min = minVector.getInt(p); + result.appendInt(p, ClampMin.process(field, min)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMinIntegerEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, min); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory min; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory min) { + this.source = source; + this.field = field; + this.min = min; + } + + @Override + public ClampMinIntegerEvaluator get(DriverContext context) { + return new ClampMinIntegerEvaluator(source, field.get(context), min.get(context), context); + } + + @Override + public String toString() { + return "ClampMinIntegerEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java new file mode 100644 index 0000000000000..39912eaf269fb --- /dev/null +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java @@ -0,0 +1,161 @@ +// 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; you may not use this file except in compliance with the Elastic License +// 2.0. +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import java.lang.IllegalArgumentException; +import java.lang.Override; +import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; +import org.elasticsearch.compute.data.Block; +import org.elasticsearch.compute.data.LongBlock; +import org.elasticsearch.compute.data.LongVector; +import org.elasticsearch.compute.data.Page; +import org.elasticsearch.compute.operator.DriverContext; +import org.elasticsearch.compute.operator.EvalOperator; +import org.elasticsearch.compute.operator.Warnings; +import org.elasticsearch.core.Releasables; +import org.elasticsearch.xpack.esql.core.tree.Source; + +/** + * {@link EvalOperator.ExpressionEvaluator} implementation for {@link ClampMin}. + * This class is generated. Edit {@code EvaluatorImplementer} instead. + */ +public final class ClampMinLongEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(ClampMinLongEvaluator.class); + + private final Source source; + + private final EvalOperator.ExpressionEvaluator field; + + private final EvalOperator.ExpressionEvaluator min; + + private final DriverContext driverContext; + + private Warnings warnings; + + public ClampMinLongEvaluator(Source source, EvalOperator.ExpressionEvaluator field, + EvalOperator.ExpressionEvaluator min, DriverContext driverContext) { + this.source = source; + this.field = field; + this.min = min; + this.driverContext = driverContext; + } + + @Override + public Block eval(Page page) { + try (LongBlock fieldBlock = (LongBlock) field.eval(page)) { + try (LongBlock minBlock = (LongBlock) min.eval(page)) { + LongVector fieldVector = fieldBlock.asVector(); + if (fieldVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + LongVector minVector = minBlock.asVector(); + if (minVector == null) { + return eval(page.getPositionCount(), fieldBlock, minBlock); + } + return eval(page.getPositionCount(), fieldVector, minVector).asBlock(); + } + } + } + + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += field.baseRamBytesUsed(); + baseRamBytesUsed += min.baseRamBytesUsed(); + return baseRamBytesUsed; + } + + public LongBlock eval(int positionCount, LongBlock fieldBlock, LongBlock minBlock) { + try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + switch (fieldBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + switch (minBlock.getValueCount(p)) { + case 0: + result.appendNull(); + continue position; + case 1: + break; + default: + warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value")); + result.appendNull(); + continue position; + } + long field = fieldBlock.getLong(fieldBlock.getFirstValueIndex(p)); + long min = minBlock.getLong(minBlock.getFirstValueIndex(p)); + result.appendLong(ClampMin.process(field, min)); + } + return result.build(); + } + } + + public LongVector eval(int positionCount, LongVector fieldVector, LongVector minVector) { + try(LongVector.FixedBuilder result = driverContext.blockFactory().newLongVectorFixedBuilder(positionCount)) { + position: for (int p = 0; p < positionCount; p++) { + long field = fieldVector.getLong(p); + long min = minVector.getLong(p); + result.appendLong(p, ClampMin.process(field, min)); + } + return result.build(); + } + } + + @Override + public String toString() { + return "ClampMinLongEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + + @Override + public void close() { + Releasables.closeExpectNoException(field, min); + } + + private Warnings warnings() { + if (warnings == null) { + this.warnings = Warnings.createWarnings( + driverContext.warningsMode(), + source.source().getLineNumber(), + source.source().getColumnNumber(), + source.text() + ); + } + return warnings; + } + + static class Factory implements EvalOperator.ExpressionEvaluator.Factory { + private final Source source; + + private final EvalOperator.ExpressionEvaluator.Factory field; + + private final EvalOperator.ExpressionEvaluator.Factory min; + + public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory field, + EvalOperator.ExpressionEvaluator.Factory min) { + this.source = source; + this.field = field; + this.min = min; + } + + @Override + public ClampMinLongEvaluator get(DriverContext context) { + return new ClampMinLongEvaluator(source, field.get(context), min.get(context), context); + } + + @Override + public String toString() { + return "ClampMinLongEvaluator[" + "field=" + field + ", min=" + min + "]"; + } + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java index e80ebee86ffc6..b431acd0cd03b 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java @@ -1135,6 +1135,7 @@ public enum Cap { */ INCREASE, DELTA_TS_AGG, + CLAMP_FUNCTIONS, /** * Resolve groupings before resolving references to groupings in the aggregations. diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java index 3c8ffb45585db..c6520c8563d6d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java @@ -64,7 +64,10 @@ import org.elasticsearch.xpack.esql.expression.function.grouping.Categorize; import org.elasticsearch.xpack.esql.expression.function.grouping.TBucket; import org.elasticsearch.xpack.esql.expression.function.inference.TextEmbedding; +import org.elasticsearch.xpack.esql.expression.function.scalar.Clamp; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case; +import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMax; +import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMin; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Greatest; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Least; import org.elasticsearch.xpack.esql.expression.function.scalar.convert.FromBase64; @@ -341,6 +344,7 @@ private static FunctionDefinition[][] functions() { // use casting to disambiguate between the two new FunctionDefinition[] { def(Avg.class, uni(Avg::new), "avg"), + def(Clamp.class, tri(Clamp::new), "clamp"), def(Count.class, uni(Count::new), "count"), def(CountDistinct.class, bi(CountDistinct::new), "count_distinct"), def(Max.class, uni(Max::new), "max"), @@ -376,6 +380,8 @@ private static FunctionDefinition[][] functions() { def(Log.class, Log::new, "log"), def(Log10.class, Log10::new, "log10"), def(Least.class, Least::new, "least"), + def(ClampMax.class, ClampMax::new, "clamp_max"), + def(ClampMin.class, ClampMin::new, "clamp_min"), def(Pi.class, Pi::new, "pi"), def(Pow.class, Pow::new, "pow"), def(Round.class, Round::new, "round"), diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java new file mode 100644 index 0000000000000..21c3271163202 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java @@ -0,0 +1,138 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar; + +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; +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.SurrogateExpression; +import org.elasticsearch.xpack.esql.expression.function.Example; +import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; +import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMax; +import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMin; + +import java.io.IOException; +import java.util.List; + +import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; + +/** + * Clamps the values of all samples to have a lower limit of min and an upper limit of max. + */ +public class Clamp extends EsqlScalarFunction implements SurrogateExpression { + private final Expression field; + private final Expression min; + private final Expression max; + + @FunctionInfo( + returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "Clamps the values of all samples to have a lower limit of min and an upper limit of max.", + examples = { @Example(file = "k8s-timeseries-clamp", tag = "clamp") } + ) + public Clamp( + Source source, + @Param( + name = "field", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "Numeric expression. If `null`, the function returns `null`." + ) Expression field, + @Param( + name = "min", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "The min value to clamp data into." + ) Expression min, + @Param( + name = "max", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "The max value to clamp data into." + ) Expression max + ) { + super(source, List.of(field, min, max)); + this.field = field; + this.min = min; + this.max = max; + } + + @Override + public String getWriteableName() { + throw new UnsupportedOperationException("Clamp does not support serialization."); + } + + @Override + protected TypeResolution resolveType() { + if (childrenResolved() == false) { + return new TypeResolution("Unresolved children"); + } + + var field = children().get(0); + var fieldDataType = field.dataType().noText(); + TypeResolution resolution = TypeResolutions.isType( + field, + t -> t.isNumeric() || t == DataType.BOOLEAN || t.isDate() || DataType.isString(t) || t == DataType.IP || t == DataType.VERSION, + sourceText(), + TypeResolutions.ParamOrdinal.FIRST, + fieldDataType.typeName() + ); + if (resolution.unresolved()) { + return resolution; + } + if (fieldDataType == NULL) { + return new TypeResolution("'field' must not be null in clamp()"); + } + for (Expression child : List.of(children().get(1), children().get(2))) { + var childRes = TypeResolutions.isType( + child, + t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType, + sourceText(), + child == children().get(1) ? TypeResolutions.ParamOrdinal.SECOND : TypeResolutions.ParamOrdinal.THIRD, + fieldDataType.typeName() + ); + if (childRes.unresolved()) { + return childRes; + } + } + return TypeResolution.TYPE_RESOLVED; + } + + @Override + public DataType dataType() { + return field.dataType(); + } + + @Override + public Expression replaceChildren(List newChildren) { + return new Clamp(source(), newChildren.get(0), newChildren.get(1), newChildren.get(2)); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, Clamp::new, field, children().get(1), children().get(2)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + throw new UnsupportedOperationException("Clamp does not support serialization."); + } + + @Override + public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { + throw new UnsupportedOperationException( + "Clamp should have been replaced by ClampMin and ClampMax. Something went wrong in the compute engine." + ); + } + + @Override + public Expression surrogate() { + return new ClampMax(source(), new ClampMin(source(), field, min), max); + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java index 978f0218a798f..961d577692aa0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ScalarFunctionWritables.java @@ -10,6 +10,8 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.xpack.esql.expression.function.grouping.GroupingWritables; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Case; +import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMax; +import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.ClampMin; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Greatest; import org.elasticsearch.xpack.esql.expression.function.scalar.conditional.Least; import org.elasticsearch.xpack.esql.expression.function.scalar.convert.FromAggregateMetricDouble; @@ -90,6 +92,8 @@ public static List getNamedWriteables() { entries.add(MonthName.ENTRY); entries.add(IpPrefix.ENTRY); entries.add(Least.ENTRY); + entries.add(ClampMax.ENTRY); + entries.add(ClampMin.ENTRY); entries.add(Left.ENTRY); entries.add(Locate.ENTRY); entries.add(Log.ENTRY); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java new file mode 100644 index 0000000000000..1d9c7b2c703d8 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java @@ -0,0 +1,186 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.Expressions; +import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; +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.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; +import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction; +import org.elasticsearch.xpack.esql.expression.function.scalar.math.Cast; +import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; +import org.elasticsearch.xpack.esql.planner.PlannerUtils; + +import java.io.IOException; +import java.util.List; + +import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; + +/** + * Clamps the input values to have an upper limit of max. + */ +public class ClampMax extends EsqlScalarFunction { + public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "ClampMax", ClampMax::new); + + @FunctionInfo( + returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "Returns clamps the values of all input samples clamped to have an upper limit of max.", + examples = @Example(file = "k8s-timeseries-clamp", tag = "clamp-max") + ) + public ClampMax( + Source source, + @Param( + name = "field", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "field to clamp." + ) Expression field, + @Param( + name = "max", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "The max value to clamp data into." + ) Expression max + ) { + super(source, List.of(field, max)); + } + + private ClampMax(StreamInput in) throws IOException { + this(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(Expression.class), in.readNamedWriteable(Expression.class)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + source().writeTo(out); + out.writeNamedWriteable(children().get(0)); + out.writeNamedWriteable(children().get(1)); + } + + @Override + public String getWriteableName() { + return ENTRY.name; + } + + @Override + public DataType dataType() { + return children().getFirst().dataType(); + } + + @Override + protected TypeResolution resolveType() { + if (childrenResolved() == false) { + return new TypeResolution("Unresolved children"); + } + + var field = children().get(0); + var min = children().get(1); + var fieldDataType = field.dataType(); + TypeResolution resolution = TypeResolutions.isType( + field, + t -> t.isNumeric() || t == DataType.BOOLEAN || t.isDate() || DataType.isString(t) || t == DataType.IP || t == DataType.VERSION, + sourceText(), + TypeResolutions.ParamOrdinal.FIRST, + fieldDataType.typeName() + ); + if (resolution.unresolved()) { + return resolution; + } + if (fieldDataType == NULL) { + return new TypeResolution("'field' must not be null in clamp()"); + } + resolution = TypeResolutions.isType( + min, + t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType, + sourceText(), + TypeResolutions.ParamOrdinal.SECOND, + fieldDataType.typeName() + ); + if (resolution.unresolved()) { + return resolution; + } + return TypeResolution.TYPE_RESOLVED; + } + + @Override + public Expression replaceChildren(List newChildren) { + return new ClampMax(source(), newChildren.get(0), newChildren.get(1)); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ClampMax::new, children().get(0), children().get(1)); + } + + @Override + public boolean foldable() { + return Expressions.foldable(children()); + } + + @Override + public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { + // force datatype initialization + var outputType = dataType(); + + var max = children().get(1); + var maxF = outputType != max.dataType() + ? Cast.cast(source(), max.dataType(), outputType, toEvaluator.apply(max)) + : toEvaluator.apply(max); + + return switch (PlannerUtils.toElementType(outputType)) { + case BOOLEAN -> new ClampMaxBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + case DOUBLE -> new ClampMaxDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + case INT -> new ClampMaxIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + case LONG -> new ClampMaxLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + case BYTES_REF -> new ClampMaxBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + default -> throw EsqlIllegalArgumentException.illegalDataType(outputType); + }; + } + + @Evaluator(extraName = "Boolean") + static boolean process(boolean field, boolean max) { + if (max == false) { + return false; + } else { + return field; + } + } + + @Evaluator(extraName = "BytesRef") + static BytesRef process(BytesRef field, BytesRef max) { + if (field.compareTo(max) > 0) { + return max; + } else { + return field; + } + } + + @Evaluator(extraName = "Integer") + static int process(int field, int max) { + return Math.min(field, max); + } + + @Evaluator(extraName = "Long") + static long process(long field, long max) { + return Math.min(field, max); + } + + @Evaluator(extraName = "Double") + static double process(double field, double max) { + return Math.min(field, max); + } +} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java new file mode 100644 index 0000000000000..0ecdddb678059 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java @@ -0,0 +1,182 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.io.stream.NamedWriteableRegistry; +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.compute.ann.Evaluator; +import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; +import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.Expressions; +import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; +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.FunctionInfo; +import org.elasticsearch.xpack.esql.expression.function.Param; +import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction; +import org.elasticsearch.xpack.esql.expression.function.scalar.math.Cast; +import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; +import org.elasticsearch.xpack.esql.planner.PlannerUtils; + +import java.io.IOException; +import java.util.List; + +import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; + +/** + * Clamps input values to have a lower limit of min. + */ +public class ClampMin extends EsqlScalarFunction { + public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "ClampMin", ClampMin::new); + + @FunctionInfo( + returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "Returns clamps the values of all input samples clamped to have a lower limit of min.", + examples = @Example(file = "k8s-timeseries-clamp", tag = "clamp-min") + ) + public ClampMin( + Source source, + @Param( + name = "field", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "field to clamp." + ) Expression field, + @Param( + name = "min", + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, + description = "The min value to clamp data into." + ) Expression min + ) { + super(source, List.of(field, min)); + } + + private ClampMin(StreamInput in) throws IOException { + this(Source.readFrom((PlanStreamInput) in), in.readNamedWriteable(Expression.class), in.readNamedWriteable(Expression.class)); + } + + @Override + public void writeTo(StreamOutput out) throws IOException { + source().writeTo(out); + out.writeNamedWriteable(children().get(0)); + out.writeNamedWriteable(children().get(1)); + } + + @Override + public String getWriteableName() { + return ENTRY.name; + } + + @Override + public DataType dataType() { + return children().getFirst().dataType(); + } + + @Override + protected TypeResolution resolveType() { + if (childrenResolved() == false) { + return new TypeResolution("Unresolved children"); + } + + var field = children().get(0); + var min = children().get(1); + var fieldDataType = field.dataType(); + TypeResolution resolution = TypeResolutions.isType( + field, + t -> t.isNumeric() || t == DataType.BOOLEAN || t.isDate() || DataType.isString(t) || t == DataType.IP || t == DataType.VERSION, + sourceText(), + TypeResolutions.ParamOrdinal.FIRST, + fieldDataType.typeName() + ); + if (resolution.unresolved()) { + return resolution; + } + if (fieldDataType == NULL) { + return new TypeResolution("'field' must not be null in clamp()"); + } + resolution = TypeResolutions.isType( + min, + t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType, + sourceText(), + TypeResolutions.ParamOrdinal.SECOND, + fieldDataType.typeName() + ); + if (resolution.unresolved()) { + return resolution; + } + return TypeResolution.TYPE_RESOLVED; + } + + @Override + public Expression replaceChildren(List newChildren) { + return new ClampMin(source(), newChildren.get(0), newChildren.get(1)); + } + + @Override + protected NodeInfo info() { + return NodeInfo.create(this, ClampMin::new, children().get(0), children().get(1)); + } + + @Override + public boolean foldable() { + return Expressions.foldable(children()); + } + + @Override + public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { + // force datatype initialization + var outputType = dataType(); + + var min = children().get(1); + var minF = outputType != min.dataType() + ? Cast.cast(source(), min.dataType(), outputType, toEvaluator.apply(min)) + : toEvaluator.apply(min); + + return switch (PlannerUtils.toElementType(outputType)) { + case BOOLEAN -> new ClampMinBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + case DOUBLE -> new ClampMinDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + case INT -> new ClampMinIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + case LONG -> new ClampMinLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + case BYTES_REF -> new ClampMinBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + default -> throw EsqlIllegalArgumentException.illegalDataType(outputType); + }; + } + + @Evaluator(extraName = "Boolean") + static boolean process(boolean field, boolean min) { + if (min) { + return true; + } else { + return field; + } + } + + @Evaluator(extraName = "BytesRef") + static BytesRef process(BytesRef field, BytesRef min) { + return field.compareTo(min) < 0 ? min : field; + } + + @Evaluator(extraName = "Integer") + static int process(int field, int min) { + return Math.max(field, min); + } + + @Evaluator(extraName = "Long") + static long process(long field, long min) { + return Math.max(field, min); + } + + @Evaluator(extraName = "Double") + static double process(double field, double min) { + return Math.max(field, min); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java new file mode 100644 index 0000000000000..c331cc719686f --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java @@ -0,0 +1,191 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.core.Tuple; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; +import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; +import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction; +import org.hamcrest.Matchers; + +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ClampMaxTests extends AbstractScalarFunctionTestCase { + public ClampMaxTests(@Name("TestCase") Supplier testCaseSupplier) { + this.testCase = testCaseSupplier.get(); + } + + @ParametersFactory + public static Iterable parameters() { + List suppliers = new java.util.ArrayList<>(); + for (DataType stringType : DataType.stringTypes()) { + if (stringType == DataType.TEXT || stringType == DataType.BYTE) { + continue; + } + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(stringType, stringType), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("a"), stringType, "a"), + new TestCaseSupplier.TypedData(new BytesRef("b"), stringType, "b") + ), + "ClampMaxBytesRefEvaluator[field=Attribute[channel=0], " + "max=Attribute[channel=1]]", + stringType, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + } + // function to return the correct type of number (int, long, float, double) based on the DataType + Function, Object> numberValue = dt -> { + if (dt.v1() == DataType.INTEGER) return dt.v2(); + if (dt.v1() == DataType.LONG || dt.v1() == DataType.DATETIME) return dt.v2().longValue(); + if (dt.v1() == DataType.FLOAT) return dt.v2().floatValue(); + if (dt.v1() == DataType.DOUBLE) return dt.v2().doubleValue(); + if (dt.v1() == DataType.UNSIGNED_LONG) return dt.v2().longValue(); // we will use long to represent unsigned long + throw new IllegalArgumentException("Unsupported data type: " + dt); + }; + // function to make first letter uppercase + Function capitalize = s -> s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); + for (DataType numericType : DataType.types().stream().filter(DataType::isNumeric).toList()) { + if (numericType == DataType.HALF_FLOAT + || numericType == DataType.SCALED_FLOAT + || numericType == DataType.SHORT + || numericType == DataType.BYTE + // || numericType == DataType.UNSIGNED_LONG // TODO: shouldnt unsigned long be supported? it was giving trouble... + || numericType == DataType.FLOAT) { // TODO: shouldnt float be supported? + continue; + } + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(numericType, numericType), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 1)), numericType, "a"), + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 2)), numericType, "b") + ), + String.format( + Locale.ROOT, + "ClampMax%sEvaluator[field=Attribute[channel=0], max=Attribute[channel=1]]", + numericType == DataType.UNSIGNED_LONG ? "Long" : capitalize.apply(numericType.esType()) + ), + numericType, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + } + // boolean type + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.BOOLEAN, DataType.BOOLEAN), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(true, DataType.BOOLEAN, "a"), + new TestCaseSupplier.TypedData(false, DataType.BOOLEAN, "b") + ), + "ClampMaxBooleanEvaluator[field=Attribute[channel=0], max=Attribute[channel=1]]", + DataType.BOOLEAN, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.VERSION, DataType.VERSION), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("1"), DataType.VERSION, "a"), + new TestCaseSupplier.TypedData(new BytesRef("2"), DataType.VERSION, "b") + ), + "ClampMaxBytesRefEvaluator[field=Attribute[channel=0], max=Attribute[channel=1]]", + DataType.VERSION, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.IP, DataType.IP), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.1"), DataType.IP, "a"), + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.3"), DataType.IP, "b") + ), + "ClampMaxBytesRefEvaluator[field=Attribute[channel=0], max=Attribute[channel=1]]", + DataType.IP, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.DATETIME, DataType.DATETIME), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(1727877348000L, DataType.DATETIME, "a"), + new TestCaseSupplier.TypedData(1727790948000L, DataType.DATETIME, "b") + ), + "ClampMaxLongEvaluator[field=Attribute[channel=0], max=Attribute[channel=1]]", + DataType.DATETIME, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + return parameterSuppliersFromTypedData(suppliers); + } + + @Override + public void testFold() {} + + @Override + protected EsqlScalarFunction build(Source source, List args) { + return new ClampMax(source, args.get(0), args.get(1)); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinTests.java new file mode 100644 index 0000000000000..888898fbfd2f2 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinTests.java @@ -0,0 +1,196 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.core.Tuple; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; +import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; +import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction; +import org.hamcrest.Matchers; + +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ClampMinTests extends AbstractScalarFunctionTestCase { + public ClampMinTests(@Name("TestCase") Supplier testCaseSupplier) { + this.testCase = testCaseSupplier.get(); + } + + static String clampFnName() { + return "ClampMin"; + } + + @ParametersFactory + public static Iterable parameters() { + List suppliers = new java.util.ArrayList<>(); + for (DataType stringType : DataType.stringTypes()) { + if (stringType == DataType.TEXT || stringType == DataType.BYTE) { + continue; + } + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(stringType, stringType), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("a"), stringType, "a"), + new TestCaseSupplier.TypedData(new BytesRef("b"), stringType, "b") + ), + "ClampMinBytesRefEvaluator[field=Attribute[channel=0], " + "min=Attribute[channel=1]]", + stringType, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + } + // function to return the correct type of number (int, long, float, double) based on the DataType + Function, Object> numberValue = dt -> { + if (dt.v1() == DataType.INTEGER) return dt.v2(); + if (dt.v1() == DataType.LONG || dt.v1() == DataType.DATETIME) return dt.v2().longValue(); + if (dt.v1() == DataType.FLOAT) return dt.v2().floatValue(); + if (dt.v1() == DataType.DOUBLE) return dt.v2().doubleValue(); + if (dt.v1() == DataType.UNSIGNED_LONG) return dt.v2().longValue(); // we will use long to represent unsigned long + throw new IllegalArgumentException("Unsupported data type: " + dt); + }; + // function to make first letter uppercase + Function capitalize = s -> s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); + for (DataType numericType : DataType.types().stream().filter(DataType::isNumeric).toList()) { + if (numericType == DataType.HALF_FLOAT + || numericType == DataType.SCALED_FLOAT + || numericType == DataType.SHORT + || numericType == DataType.BYTE + // || numericType == DataType.UNSIGNED_LONG // TODO: shouldnt unsigned long be supported? it was giving trouble... + || numericType == DataType.FLOAT) { // TODO: shouldnt float be supported? + continue; + } + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(numericType, numericType), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 1)), numericType, "a"), + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 2)), numericType, "b") + ), + String.format( + Locale.ROOT, + "ClampMin%sEvaluator[field=Attribute[channel=0], min=Attribute[channel=1]]", + numericType == DataType.UNSIGNED_LONG ? "Long" : capitalize.apply(numericType.esType()), + numericType == DataType.UNSIGNED_LONG ? "Long" : capitalize.apply(numericType.esType()) + ), + numericType, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + } + // boolean type + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.BOOLEAN, DataType.BOOLEAN), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(true, DataType.BOOLEAN, "a"), + new TestCaseSupplier.TypedData(false, DataType.BOOLEAN, "b") + ), + "ClampMinBooleanEvaluator[field=Attribute[channel=0], min=Attribute[channel=1]]", + DataType.BOOLEAN, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.VERSION, DataType.VERSION), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("1"), DataType.VERSION, "a"), + new TestCaseSupplier.TypedData(new BytesRef("2"), DataType.VERSION, "b") + ), + "ClampMinBytesRefEvaluator[field=Attribute[channel=0], min=Attribute[channel=1]]", + DataType.VERSION, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.IP, DataType.IP), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.1"), DataType.IP, "a"), + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.3"), DataType.IP, "b") + ), + "ClampMinBytesRefEvaluator[field=Attribute[channel=0], min=Attribute[channel=1]]", + DataType.IP, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b)", + List.of(DataType.DATETIME, DataType.DATETIME), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(1727877348000L, DataType.DATETIME, "a"), + new TestCaseSupplier.TypedData(1727790948000L, DataType.DATETIME, "b") + ), + "ClampMinLongEvaluator[field=Attribute[channel=0], min=Attribute[channel=1]]", + DataType.DATETIME, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + return parameterSuppliersFromTypedData(suppliers); + } + + @Override + public void testFold() {} + + @Override + protected EsqlScalarFunction build(Source source, List args) { + return new ClampMin(source, args.get(0), args.get(1)); + } +} diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampTests.java new file mode 100644 index 0000000000000..d56c885fed1a3 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampTests.java @@ -0,0 +1,211 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +package org.elasticsearch.xpack.esql.expression.function.scalar.conditional; + +import com.carrotsearch.randomizedtesting.annotations.Name; +import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; + +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.core.Tuple; +import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; +import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; +import org.elasticsearch.xpack.esql.expression.function.scalar.Clamp; +import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction; +import org.hamcrest.Matchers; + +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ClampTests extends AbstractScalarFunctionTestCase { + public ClampTests(@Name("TestCase") Supplier testCaseSupplier) { + this.testCase = testCaseSupplier.get(); + } + + @Override + protected boolean canSerialize() { + return false; + } + + @ParametersFactory + public static Iterable parameters() { + List suppliers = new java.util.ArrayList<>(); + for (DataType stringType : DataType.stringTypes()) { + if (stringType == DataType.TEXT || stringType == DataType.BYTE) { + continue; + } + suppliers.add( + new TestCaseSupplier( + "(a, b, c)", + List.of(stringType, stringType, stringType), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("a"), stringType, "a"), + new TestCaseSupplier.TypedData(new BytesRef("b"), stringType, "b"), + new TestCaseSupplier.TypedData(new BytesRef("b"), stringType, "c") + ), + "ClampMaxBytesRefEvaluator[field=ClampMinBytesRefEvaluator[field=Attribute[channel=0], " + + "min=Attribute[channel=1]], max=Attribute[channel=2]]", + stringType, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + } + // function to return the correct type of number (int, long, float, double) based on the DataType + Function, Object> numberValue = dt -> { + if (dt.v1() == DataType.INTEGER) return dt.v2(); + if (dt.v1() == DataType.LONG || dt.v1() == DataType.DATETIME) return dt.v2().longValue(); + if (dt.v1() == DataType.FLOAT) return dt.v2().floatValue(); + if (dt.v1() == DataType.DOUBLE) return dt.v2().doubleValue(); + if (dt.v1() == DataType.UNSIGNED_LONG) return dt.v2().longValue(); // we will use long to represent unsigned long + throw new IllegalArgumentException("Unsupported data type: " + dt); + }; + // function to make first letter uppercase + Function capitalize = s -> s.substring(0, 1).toUpperCase(Locale.ROOT) + s.substring(1); + for (DataType numericType : DataType.types().stream().filter(DataType::isNumeric).toList()) { + if (numericType == DataType.HALF_FLOAT + || numericType == DataType.SCALED_FLOAT + || numericType == DataType.SHORT + || numericType == DataType.BYTE + // || numericType == DataType.UNSIGNED_LONG // TODO: shouldnt unsigned long be supported? it was giving trouble... + || numericType == DataType.FLOAT) { // TODO: shouldnt float be supported? + continue; + } + suppliers.add( + new TestCaseSupplier( + "(a, b, c)", + List.of(numericType, numericType, numericType), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 1)), numericType, "a"), + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 2)), numericType, "b"), + new TestCaseSupplier.TypedData(numberValue.apply(Tuple.tuple(numericType, 3)), numericType, "c") + ), + String.format( + Locale.ROOT, + "ClampMax%sEvaluator[field=ClampMin%sEvaluator[field=Attribute[channel=0], min=Attribute[channel=1]], " + + "max=Attribute[channel=2]]", + numericType == DataType.UNSIGNED_LONG ? "Long" : capitalize.apply(numericType.esType()), + numericType == DataType.UNSIGNED_LONG ? "Long" : capitalize.apply(numericType.esType()) + ), + numericType, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + } + // boolean type + suppliers.add( + new TestCaseSupplier( + "(a, b, c)", + List.of(DataType.BOOLEAN, DataType.BOOLEAN, DataType.BOOLEAN), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(true, DataType.BOOLEAN, "a"), + new TestCaseSupplier.TypedData(false, DataType.BOOLEAN, "b"), + new TestCaseSupplier.TypedData(true, DataType.BOOLEAN, "c") + ), + "ClampMaxBooleanEvaluator[field=ClampMinBooleanEvaluator[field=Attribute[channel=0], " + + "min=Attribute[channel=1]], max=Attribute[channel=2]]", + DataType.BOOLEAN, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b, c)", + List.of(DataType.VERSION, DataType.VERSION, DataType.VERSION), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("1"), DataType.VERSION, "a"), + new TestCaseSupplier.TypedData(new BytesRef("2"), DataType.VERSION, "b"), + new TestCaseSupplier.TypedData(new BytesRef("3"), DataType.VERSION, "c") + ), + "ClampMaxBytesRefEvaluator[field=ClampMinBytesRefEvaluator[field=Attribute[channel=0], " + + "min=Attribute[channel=1]], max=Attribute[channel=2]]", + DataType.VERSION, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b, c)", + List.of(DataType.IP, DataType.IP, DataType.IP), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.1"), DataType.IP, "a"), + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.2"), DataType.IP, "b"), + new TestCaseSupplier.TypedData(new BytesRef("127.0.0.3"), DataType.IP, "c") + ), + "ClampMaxBytesRefEvaluator[field=ClampMinBytesRefEvaluator[field=Attribute[channel=0], " + + "min=Attribute[channel=1]], max=Attribute[channel=2]]", + DataType.IP, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + suppliers.add( + new TestCaseSupplier( + "(a, b, c)", + List.of(DataType.DATETIME, DataType.DATETIME, DataType.DATETIME), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(1727877348000L, DataType.DATETIME, "a"), + new TestCaseSupplier.TypedData(1727790948000L, DataType.DATETIME, "b"), + new TestCaseSupplier.TypedData(1727963748000L, DataType.DATETIME, "c") + ), + "ClampMaxLongEvaluator[field=ClampMinLongEvaluator[field=Attribute[channel=0], " + + "min=Attribute[channel=1]], max=Attribute[channel=2]]", + DataType.DATETIME, + Matchers.allOf( + Matchers.notNullValue(), + Matchers.not(Matchers.notANumber()), + Matchers.not(Matchers.in(List.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY))) + ) + ) + ) + ); + // TODO make sure that we handle nulls properly + return parameterSuppliersFromTypedData(suppliers); + } + + @Override + public void testFold() {} + + @Override + protected EsqlScalarFunction build(Source source, List args) { + return new Clamp(source, args.get(0), args.get(1), args.get(2)); + } +}