From d48b17b99225d7cb9fad335eb61cc5d14e26e04f Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 30 Sep 2025 15:43:12 -0700 Subject: [PATCH 01/14] clamp test suite for csv --- .../resources/k8s-timeseries-clamp.csv-spec | 326 ++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec 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..1eb5ff8050dc0 --- /dev/null +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/k8s-timeseries-clamp.csv-spec @@ -0,0 +1,326 @@ +clamp_of_double_no_grouping +required_capability: ts_command_v0 +TS k8s +| STATS cost=sum(clamp_max(network.cost, 1)) BY time_bucket = bucket(@timestamp,1minute) +| SORT cost DESC, time_bucket DESC | LIMIT 10; + +cost:double | time_bucket:datetime +9.0 | 2024-05-10T00:09:00.000Z +8.0 | 2024-05-10T00:15:00.000Z +8.0 | 2024-05-10T00:08:00.000Z +7.5 | 2024-05-10T00:18:00.000Z +7.0 | 2024-05-10T00:22:00.000Z +6.5 | 2024-05-10T00:17:00.000Z +6.125 | 2024-05-10T00:14:00.000Z +6.0 | 2024-05-10T00:13:00.000Z +6.0 | 2024-05-10T00:02:00.000Z +5.875 | 2024-05-10T00:20:00.000Z +; + +clamp_of_long +required_capability: ts_command_v0 +TS k8s | STATS bytes_in = sum(clamp_max(network.bytes_in, 1000)) BY time_bucket = bucket(@timestamp,1minute) | SORT bytes_in DESC, time_bucket | LIMIT 10; + +bytes_in:long | time_bucket:datetime +5587 | 2024-05-10T00:18:00.000Z +5580 | 2024-05-10T00:20:00.000Z +4384 | 2024-05-10T00:17:00.000Z +4146 | 2024-05-10T00:15:00.000Z +4030 | 2024-05-10T00:14:00.000Z +3850 | 2024-05-10T00:09:00.000Z +3594 | 2024-05-10T00:06:00.000Z +3487 | 2024-05-10T00:02:00.000Z +3188 | 2024-05-10T00:08:00.000Z +3165 | 2024-05-10T00:22:00.000Z + +; + +clamp_of_long_grouping +required_capability: ts_command_v0 +TS k8s | STATS bytes_in = sum(clamp(network.bytes_in)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT bytes_in DESC, time_bucket | LIMIT 10; + +bytes_in:long | cluster:keyword | time_bucket:datetime +3013 | prod | 2024-05-10T00:18:00.000Z +2848 | prod | 2024-05-10T00:17:00.000Z +2483 | prod | 2024-05-10T00:20:00.000Z +2247 | qa | 2024-05-10T00:18:00.000Z +2153 | qa | 2024-05-10T00:15:00.000Z +2087 | qa | 2024-05-10T00:17:00.000Z +2035 | staging | 2024-05-10T00:09:00.000Z +1908 | qa | 2024-05-10T00:20:00.000Z +1904 | qa | 2024-05-10T00:06:00.000Z +1811 | prod | 2024-05-10T00:13:00.000Z +; + +clamp_of_date_nanos +required_capability: ts_command_v0 +TS k8s | STATS last_up = max(clamp(network.eth0.last_up)) BY time_bucket = bucket(@timestamp,1minute) | SORT time_bucket | LIMIT 10; + +last_up:date_nanos | time_bucket:datetime +2024-05-03T15:37:33.324440296Z | 2024-05-10T00:00:00.000Z +2024-04-09T03:20:29.012174065Z | 2024-05-10T00:01:00.000Z +2024-05-08T10:03:43.290628939Z | 2024-05-10T00:02:00.000Z +2024-05-02T09:02:26.799002478Z | 2024-05-10T00:03:00.000Z +2024-05-09T23:18:49.214537497Z | 2024-05-10T00:04:00.000Z +2024-04-25T19:33:06.187780912Z | 2024-05-10T00:05:00.000Z +2024-05-08T01:35:07.955894784Z | 2024-05-10T00:06:00.000Z +2024-05-02T07:53:33.802839368Z | 2024-05-10T00:07:00.000Z +2024-05-08T18:36:40.027496014Z | 2024-05-10T00:08:00.000Z +2024-05-08T23:33:34.42759108Z | 2024-05-10T00:09:00.000Z +; + +clamp_of_date_nanos_grouping +required_capability: ts_command_v0 +TS k8s | STATS last_up = max(clamp(network.eth0.last_up)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT time_bucket, cluster | LIMIT 10; + +last_up:date_nanos | cluster:keyword | time_bucket:datetime +2024-03-25T09:57:51.755620402Z | prod | 2024-05-10T00:00:00.000Z +2024-05-03T15:37:33.324440296Z | staging | 2024-05-10T00:00:00.000Z +2024-04-09T03:20:29.012174065Z | prod | 2024-05-10T00:01:00.000Z +2024-03-31T23:14:25.575848186Z | qa | 2024-05-10T00:01:00.000Z +2024-05-06T19:01:53.843370676Z | prod | 2024-05-10T00:02:00.000Z +2024-05-02T13:22:35.021783722Z | qa | 2024-05-10T00:02:00.000Z +2024-05-08T10:03:43.290628939Z | staging | 2024-05-10T00:02:00.000Z +2024-04-04T01:13:26.805245198Z | prod | 2024-05-10T00:03:00.000Z +2024-04-28T06:40:53.255612616Z | qa | 2024-05-10T00:03:00.000Z +2024-05-02T09:02:26.799002478Z | staging | 2024-05-10T00:03:00.000Z +; + +clamp_of_version +required_capability: ts_command_v0 +TS k8s | STATS version = max(clamp(network.eth0.firmware_version)) BY time_bucket = bucket(@timestamp,1minute) | SORT time_bucket | LIMIT 10; + +version:version | time_bucket:datetime +13.10.4 | 2024-05-10T00:00:00.000Z +13.10.4 | 2024-05-10T00:01:00.000Z +13.10.4 | 2024-05-10T00:02:00.000Z +15.6.9 | 2024-05-10T00:03:00.000Z +10.3.2 | 2024-05-10T00:04:00.000Z +13.10.4 | 2024-05-10T00:05:00.000Z +13.10.4 | 2024-05-10T00:06:00.000Z +10.4.3 | 2024-05-10T00:07:00.000Z +15.6.9 | 2024-05-10T00:08:00.000Z +15.6.9 | 2024-05-10T00:09:00.000Z +; + +clamp_of_version_grouping +required_capability: ts_command_v0 +TS k8s | STATS version = max(clamp(network.eth0.firmware_version)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT time_bucket, cluster | LIMIT 10; + +version:version | cluster:keyword | time_bucket:datetime +13.10.4 | prod | 2024-05-10T00:00:00.000Z +7.8.4 | staging | 2024-05-10T00:00:00.000Z +13.10.4 | prod | 2024-05-10T00:01:00.000Z +10.3.2 | qa | 2024-05-10T00:01:00.000Z +13.10.4 | prod | 2024-05-10T00:02:00.000Z +1.7.2 | qa | 2024-05-10T00:02:00.000Z +7.8.4 | staging | 2024-05-10T00:02:00.000Z +9.10.4 | prod | 2024-05-10T00:03:00.000Z +1.7.2 | qa | 2024-05-10T00:03:00.000Z +15.6.9 | staging | 2024-05-10T00:03:00.000Z +; + +clamp_of_integer +required_capability: ts_command_v0 +TS k8s | STATS clients = avg(clamp(network.eth0.currently_connected_clients)) BY time_bucket = bucket(@timestamp,1minute) | SORT time_bucket | LIMIT 10; + +clients:double | time_bucket:datetime +726.6666666666666 | 2024-05-10T00:00:00.000Z +418.25 | 2024-05-10T00:01:00.000Z +550.3333333333334 | 2024-05-10T00:02:00.000Z +598.8 | 2024-05-10T00:03:00.000Z +546.3333333333334 | 2024-05-10T00:04:00.000Z +809.8 | 2024-05-10T00:05:00.000Z +656.8333333333334 | 2024-05-10T00:06:00.000Z +822.6666666666666 | 2024-05-10T00:07:00.000Z +605.25 | 2024-05-10T00:08:00.000Z +642.1111111111111 | 2024-05-10T00:09:00.000Z +; + +clamp_of_integer_grouping +required_capability: ts_command_v0 +TS k8s | STATS clients = avg(clamp(network.eth0.currently_connected_clients)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT time_bucket, cluster | LIMIT 10; + +clients:double | cluster:keyword | time_bucket:datetime +949.0 | prod | 2024-05-10T00:00:00.000Z +615.5 | staging | 2024-05-10T00:00:00.000Z +396.5 | prod | 2024-05-10T00:01:00.000Z +440.0 | qa | 2024-05-10T00:01:00.000Z +659.5 | prod | 2024-05-10T00:02:00.000Z +565.0 | qa | 2024-05-10T00:02:00.000Z +426.5 | staging | 2024-05-10T00:02:00.000Z +742.0 | prod | 2024-05-10T00:03:00.000Z +454.0 | qa | 2024-05-10T00:03:00.000Z +672.0 | staging | 2024-05-10T00:03:00.000Z +; + +clamp_of_aggregate_metric_double +required_capability: ts_command_v0 +required_capability: aggregate_metric_double +TS k8s-downsampled | STATS tx = sum(clamp(network.eth0.tx)) BY time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket | LIMIT 10; +tx:double | time_bucket:datetime +6053.0 | 2024-05-09T23:30:00.000Z +6699.0 | 2024-05-09T23:40:00.000Z +5895.0 | 2024-05-09T23:50:00.000Z +; + +clamp_of_aggregate_metric_double_grouping +required_capability: ts_command_v0 +required_capability: aggregate_metric_double +TS k8s-downsampled | STATS tx = sum(clamp(network.eth0.tx)) BY cluster, time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket, cluster | LIMIT 10; + +tx:double | cluster:keyword | time_bucket:datetime +1601.0 | prod | 2024-05-09T23:30:00.000Z +2109.0 | qa | 2024-05-09T23:30:00.000Z +2343.0 | staging | 2024-05-09T23:30:00.000Z +1854.0 | prod | 2024-05-09T23:40:00.000Z +2975.0 | qa | 2024-05-09T23:40:00.000Z +1870.0 | staging | 2024-05-09T23:40:00.000Z +2377.0 | prod | 2024-05-09T23:50:00.000Z +1686.0 | qa | 2024-05-09T23:50:00.000Z +1832.0 | staging | 2024-05-09T23:50:00.000Z +; + +clamp_with_filtering +required_capability: ts_command_v0 +TS k8s | WHERE pod == "one" | STATS tx = sum(clamp(network.bytes_in)) BY cluster, time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket, cluster | LIMIT 10; + +tx:long | cluster:keyword | time_bucket:datetime +970 | prod | 2024-05-10T00:00:00.000Z +842 | qa | 2024-05-10T00:00:00.000Z +753 | staging | 2024-05-10T00:00:00.000Z +990 | prod | 2024-05-10T00:10:00.000Z +1006 | qa | 2024-05-10T00:10:00.000Z +947 | staging | 2024-05-10T00:10:00.000Z +953 | prod | 2024-05-10T00:20:00.000Z +917 | qa | 2024-05-10T00:20:00.000Z +749 | staging | 2024-05-10T00:20:00.000Z +; + +clamp_older_than_10h +required_capability: ts_command_v0 +required_capability: aggregate_metric_double +TS k8s-downsampled | WHERE cluster == "qa" AND @timestamp < now() - 10 day | STATS cost = avg(clamp(network.eth0.rx)) BY pod, time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket, pod | LIMIT 5; + +cost:double | pod:keyword | time_bucket:datetime +655.0 | one | 2024-05-09T23:30:00.000Z +1.0 | three | 2024-05-09T23:30:00.000Z +461.0 | two | 2024-05-09T23:30:00.000Z +1049.0 | one | 2024-05-09T23:40:00.000Z +1237.0 | three | 2024-05-09T23:40:00.000Z +; + +eval_on_clamp +required_capability: ts_command_v0 +TS k8s | STATS max_bytes = avg(clamp(network.bytes_in)) BY cluster, time_bucket = bucket(@timestamp, 10minute) | EVAL kb_minus_offset = (max_bytes - 100) / 1000.0 | LIMIT 10 | SORT time_bucket, cluster ; + +max_bytes:double | cluster:keyword | time_bucket:datetime | kb_minus_offset:double +909.3333333333334 | prod | 2024-05-10T00:00:00.000Z | 0.8093333333333333 +908.6666666666666 | qa | 2024-05-10T00:00:00.000Z | 0.8086666666666666 +794.0 | staging | 2024-05-10T00:00:00.000Z | 0.694 +1005.0 | prod | 2024-05-10T00:10:00.000Z | 0.905 +980.0 | qa | 2024-05-10T00:10:00.000Z | 0.88 +917.6666666666666 | staging | 2024-05-10T00:10:00.000Z | 0.8176666666666667 +846.3333333333334 | prod | 2024-05-10T00:20:00.000Z | 0.7463333333333334 +941.6666666666666 | qa | 2024-05-10T00:20:00.000Z | 0.8416666666666667 +786.0 | staging | 2024-05-10T00:20:00.000Z | 0.686 +; + +clamp_multi_values +required_capability: ts_command_v0 +TS k8s | WHERE @timestamp < "2024-05-10T00:10:00.000Z" | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 1minute) | SORT events desc, time_bucket | LIMIT 10; + +events:long | pod:keyword | time_bucket:datetime +27 | two | 2024-05-10T00:08:00.000Z +27 | one | 2024-05-10T00:09:00.000Z +26 | one | 2024-05-10T00:08:00.000Z +24 | three | 2024-05-10T00:06:00.000Z +24 | three | 2024-05-10T00:09:00.000Z +21 | two | 2024-05-10T00:02:00.000Z +20 | two | 2024-05-10T00:09:00.000Z +18 | one | 2024-05-10T00:01:00.000Z +17 | one | 2024-05-10T00:05:00.000Z +14 | three | 2024-05-10T00:00:00.000Z +; + +clamp_null_values +required_capability: ts_command_v0 +TS k8s | WHERE @timestamp > "2024-05-10T00:10:00.000Z" and @timestamp < "2024-05-10T00:15:00.000Z" | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 1minute) | SORT events desc, time_bucket | LIMIT 10; + +events:long | pod:keyword | time_bucket:datetime +null | one | 2024-05-10T00:12:00.000Z +null | two | 2024-05-10T00:13:00.000Z +20 | two | 2024-05-10T00:14:00.000Z +18 | two | 2024-05-10T00:12:00.000Z +17 | one | 2024-05-10T00:13:00.000Z +16 | one | 2024-05-10T00:14:00.000Z +11 | one | 2024-05-10T00:10:00.000Z +9 | one | 2024-05-10T00:11:00.000Z +9 | three | 2024-05-10T00:13:00.000Z +7 | two | 2024-05-10T00:10:00.000Z +; + +clamp_null_values +required_capability: ts_command_v0 +TS k8s | WHERE @timestamp > "2024-05-10T00:10:00.000Z" and @timestamp < "2024-05-10T00:15:00.000Z" | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 1minute) | SORT events desc, time_bucket | LIMIT 10; + +events:long | pod:keyword | time_bucket:datetime +null | one | 2024-05-10T00:12:00.000Z +null | two | 2024-05-10T00:13:00.000Z +20 | two | 2024-05-10T00:14:00.000Z +18 | two | 2024-05-10T00:12:00.000Z +17 | one | 2024-05-10T00:13:00.000Z +16 | one | 2024-05-10T00:14:00.000Z +11 | one | 2024-05-10T00:10:00.000Z +9 | one | 2024-05-10T00:11:00.000Z +9 | three | 2024-05-10T00:13:00.000Z +7 | two | 2024-05-10T00:10:00.000Z +; + +clamp_all_value_types +required_capability: ts_command_v0 +TS k8s | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 10minute) | SORT events desc, pod, time_bucket | LIMIT 10 ; + +events:long | pod:keyword | time_bucket:datetime +30 | one | 2024-05-10T00:10:00.000Z +30 | two | 2024-05-10T00:10:00.000Z +29 | one | 2024-05-10T00:00:00.000Z +29 | three | 2024-05-10T00:00:00.000Z +29 | two | 2024-05-10T00:00:00.000Z +28 | three | 2024-05-10T00:10:00.000Z +21 | three | 2024-05-10T00:20:00.000Z +21 | two | 2024-05-10T00:20:00.000Z +19 | one | 2024-05-10T00:20:00.000Z +; + +clamp_aggregate_metric_double_implicit_casting +required_capability: ts_command_v0 +required_capability: aggregate_metric_double_implicit_casting_in_aggs +TS k8s* | STATS bytes = sum(clamp(network.eth0.rx)) by time_bucket = bucket(@timestamp, 10minute) | SORT bytes desc, time_bucket | LIMIT 10 ; + +bytes:double | time_bucket:datetime +9058.0 | 2024-05-10T00:20:00.000Z +8070.0 | 2024-05-10T00:10:00.000Z +7088.0 | 2024-05-09T23:50:00.000Z +6380.0 | 2024-05-09T23:30:00.000Z +6095.0 | 2024-05-09T23:40:00.000Z +4290.0 | 2024-05-10T00:00:00.000Z +; + +clamp_aggregate_metric_double_implicit_casting_grouping +required_capability: ts_command_v0 +required_capability: aggregate_metric_double_implicit_casting_in_aggs +TS k8s* | STATS bytes = sum(clamp(network.eth0.rx)) by pod, time_bucket = bucket(@timestamp, 10minute) | SORT bytes desc, pod, time_bucket | LIMIT 10 ; + +bytes:double | pod:keyword | time_bucket:datetime +3156.0 | one | 2024-05-10T00:20:00.000Z +3028.0 | three | 2024-05-10T00:20:00.000Z +2874.0 | two | 2024-05-10T00:20:00.000Z +2825.0 | one | 2024-05-10T00:10:00.000Z +2810.0 | three | 2024-05-09T23:40:00.000Z +2710.0 | three | 2024-05-10T00:10:00.000Z +2653.0 | three | 2024-05-09T23:50:00.000Z +2570.0 | one | 2024-05-09T23:30:00.000Z +2535.0 | two | 2024-05-10T00:10:00.000Z +2478.0 | one | 2024-05-09T23:50:00.000Z +; From 7b9b44943ef95cf68b8a0bff50cfb14deb717655 Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 1 Oct 2025 20:34:44 -0700 Subject: [PATCH 02/14] Working con clamp family of functions --- .../_snippets/functions/description/clamp.md | 6 + .../_snippets/functions/examples/clamp.md | 20 + .../esql/_snippets/functions/layout/clamp.md | 23 ++ .../_snippets/functions/parameters/clamp.md | 13 + .../esql/_snippets/functions/types/clamp.md | 16 + .../esql/images/functions/clamp.svg | 1 + .../kibana/definition/functions/clamp.json | 229 ++++++++++++ .../esql/kibana/docs/functions/clamp.md | 12 + .../resources/k8s-timeseries-clamp.csv-spec | 347 ++---------------- .../conditional/ClampMaxBooleanEvaluator.java | 161 ++++++++ .../ClampMaxBytesRefEvaluator.java | 167 +++++++++ .../conditional/ClampMaxDoubleEvaluator.java | 161 ++++++++ .../conditional/ClampMaxIntEvaluator.java | 161 ++++++++ .../conditional/ClampMaxIntegerEvaluator.java | 161 ++++++++ .../conditional/ClampMaxLongEvaluator.java | 161 ++++++++ .../conditional/ClampMinBooleanEvaluator.java | 161 ++++++++ .../ClampMinBytesRefEvaluator.java | 167 +++++++++ .../conditional/ClampMinDoubleEvaluator.java | 161 ++++++++ .../conditional/ClampMinIntEvaluator.java | 161 ++++++++ .../conditional/ClampMinIntegerEvaluator.java | 161 ++++++++ .../conditional/ClampMinLongEvaluator.java | 161 ++++++++ .../function/EsqlFunctionRegistry.java | 6 + .../expression/function/scalar/Clamp.java | 161 ++++++++ .../scalar/ScalarFunctionWritables.java | 5 + .../esql/expression/function/scalar/adsads | 36 ++ .../esql/expression/function/scalar/asdasd | 2 + .../function/scalar/conditional/ClampMax.java | 206 +++++++++++ .../function/scalar/conditional/ClampMin.java | 204 ++++++++++ .../scalar/conditional/ClampTests.java | 195 ++++++++++ 29 files changed, 3115 insertions(+), 311 deletions(-) create mode 100644 docs/reference/query-languages/esql/_snippets/functions/description/clamp.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/examples/clamp.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/layout/clamp.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/parameters/clamp.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/types/clamp.md create mode 100644 docs/reference/query-languages/esql/images/functions/clamp.svg create mode 100644 docs/reference/query-languages/esql/kibana/definition/functions/clamp.json create mode 100644 docs/reference/query-languages/esql/kibana/docs/functions/clamp.md create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBooleanEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxBytesRefEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxDoubleEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxIntegerEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxLongEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBooleanEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinBytesRefEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinDoubleEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinIntegerEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinLongEvaluator.java create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.java create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java create mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java create mode 100644 x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampTests.java 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/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/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/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/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/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/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/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/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 index 1eb5ff8050dc0..055707634413f 100644 --- 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 @@ -1,326 +1,51 @@ clamp_of_double_no_grouping required_capability: ts_command_v0 -TS k8s -| STATS cost=sum(clamp_max(network.cost, 1)) BY time_bucket = bucket(@timestamp,1minute) -| SORT cost DESC, time_bucket DESC | LIMIT 10; - -cost:double | time_bucket:datetime -9.0 | 2024-05-10T00:09:00.000Z -8.0 | 2024-05-10T00:15:00.000Z -8.0 | 2024-05-10T00:08:00.000Z -7.5 | 2024-05-10T00:18:00.000Z -7.0 | 2024-05-10T00:22:00.000Z -6.5 | 2024-05-10T00:17:00.000Z -6.125 | 2024-05-10T00:14:00.000Z -6.0 | 2024-05-10T00:13:00.000Z -6.0 | 2024-05-10T00:02:00.000Z -5.875 | 2024-05-10T00:20:00.000Z -; - -clamp_of_long -required_capability: ts_command_v0 -TS k8s | STATS bytes_in = sum(clamp_max(network.bytes_in, 1000)) BY time_bucket = bucket(@timestamp,1minute) | SORT bytes_in DESC, time_bucket | LIMIT 10; - -bytes_in:long | time_bucket:datetime -5587 | 2024-05-10T00:18:00.000Z -5580 | 2024-05-10T00:20:00.000Z -4384 | 2024-05-10T00:17:00.000Z -4146 | 2024-05-10T00:15:00.000Z -4030 | 2024-05-10T00:14:00.000Z -3850 | 2024-05-10T00:09:00.000Z -3594 | 2024-05-10T00:06:00.000Z -3487 | 2024-05-10T00:02:00.000Z -3188 | 2024-05-10T00:08:00.000Z -3165 | 2024-05-10T00:22:00.000Z - -; - -clamp_of_long_grouping -required_capability: ts_command_v0 -TS k8s | STATS bytes_in = sum(clamp(network.bytes_in)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT bytes_in DESC, time_bucket | LIMIT 10; - -bytes_in:long | cluster:keyword | time_bucket:datetime -3013 | prod | 2024-05-10T00:18:00.000Z -2848 | prod | 2024-05-10T00:17:00.000Z -2483 | prod | 2024-05-10T00:20:00.000Z -2247 | qa | 2024-05-10T00:18:00.000Z -2153 | qa | 2024-05-10T00:15:00.000Z -2087 | qa | 2024-05-10T00:17:00.000Z -2035 | staging | 2024-05-10T00:09:00.000Z -1908 | qa | 2024-05-10T00:20:00.000Z -1904 | qa | 2024-05-10T00:06:00.000Z -1811 | prod | 2024-05-10T00:13:00.000Z -; - -clamp_of_date_nanos -required_capability: ts_command_v0 -TS k8s | STATS last_up = max(clamp(network.eth0.last_up)) BY time_bucket = bucket(@timestamp,1minute) | SORT time_bucket | LIMIT 10; - -last_up:date_nanos | time_bucket:datetime -2024-05-03T15:37:33.324440296Z | 2024-05-10T00:00:00.000Z -2024-04-09T03:20:29.012174065Z | 2024-05-10T00:01:00.000Z -2024-05-08T10:03:43.290628939Z | 2024-05-10T00:02:00.000Z -2024-05-02T09:02:26.799002478Z | 2024-05-10T00:03:00.000Z -2024-05-09T23:18:49.214537497Z | 2024-05-10T00:04:00.000Z -2024-04-25T19:33:06.187780912Z | 2024-05-10T00:05:00.000Z -2024-05-08T01:35:07.955894784Z | 2024-05-10T00:06:00.000Z -2024-05-02T07:53:33.802839368Z | 2024-05-10T00:07:00.000Z -2024-05-08T18:36:40.027496014Z | 2024-05-10T00:08:00.000Z -2024-05-08T23:33:34.42759108Z | 2024-05-10T00:09:00.000Z -; - -clamp_of_date_nanos_grouping -required_capability: ts_command_v0 -TS k8s | STATS last_up = max(clamp(network.eth0.last_up)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT time_bucket, cluster | LIMIT 10; - -last_up:date_nanos | cluster:keyword | time_bucket:datetime -2024-03-25T09:57:51.755620402Z | prod | 2024-05-10T00:00:00.000Z -2024-05-03T15:37:33.324440296Z | staging | 2024-05-10T00:00:00.000Z -2024-04-09T03:20:29.012174065Z | prod | 2024-05-10T00:01:00.000Z -2024-03-31T23:14:25.575848186Z | qa | 2024-05-10T00:01:00.000Z -2024-05-06T19:01:53.843370676Z | prod | 2024-05-10T00:02:00.000Z -2024-05-02T13:22:35.021783722Z | qa | 2024-05-10T00:02:00.000Z -2024-05-08T10:03:43.290628939Z | staging | 2024-05-10T00:02:00.000Z -2024-04-04T01:13:26.805245198Z | prod | 2024-05-10T00:03:00.000Z -2024-04-28T06:40:53.255612616Z | qa | 2024-05-10T00:03:00.000Z -2024-05-02T09:02:26.799002478Z | staging | 2024-05-10T00:03:00.000Z -; - -clamp_of_version -required_capability: ts_command_v0 -TS k8s | STATS version = max(clamp(network.eth0.firmware_version)) BY time_bucket = bucket(@timestamp,1minute) | SORT time_bucket | LIMIT 10; - -version:version | time_bucket:datetime -13.10.4 | 2024-05-10T00:00:00.000Z -13.10.4 | 2024-05-10T00:01:00.000Z -13.10.4 | 2024-05-10T00:02:00.000Z -15.6.9 | 2024-05-10T00:03:00.000Z -10.3.2 | 2024-05-10T00:04:00.000Z -13.10.4 | 2024-05-10T00:05:00.000Z -13.10.4 | 2024-05-10T00:06:00.000Z -10.4.3 | 2024-05-10T00:07:00.000Z -15.6.9 | 2024-05-10T00:08:00.000Z -15.6.9 | 2024-05-10T00:09:00.000Z -; - -clamp_of_version_grouping -required_capability: ts_command_v0 -TS k8s | STATS version = max(clamp(network.eth0.firmware_version)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT time_bucket, cluster | LIMIT 10; - -version:version | cluster:keyword | time_bucket:datetime -13.10.4 | prod | 2024-05-10T00:00:00.000Z -7.8.4 | staging | 2024-05-10T00:00:00.000Z -13.10.4 | prod | 2024-05-10T00:01:00.000Z -10.3.2 | qa | 2024-05-10T00:01:00.000Z -13.10.4 | prod | 2024-05-10T00:02:00.000Z -1.7.2 | qa | 2024-05-10T00:02:00.000Z -7.8.4 | staging | 2024-05-10T00:02:00.000Z -9.10.4 | prod | 2024-05-10T00:03:00.000Z -1.7.2 | qa | 2024-05-10T00:03:00.000Z -15.6.9 | staging | 2024-05-10T00:03:00.000Z -; - -clamp_of_integer -required_capability: ts_command_v0 -TS k8s | STATS clients = avg(clamp(network.eth0.currently_connected_clients)) BY time_bucket = bucket(@timestamp,1minute) | SORT time_bucket | LIMIT 10; - -clients:double | time_bucket:datetime -726.6666666666666 | 2024-05-10T00:00:00.000Z -418.25 | 2024-05-10T00:01:00.000Z -550.3333333333334 | 2024-05-10T00:02:00.000Z -598.8 | 2024-05-10T00:03:00.000Z -546.3333333333334 | 2024-05-10T00:04:00.000Z -809.8 | 2024-05-10T00:05:00.000Z -656.8333333333334 | 2024-05-10T00:06:00.000Z -822.6666666666666 | 2024-05-10T00:07:00.000Z -605.25 | 2024-05-10T00:08:00.000Z -642.1111111111111 | 2024-05-10T00:09:00.000Z -; - -clamp_of_integer_grouping -required_capability: ts_command_v0 -TS k8s | STATS clients = avg(clamp(network.eth0.currently_connected_clients)) BY cluster, time_bucket = bucket(@timestamp,1minute) | SORT time_bucket, cluster | LIMIT 10; -clients:double | cluster:keyword | time_bucket:datetime -949.0 | prod | 2024-05-10T00:00:00.000Z -615.5 | staging | 2024-05-10T00:00:00.000Z -396.5 | prod | 2024-05-10T00:01:00.000Z -440.0 | qa | 2024-05-10T00:01:00.000Z -659.5 | prod | 2024-05-10T00:02:00.000Z -565.0 | qa | 2024-05-10T00:02:00.000Z -426.5 | staging | 2024-05-10T00:02:00.000Z -742.0 | prod | 2024-05-10T00:03:00.000Z -454.0 | qa | 2024-05-10T00:03:00.000Z -672.0 | staging | 2024-05-10T00:03:00.000Z -; - -clamp_of_aggregate_metric_double -required_capability: ts_command_v0 -required_capability: aggregate_metric_double -TS k8s-downsampled | STATS tx = sum(clamp(network.eth0.tx)) BY time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket | LIMIT 10; -tx:double | time_bucket:datetime -6053.0 | 2024-05-09T23:30:00.000Z -6699.0 | 2024-05-09T23:40:00.000Z -5895.0 | 2024-05-09T23:50:00.000Z -; - -clamp_of_aggregate_metric_double_grouping -required_capability: ts_command_v0 -required_capability: aggregate_metric_double -TS k8s-downsampled | STATS tx = sum(clamp(network.eth0.tx)) BY cluster, time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket, cluster | LIMIT 10; - -tx:double | cluster:keyword | time_bucket:datetime -1601.0 | prod | 2024-05-09T23:30:00.000Z -2109.0 | qa | 2024-05-09T23:30:00.000Z -2343.0 | staging | 2024-05-09T23:30:00.000Z -1854.0 | prod | 2024-05-09T23:40:00.000Z -2975.0 | qa | 2024-05-09T23:40:00.000Z -1870.0 | staging | 2024-05-09T23:40:00.000Z -2377.0 | prod | 2024-05-09T23:50:00.000Z -1686.0 | qa | 2024-05-09T23:50:00.000Z -1832.0 | staging | 2024-05-09T23:50:00.000Z -; - -clamp_with_filtering -required_capability: ts_command_v0 -TS k8s | WHERE pod == "one" | STATS tx = sum(clamp(network.bytes_in)) BY cluster, time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket, cluster | LIMIT 10; - -tx:long | cluster:keyword | time_bucket:datetime -970 | prod | 2024-05-10T00:00:00.000Z -842 | qa | 2024-05-10T00:00:00.000Z -753 | staging | 2024-05-10T00:00:00.000Z -990 | prod | 2024-05-10T00:10:00.000Z -1006 | qa | 2024-05-10T00:10:00.000Z -947 | staging | 2024-05-10T00:10:00.000Z -953 | prod | 2024-05-10T00:20:00.000Z -917 | qa | 2024-05-10T00:20:00.000Z -749 | staging | 2024-05-10T00:20:00.000Z -; - -clamp_older_than_10h -required_capability: ts_command_v0 -required_capability: aggregate_metric_double -TS k8s-downsampled | WHERE cluster == "qa" AND @timestamp < now() - 10 day | STATS cost = avg(clamp(network.eth0.rx)) BY pod, time_bucket = bucket(@timestamp, 10minute) | SORT time_bucket, pod | LIMIT 5; - -cost:double | pod:keyword | time_bucket:datetime -655.0 | one | 2024-05-09T23:30:00.000Z -1.0 | three | 2024-05-09T23:30:00.000Z -461.0 | two | 2024-05-09T23:30:00.000Z -1049.0 | one | 2024-05-09T23:40:00.000Z -1237.0 | three | 2024-05-09T23:40:00.000Z -; - -eval_on_clamp -required_capability: ts_command_v0 -TS k8s | STATS max_bytes = avg(clamp(network.bytes_in)) BY cluster, time_bucket = bucket(@timestamp, 10minute) | EVAL kb_minus_offset = (max_bytes - 100) / 1000.0 | LIMIT 10 | SORT time_bucket, cluster ; - -max_bytes:double | cluster:keyword | time_bucket:datetime | kb_minus_offset:double -909.3333333333334 | prod | 2024-05-10T00:00:00.000Z | 0.8093333333333333 -908.6666666666666 | qa | 2024-05-10T00:00:00.000Z | 0.8086666666666666 -794.0 | staging | 2024-05-10T00:00:00.000Z | 0.694 -1005.0 | prod | 2024-05-10T00:10:00.000Z | 0.905 -980.0 | qa | 2024-05-10T00:10:00.000Z | 0.88 -917.6666666666666 | staging | 2024-05-10T00:10:00.000Z | 0.8176666666666667 -846.3333333333334 | prod | 2024-05-10T00:20:00.000Z | 0.7463333333333334 -941.6666666666666 | qa | 2024-05-10T00:20:00.000Z | 0.8416666666666667 -786.0 | staging | 2024-05-10T00:20:00.000Z | 0.686 -; - -clamp_multi_values -required_capability: ts_command_v0 -TS k8s | WHERE @timestamp < "2024-05-10T00:10:00.000Z" | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 1minute) | SORT events desc, time_bucket | LIMIT 10; - -events:long | pod:keyword | time_bucket:datetime -27 | two | 2024-05-10T00:08:00.000Z -27 | one | 2024-05-10T00:09:00.000Z -26 | one | 2024-05-10T00:08:00.000Z -24 | three | 2024-05-10T00:06:00.000Z -24 | three | 2024-05-10T00:09:00.000Z -21 | two | 2024-05-10T00:02:00.000Z -20 | two | 2024-05-10T00:09:00.000Z -18 | one | 2024-05-10T00:01:00.000Z -17 | one | 2024-05-10T00:05:00.000Z -14 | three | 2024-05-10T00:00:00.000Z -; +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) +| SORT full_clamped_cost DESC, time_bucket DESC | LIMIT 10; -clamp_null_values -required_capability: ts_command_v0 -TS k8s | WHERE @timestamp > "2024-05-10T00:10:00.000Z" and @timestamp < "2024-05-10T00:15:00.000Z" | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 1minute) | SORT events desc, time_bucket | LIMIT 10; +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 +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 -events:long | pod:keyword | time_bucket:datetime -null | one | 2024-05-10T00:12:00.000Z -null | two | 2024-05-10T00:13:00.000Z -20 | two | 2024-05-10T00:14:00.000Z -18 | two | 2024-05-10T00:12:00.000Z -17 | one | 2024-05-10T00:13:00.000Z -16 | one | 2024-05-10T00:14:00.000Z -11 | one | 2024-05-10T00:10:00.000Z -9 | one | 2024-05-10T00:11:00.000Z -9 | three | 2024-05-10T00:13:00.000Z -7 | two | 2024-05-10T00:10:00.000Z ; -clamp_null_values +clamp_of_long required_capability: ts_command_v0 -TS k8s | WHERE @timestamp > "2024-05-10T00:10:00.000Z" and @timestamp < "2024-05-10T00:15:00.000Z" | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 1minute) | SORT events desc, time_bucket | LIMIT 10; - -events:long | pod:keyword | time_bucket:datetime -null | one | 2024-05-10T00:12:00.000Z -null | two | 2024-05-10T00:13:00.000Z -20 | two | 2024-05-10T00:14:00.000Z -18 | two | 2024-05-10T00:12:00.000Z -17 | one | 2024-05-10T00:13:00.000Z -16 | one | 2024-05-10T00:14:00.000Z -11 | one | 2024-05-10T00:10:00.000Z -9 | one | 2024-05-10T00:11:00.000Z -9 | three | 2024-05-10T00:13:00.000Z -7 | two | 2024-05-10T00:10:00.000Z -; +// 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; -clamp_all_value_types -required_capability: ts_command_v0 -TS k8s | STATS events = sum(clamp(events_received)) by pod, time_bucket = bucket(@timestamp, 10minute) | SORT events desc, pod, time_bucket | 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 -events:long | pod:keyword | time_bucket:datetime -30 | one | 2024-05-10T00:10:00.000Z -30 | two | 2024-05-10T00:10:00.000Z -29 | one | 2024-05-10T00:00:00.000Z -29 | three | 2024-05-10T00:00:00.000Z -29 | two | 2024-05-10T00:00:00.000Z -28 | three | 2024-05-10T00:10:00.000Z -21 | three | 2024-05-10T00:20:00.000Z -21 | two | 2024-05-10T00:20:00.000Z -19 | one | 2024-05-10T00:20:00.000Z -; -clamp_aggregate_metric_double_implicit_casting -required_capability: ts_command_v0 -required_capability: aggregate_metric_double_implicit_casting_in_aggs -TS k8s* | STATS bytes = sum(clamp(network.eth0.rx)) by time_bucket = bucket(@timestamp, 10minute) | SORT bytes desc, time_bucket | LIMIT 10 ; -bytes:double | time_bucket:datetime -9058.0 | 2024-05-10T00:20:00.000Z -8070.0 | 2024-05-10T00:10:00.000Z -7088.0 | 2024-05-09T23:50:00.000Z -6380.0 | 2024-05-09T23:30:00.000Z -6095.0 | 2024-05-09T23:40:00.000Z -4290.0 | 2024-05-10T00:00:00.000Z -; -clamp_aggregate_metric_double_implicit_casting_grouping -required_capability: ts_command_v0 -required_capability: aggregate_metric_double_implicit_casting_in_aggs -TS k8s* | STATS bytes = sum(clamp(network.eth0.rx)) by pod, time_bucket = bucket(@timestamp, 10minute) | SORT bytes desc, pod, time_bucket | LIMIT 10 ; -bytes:double | pod:keyword | time_bucket:datetime -3156.0 | one | 2024-05-10T00:20:00.000Z -3028.0 | three | 2024-05-10T00:20:00.000Z -2874.0 | two | 2024-05-10T00:20:00.000Z -2825.0 | one | 2024-05-10T00:10:00.000Z -2810.0 | three | 2024-05-09T23:40:00.000Z -2710.0 | three | 2024-05-10T00:10:00.000Z -2653.0 | three | 2024-05-09T23:50:00.000Z -2570.0 | one | 2024-05-09T23:30:00.000Z -2535.0 | two | 2024-05-10T00:10:00.000Z -2478.0 | one | 2024-05-09T23:50:00.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..a202b3ed517c9 --- /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++) { + 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; + } + 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..b03a0fe2ea190 --- /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++) { + 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; + } + 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..67e8c992f405a --- /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++) { + 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; + } + 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..e3e947d3b7ac8 --- /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++) { + 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 "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..20f1582f4dab1 --- /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++) { + 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; + } + 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..44d7c3fc95ca1 --- /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++) { + 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; + } + 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..3dc87157eb091 --- /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++) { + 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; + } + 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..9847370a0deba --- /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++) { + 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; + } + 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..f000190054d3f --- /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++) { + 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 "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..ce082ed520df1 --- /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++) { + 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; + } + 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/expression/function/EsqlFunctionRegistry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/EsqlFunctionRegistry.java index 2d2c644bbcace..7eeb12df641c8 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 @@ -65,7 +65,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; @@ -342,6 +345,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"), @@ -377,6 +381,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..75da9d10f6927 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/Clamp.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; + +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.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 org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; + +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 { + public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Clamp", Clamp::new); + + private final Expression field; + private final Expression min; + private final Expression max; + private DataType dataType; + + @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; + } + + private Clamp(StreamInput in) throws IOException { + this( + Source.readFrom((PlanStreamInput) in), + in.readNamedWriteable(Expression.class), + in.readNamedWriteable(Expression.class), + in.readNamedWriteable(Expression.class) + ); + } + + @Override + public String getWriteableName() { + return ENTRY.name; + } + + @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()) { + dataType = NULL; + return resolution; + } + if (fieldDataType == NULL) { + dataType = 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(), + TypeResolutions.ParamOrdinal.SECOND, + fieldDataType.typeName() + ); + if (childRes.unresolved()) { + dataType = NULL; + return childRes; + } + } + dataType = fieldDataType; + return TypeResolution.TYPE_RESOLVED; + } + + @Override + public DataType dataType() { + if (dataType == null) { + resolveType(); + } + return 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 { + source().writeTo(out); + out.writeNamedWriteable(field); + out.writeNamedWriteable(min); + out.writeNamedWriteable(max); + } + + @Override + public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { + return null; + } + + @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..d1dab4debb8c2 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,9 @@ public static List getNamedWriteables() { entries.add(MonthName.ENTRY); entries.add(IpPrefix.ENTRY); entries.add(Least.ENTRY); + entries.add(Clamp.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/adsads b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads new file mode 100644 index 0000000000000..38d546fdf29dd --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads @@ -0,0 +1,36 @@ +<[boolean, byte, date, date_nanos, double, float, half_float, integer, ip, keyword, long, scaled_float, short, text, unsigned_long, version]> but was: +<[boolean, date, datetime, double, integer, ip, long, string, time, timestamp, unsigned_long, version]> + + +<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: +<[boolean, byte, date, datetime, double, float, integer, ip, long, string, time, timestamp, unsigned_long, version]> + + +<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: +<[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, unsigned_long, version]> + + +:<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: + <[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, time, timestamp, unsigned_long, version]> + + + +<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: +<[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, time, timestamp, unsigned_long, version]> + + + +<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: +<[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, unsigned_long, version]> + + +<[boolean, byte, date, double, float, integer, ip, keyword, long, unsigned_long, version]> but was: +<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> + + +:<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: + <[boolean, byte, date, double, float, integer, ip, keyword, long, unsigned_long, version]> + + +<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: +<[boolean, byte, date, double, float, integer, ip, keyword, long, unsigned_long, version]> diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd new file mode 100644 index 0000000000000..bc50fd510af10 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd @@ -0,0 +1,2 @@ +:<[boolean, date, double, integer, ip, keyword, long, unsigned_long, version]> but was: + <[boolean, date, double, float, integer, ip, keyword, long, unsigned_long, version]> 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..caf9ca6d67476 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMax.java @@ -0,0 +1,206 @@ +/* + * 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 java.io.IOException; +import java.util.List; + +import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; + +/** + * Returns the input values clamped 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); + + private DataType dataType; + + @FunctionInfo( + returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "version" }, + description = "Returns clamps the values of all input samples clamped to have an upper limit of max.", + examples = @Example(file = "math", tag = "ClampMax") + ) + public ClampMax( + Source source, + @Param( + name = "field", + type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" }, + description = "field to clamp." + ) Expression field, + @Param( + name = "max", + type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "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() { + if (dataType == null) { + resolveType(); + } + return 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()) { + dataType = NULL; + return resolution; + } + if (fieldDataType == NULL) { + dataType = 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()) { + dataType = NULL; + return resolution; + } + dataType = fieldDataType; + 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); + + if (dataType == DataType.BOOLEAN) { + return new ClampMaxBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + } + if (dataType == DataType.DOUBLE) { + return new ClampMaxDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + } + if (dataType == DataType.INTEGER) { + return new ClampMaxIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + } + if (dataType == DataType.UNSIGNED_LONG + || dataType == DataType.LONG + || dataType == DataType.DATETIME + || dataType == DataType.DATE_NANOS) { + return new ClampMaxLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + } + if (DataType.isString(dataType) || dataType == DataType.IP || dataType == DataType.VERSION || dataType == DataType.UNSUPPORTED) { + + return new ClampMaxBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); + } + throw EsqlIllegalArgumentException.illegalDataType(dataType); + } + + @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..57ce3bb393579 --- /dev/null +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMin.java @@ -0,0 +1,204 @@ +/* + * 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 java.io.IOException; +import java.util.List; + +import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; + +/** + * Returns the input values clamped 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); + + private DataType dataType; + + @FunctionInfo( + returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "version" }, + description = "Returns clamps the values of all input samples clamped to have a lower limit of min.", + examples = @Example(file = "math", tag = "Clampmin") + ) + public ClampMin( + Source source, + @Param( + name = "field", + type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" }, + description = "field to clamp." + ) Expression field, + @Param( + name = "min", + type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "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() { + if (dataType == null) { + resolveType(); + } + return 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()) { + dataType = NULL; + return resolution; + } + if (fieldDataType == NULL) { + dataType = 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()) { + dataType = NULL; + return resolution; + } + dataType = fieldDataType; + 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); + if (outputType == DataType.BOOLEAN) { + return new ClampMinBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + } + if (outputType == DataType.DOUBLE) { + return new ClampMinDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + } + if (outputType == DataType.INTEGER) { + return new ClampMinIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + } + if (outputType == DataType.UNSIGNED_LONG + || outputType == DataType.LONG + || outputType == DataType.DATETIME + || outputType == DataType.DATE_NANOS) { + return new ClampMinLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + } + if (DataType.isString(outputType) + || outputType == DataType.IP + || outputType == DataType.VERSION + || outputType == DataType.UNSUPPORTED) { + + return new ClampMinBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); + } + 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/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..f45aa949fd899 --- /dev/null +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampTests.java @@ -0,0 +1,195 @@ +/* + * 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.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.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(); + } + + @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 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(1, numericType, "a"), + new TestCaseSupplier.TypedData(2, numericType, "b"), + new TestCaseSupplier.TypedData(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 Clamp build(Source source, List args) { + return new Clamp(source, args.get(0), args.get(1), args.get(2)); + } +} From fb80136a7b5c2d2cd8a0b900744454f18079b0b4 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 3 Oct 2025 11:35:31 -0700 Subject: [PATCH 03/14] Finalized Clamp function --- .../functions/description/clamp_max.md | 6 + .../functions/description/clamp_min.md | 6 + .../_snippets/functions/examples/clamp_max.md | 18 ++ .../_snippets/functions/examples/clamp_min.md | 18 ++ .../_snippets/functions/layout/clamp_max.md | 23 ++ .../_snippets/functions/layout/clamp_min.md | 23 ++ .../functions/parameters/clamp_max.md | 10 + .../functions/parameters/clamp_min.md | 10 + .../_snippets/functions/types/clamp_max.md | 16 ++ .../_snippets/functions/types/clamp_min.md | 16 ++ .../esql/images/functions/clamp_max.svg | 1 + .../esql/images/functions/clamp_min.svg | 1 + .../definition/functions/clamp_max.json | 175 ++++++++++++++++ .../definition/functions/clamp_min.json | 175 ++++++++++++++++ .../esql/kibana/docs/functions/clamp_max.md | 9 + .../esql/kibana/docs/functions/clamp_min.md | 9 + .../resources/k8s-timeseries-clamp.csv-spec | 27 +++ .../function/scalar/conditional/ClampMax.java | 8 +- .../function/scalar/conditional/ClampMin.java | 8 +- .../scalar/conditional/ClampMaxTests.java | 191 +++++++++++++++++ .../scalar/conditional/ClampMinTests.java | 196 ++++++++++++++++++ .../scalar/conditional/ClampTests.java | 19 +- 22 files changed, 953 insertions(+), 12 deletions(-) create mode 100644 docs/reference/query-languages/esql/_snippets/functions/description/clamp_max.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/description/clamp_min.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/layout/clamp_max.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/layout/clamp_min.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_max.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/parameters/clamp_min.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/types/clamp_max.md create mode 100644 docs/reference/query-languages/esql/_snippets/functions/types/clamp_min.md create mode 100644 docs/reference/query-languages/esql/images/functions/clamp_max.svg create mode 100644 docs/reference/query-languages/esql/images/functions/clamp_min.svg create mode 100644 docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json create mode 100644 docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json create mode 100644 docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md create mode 100644 docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md create mode 100644 x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMaxTests.java create mode 100644 x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/ClampMinTests.java 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_max.md b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md new file mode 100644 index 0000000000000..60e89091138b2 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md @@ -0,0 +1,18 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Example** + +```esql +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) +``` + +| 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..60e89091138b2 --- /dev/null +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md @@ -0,0 +1,18 @@ +% This is generated by ESQL's AbstractFunctionTestCase. Do not edit it. See ../README.md for how to regenerate it. + +**Example** + +```esql +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) +``` + +| 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/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_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_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_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_max.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json new file mode 100644 index 0000000000000..e9c8d27baedfb --- /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| 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/definition/functions/clamp_min.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json new file mode 100644 index 0000000000000..6694867a00b16 --- /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" : [ + "TS 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_max.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md new file mode 100644 index 0000000000000..f147101dac702 --- /dev/null +++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.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 MAX +Returns clamps the values of all input samples clamped to have an upper limit of max. + +```esql +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) +``` 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..2e887ec9722ea --- /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 +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) +``` 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 index 055707634413f..7563764e48b4f 100644 --- 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 @@ -1,16 +1,43 @@ clamp_of_double_no_grouping required_capability: ts_command_v0 +// tag::clamp-min[] +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-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 +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-min-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_double_no_grouping +required_capability: ts_command_v0 +// 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 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 index caf9ca6d67476..8d05d2ca2ea52 100644 --- 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 @@ -41,20 +41,20 @@ public class ClampMax extends EsqlScalarFunction { private DataType dataType; @FunctionInfo( - returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "version" }, + 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 = "math", tag = "ClampMax") + examples = @Example(file = "k8s-timeseries-clamp", tag = "clamp-max") ) public ClampMax( Source source, @Param( name = "field", - type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" }, + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, description = "field to clamp." ) Expression field, @Param( name = "max", - type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" }, + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, description = "The max value to clamp data into." ) Expression 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 index 57ce3bb393579..19330ce4cac24 100644 --- 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 @@ -41,20 +41,20 @@ public class ClampMin extends EsqlScalarFunction { private DataType dataType; @FunctionInfo( - returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "version" }, + 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 = "math", tag = "Clampmin") + examples = @Example(file = "k8s-timeseries-clamp", tag = "clamp-min") ) public ClampMin( Source source, @Param( name = "field", - type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" }, + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, description = "field to clamp." ) Expression field, @Param( name = "min", - type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" }, + type = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, description = "The min value to clamp data into." ) Expression 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 index f45aa949fd899..178c983b4a8d9 100644 --- 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 @@ -11,12 +11,14 @@ 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; @@ -58,6 +60,15 @@ public static Iterable parameters() { ) ); } + // 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()) { @@ -75,9 +86,9 @@ public static Iterable parameters() { List.of(numericType, numericType, numericType), () -> new TestCaseSupplier.TestCase( List.of( - new TestCaseSupplier.TypedData(1, numericType, "a"), - new TestCaseSupplier.TypedData(2, numericType, "b"), - new TestCaseSupplier.TypedData(3, numericType, "c") + 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, @@ -189,7 +200,7 @@ public static Iterable parameters() { public void testFold() {} @Override - protected Clamp build(Source source, List args) { + protected EsqlScalarFunction build(Source source, List args) { return new Clamp(source, args.get(0), args.get(1), args.get(2)); } } From 32c6735c66c60a3109d796c0bd362f25f27085cc Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 3 Oct 2025 14:20:03 -0700 Subject: [PATCH 04/14] fixup --- .../src/main/resources/k8s-timeseries-clamp.csv-spec | 7 +++---- .../elasticsearch/xpack/esql/action/EsqlCapabilities.java | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) 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 index 7563764e48b4f..3bfeac6836d12 100644 --- 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 @@ -1,5 +1,6 @@ clamp_of_double_no_grouping required_capability: ts_command_v0 +required_capability: clamp_functions // tag::clamp-min[] 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) @@ -24,6 +25,7 @@ full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_ 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) @@ -48,6 +50,7 @@ full_clamped_cost:double | clamped_cost:double | clamped_min_cost:double | time_ clamp_of_long required_capability: ts_command_v0 +required_capability: clamp_functions // tag::clamp[] TS k8s | STATS bytes_in = sum(network.bytes_in), @@ -71,8 +74,4 @@ bytes_in:long | clamped_network_bytes_in:long | time_bucket:datetime 3188 | 1295 | 2024-05-10T00:08:00.000Z 3850 | 1468 | 2024-05-10T00:09:00.000Z - - - - ; 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 69645ade20cd7..aff285b0690d7 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 @@ -1226,6 +1226,7 @@ public enum Cap { */ INCREASE, DELTA_TS_AGG, + CLAMP_FUNCTIONS, /** * Extra field types in the k8s.csv dataset From b0a43482480605b4e451180fb1faf7eb92eede49 Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 7 Oct 2025 15:28:41 -0700 Subject: [PATCH 05/14] comments --- .../expression/function/scalar/Clamp.java | 12 +----- .../esql/expression/function/scalar/adsads | 36 ----------------- .../esql/expression/function/scalar/asdasd | 2 - .../function/scalar/conditional/ClampMax.java | 40 +++++-------------- .../function/scalar/conditional/ClampMin.java | 31 +++++--------- 5 files changed, 21 insertions(+), 100 deletions(-) delete mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads delete mode 100644 x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd 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 index 75da9d10f6927..e8c3323ae6963 100644 --- 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 @@ -38,7 +38,6 @@ public class Clamp extends EsqlScalarFunction implements SurrogateExpression { private final Expression field; private final Expression min; private final Expression max; - private DataType dataType; @FunctionInfo( returnType = { "double", "integer", "long", "double", "unsigned_long", "keyword", "ip", "boolean", "date", "version" }, @@ -99,11 +98,9 @@ protected TypeResolution resolveType() { fieldDataType.typeName() ); if (resolution.unresolved()) { - dataType = NULL; return resolution; } if (fieldDataType == NULL) { - dataType = NULL; return new TypeResolution("'field' must not be null in clamp()"); } for (Expression child : List.of(children().get(1), children().get(2))) { @@ -111,24 +108,19 @@ protected TypeResolution resolveType() { child, t -> t.isNumeric() ? fieldDataType.isNumeric() : t.noText() == fieldDataType, sourceText(), - TypeResolutions.ParamOrdinal.SECOND, + child == children().get(1) ? TypeResolutions.ParamOrdinal.SECOND : TypeResolutions.ParamOrdinal.THIRD, fieldDataType.typeName() ); if (childRes.unresolved()) { - dataType = NULL; return childRes; } } - dataType = fieldDataType; return TypeResolution.TYPE_RESOLVED; } @Override public DataType dataType() { - if (dataType == null) { - resolveType(); - } - return dataType; + return field.dataType(); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads deleted file mode 100644 index 38d546fdf29dd..0000000000000 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/adsads +++ /dev/null @@ -1,36 +0,0 @@ -<[boolean, byte, date, date_nanos, double, float, half_float, integer, ip, keyword, long, scaled_float, short, text, unsigned_long, version]> but was: -<[boolean, date, datetime, double, integer, ip, long, string, time, timestamp, unsigned_long, version]> - - -<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: -<[boolean, byte, date, datetime, double, float, integer, ip, long, string, time, timestamp, unsigned_long, version]> - - -<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: -<[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, unsigned_long, version]> - - -:<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: - <[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, time, timestamp, unsigned_long, version]> - - - -<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: -<[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, time, timestamp, unsigned_long, version]> - - - -<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: -<[boolean, byte, date, datetime, double, float, integer, ip, keyword, long, text, unsigned_long, version]> - - -<[boolean, byte, date, double, float, integer, ip, keyword, long, unsigned_long, version]> but was: -<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> - - -:<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: - <[boolean, byte, date, double, float, integer, ip, keyword, long, unsigned_long, version]> - - -<[boolean, byte, date, double, float, integer, ip, keyword, long, text, unsigned_long, version]> but was: -<[boolean, byte, date, double, float, integer, ip, keyword, long, unsigned_long, version]> diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd deleted file mode 100644 index bc50fd510af10..0000000000000 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/asdasd +++ /dev/null @@ -1,2 +0,0 @@ -:<[boolean, date, double, integer, ip, keyword, long, unsigned_long, version]> but was: - <[boolean, date, double, float, integer, ip, keyword, long, unsigned_long, version]> 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 index 8d05d2ca2ea52..8cbafe7384da2 100644 --- 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 @@ -26,6 +26,7 @@ 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; @@ -38,8 +39,6 @@ public class ClampMax extends EsqlScalarFunction { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "ClampMax", ClampMax::new); - private DataType dataType; - @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.", @@ -79,10 +78,7 @@ public String getWriteableName() { @Override public DataType dataType() { - if (dataType == null) { - resolveType(); - } - return dataType; + return children().getFirst().dataType(); } @Override @@ -102,11 +98,9 @@ protected TypeResolution resolveType() { fieldDataType.typeName() ); if (resolution.unresolved()) { - dataType = NULL; return resolution; } if (fieldDataType == NULL) { - dataType = NULL; return new TypeResolution("'field' must not be null in clamp()"); } resolution = TypeResolutions.isType( @@ -117,10 +111,8 @@ protected TypeResolution resolveType() { fieldDataType.typeName() ); if (resolution.unresolved()) { - dataType = NULL; return resolution; } - dataType = fieldDataType; return TypeResolution.TYPE_RESOLVED; } @@ -149,26 +141,14 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { ? Cast.cast(source(), max.dataType(), outputType, toEvaluator.apply(max)) : toEvaluator.apply(max); - if (dataType == DataType.BOOLEAN) { - return new ClampMaxBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); - } - if (dataType == DataType.DOUBLE) { - return new ClampMaxDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); - } - if (dataType == DataType.INTEGER) { - return new ClampMaxIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); - } - if (dataType == DataType.UNSIGNED_LONG - || dataType == DataType.LONG - || dataType == DataType.DATETIME - || dataType == DataType.DATE_NANOS) { - return new ClampMaxLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); - } - if (DataType.isString(dataType) || dataType == DataType.IP || dataType == DataType.VERSION || dataType == DataType.UNSUPPORTED) { - - return new ClampMaxBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), maxF); - } - throw EsqlIllegalArgumentException.illegalDataType(dataType); + 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") 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 index 19330ce4cac24..5586199ca050e 100644 --- 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 @@ -26,6 +26,7 @@ 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; @@ -148,29 +149,15 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { var minF = outputType != min.dataType() ? Cast.cast(source(), min.dataType(), outputType, toEvaluator.apply(min)) : toEvaluator.apply(min); - if (outputType == DataType.BOOLEAN) { - return new ClampMinBooleanEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); - } - if (outputType == DataType.DOUBLE) { - return new ClampMinDoubleEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); - } - if (outputType == DataType.INTEGER) { - return new ClampMinIntegerEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); - } - if (outputType == DataType.UNSIGNED_LONG - || outputType == DataType.LONG - || outputType == DataType.DATETIME - || outputType == DataType.DATE_NANOS) { - return new ClampMinLongEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); - } - if (DataType.isString(outputType) - || outputType == DataType.IP - || outputType == DataType.VERSION - || outputType == DataType.UNSUPPORTED) { - return new ClampMinBytesRefEvaluator.Factory(source(), toEvaluator.apply(children().get(0)), minF); - } - throw EsqlIllegalArgumentException.illegalDataType(outputType); + 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") From 14c12145219b8898096f391c4c4501aa24497485 Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 7 Oct 2025 15:38:42 -0700 Subject: [PATCH 06/14] fix the tests --- .../_snippets/functions/examples/clamp_min.md | 2 +- .../definition/functions/clamp_min.json | 2 +- .../esql/kibana/docs/functions/clamp_min.md | 2 +- .../resources/k8s-timeseries-clamp.csv-spec | 22 +++++++++---------- 4 files changed, 14 insertions(+), 14 deletions(-) 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 index 60e89091138b2..7f835aab3ebfb 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md @@ -3,7 +3,7 @@ **Example** ```esql -TS k8s +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/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json index 6694867a00b16..5572b05f3a9bb 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_min.json @@ -168,7 +168,7 @@ } ], "examples" : [ - "TS 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)" + "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_min.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md index 2e887ec9722ea..c267ec1aac292 100644 --- a/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md +++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_min.md @@ -4,6 +4,6 @@ Returns clamps the values of all input samples clamped to have a lower limit of min. ```esql -TS k8s +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 index 3bfeac6836d12..e64dc832c43ac 100644 --- 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 @@ -2,24 +2,24 @@ clamp_of_double_no_grouping required_capability: ts_command_v0 required_capability: clamp_functions // tag::clamp-min[] -TS k8s +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 -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 +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[] -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 +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 ; From 36e8b0abe77cf823b971cf68dd50495dbd0c3601 Mon Sep 17 00:00:00 2001 From: Pablo Date: Tue, 7 Oct 2025 19:44:33 -0700 Subject: [PATCH 07/14] fixup --- .../esql/_snippets/functions/examples/clamp_min.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 index 7f835aab3ebfb..2d4edc73a8e96 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_min.md @@ -9,10 +9,9 @@ FROM k8s | 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 | +| 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 | From 10d640d2425ccc44f7725750e196ad9f887fac4b Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 8 Oct 2025 09:38:09 -0700 Subject: [PATCH 08/14] fixup - comments --- .../resources/k8s-timeseries-clamp.csv-spec | 23 ++++++++++++++++++ .../expression/function/scalar/Clamp.java | 24 ++++--------------- .../scalar/ScalarFunctionWritables.java | 1 - .../function/scalar/conditional/ClampMax.java | 2 +- .../function/scalar/conditional/ClampMin.java | 13 ++-------- 5 files changed, 31 insertions(+), 32 deletions(-) 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 index e64dc832c43ac..ccfdffff2745d 100644 --- 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 @@ -75,3 +75,26 @@ bytes_in:long | clamped_network_bytes_in:long | time_bucket:datetime 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/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 index e8c3323ae6963..0d65105da897a 100644 --- 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 @@ -7,8 +7,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar; -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.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.core.expression.Expression; @@ -22,7 +20,6 @@ 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 org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; import java.io.IOException; import java.util.List; @@ -33,7 +30,6 @@ * 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 { - public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Clamp", Clamp::new); private final Expression field; private final Expression min; @@ -68,18 +64,9 @@ public Clamp( this.max = max; } - private Clamp(StreamInput in) throws IOException { - this( - Source.readFrom((PlanStreamInput) in), - in.readNamedWriteable(Expression.class), - in.readNamedWriteable(Expression.class), - in.readNamedWriteable(Expression.class) - ); - } - @Override public String getWriteableName() { - return ENTRY.name; + throw new UnsupportedOperationException("Clamp does not support serialization (as it should be replaced by ClampMin and ClampMax)"); } @Override @@ -135,15 +122,14 @@ protected NodeInfo info() { @Override public void writeTo(StreamOutput out) throws IOException { - source().writeTo(out); - out.writeNamedWriteable(field); - out.writeNamedWriteable(min); - out.writeNamedWriteable(max); + throw new UnsupportedOperationException("Clamp does not support serialization (as it should be replaced by ClampMin and ClampMax)"); } @Override public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { - return null; + throw new UnsupportedOperationException( + "Clamp should have been replaced by ClampMin and ClampMax. Something went wrong in the ESQL engine." + ); } @Override 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 d1dab4debb8c2..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 @@ -92,7 +92,6 @@ public static List getNamedWriteables() { entries.add(MonthName.ENTRY); entries.add(IpPrefix.ENTRY); entries.add(Least.ENTRY); - entries.add(Clamp.ENTRY); entries.add(ClampMax.ENTRY); entries.add(ClampMin.ENTRY); entries.add(Left.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 index 8cbafe7384da2..1d9c7b2c703d8 100644 --- 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 @@ -34,7 +34,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; /** - * Returns the input values clamped to have an upper limit of max. + * 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); 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 index 5586199ca050e..0ecdddb678059 100644 --- 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 @@ -34,13 +34,11 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.NULL; /** - * Returns the input values clamped to have a lower limit of min. + * 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); - private DataType dataType; - @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.", @@ -80,10 +78,7 @@ public String getWriteableName() { @Override public DataType dataType() { - if (dataType == null) { - resolveType(); - } - return dataType; + return children().getFirst().dataType(); } @Override @@ -103,11 +98,9 @@ protected TypeResolution resolveType() { fieldDataType.typeName() ); if (resolution.unresolved()) { - dataType = NULL; return resolution; } if (fieldDataType == NULL) { - dataType = NULL; return new TypeResolution("'field' must not be null in clamp()"); } resolution = TypeResolutions.isType( @@ -118,10 +111,8 @@ protected TypeResolution resolveType() { fieldDataType.typeName() ); if (resolution.unresolved()) { - dataType = NULL; return resolution; } - dataType = fieldDataType; return TypeResolution.TYPE_RESOLVED; } From 5df1d1c2c26805b7cd143d7eb70a855e5ae850b8 Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 8 Oct 2025 11:07:04 -0700 Subject: [PATCH 09/14] fxup --- .../expression/function/scalar/Clamp.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) 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 index 0d65105da897a..5cdfd3929fcd8 100644 --- 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 @@ -7,6 +7,8 @@ package org.elasticsearch.xpack.esql.expression.function.scalar; +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.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.core.expression.Expression; @@ -20,6 +22,7 @@ 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 org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; import java.io.IOException; import java.util.List; @@ -30,6 +33,7 @@ * 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 { + public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Clamp", Clamp::new); private final Expression field; private final Expression min; @@ -64,9 +68,18 @@ public Clamp( this.max = max; } + private Clamp(StreamInput in) throws IOException { + this( + Source.readFrom((PlanStreamInput) in), + in.readNamedWriteable(Expression.class), + in.readNamedWriteable(Expression.class), + in.readNamedWriteable(Expression.class) + ); + } + @Override public String getWriteableName() { - throw new UnsupportedOperationException("Clamp does not support serialization (as it should be replaced by ClampMin and ClampMax)"); + return ENTRY.name; } @Override @@ -122,14 +135,16 @@ protected NodeInfo info() { @Override public void writeTo(StreamOutput out) throws IOException { - throw new UnsupportedOperationException("Clamp does not support serialization (as it should be replaced by ClampMin and ClampMax)"); + source().writeTo(out); + out.writeNamedWriteable(field); + out.writeNamedWriteable(min); + out.writeNamedWriteable(max); } @Override public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { throw new UnsupportedOperationException( - "Clamp should have been replaced by ClampMin and ClampMax. Something went wrong in the ESQL engine." - ); + "Clamp should have been replaced by ClampMin and ClampMax. Something went wrong in the compute engine."); } @Override From 60528088ad75cc12561d563d2b9b03a7f47508ce Mon Sep 17 00:00:00 2001 From: elasticsearchmachine Date: Wed, 8 Oct 2025 18:17:40 +0000 Subject: [PATCH 10/14] [CI] Auto commit changes from spotless --- .../xpack/esql/expression/function/scalar/Clamp.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 5cdfd3929fcd8..4314436c6abc3 100644 --- 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 @@ -144,7 +144,8 @@ public void writeTo(StreamOutput out) throws IOException { @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."); + "Clamp should have been replaced by ClampMin and ClampMax. Something went wrong in the compute engine." + ); } @Override From e78023baccfed3d6e4dd360c802f4f8c8e51c2f5 Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 8 Oct 2025 12:06:32 -0700 Subject: [PATCH 11/14] fixup writable --- .../conditional/ClampMaxBooleanEvaluator.java | 40 +++++++++---------- .../ClampMaxBytesRefEvaluator.java | 40 +++++++++---------- .../conditional/ClampMaxDoubleEvaluator.java | 40 +++++++++---------- .../conditional/ClampMaxIntegerEvaluator.java | 40 +++++++++---------- .../conditional/ClampMaxLongEvaluator.java | 40 +++++++++---------- .../conditional/ClampMinBooleanEvaluator.java | 40 +++++++++---------- .../ClampMinBytesRefEvaluator.java | 40 +++++++++---------- .../conditional/ClampMinDoubleEvaluator.java | 40 +++++++++---------- .../conditional/ClampMinIntegerEvaluator.java | 40 +++++++++---------- .../conditional/ClampMinLongEvaluator.java | 40 +++++++++---------- .../scalar/ScalarFunctionWritables.java | 1 + 11 files changed, 201 insertions(+), 200 deletions(-) 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 index a202b3ed517c9..8a828c781e6db 100644 --- 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 @@ -71,27 +71,27 @@ public long 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++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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)); 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 index b03a0fe2ea190..38d47429edcc5 100644 --- 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 @@ -74,27 +74,27 @@ public BytesRefBlock eval(int positionCount, BytesRefBlock fieldBlock, BytesRefB BytesRef fieldScratch = new BytesRef(); BytesRef maxScratch = new BytesRef(); position: for (int p = 0; p < positionCount; p++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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); 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 index 67e8c992f405a..f931e15956179 100644 --- 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 @@ -71,27 +71,27 @@ public long 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++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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)); 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 index e3e947d3b7ac8..7a60af399851d 100644 --- 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 @@ -71,27 +71,27 @@ public long 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; + 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; } - 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; + 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)); 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 index 20f1582f4dab1..21f5b65ef871e 100644 --- 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 @@ -71,27 +71,27 @@ public long 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++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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)); 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 index 44d7c3fc95ca1..ce4fc04004e9a 100644 --- 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 @@ -71,27 +71,27 @@ public long 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++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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)); 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 index 3dc87157eb091..474aedc4306f8 100644 --- 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 @@ -74,27 +74,27 @@ public BytesRefBlock eval(int positionCount, BytesRefBlock fieldBlock, BytesRefB BytesRef fieldScratch = new BytesRef(); BytesRef minScratch = new BytesRef(); position: for (int p = 0; p < positionCount; p++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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); 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 index 9847370a0deba..ea7a91d42e23f 100644 --- 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 @@ -71,27 +71,27 @@ public long 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++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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)); 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 index f000190054d3f..a8ec2068bd571 100644 --- 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 @@ -71,27 +71,27 @@ public long 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; + 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; } - 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; + 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)); 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 index ce082ed520df1..39912eaf269fb 100644 --- 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 @@ -71,27 +71,27 @@ public long 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++) { - if (fieldBlock.isNull(p)) { - result.appendNull(); - continue position; + 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; } - 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; + 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)); 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 961d577692aa0..d1dab4debb8c2 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 @@ -92,6 +92,7 @@ public static List getNamedWriteables() { entries.add(MonthName.ENTRY); entries.add(IpPrefix.ENTRY); entries.add(Least.ENTRY); + entries.add(Clamp.ENTRY); entries.add(ClampMax.ENTRY); entries.add(ClampMin.ENTRY); entries.add(Left.ENTRY); From 18ae4efbbf030db3a96aaff9a398f2bc9b82d081 Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 8 Oct 2025 13:16:14 -0700 Subject: [PATCH 12/14] fixup --- .../esql/expression/function/scalar/Clamp.java | 18 ++---------------- .../scalar/ScalarFunctionWritables.java | 1 - .../scalar/conditional/ClampTests.java | 7 ++++++- 3 files changed, 8 insertions(+), 18 deletions(-) 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 index 4314436c6abc3..5ef74c4c7044a 100644 --- 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 @@ -33,8 +33,6 @@ * 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 { - public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Clamp", Clamp::new); - private final Expression field; private final Expression min; private final Expression max; @@ -68,18 +66,9 @@ public Clamp( this.max = max; } - private Clamp(StreamInput in) throws IOException { - this( - Source.readFrom((PlanStreamInput) in), - in.readNamedWriteable(Expression.class), - in.readNamedWriteable(Expression.class), - in.readNamedWriteable(Expression.class) - ); - } - @Override public String getWriteableName() { - return ENTRY.name; + throw new UnsupportedOperationException("Clamp does not support serialization."); } @Override @@ -135,10 +124,7 @@ protected NodeInfo info() { @Override public void writeTo(StreamOutput out) throws IOException { - source().writeTo(out); - out.writeNamedWriteable(field); - out.writeNamedWriteable(min); - out.writeNamedWriteable(max); + throw new UnsupportedOperationException("Clamp does not support serialization."); } @Override 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 d1dab4debb8c2..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 @@ -92,7 +92,6 @@ public static List getNamedWriteables() { entries.add(MonthName.ENTRY); entries.add(IpPrefix.ENTRY); entries.add(Least.ENTRY); - entries.add(Clamp.ENTRY); entries.add(ClampMax.ENTRY); entries.add(ClampMin.ENTRY); entries.add(Left.ENTRY); 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 index 178c983b4a8d9..0a71793c3d529 100644 --- 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 @@ -31,7 +31,12 @@ public ClampTests(@Name("TestCase") Supplier testCase this.testCase = testCaseSupplier.get(); } - @ParametersFactory + @Override + protected boolean canSerialize() { + return false; + } + + @ParametersFactory public static Iterable parameters() { List suppliers = new java.util.ArrayList<>(); for (DataType stringType : DataType.stringTypes()) { From f8460f29f2ab7ea40f41d190ab55897114e3315c Mon Sep 17 00:00:00 2001 From: elasticsearchmachine Date: Wed, 8 Oct 2025 20:23:46 +0000 Subject: [PATCH 13/14] [CI] Auto commit changes from spotless --- .../xpack/esql/expression/function/scalar/Clamp.java | 3 --- .../expression/function/scalar/conditional/ClampTests.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) 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 index 5ef74c4c7044a..21c3271163202 100644 --- 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 @@ -7,8 +7,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar; -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.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.core.expression.Expression; @@ -22,7 +20,6 @@ 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 org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; import java.io.IOException; import java.util.List; 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 index 0a71793c3d529..d56c885fed1a3 100644 --- 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 @@ -36,7 +36,7 @@ protected boolean canSerialize() { return false; } - @ParametersFactory + @ParametersFactory public static Iterable parameters() { List suppliers = new java.util.ArrayList<>(); for (DataType stringType : DataType.stringTypes()) { From 1be59d7b3f7c2b5491ebd5d570e5cd9113c9f9d2 Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 8 Oct 2025 14:13:17 -0700 Subject: [PATCH 14/14] fixup --- .../esql/_snippets/functions/examples/clamp_max.md | 3 ++- .../esql/kibana/definition/functions/clamp_max.json | 2 +- .../query-languages/esql/kibana/docs/functions/clamp_max.md | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) 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 index 60e89091138b2..d4d25c611aa85 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md +++ b/docs/reference/query-languages/esql/_snippets/functions/examples/clamp_max.md @@ -4,7 +4,8 @@ ```esql 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) +| 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 | 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 index e9c8d27baedfb..594cf22e0c931 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/clamp_max.json @@ -168,7 +168,7 @@ } ], "examples" : [ - "TS 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)" + "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/docs/functions/clamp_max.md b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md index f147101dac702..793a219d5a682 100644 --- a/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md +++ b/docs/reference/query-languages/esql/kibana/docs/functions/clamp_max.md @@ -5,5 +5,6 @@ Returns clamps the values of all input samples clamped to have an upper limit of ```esql 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) +| EVAL full_clamped_cost = clamp(network.cost, 1, 20) +| KEEP full_clamped_cost, @timestamp ```