diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash.md
new file mode 100644
index 0000000000000..2527a90db74e7
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Calculates the `geohash` of the supplied geo_point at the specified precision. The result is long encoded. Use [ST_GEOHASH_TO_STRING](#esql-st_geohash_to_string) to convert the result to a string. Or use [ST_GEOHASH_TO_GEOSHAPE](#esql-st_geohash_to_geoshape) to convert either the long or string `geohash` to a These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query) and the [`geohash_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geohashgrid-aggregation).
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_geoshape.md
new file mode 100644
index 0000000000000..98f834a5e8587
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_geoshape.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value to a `geo_shape` value. The input values are expected to be the grid-ids of geohash grids, in either long or string format.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_long.md
new file mode 100644
index 0000000000000..2abb0c416addb
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_long.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value representing a geohash grid-ID in string format into a long.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_string.md
new file mode 100644
index 0000000000000..5e1cc240c4731
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohash_to_string.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value representing a geohash grid-ID in long format into a string.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex.md
new file mode 100644
index 0000000000000..2b31926bef047
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Calculates the `geohex`, the H3 cell-id, of the supplied geo_point at the specified precision. The result is long encoded. Use [ST_GEOHEX_TO_STRING](#esql-st_geohex_to_string) to convert the result to a string. Or use [ST_GEOHEX_TO_GEOSHAPE](#esql-st_geohex_to_geoshape) to convert either the long or string `geohex` to a POLYGON geo_shape. These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query) and the [`geohex_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geohexgrid-aggregation).
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_geoshape.md
new file mode 100644
index 0000000000000..10547ae8c4bbc
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_geoshape.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value to a `geo_shape` value. The input values are expected to be the grid-ids of H3 grids, in either long or string format.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_long.md
new file mode 100644
index 0000000000000..b391ae1d685eb
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_long.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value representing a geohex grid-ID in string format into a long.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_string.md
new file mode 100644
index 0000000000000..7915ff0170e05
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geohex_to_string.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value representing a Geohex grid-ID in long format into a string.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile.md
new file mode 100644
index 0000000000000..c473312687c7a
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Calculates the `geotile` of the supplied geo_point at the specified precision. The result is long encoded. Use [ST_GEOTILE_TO_STRING](#esql-st_geotile_to_string) to convert the result to a string. Or use [ST_GEOTILE_TO_GEOSHAPE](#esql-st_geotile_to_geoshape) to convert either the long or string `geotile` to a POLYGON geo_shape. These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query) and the [`geotile_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geotilegrid-aggregation).
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_geoshape.md
new file mode 100644
index 0000000000000..f8cbfa8878d15
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_geoshape.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value to a `geo_shape` value. The input values are expected to be the grid-ids of geotile grids, in either long or string format.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_long.md
new file mode 100644
index 0000000000000..7ebe7a08d2a4d
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_long.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value representing a geotile grid-ID in string format into a long.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_string.md
new file mode 100644
index 0000000000000..71fd355372975
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/description/st_geotile_to_string.md
@@ -0,0 +1,6 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Description**
+
+Converts an input value representing a geotile grid-ID in long format into a string.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash.md
new file mode 100644
index 0000000000000..f9fab4135d31f
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash.md
@@ -0,0 +1,38 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+FROM airports
+| EVAL geohash = ST_GEOHASH(location, 1)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| WHERE count >= 10
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| KEEP count, centroid, geohashString
+| SORT count DESC, geohashString ASC
+```
+
+| count:long | centroid:geo_point | geohashString:keyword |
+| --- | --- | --- |
+| 118 | POINT (-77.41857436454018 26.96522968734409) | d |
+| 96 | POINT (23.181679135886952 27.295384635654045) | s |
+| 94 | POINT (70.94076107503807 25.691916451026547) | t |
+| 90 | POINT (-104.3941700803116 30.811849871650338) | 9 |
+| 89 | POINT (18.71573683606942 53.165169130707305) | u |
+| 85 | POINT (114.3722876966657 24.908398092505248) | w |
+| 51 | POINT (-61.44522591713159 -22.87209844956284) | 6 |
+| 38 | POINT (-9.429514887252529 25.497624435045413) | e |
+| 34 | POINT (-111.8071846965262 52.464381378993174) | c |
+| 30 | POINT (28.7045472683385 -14.706001980230212) | k |
+| 28 | POINT (159.52750137208827 -25.555616633001982) | r |
+| 22 | POINT (-4.410395708612421 54.90304926367985) | g |
+| 21 | POINT (-69.40534970590046 50.93379438189523) | f |
+| 17 | POINT (114.05526293222519 -10.898114638950895) | q |
+| 16 | POINT (147.40052131412085 21.054660080408212) | x |
+| 13 | POINT (63.64716878519035 54.37333276101317) | v |
+| 12 | POINT (-39.53510569408536 -11.72166372067295) | 7 |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_geoshape.md
new file mode 100644
index 0000000000000..4990583a2f8c6
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_geoshape.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geohash = "u3bu"
+| EVAL boundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+```
+
+| geohash:keyword | boundary:geo_shape |
+| --- | --- |
+| u3bu | POLYGON((12.3046875 55.546875, 12.65625 55.546875, 12.65625 55.72265625, 12.3046875 55.72265625, 12.3046875 55.546875)) |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_long.md
new file mode 100644
index 0000000000000..a5dd10b329f6f
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_long.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geohash = "u3bu"
+| EVAL geohashLong = ST_GEOHASH_TO_LONG(geohash)
+```
+
+| geohash:keyword | geohashLong:long |
+| --- | --- |
+| u3bu | 13686180 |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_string.md
new file mode 100644
index 0000000000000..dd6327499254f
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohash_to_string.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geohash = TO_LONG(13686180)
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+```
+
+| geohash:long | geohashString:keyword |
+| --- | --- |
+| 13686180 | u3bu |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex.md
new file mode 100644
index 0000000000000..875add22968d6
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex.md
@@ -0,0 +1,32 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+FROM airports
+| EVAL geohex = ST_GEOHEX(location, 1)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| WHERE count >= 10
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| KEEP count, centroid, geohexString
+| SORT count DESC, geohexString ASC
+```
+
+| count:long | centroid:geo_point | geohexString:keyword |
+| --- | --- | --- |
+| 22 | POINT (7.250850197689777 48.21363834643059) | 811fbffffffffff |
+| 18 | POINT (-80.64959161449224 40.04119813675061) | 812abffffffffff |
+| 17 | POINT (-0.7606179875266903 52.86413913565304) | 81197ffffffffff |
+| 13 | POINT (22.53157936179867 41.98255742864254) | 811efffffffffff |
+| 13 | POINT (78.30096947387435 26.073904778951636) | 813dbffffffffff |
+| 12 | POINT (-76.39781514415517 45.16300531569868) | 812bbffffffffff |
+| 12 | POINT (-100.30120467301458 20.114154297625646) | 8149bffffffffff |
+| 11 | POINT (18.037187419831753 48.66540593306788) | 811e3ffffffffff |
+| 11 | POINT (-83.42379064553164 33.18388901439241) | 8144fffffffffff |
+| 11 | POINT (-99.4237939513881 27.100012352774765) | 8148bffffffffff |
+| 10 | POINT (128.01009018346667 35.8699960866943) | 8130fffffffffff |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_geoshape.md
new file mode 100644
index 0000000000000..544e9d4dfa39d
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_geoshape.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geohex = "841f059ffffffff"
+| EVAL boundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+```
+
+| geohex:keyword | boundary:geo_shape |
+| --- | --- |
+| 841f059ffffffff | POLYGON ((12.353546327258265 55.80335405461356, 12.2434612967008 55.60502874054935, 12.51733872608954 55.470800201545316, 12.901880149865134 55.53443156197633, 13.014270156228921 55.73262985778208, 12.739822958959069 55.8673267391136, 12.353546327258265 55.80335405461356)) |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_long.md
new file mode 100644
index 0000000000000..be94b611917aa
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_long.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geohex = "841f059ffffffff"
+| EVAL geohexLong = ST_GEOHEX_TO_LONG(geohex)
+```
+
+| geohex:keyword | geohexLong:long |
+| --- | --- |
+| 841f059ffffffff | 595020895127339007 |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_string.md
new file mode 100644
index 0000000000000..b3eabacd5ec14
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geohex_to_string.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geohex = 595020895127339007
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+```
+
+| geohex:long | geohexString:keyword |
+| --- | --- |
+| 595020895127339007 | 841f059ffffffff |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile.md
new file mode 100644
index 0000000000000..0653f55c969c7
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile.md
@@ -0,0 +1,31 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+FROM airports
+| EVAL geotile = ST_GEOTILE(location, 2)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| SORT count DESC, geotileString ASC
+| KEEP count, centroid, geotileString
+```
+
+| count:long | centroid:geo_point | geotileString:keyword |
+| --- | --- | --- |
+| 286 | POINT (39.31202001609169 35.149993664386415) | 2/2/1 |
+| 197 | POINT (-55.387361375756825 31.952955322292855) | 2/1/1 |
+| 136 | POINT (-110.97162496141048 36.87185255084734) | 2/0/1 |
+| 106 | POINT (119.35907618669827 25.46263281488791) | 2/3/1 |
+| 67 | POINT (-58.031108492373754 -22.624166105151065) | 2/1/2 |
+| 46 | POINT (142.95455511274707 -20.581492295427978) | 2/3/2 |
+| 34 | POINT (31.38476753634784 -14.64374022804858) | 2/2/2 |
+| 8 | POINT (-160.0723083713092 -19.124013530672528) | 2/0/2 |
+| 6 | POINT (23.95813101902604 70.17537698848173) | 2/2/0 |
+| 3 | POINT (-133.4001641627401 72.06833167467266) | 2/0/0 |
+| 2 | POINT (-68.47209956031293 66.77569948369637) | 2/1/0 |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_geoshape.md
new file mode 100644
index 0000000000000..9f0331fe632b8
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_geoshape.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geotile = "4/8/5"
+| EVAL boundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+```
+
+| geotile:keyword | boundary:geo_shape |
+| --- | --- |
+| 4/8/5 | POLYGON((0.0 40.979898069620134, 22.5 40.979898069620134, 22.5 55.77657301866769, 0.0 55.77657301866769, 0.0 40.979898069620134)) |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_long.md
new file mode 100644
index 0000000000000..995f04ee6f355
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_long.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geotile = "4/8/5"
+| EVAL geotileLong = ST_GEOTILE_TO_LONG(geotile)
+```
+
+| geotile:keyword | geotileLong:long |
+| --- | --- |
+| 4/8/5 | 1152921508901814277 |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_string.md
new file mode 100644
index 0000000000000..6e7a95b6ff342
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/examples/st_geotile_to_string.md
@@ -0,0 +1,14 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Example**
+
+```esql
+ROW geotile = 1152921508901814277
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+```
+
+| geotile:long | geotileString:keyword |
+| --- | --- |
+| 1152921508901814277 | 4/8/5 |
+
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash.md
new file mode 100644
index 0000000000000..f84175b382f21
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+## `ST_GEOHASH` [esql-st_geohash]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohash.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohash.md
+:::
+
+:::{include} ../description/st_geohash.md
+:::
+
+:::{include} ../types/st_geohash.md
+:::
+
+:::{include} ../examples/st_geohash.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_geoshape.md
new file mode 100644
index 0000000000000..616208fab61ab
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_geoshape.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOHASH_TO_GEOSHAPE` [esql-st_geohash_to_geoshape]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohash_to_geoshape.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohash_to_geoshape.md
+:::
+
+:::{include} ../description/st_geohash_to_geoshape.md
+:::
+
+:::{include} ../types/st_geohash_to_geoshape.md
+:::
+
+:::{include} ../examples/st_geohash_to_geoshape.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_long.md
new file mode 100644
index 0000000000000..e9f37660cedf9
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_long.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOHASH_TO_LONG` [esql-st_geohash_to_long]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohash_to_long.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohash_to_long.md
+:::
+
+:::{include} ../description/st_geohash_to_long.md
+:::
+
+:::{include} ../types/st_geohash_to_long.md
+:::
+
+:::{include} ../examples/st_geohash_to_long.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_string.md
new file mode 100644
index 0000000000000..cfed286eeee68
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohash_to_string.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOHASH_TO_STRING` [esql-st_geohash_to_string]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohash_to_string.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohash_to_string.md
+:::
+
+:::{include} ../description/st_geohash_to_string.md
+:::
+
+:::{include} ../types/st_geohash_to_string.md
+:::
+
+:::{include} ../examples/st_geohash_to_string.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex.md
new file mode 100644
index 0000000000000..95c7c49ed50bc
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+## `ST_GEOHEX` [esql-st_geohex]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohex.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohex.md
+:::
+
+:::{include} ../description/st_geohex.md
+:::
+
+:::{include} ../types/st_geohex.md
+:::
+
+:::{include} ../examples/st_geohex.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_geoshape.md
new file mode 100644
index 0000000000000..debfd69bc1af2
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_geoshape.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOHEX_TO_GEOSHAPE` [esql-st_geohex_to_geoshape]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohex_to_geoshape.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohex_to_geoshape.md
+:::
+
+:::{include} ../description/st_geohex_to_geoshape.md
+:::
+
+:::{include} ../types/st_geohex_to_geoshape.md
+:::
+
+:::{include} ../examples/st_geohex_to_geoshape.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_long.md
new file mode 100644
index 0000000000000..6fe48a030d188
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_long.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOHEX_TO_LONG` [esql-st_geohex_to_long]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohex_to_long.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohex_to_long.md
+:::
+
+:::{include} ../description/st_geohex_to_long.md
+:::
+
+:::{include} ../types/st_geohex_to_long.md
+:::
+
+:::{include} ../examples/st_geohex_to_long.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_string.md
new file mode 100644
index 0000000000000..be1b882a50676
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geohex_to_string.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOHEX_TO_STRING` [esql-st_geohex_to_string]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geohex_to_string.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geohex_to_string.md
+:::
+
+:::{include} ../description/st_geohex_to_string.md
+:::
+
+:::{include} ../types/st_geohex_to_string.md
+:::
+
+:::{include} ../examples/st_geohex_to_string.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile.md
new file mode 100644
index 0000000000000..b6a8d1b70ffe8
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+## `ST_GEOTILE` [esql-st_geotile]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geotile.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geotile.md
+:::
+
+:::{include} ../description/st_geotile.md
+:::
+
+:::{include} ../types/st_geotile.md
+:::
+
+:::{include} ../examples/st_geotile.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_geoshape.md
new file mode 100644
index 0000000000000..526c536c6c8b3
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_geoshape.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOTILE_TO_GEOSHAPE` [esql-st_geotile_to_geoshape]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geotile_to_geoshape.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geotile_to_geoshape.md
+:::
+
+:::{include} ../description/st_geotile_to_geoshape.md
+:::
+
+:::{include} ../types/st_geotile_to_geoshape.md
+:::
+
+:::{include} ../examples/st_geotile_to_geoshape.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_long.md
new file mode 100644
index 0000000000000..eb5828218376d
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_long.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOTILE_TO_LONG` [esql-st_geotile_to_long]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geotile_to_long.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geotile_to_long.md
+:::
+
+:::{include} ../description/st_geotile_to_long.md
+:::
+
+:::{include} ../types/st_geotile_to_long.md
+:::
+
+:::{include} ../examples/st_geotile_to_long.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_string.md
new file mode 100644
index 0000000000000..b1481b33788a4
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/layout/st_geotile_to_string.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### `ST_GEOTILE_TO_STRING` [esql-st_geotile_to_string]
+
+**Syntax**
+
+:::{image} ../../../images/functions/st_geotile_to_string.svg
+:alt: Embedded
+:class: text-center
+:::
+
+
+:::{include} ../parameters/st_geotile_to_string.md
+:::
+
+:::{include} ../description/st_geotile_to_string.md
+:::
+
+:::{include} ../types/st_geotile_to_string.md
+:::
+
+:::{include} ../examples/st_geotile_to_string.md
+:::
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash.md
new file mode 100644
index 0000000000000..6980bf5442a1d
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash.md
@@ -0,0 +1,13 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`geometry`
+: Expression of type `geo_point`. If `null`, the function returns `null`.
+
+`precision`
+: Expression of type `integer`. If `null`, the function returns `null`. Valid values are between [1 and 12](https://en.wikipedia.org/wiki/Geohash).
+
+`bounds`
+: Optional bounds to filter the grid tiles, either a `geo_shape` or an array of at least two `geo_point`s. The envelope of the `geo_shape` is used as bounds.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_geoshape.md
new file mode 100644
index 0000000000000..5db5ddfdfcd2c
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_geoshape.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geohash grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_long.md
new file mode 100644
index 0000000000000..5db5ddfdfcd2c
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_long.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geohash grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_string.md
new file mode 100644
index 0000000000000..5db5ddfdfcd2c
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohash_to_string.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geohash grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex.md
new file mode 100644
index 0000000000000..a9fecf05bb84a
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex.md
@@ -0,0 +1,13 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`geometry`
+: Expression of type `geo_point`. If `null`, the function returns `null`.
+
+`precision`
+: Expression of type `integer`. If `null`, the function returns `null`. Valid values are between [0 and 15](https://h3geo.org/docs/core-library/restable/).
+
+`bounds`
+: Optional bounds to filter the grid tiles, either a `geo_shape` or an array of at least two `geo_point`s. The envelope of the `geo_shape` is used as bounds.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_geoshape.md
new file mode 100644
index 0000000000000..c929ae395f5fc
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_geoshape.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input H3 grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_long.md
new file mode 100644
index 0000000000000..e86ac82e67003
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_long.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geohex grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_string.md
new file mode 100644
index 0000000000000..cb78878f500bf
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geohex_to_string.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input Geohex grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile.md
new file mode 100644
index 0000000000000..7f5e4e6f7c80c
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile.md
@@ -0,0 +1,13 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`geometry`
+: Expression of type `geo_point`. If `null`, the function returns `null`.
+
+`precision`
+: Expression of type `integer`. If `null`, the function returns `null`. Valid values are between [0 and 29](https://wiki.openstreetmap.org/wiki/Zoom_levels).
+
+`bounds`
+: Optional bounds to filter the grid tiles, either a `geo_shape` or an array of at least two `geo_point`s. The envelope of the `geo_shape` is used as bounds.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_geoshape.md
new file mode 100644
index 0000000000000..4a16a5a87faad
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_geoshape.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geotile grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_long.md
new file mode 100644
index 0000000000000..4a16a5a87faad
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_long.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geotile grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_string.md
new file mode 100644
index 0000000000000..4a16a5a87faad
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_geotile_to_string.md
@@ -0,0 +1,7 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Parameters**
+
+`grid_id`
+: Input geotile grid-id. The input can be a single- or multi-valued column or an expression.
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash.md
new file mode 100644
index 0000000000000..c7f908dd18458
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash.md
@@ -0,0 +1,8 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| geometry | precision | bounds | result |
+| --- | --- | --- | --- |
+| geo_point | integer | | long |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_geoshape.md
new file mode 100644
index 0000000000000..5dec4762ba6ef
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_geoshape.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | geo_shape |
+| long | geo_shape |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_long.md
new file mode 100644
index 0000000000000..fb31bb5d9f657
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_long.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | long |
+| long | long |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_string.md
new file mode 100644
index 0000000000000..5d10b76e7bd09
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohash_to_string.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | keyword |
+| long | keyword |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex.md
new file mode 100644
index 0000000000000..c7f908dd18458
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex.md
@@ -0,0 +1,8 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| geometry | precision | bounds | result |
+| --- | --- | --- | --- |
+| geo_point | integer | | long |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_geoshape.md
new file mode 100644
index 0000000000000..5dec4762ba6ef
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_geoshape.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | geo_shape |
+| long | geo_shape |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_long.md
new file mode 100644
index 0000000000000..fb31bb5d9f657
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_long.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | long |
+| long | long |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_string.md
new file mode 100644
index 0000000000000..5d10b76e7bd09
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geohex_to_string.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | keyword |
+| long | keyword |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile.md
new file mode 100644
index 0000000000000..c7f908dd18458
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile.md
@@ -0,0 +1,8 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| geometry | precision | bounds | result |
+| --- | --- | --- | --- |
+| geo_point | integer | | long |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_geoshape.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_geoshape.md
new file mode 100644
index 0000000000000..5dec4762ba6ef
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_geoshape.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | geo_shape |
+| long | geo_shape |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_long.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_long.md
new file mode 100644
index 0000000000000..fb31bb5d9f657
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_long.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | long |
+| long | long |
+
diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_string.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_string.md
new file mode 100644
index 0000000000000..5d10b76e7bd09
--- /dev/null
+++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_geotile_to_string.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+**Supported types**
+
+| grid_id | result |
+| --- | --- |
+| keyword | keyword |
+| long | keyword |
+
diff --git a/docs/reference/query-languages/esql/_snippets/lists/spatial-functions.md b/docs/reference/query-languages/esql/_snippets/lists/spatial-functions.md
index 83cfdd4c60867..3621e44ac4f53 100644
--- a/docs/reference/query-languages/esql/_snippets/lists/spatial-functions.md
+++ b/docs/reference/query-languages/esql/_snippets/lists/spatial-functions.md
@@ -10,3 +10,15 @@
* [preview] [`ST_XMIN`](../../functions-operators/spatial-functions.md#esql-st_xmin)
* [preview] [`ST_YMAX`](../../functions-operators/spatial-functions.md#esql-st_ymax)
* [preview] [`ST_YMIN`](../../functions-operators/spatial-functions.md#esql-st_ymin)
+* [preview] [`ST_GEOTILE`](../../functions-operators/spatial-functions.md#esql-st_geotile)
+ * [preview] [`ST_GEOTILE_TO_STRING`](../../functions-operators/spatial-functions.md#esql-st_geotile_to_string)
+ * [preview] [`ST_GEOTILE_TO_LONG`](../../functions-operators/spatial-functions.md#esql-st_geotile_to_long)
+ * [preview] [`ST_GEOTILE_TO_GEOSHAPE`](../../functions-operators/spatial-functions.md#esql-st_geotile_to_geoshape)
+* [preview] [`ST_GEOHEX`](../../functions-operators/spatial-functions.md#esql-st_geohex)
+ * [preview] [`ST_GEOHEX_TO_STRING`](../../functions-operators/spatial-functions.md#esql-st_geohex_to_string)
+ * [preview] [`ST_GEOHEX_TO_LONG`](../../functions-operators/spatial-functions.md#esql-st_geohex_to_long)
+ * [preview] [`ST_GEOHEX_TO_GEOSHAPE`](../../functions-operators/spatial-functions.md#esql-st_geohex_to_geoshape)
+* [preview] [`ST_GEOHASH`](../../functions-operators/spatial-functions.md#esql-st_geohash)
+ * [preview] [`ST_GEOHASH_TO_STRING`](../../functions-operators/spatial-functions.md#esql-st_geohash_to_string)
+ * [preview] [`ST_GEOHASH_TO_LONG`](../../functions-operators/spatial-functions.md#esql-st_geohash_to_long)
+ * [preview] [`ST_GEOHASH_TO_GEOSHAPE`](../../functions-operators/spatial-functions.md#esql-st_geohash_to_geoshape)
diff --git a/docs/reference/query-languages/esql/functions-operators/spatial-functions.md b/docs/reference/query-languages/esql/functions-operators/spatial-functions.md
index 806e5e8157a63..c4441e12a14ae 100644
--- a/docs/reference/query-languages/esql/functions-operators/spatial-functions.md
+++ b/docs/reference/query-languages/esql/functions-operators/spatial-functions.md
@@ -48,3 +48,38 @@ mapped_pages:
:::{include} ../_snippets/functions/layout/st_ymin.md
:::
+:::{include} ../_snippets/functions/layout/st_geotile.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geotile_to_string.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geotile_to_long.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geotile_to_geoshape.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohex.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohex_to_string.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohex_to_long.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohex_to_geoshape.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohash.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohash_to_string.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohash_to_long.md
+:::
+
+:::{include} ../_snippets/functions/layout/st_geohash_to_geoshape.md
+:::
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohash.svg b/docs/reference/query-languages/esql/images/functions/st_geohash.svg
new file mode 100644
index 0000000000000..231866d25d521
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohash_to_geoshape.svg b/docs/reference/query-languages/esql/images/functions/st_geohash_to_geoshape.svg
new file mode 100644
index 0000000000000..bac257c45dd0b
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohash_to_geoshape.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohash_to_long.svg b/docs/reference/query-languages/esql/images/functions/st_geohash_to_long.svg
new file mode 100644
index 0000000000000..869af17d522de
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohash_to_long.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohash_to_string.svg b/docs/reference/query-languages/esql/images/functions/st_geohash_to_string.svg
new file mode 100644
index 0000000000000..dcb7e909d81dd
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohash_to_string.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohex.svg b/docs/reference/query-languages/esql/images/functions/st_geohex.svg
new file mode 100644
index 0000000000000..588cb5374a617
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohex.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohex_to_geoshape.svg b/docs/reference/query-languages/esql/images/functions/st_geohex_to_geoshape.svg
new file mode 100644
index 0000000000000..0d814904e233f
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohex_to_geoshape.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohex_to_long.svg b/docs/reference/query-languages/esql/images/functions/st_geohex_to_long.svg
new file mode 100644
index 0000000000000..39181484ebe7a
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohex_to_long.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geohex_to_string.svg b/docs/reference/query-languages/esql/images/functions/st_geohex_to_string.svg
new file mode 100644
index 0000000000000..3d96677e1f87e
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geohex_to_string.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geotile.svg b/docs/reference/query-languages/esql/images/functions/st_geotile.svg
new file mode 100644
index 0000000000000..ee97511f568cc
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geotile.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geotile_to_geoshape.svg b/docs/reference/query-languages/esql/images/functions/st_geotile_to_geoshape.svg
new file mode 100644
index 0000000000000..26d8d6c7a0778
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geotile_to_geoshape.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geotile_to_long.svg b/docs/reference/query-languages/esql/images/functions/st_geotile_to_long.svg
new file mode 100644
index 0000000000000..8ac85e2baa2d4
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geotile_to_long.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/images/functions/st_geotile_to_string.svg b/docs/reference/query-languages/esql/images/functions/st_geotile_to_string.svg
new file mode 100644
index 0000000000000..cbe406dd19211
--- /dev/null
+++ b/docs/reference/query-languages/esql/images/functions/st_geotile_to_string.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash.json
new file mode 100644
index 0000000000000..7b6c4ae3d92d0
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash.json
@@ -0,0 +1,31 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohash",
+ "description" : "Calculates the `geohash` of the supplied geo_point at the specified precision.\nThe result is long encoded. Use ST_GEOHASH_TO_STRING to convert the result to a string.\nOr use ST_GEOHASH_TO_GEOSHAPE to convert either the long or string `geohash` to a\n\nThese functions are related to the `geo_grid` query\nand the `geohash_grid` aggregation.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "geometry",
+ "type" : "geo_point",
+ "optional" : false,
+ "description" : "Expression of type `geo_point`. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "precision",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "Expression of type `integer`. If `null`, the function returns `null`. Valid values are between [1 and 12](https://en.wikipedia.org/wiki/Geohash)."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ }
+ ],
+ "examples" : [
+ "FROM airports\n| EVAL geohash = ST_GEOHASH(location, 1)\n| STATS\n count = COUNT(*),\n centroid = ST_CENTROID_AGG(location)\n BY geohash\n| WHERE count >= 10\n| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)\n| KEEP count, centroid, geohashString\n| SORT count DESC, geohashString ASC"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_geoshape.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_geoshape.json
new file mode 100644
index 0000000000000..9997c0f166d95
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_geoshape.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohash_to_geoshape",
+ "description" : "Converts an input value to a `geo_shape` value.\nThe input values are expected to be the grid-ids of geohash grids, in either long or string format.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "geo_shape"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "geo_shape"
+ }
+ ],
+ "examples" : [
+ "ROW geohash = \"u3bu\"\n| EVAL boundary = ST_GEOHASH_TO_GEOSHAPE(geohash)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_long.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_long.json
new file mode 100644
index 0000000000000..f6b14f780e955
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_long.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohash_to_long",
+ "description" : "Converts an input value representing a geohash grid-ID in string format into a long.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ }
+ ],
+ "examples" : [
+ "ROW geohash = \"u3bu\"\n| EVAL geohashLong = ST_GEOHASH_TO_LONG(geohash)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_string.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_string.json
new file mode 100644
index 0000000000000..d6b55c483ced5
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohash_to_string.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohash_to_string",
+ "description" : "Converts an input value representing a geohash grid-ID in long format into a string.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ }
+ ],
+ "examples" : [
+ "ROW geohash = TO_LONG(13686180)\n| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex.json
new file mode 100644
index 0000000000000..0f2faadd1da28
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex.json
@@ -0,0 +1,31 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohex",
+ "description" : "Calculates the `geohex`, the H3 cell-id, of the supplied geo_point at the specified precision.\nThe result is long encoded. Use ST_GEOHEX_TO_STRING to convert the result to a string.\nOr use ST_GEOHEX_TO_GEOSHAPE to convert either the long or string `geohex` to a\nPOLYGON geo_shape.\n\nThese functions are related to the `geo_grid` query\nand the `geohex_grid` aggregation.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "geometry",
+ "type" : "geo_point",
+ "optional" : false,
+ "description" : "Expression of type `geo_point`. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "precision",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "Expression of type `integer`. If `null`, the function returns `null`. Valid values are between [0 and 15](https://h3geo.org/docs/core-library/restable/)."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ }
+ ],
+ "examples" : [
+ "FROM airports\n| EVAL geohex = ST_GEOHEX(location, 1)\n| STATS\n count = COUNT(*),\n centroid = ST_CENTROID_AGG(location)\n BY geohex\n| WHERE count >= 10\n| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)\n| KEEP count, centroid, geohexString\n| SORT count DESC, geohexString ASC"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_geoshape.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_geoshape.json
new file mode 100644
index 0000000000000..dde729a91ed2a
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_geoshape.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohex_to_geoshape",
+ "description" : "Converts an input value to a `geo_shape` value.\nThe input values are expected to be the grid-ids of H3 grids, in either long or string format.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input H3 grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "geo_shape"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input H3 grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "geo_shape"
+ }
+ ],
+ "examples" : [
+ "ROW geohex = \"841f059ffffffff\"\n| EVAL boundary = ST_GEOHEX_TO_GEOSHAPE(geohex)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_long.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_long.json
new file mode 100644
index 0000000000000..474c4f63ea8a6
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_long.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohex_to_long",
+ "description" : "Converts an input value representing a geohex grid-ID in string format into a long.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geohex grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geohex grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ }
+ ],
+ "examples" : [
+ "ROW geohex = \"841f059ffffffff\"\n| EVAL geohexLong = ST_GEOHEX_TO_LONG(geohex)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_string.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_string.json
new file mode 100644
index 0000000000000..0d83038db609e
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geohex_to_string.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geohex_to_string",
+ "description" : "Converts an input value representing a Geohex grid-ID in long format into a string.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input Geohex grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input Geohex grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ }
+ ],
+ "examples" : [
+ "ROW geohex = 595020895127339007\n| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile.json
new file mode 100644
index 0000000000000..e3e9dfcade0af
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile.json
@@ -0,0 +1,31 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geotile",
+ "description" : "Calculates the `geotile` of the supplied geo_point at the specified precision.\nThe result is long encoded. Use ST_GEOTILE_TO_STRING to convert the result to a string.\nOr use ST_GEOTILE_TO_GEOSHAPE to convert either the long or string `geotile` to a\nPOLYGON geo_shape.\n\nThese functions are related to the `geo_grid` query\nand the `geotile_grid` aggregation.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "geometry",
+ "type" : "geo_point",
+ "optional" : false,
+ "description" : "Expression of type `geo_point`. If `null`, the function returns `null`."
+ },
+ {
+ "name" : "precision",
+ "type" : "integer",
+ "optional" : false,
+ "description" : "Expression of type `integer`. If `null`, the function returns `null`. Valid values are between [0 and 29](https://wiki.openstreetmap.org/wiki/Zoom_levels)."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ }
+ ],
+ "examples" : [
+ "FROM airports\n| EVAL geotile = ST_GEOTILE(location, 2)\n| STATS\n count = COUNT(*),\n centroid = ST_CENTROID_AGG(location)\n BY geotile\n| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)\n| SORT count DESC, geotileString ASC\n| KEEP count, centroid, geotileString"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_geoshape.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_geoshape.json
new file mode 100644
index 0000000000000..1801f8d9735a3
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_geoshape.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geotile_to_geoshape",
+ "description" : "Converts an input value to a `geo_shape` value.\nThe input values are expected to be the grid-ids of geotile grids, in either long or string format.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "geo_shape"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "geo_shape"
+ }
+ ],
+ "examples" : [
+ "ROW geotile = \"4/8/5\"\n| EVAL boundary = ST_GEOTILE_TO_GEOSHAPE(geotile)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_long.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_long.json
new file mode 100644
index 0000000000000..e2aefafa33fdc
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_long.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geotile_to_long",
+ "description" : "Converts an input value representing a geotile grid-ID in string format into a long.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "long"
+ }
+ ],
+ "examples" : [
+ "ROW geotile = \"4/8/5\"\n| EVAL geotileLong = ST_GEOTILE_TO_LONG(geotile)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_string.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_string.json
new file mode 100644
index 0000000000000..effe374bab93c
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_geotile_to_string.json
@@ -0,0 +1,37 @@
+{
+ "comment" : "This is generated by ESQL’s AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.",
+ "type" : "scalar",
+ "name" : "st_geotile_to_string",
+ "description" : "Converts an input value representing a geotile grid-ID in long format into a string.",
+ "signatures" : [
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "keyword",
+ "optional" : false,
+ "description" : "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ },
+ {
+ "params" : [
+ {
+ "name" : "grid_id",
+ "type" : "long",
+ "optional" : false,
+ "description" : "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ }
+ ],
+ "variadic" : false,
+ "returnType" : "keyword"
+ }
+ ],
+ "examples" : [
+ "ROW geotile = 1152921508901814277\n| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)"
+ ],
+ "preview" : false,
+ "snapshot_only" : false
+}
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash.md
new file mode 100644
index 0000000000000..08b10e27a6d75
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash.md
@@ -0,0 +1,22 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHASH
+Calculates the `geohash` of the supplied geo_point at the specified precision.
+The result is long encoded. Use [ST_GEOHASH_TO_STRING](#esql-st_geohash_to_string) to convert the result to a string.
+Or use [ST_GEOHASH_TO_GEOSHAPE](#esql-st_geohash_to_geoshape) to convert either the long or string `geohash` to a
+
+These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query)
+and the [`geohash_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geohashgrid-aggregation).
+
+```esql
+FROM airports
+| EVAL geohash = ST_GEOHASH(location, 1)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| WHERE count >= 10
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| KEEP count, centroid, geohashString
+| SORT count DESC, geohashString ASC
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_geoshape.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_geoshape.md
new file mode 100644
index 0000000000000..debf86f9d1340
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_geoshape.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHASH TO GEOSHAPE
+Converts an input value to a `geo_shape` value.
+The input values are expected to be the grid-ids of geohash grids, in either long or string format.
+
+```esql
+ROW geohash = "u3bu"
+| EVAL boundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_long.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_long.md
new file mode 100644
index 0000000000000..f949358288160
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_long.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHASH TO LONG
+Converts an input value representing a geohash grid-ID in string format into a long.
+
+```esql
+ROW geohash = "u3bu"
+| EVAL geohashLong = ST_GEOHASH_TO_LONG(geohash)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_string.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_string.md
new file mode 100644
index 0000000000000..fbd7dbd3d4d0a
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohash_to_string.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHASH TO STRING
+Converts an input value representing a geohash grid-ID in long format into a string.
+
+```esql
+ROW geohash = TO_LONG(13686180)
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex.md
new file mode 100644
index 0000000000000..604319938b6b1
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex.md
@@ -0,0 +1,23 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHEX
+Calculates the `geohex`, the H3 cell-id, of the supplied geo_point at the specified precision.
+The result is long encoded. Use [ST_GEOHEX_TO_STRING](#esql-st_geohex_to_string) to convert the result to a string.
+Or use [ST_GEOHEX_TO_GEOSHAPE](#esql-st_geohex_to_geoshape) to convert either the long or string `geohex` to a
+POLYGON geo_shape.
+
+These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query)
+and the [`geohex_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geohexgrid-aggregation).
+
+```esql
+FROM airports
+| EVAL geohex = ST_GEOHEX(location, 1)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| WHERE count >= 10
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| KEEP count, centroid, geohexString
+| SORT count DESC, geohexString ASC
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_geoshape.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_geoshape.md
new file mode 100644
index 0000000000000..85a2339792739
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_geoshape.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHEX TO GEOSHAPE
+Converts an input value to a `geo_shape` value.
+The input values are expected to be the grid-ids of H3 grids, in either long or string format.
+
+```esql
+ROW geohex = "841f059ffffffff"
+| EVAL boundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_long.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_long.md
new file mode 100644
index 0000000000000..393cde5be1e99
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_long.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHEX TO LONG
+Converts an input value representing a geohex grid-ID in string format into a long.
+
+```esql
+ROW geohex = "841f059ffffffff"
+| EVAL geohexLong = ST_GEOHEX_TO_LONG(geohex)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_string.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_string.md
new file mode 100644
index 0000000000000..ba06c681d5af3
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geohex_to_string.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOHEX TO STRING
+Converts an input value representing a Geohex grid-ID in long format into a string.
+
+```esql
+ROW geohex = 595020895127339007
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile.md
new file mode 100644
index 0000000000000..aebc43fc14c4d
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile.md
@@ -0,0 +1,22 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOTILE
+Calculates the `geotile` of the supplied geo_point at the specified precision.
+The result is long encoded. Use [ST_GEOTILE_TO_STRING](#esql-st_geotile_to_string) to convert the result to a string.
+Or use [ST_GEOTILE_TO_GEOSHAPE](#esql-st_geotile_to_geoshape) to convert either the long or string `geotile` to a
+POLYGON geo_shape.
+
+These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query)
+and the [`geotile_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geotilegrid-aggregation).
+
+```esql
+FROM airports
+| EVAL geotile = ST_GEOTILE(location, 2)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| SORT count DESC, geotileString ASC
+| KEEP count, centroid, geotileString
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_geoshape.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_geoshape.md
new file mode 100644
index 0000000000000..e03fbdd662c96
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_geoshape.md
@@ -0,0 +1,10 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOTILE TO GEOSHAPE
+Converts an input value to a `geo_shape` value.
+The input values are expected to be the grid-ids of geotile grids, in either long or string format.
+
+```esql
+ROW geotile = "4/8/5"
+| EVAL boundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_long.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_long.md
new file mode 100644
index 0000000000000..75f42b84ae03e
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_long.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOTILE TO LONG
+Converts an input value representing a geotile grid-ID in string format into a long.
+
+```esql
+ROW geotile = "4/8/5"
+| EVAL geotileLong = ST_GEOTILE_TO_LONG(geotile)
+```
diff --git a/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_string.md b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_string.md
new file mode 100644
index 0000000000000..620047aa7ccc2
--- /dev/null
+++ b/docs/reference/query-languages/esql/kibana/docs/functions/st_geotile_to_string.md
@@ -0,0 +1,9 @@
+% This is generated by ESQL's AbstractFunctionTestCase. Do no edit it. See ../README.md for how to regenerate it.
+
+### ST GEOTILE TO STRING
+Converts an input value representing a geotile grid-ID in long format into a string.
+
+```esql
+ROW geotile = 1152921508901814277
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+```
diff --git a/x-pack/plugin/esql/build.gradle b/x-pack/plugin/esql/build.gradle
index 28c3e4d2b20cb..ad301c7595955 100644
--- a/x-pack/plugin/esql/build.gradle
+++ b/x-pack/plugin/esql/build.gradle
@@ -40,6 +40,8 @@ dependencies {
implementation project('compute:ann')
implementation project(':libs:dissect')
implementation project(':libs:grok')
+ api "org.apache.lucene:lucene-spatial3d:${versions.lucene}"
+ api project(":libs:h3")
implementation project('arrow')
// Also contains a dummy processor to allow compilation with unused annotations.
@@ -69,6 +71,10 @@ dependencies {
internalClusterTestImplementation project(":modules:mapper-extras")
}
+tasks.named("dependencyLicenses").configure {
+ mapping from: /lucene-.*/, to: 'lucene'
+}
+
def generatedPath = "src/main/generated"
def projectDirectory = project.layout.projectDirectory
def generatedSourceDir = projectDirectory.dir(generatedPath)
diff --git a/x-pack/plugin/esql/licenses/lucene-LICENSE.txt b/x-pack/plugin/esql/licenses/lucene-LICENSE.txt
new file mode 100644
index 0000000000000..28b134f5f8e4d
--- /dev/null
+++ b/x-pack/plugin/esql/licenses/lucene-LICENSE.txt
@@ -0,0 +1,475 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was
+derived from unicode conversion examples available at
+http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright
+from those sources:
+
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+
+Some code in core/src/java/org/apache/lucene/util/ArrayUtil.java was
+derived from Python 2.4.2 sources available at
+http://www.python.org. Full license is here:
+
+ http://www.python.org/download/releases/2.4.2/license/
+
+Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was
+derived from Python 3.1.2 sources available at
+http://www.python.org. Full license is here:
+
+ http://www.python.org/download/releases/3.1.2/license/
+
+Some code in core/src/java/org/apache/lucene/util/automaton was
+derived from Brics automaton sources available at
+www.brics.dk/automaton/. Here is the copyright from those sources:
+
+/*
+ * Copyright (c) 2001-2009 Anders Moeller
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+The levenshtein automata tables in core/src/java/org/apache/lucene/util/automaton
+were automatically generated with the moman/finenight FSA package.
+Here is the copyright for those sources:
+
+# Copyright (c) 2010, Jean-Philippe Barrette-LaPierre,
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was
+derived from ICU (http://www.icu-project.org)
+The full license is available here:
+ http://source.icu-project.org/repos/icu/icu/trunk/license.html
+
+/*
+ * Copyright (C) 1999-2010, International Business Machines
+ * Corporation and others. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * provided that the above copyright notice(s) and this permission notice appear
+ * in all copies of the Software and that both the above copyright notice(s) and
+ * this permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE
+ * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder shall not
+ * be used in advertising or otherwise to promote the sale, use or other
+ * dealings in this Software without prior written authorization of the
+ * copyright holder.
+ */
+
+The following license applies to the Snowball stemmers:
+
+Copyright (c) 2001, Dr Martin Porter
+Copyright (c) 2002, Richard Boulton
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holders nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The following license applies to the KStemmer:
+
+Copyright © 2003,
+Center for Intelligent Information Retrieval,
+University of Massachusetts, Amherst.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. The names "Center for Intelligent Information Retrieval" and
+"University of Massachusetts" must not be used to endorse or promote products
+derived from this software without prior written permission. To obtain
+permission, contact info@ciir.cs.umass.edu.
+
+THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF MASSACHUSETTS AND OTHER CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The following license applies to the Morfologik project:
+
+Copyright (c) 2006 Dawid Weiss
+Copyright (c) 2007-2011 Dawid Weiss, Marcin Miłkowski
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of Morfologik nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---
+
+The dictionary comes from Morfologik project. Morfologik uses data from
+Polish ispell/myspell dictionary hosted at http://www.sjp.pl/slownik/en/ and
+is licenced on the terms of (inter alia) LGPL and Creative Commons
+ShareAlike. The part-of-speech tags were added in Morfologik project and
+are not found in the data from sjp.pl. The tagset is similar to IPI PAN
+tagset.
+
+---
+
+The following license applies to the Morfeusz project,
+used by org.apache.lucene.analysis.morfologik.
+
+BSD-licensed dictionary of Polish (SGJP)
+http://sgjp.pl/morfeusz/
+
+Copyright © 2011 Zygmunt Saloni, Włodzimierz Gruszczyński,
+ Marcin Woliński, Robert Wołosz
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/x-pack/plugin/esql/licenses/lucene-NOTICE.txt b/x-pack/plugin/esql/licenses/lucene-NOTICE.txt
new file mode 100644
index 0000000000000..1a1d51572432a
--- /dev/null
+++ b/x-pack/plugin/esql/licenses/lucene-NOTICE.txt
@@ -0,0 +1,192 @@
+Apache Lucene
+Copyright 2014 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Includes software from other Apache Software Foundation projects,
+including, but not limited to:
+ - Apache Ant
+ - Apache Jakarta Regexp
+ - Apache Commons
+ - Apache Xerces
+
+ICU4J, (under analysis/icu) is licensed under an MIT styles license
+and Copyright (c) 1995-2008 International Business Machines Corporation and others
+
+Some data files (under analysis/icu/src/data) are derived from Unicode data such
+as the Unicode Character Database. See http://unicode.org/copyright.html for more
+details.
+
+Brics Automaton (under core/src/java/org/apache/lucene/util/automaton) is
+BSD-licensed, created by Anders Møller. See http://www.brics.dk/automaton/
+
+The levenshtein automata tables (under core/src/java/org/apache/lucene/util/automaton) were
+automatically generated with the moman/finenight FSA library, created by
+Jean-Philippe Barrette-LaPierre. This library is available under an MIT license,
+see http://sites.google.com/site/rrettesite/moman and
+http://bitbucket.org/jpbarrette/moman/overview/
+
+The class org.apache.lucene.util.WeakIdentityMap was derived from
+the Apache CXF project and is Apache License 2.0.
+
+The Google Code Prettify is Apache License 2.0.
+See http://code.google.com/p/google-code-prettify/
+
+JUnit (junit-4.10) is licensed under the Common Public License v. 1.0
+See http://junit.sourceforge.net/cpl-v10.html
+
+This product includes code (JaspellTernarySearchTrie) from Java Spelling Checkin
+g Package (jaspell): http://jaspell.sourceforge.net/
+License: The BSD License (http://www.opensource.org/licenses/bsd-license.php)
+
+The snowball stemmers in
+ analysis/common/src/java/net/sf/snowball
+were developed by Martin Porter and Richard Boulton.
+The snowball stopword lists in
+ analysis/common/src/resources/org/apache/lucene/analysis/snowball
+were developed by Martin Porter and Richard Boulton.
+The full snowball package is available from
+ http://snowball.tartarus.org/
+
+The KStem stemmer in
+ analysis/common/src/org/apache/lucene/analysis/en
+was developed by Bob Krovetz and Sergio Guzman-Lara (CIIR-UMass Amherst)
+under the BSD-license.
+
+The Arabic,Persian,Romanian,Bulgarian, Hindi and Bengali analyzers (common) come with a default
+stopword list that is BSD-licensed created by Jacques Savoy. These files reside in:
+analysis/common/src/resources/org/apache/lucene/analysis/ar/stopwords.txt,
+analysis/common/src/resources/org/apache/lucene/analysis/fa/stopwords.txt,
+analysis/common/src/resources/org/apache/lucene/analysis/ro/stopwords.txt,
+analysis/common/src/resources/org/apache/lucene/analysis/bg/stopwords.txt,
+analysis/common/src/resources/org/apache/lucene/analysis/hi/stopwords.txt,
+analysis/common/src/resources/org/apache/lucene/analysis/bn/stopwords.txt
+See http://members.unine.ch/jacques.savoy/clef/index.html.
+
+The German,Spanish,Finnish,French,Hungarian,Italian,Portuguese,Russian and Swedish light stemmers
+(common) are based on BSD-licensed reference implementations created by Jacques Savoy and
+Ljiljana Dolamic. These files reside in:
+analysis/common/src/java/org/apache/lucene/analysis/de/GermanLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/de/GermanMinimalStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/es/SpanishLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/fi/FinnishLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/fr/FrenchLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/fr/FrenchMinimalStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/hu/HungarianLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/it/ItalianLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/pt/PortugueseLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/ru/RussianLightStemmer.java
+analysis/common/src/java/org/apache/lucene/analysis/sv/SwedishLightStemmer.java
+
+The Stempel analyzer (stempel) includes BSD-licensed software developed
+by the Egothor project http://egothor.sf.net/, created by Leo Galambos, Martin Kvapil,
+and Edmond Nolan.
+
+The Polish analyzer (stempel) comes with a default
+stopword list that is BSD-licensed created by the Carrot2 project. The file resides
+in stempel/src/resources/org/apache/lucene/analysis/pl/stopwords.txt.
+See http://project.carrot2.org/license.html.
+
+The SmartChineseAnalyzer source code (smartcn) was
+provided by Xiaoping Gao and copyright 2009 by www.imdict.net.
+
+WordBreakTestUnicode_*.java (under modules/analysis/common/src/test/)
+is derived from Unicode data such as the Unicode Character Database.
+See http://unicode.org/copyright.html for more details.
+
+The Morfologik analyzer (morfologik) includes BSD-licensed software
+developed by Dawid Weiss and Marcin Miłkowski (http://morfologik.blogspot.com/).
+
+Morfologik uses data from Polish ispell/myspell dictionary
+(http://www.sjp.pl/slownik/en/) licenced on the terms of (inter alia)
+LGPL and Creative Commons ShareAlike.
+
+Morfologic includes data from BSD-licensed dictionary of Polish (SGJP)
+(http://sgjp.pl/morfeusz/)
+
+Servlet-api.jar and javax.servlet-*.jar are under the CDDL license, the original
+source code for this can be found at http://www.eclipse.org/jetty/downloads.php
+
+===========================================================================
+Kuromoji Japanese Morphological Analyzer - Apache Lucene Integration
+===========================================================================
+
+This software includes a binary and/or source version of data from
+
+ mecab-ipadic-2.7.0-20070801
+
+which can be obtained from
+
+ http://atilika.com/releases/mecab-ipadic/mecab-ipadic-2.7.0-20070801.tar.gz
+
+or
+
+ http://jaist.dl.sourceforge.net/project/mecab/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz
+
+===========================================================================
+mecab-ipadic-2.7.0-20070801 Notice
+===========================================================================
+
+Nara Institute of Science and Technology (NAIST),
+the copyright holders, disclaims all warranties with regard to this
+software, including all implied warranties of merchantability and
+fitness, in no event shall NAIST be liable for
+any special, indirect or consequential damages or any damages
+whatsoever resulting from loss of use, data or profits, whether in an
+action of contract, negligence or other tortuous action, arising out
+of or in connection with the use or performance of this software.
+
+A large portion of the dictionary entries
+originate from ICOT Free Software. The following conditions for ICOT
+Free Software applies to the current dictionary as well.
+
+Each User may also freely distribute the Program, whether in its
+original form or modified, to any third party or parties, PROVIDED
+that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear
+on, or be attached to, the Program, which is distributed substantially
+in the same form as set out herein and that such intended
+distribution, if actually made, will neither violate or otherwise
+contravene any of the laws and regulations of the countries having
+jurisdiction over the User or the intended distribution itself.
+
+NO WARRANTY
+
+The program was produced on an experimental basis in the course of the
+research and development conducted during the project and is provided
+to users as so produced on an experimental basis. Accordingly, the
+program is provided without any warranty whatsoever, whether express,
+implied, statutory or otherwise. The term "warranty" used herein
+includes, but is not limited to, any warranty of the quality,
+performance, merchantability and fitness for a particular purpose of
+the program and the nonexistence of any infringement or violation of
+any right of any third party.
+
+Each user of the program will agree and understand, and be deemed to
+have agreed and understood, that there is no warranty whatsoever for
+the program and, accordingly, the entire risk arising from or
+otherwise connected with the program is assumed by the user.
+
+Therefore, neither ICOT, the copyright holder, or any other
+organization that participated in or was otherwise related to the
+development of the program and their respective officials, directors,
+officers and other employees shall be held liable for any and all
+damages, including, without limitation, general, special, incidental
+and consequential damages, arising out of or otherwise in connection
+with the use or inability to use the program or any product, material
+or result produced or otherwise obtained by using the program,
+regardless of whether they have been advised of, or otherwise had
+knowledge of, the possibility of such damages at any time during the
+project or thereafter. Each user will be deemed to have agreed to the
+foregoing by his or her commencement of use of the program. The term
+"use" as used herein includes, but is not limited to, the use,
+modification, copying and distribution of the program and the
+production of secondary products from the program.
+
+In the case where the program, whether in its original form or
+modified, was distributed or delivered to or received by a user from
+any person, organization or entity other than ICOT, unless it makes or
+grants independently of ICOT any specific warranty to the user in
+writing, such person, organization or entity, will also be exempted
+from and not be held liable to the user for any such damages as noted
+above as far as the program is concerned.
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java
index 3f8478fe713a3..83276d006273a 100644
--- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/CsvAssert.java
@@ -323,7 +323,7 @@ private static String pipeTable(
if (values.size() > rows) {
result.append("...").append(System.lineSeparator());
}
- return result.toString();
+ return result.toString().replaceAll("\\s+" + System.lineSeparator(), System.lineSeparator());
}
private static String header(String name, Type type) {
diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial-grid.csv-spec b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial-grid.csv-spec
new file mode 100644
index 0000000000000..743a226ce6413
--- /dev/null
+++ b/x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial-grid.csv-spec
@@ -0,0 +1,977 @@
+###############################################
+# Tests for geo_grid function: ST_GEOHASH
+###############################################
+
+geohashStringToLong
+required_capability: spatial_grid
+
+// tag::geohash_to_long[]
+ROW geohash = "u3bu"
+| EVAL geohashLong = ST_GEOHASH_TO_LONG(geohash)
+// end::geohash_to_long[]
+;
+
+// tag::geohash_to_long-result[]
+geohash:keyword | geohashLong:long
+u3bu | 13686180
+// end::geohash_to_long-result[]
+;
+
+geohashLongToString
+required_capability: spatial_grid
+
+// tag::geohash_to_string[]
+ROW geohash = TO_LONG(13686180)
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+// end::geohash_to_string[]
+;
+
+// tag::geohash_to_string-result[]
+geohash:long | geohashString:keyword
+13686180 | u3bu
+// end::geohash_to_string-result[]
+;
+
+geohashStringToGeoShape
+required_capability: spatial_grid
+
+// tag::geohash_to_geoshape[]
+ROW geohash = "u3bu"
+| EVAL boundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+// end::geohash_to_geoshape[]
+;
+
+// tag::geohash_to_geoshape-result[]
+geohash:keyword | boundary:geo_shape
+u3bu | POLYGON((12.3046875 55.546875, 12.65625 55.546875, 12.65625 55.72265625, 12.3046875 55.72265625, 12.3046875 55.546875))
+// end::geohash_to_geoshape-result[]
+;
+
+geohashLongToGeoShape
+required_capability: spatial_grid
+
+ROW geohash = TO_LONG(13686180)
+| EVAL boundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+;
+
+geohash:long | boundary:geo_shape
+13686180 | POLYGON((12.3046875 55.546875, 12.65625 55.546875, 12.65625 55.72265625, 12.3046875 55.72265625, 12.3046875 55.546875))
+;
+
+geohashLiteral
+required_capability: spatial_grid
+
+// tag::st_geohash-literal[]
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)")
+| EVAL geohash4 = ST_GEOHASH(location, 4),
+ geohash3 = ST_GEOHASH(location, 3),
+ geohash2 = ST_GEOHASH(location, 2),
+ geohash1 = ST_GEOHASH(location, 1)
+// end::st_geohash-literal[]
+;
+
+// tag::st_geohash-literal-result[]
+location:geo_point | geohash4:long | geohash3:long | geohash2:long | geohash1:long
+POINT (12.6493508684508 55.6285017221528) | 13686180 | 427683 | 13362 | 417
+// end::st_geohash-literal-result[]
+;
+
+geohashLiteralString
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)")
+| EVAL geohash4 = ST_GEOHASH_TO_STRING(ST_GEOHASH(location, 4)),
+ geohash3 = ST_GEOHASH_TO_STRING(ST_GEOHASH(location, 3)),
+ geohash2 = ST_GEOHASH_TO_STRING(ST_GEOHASH(location, 2)),
+ geohash1 = ST_GEOHASH_TO_STRING(ST_GEOHASH(location, 1))
+;
+
+location:geo_point | geohash4:keyword | geohash3:keyword | geohash2:keyword | geohash1:keyword
+POINT(12.6493508684508 55.6285017221528) | u3bu | u3b | u3 | u
+;
+
+geohashLiteralMv
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)"), precision = [1,2,3,4,5]
+| MV_EXPAND precision
+| EVAL geohash = ST_GEOHASH(location, precision)
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+;
+
+location:geo_point | precision:integer | geohash:long | geohashString:keyword
+POINT (12.6493508684508 55.6285017221528) | 1 | 417 | u
+POINT (12.6493508684508 55.6285017221528) | 2 | 13362 | u3
+POINT (12.6493508684508 55.6285017221528) | 3 | 427683 | u3b
+POINT (12.6493508684508 55.6285017221528) | 4 | 13686180 | u3bu
+POINT (12.6493508684508 55.6285017221528) | 5 | 437958005 | u3bur
+;
+
+geohashLiteralMvBoundary
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)"), precision = [1,2,3,4,5]
+| MV_EXPAND precision
+| EVAL geohash = ST_GEOHASH_TO_STRING(ST_GEOHASH(location, precision))
+| EVAL cellBoundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+;
+
+location:geo_point | precision:integer | geohash:keyword | cellBoundary:geo_shape
+POINT (12.6493508684508 55.6285017221528) | 1 | u | POLYGON((0.0 45.0, 45.0 45.0, 45.0 90.0, 0.0 90.0, 0.0 45.0))
+POINT (12.6493508684508 55.6285017221528) | 2 | u3 | POLYGON((11.25 50.625, 22.5 50.625, 22.5 56.25, 11.25 56.25, 11.25 50.625))
+POINT (12.6493508684508 55.6285017221528) | 3 | u3b | POLYGON((11.25 54.84375, 12.65625 54.84375, 12.65625 56.25, 11.25 56.25, 11.25 54.84375))
+POINT (12.6493508684508 55.6285017221528) | 4 | u3bu | POLYGON((12.3046875 55.546875, 12.65625 55.546875, 12.65625 55.72265625, 12.3046875 55.72265625, 12.3046875 55.546875))
+POINT (12.6493508684508 55.6285017221528) | 5 | u3bur | POLYGON((12.6123046875 55.5908203125, 12.65625 55.5908203125, 12.65625 55.634765625, 12.6123046875 55.634765625, 12.6123046875 55.5908203125))
+;
+
+geohashField
+required_capability: spatial_grid
+
+FROM airports
+| WHERE abbrev == "CPH"
+| EVAL geohash = ST_GEOHASH(location, 7)
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| KEEP geohash, geohashString, abbrev, name, location
+;
+
+geohash:long | geohashString:keyword | abbrev:keyword | name:text | location:geo_point
+448469007591 | u3buryf | CPH | Copenhagen | POINT (12.6493508684508 55.6285017221528)
+;
+
+gridGeohashStatsBy
+required_capability: spatial_grid
+
+// tag::st_geohash-grid[]
+FROM airports
+| EVAL geohash = ST_GEOHASH(location, 1)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| WHERE count >= 10
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| KEEP count, centroid, geohashString
+| SORT count DESC, geohashString ASC
+// end::st_geohash-grid[]
+;
+
+// tag::st_geohash-grid-result[]
+count:long | centroid:geo_point | geohashString:keyword
+118 | POINT (-77.41857436454018 26.96522968734409) | d
+96 | POINT (23.181679135886952 27.295384635654045) | s
+94 | POINT (70.94076107503807 25.691916451026547) | t
+90 | POINT (-104.3941700803116 30.811849871650338) | 9
+89 | POINT (18.71573683606942 53.165169130707305) | u
+85 | POINT (114.3722876966657 24.908398092505248) | w
+51 | POINT (-61.44522591713159 -22.87209844956284) | 6
+38 | POINT (-9.429514887252529 25.497624435045413) | e
+34 | POINT (-111.8071846965262 52.464381378993174) | c
+30 | POINT (28.7045472683385 -14.706001980230212) | k
+28 | POINT (159.52750137208827 -25.555616633001982) | r
+22 | POINT (-4.410395708612421 54.90304926367985) | g
+21 | POINT (-69.40534970590046 50.93379438189523) | f
+17 | POINT (114.05526293222519 -10.898114638950895) | q
+16 | POINT (147.40052131412085 21.054660080408212) | x
+13 | POINT (63.64716878519035 54.37333276101317) | v
+12 | POINT (-39.53510569408536 -11.72166372067295) | 7
+// end::st_geohash-grid-result[]
+;
+
+gridGeohashStatsByBounds
+required_capability: spatial_grid
+
+FROM airports
+| EVAL geohash = ST_GEOHASH(location, 2, TO_GEOSHAPE("POLYGON((0.0 30.0, 12.0 30.0, 12.0 60.0, 0.0 60.0, 0.0 30.0))"))
+| WHERE geohash IS NOT NULL
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| EVAL cellBoundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+| KEEP count, centroid, geohashString, cellBoundary
+| SORT count DESC, geohashString ASC
+;
+
+count:long | centroid:geo_point | geohashString:keyword | cellBoundary:geo_shape
+19 | POINT (6.360728044651057 47.94084087577894) | u0 | POLYGON((0.0 45.0, 11.25 45.0, 11.25 50.625, 0.0 50.625, 0.0 45.0))
+10 | POINT (15.350638423115015 47.80751353036612) | u2 | POLYGON((11.25 45.0, 22.5 45.0, 22.5 50.625, 11.25 50.625, 11.25 45.0))
+9 | POINT (18.5217544157058 42.1394603792578) | sr | POLYGON((11.25 39.375, 22.5 39.375, 22.5 45.0, 11.25 45.0, 11.25 39.375))
+8 | POINT (6.351574736181647 51.8981519783847) | u1 | POLYGON((0.0 50.625, 11.25 50.625, 11.25 56.25, 0.0 56.25, 0.0 50.625))
+7 | POINT (5.268637698941997 42.747250193330856) | sp | POLYGON((0.0 39.375, 11.25 39.375, 11.25 45.0, 0.0 45.0, 0.0 39.375))
+7 | POINT (17.092350951528974 53.365471504096476) | u3 | POLYGON((11.25 50.625, 22.5 50.625, 22.5 56.25, 11.25 56.25, 11.25 50.625))
+5 | POINT (16.2651440910995 58.812188878655434) | u6 | POLYGON((11.25 56.25, 22.5 56.25, 22.5 61.875, 11.25 61.875, 11.25 56.25))
+4 | POINT (7.7012718468904495 36.39783004182391) | sn | POLYGON((0.0 33.75, 11.25 33.75, 11.25 39.375, 0.0 39.375, 0.0 33.75))
+3 | POINT (14.222751930356026 37.168446206487715) | sq | POLYGON((11.25 33.75, 22.5 33.75, 22.5 39.375, 11.25 39.375, 11.25 33.75))
+3 | POINT (7.318722177296877 59.788265260867774) | u4 | POLYGON((0.0 56.25, 11.25 56.25, 11.25 61.875, 0.0 61.875, 0.0 56.25))
+2 | POINT (16.706149326637387 32.37822346854955) | sm | POLYGON((11.25 28.125, 22.5 28.125, 22.5 33.75, 11.25 33.75, 11.25 28.125))
+;
+
+gridGeohashStatsByPointsBounds
+required_capability: spatial_grid
+
+FROM airports
+| EVAL points = ["POINT(0.0 30.0)", "POINT(12.0 60.0)"]
+| EVAL geohash = ST_GEOHASH(location, 2, TO_GEOPOINT(points))
+| WHERE geohash IS NOT NULL
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| EVAL cellBoundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+| KEEP count, centroid, geohashString, cellBoundary
+| SORT count DESC, geohashString ASC
+;
+
+count:long | centroid:geo_point | geohashString:keyword | cellBoundary:geo_shape
+19 | POINT (6.360728044651057 47.94084087577894) | u0 | POLYGON((0.0 45.0, 11.25 45.0, 11.25 50.625, 0.0 50.625, 0.0 45.0))
+10 | POINT (15.350638423115015 47.80751353036612) | u2 | POLYGON((11.25 45.0, 22.5 45.0, 22.5 50.625, 11.25 50.625, 11.25 45.0))
+9 | POINT (18.5217544157058 42.1394603792578) | sr | POLYGON((11.25 39.375, 22.5 39.375, 22.5 45.0, 11.25 45.0, 11.25 39.375))
+8 | POINT (6.351574736181647 51.8981519783847) | u1 | POLYGON((0.0 50.625, 11.25 50.625, 11.25 56.25, 0.0 56.25, 0.0 50.625))
+7 | POINT (5.268637698941997 42.747250193330856) | sp | POLYGON((0.0 39.375, 11.25 39.375, 11.25 45.0, 0.0 45.0, 0.0 39.375))
+7 | POINT (17.092350951528974 53.365471504096476) | u3 | POLYGON((11.25 50.625, 22.5 50.625, 22.5 56.25, 11.25 56.25, 11.25 50.625))
+5 | POINT (16.2651440910995 58.812188878655434) | u6 | POLYGON((11.25 56.25, 22.5 56.25, 22.5 61.875, 11.25 61.875, 11.25 56.25))
+4 | POINT (7.7012718468904495 36.39783004182391) | sn | POLYGON((0.0 33.75, 11.25 33.75, 11.25 39.375, 0.0 39.375, 0.0 33.75))
+3 | POINT (14.222751930356026 37.168446206487715) | sq | POLYGON((11.25 33.75, 22.5 33.75, 22.5 39.375, 11.25 39.375, 11.25 33.75))
+3 | POINT (7.318722177296877 59.788265260867774) | u4 | POLYGON((0.0 56.25, 11.25 56.25, 11.25 61.875, 0.0 61.875, 0.0 56.25))
+2 | POINT (16.706149326637387 32.37822346854955) | sm | POLYGON((11.25 28.125, 22.5 28.125, 22.5 33.75, 11.25 33.75, 11.25 28.125))
+;
+
+gridGeohashDocsFromCell
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, ST_GEOHASH_TO_GEOSHAPE("u1"))
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+;
+
+count:long | centroid:geo_point
+8 | POINT (6.351574736181647 51.8981519783847)
+;
+
+gridGeohashStatsByWhereUK
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| EVAL geohash = ST_GEOHASH(location, 2)
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| EVAL cellBoundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+| KEEP count, centroid, geohashString, cellBoundary
+| SORT count DESC
+;
+
+count:long | centroid:geo_point | geohashString:keyword | cellBoundary:geo_shape
+14 | POINT (-2.5644131543646966 53.38093495994274) | gc | POLYGON((-11.25 50.625, 0.0 50.625, 0.0 56.25, -11.25 56.25, -11.25 50.625))
+3 | POINT (-2.7510103583335876 58.79020635969937) | gf | POLYGON((-11.25 56.25, 0.0 56.25, 0.0 61.875, -11.25 61.875, -11.25 56.25))
+;
+
+gridGeohashStatsByBoundsUK
+required_capability: spatial_grid
+
+FROM airports
+| EVAL bounds = ST_ENVELOPE(TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| EVAL geohash = ST_GEOHASH(location, 2, bounds)
+| WHERE geohash IS NOT NULL
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohash
+| EVAL geohashString = ST_GEOHASH_TO_STRING(geohash)
+| EVAL cellBoundary = ST_GEOHASH_TO_GEOSHAPE(geohash)
+| KEEP count, centroid, geohashString, cellBoundary
+| SORT count DESC, geohashString ASC
+;
+
+count:long | centroid:geo_point | geohashString:keyword | cellBoundary:geo_shape
+19 | POINT (6.360728044651057 47.94084087577894) | u0 | POLYGON((0.0 45.0, 11.25 45.0, 11.25 50.625, 0.0 50.625, 0.0 45.0))
+17 | POINT (-3.5034258844440473 53.25306422789307) | gc | POLYGON((-11.25 50.625, 0.0 50.625, 0.0 56.25, -11.25 56.25, -11.25 50.625))
+8 | POINT (6.351574736181647 51.8981519783847) | u1 | POLYGON((0.0 50.625, 11.25 50.625, 11.25 56.25, 0.0 56.25, 0.0 50.625))
+3 | POINT (-2.7510103583335876 58.79020635969937) | gf | POLYGON((-11.25 56.25, 0.0 56.25, 0.0 61.875, -11.25 61.875, -11.25 56.25))
+3 | POINT (7.318722177296877 59.788265260867774) | u4 | POLYGON((0.0 56.25, 11.25 56.25, 11.25 61.875, 0.0 61.875, 0.0 56.25))
+;
+
+gridGeohashInStatsBy
+required_capability: spatial_grid
+
+FROM airports
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY ST_GEOHASH(location, 1)
+| SORT count DESC
+| KEEP count, centroid
+| LIMIT 10
+;
+
+count:long | centroid:geo_point
+ 118 | POINT (-77.41857436454018 26.96522968734409)
+ 96 | POINT (23.181679135886952 27.295384635654045)
+ 94 | POINT (70.94076107503807 25.691916451026547)
+ 90 | POINT (-104.3941700803116 30.811849871650338)
+ 89 | POINT (18.71573683606942 53.165169130707305)
+ 85 | POINT (114.3722876966657 24.908398092505248)
+ 51 | POINT (-61.44522591713159 -22.87209844956284)
+ 38 | POINT (-9.429514887252529 25.497624435045413)
+ 34 | POINT (-111.8071846965262 52.464381378993174)
+ 30 | POINT (28.7045472683385 -14.706001980230212)
+;
+
+gridGeohashInStatsByWhereUK
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY ST_GEOHASH(location, 2)
+| KEEP count, centroid
+| SORT count DESC
+;
+
+count:long | centroid:geo_point
+14 | POINT (-2.5644131543646966 53.38093495994274)
+3 | POINT (-2.7510103583335876 58.79020635969937)
+;
+
+###############################################
+# Tests for geo_grid function: ST_GEOTILE
+###############################################
+
+geotileStringToLong
+required_capability: spatial_grid
+
+// tag::geotile_to_long[]
+ROW geotile = "4/8/5"
+| EVAL geotileLong = ST_GEOTILE_TO_LONG(geotile)
+// end::geotile_to_long[]
+;
+
+// tag::geotile_to_long-result[]
+geotile:keyword | geotileLong:long
+4/8/5 | 1152921508901814277
+// end::geotile_to_long-result[]
+;
+
+geotileLongToString
+required_capability: spatial_grid
+
+// tag::geotile_to_string[]
+ROW geotile = 1152921508901814277
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+// end::geotile_to_string[]
+;
+
+// tag::geotile_to_string-result[]
+geotile:long | geotileString:keyword
+1152921508901814277 | 4/8/5
+// end::geotile_to_string-result[]
+;
+
+geotileStringToGeoshape
+required_capability: spatial_grid
+
+// tag::geotile_to_geoshape[]
+ROW geotile = "4/8/5"
+| EVAL boundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+// end::geotile_to_geoshape[]
+;
+
+// tag::geotile_to_geoshape-result[]
+geotile:keyword | boundary:geo_shape
+4/8/5 | POLYGON((0.0 40.979898069620134, 22.5 40.979898069620134, 22.5 55.77657301866769, 0.0 55.77657301866769, 0.0 40.979898069620134))
+// end::geotile_to_geoshape-result[]
+;
+
+geotileLiteral
+required_capability: spatial_grid
+
+// tag::st_geotile-literal[]
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)")
+| EVAL geotile4 = ST_GEOTILE(location, 4),
+ geotile3 = ST_GEOTILE(location, 3),
+ geotile2 = ST_GEOTILE(location, 2),
+ geotile1 = ST_GEOTILE(location, 1)
+// end::st_geotile-literal[]
+;
+
+// tag::st_geotile-literal-result[]
+location:geo_point | geotile4:long | geotile3:long | geotile2:long | geotile1:long
+POINT (12.6493508684508 55.6285017221528) | 1152921508901814277 | 864691130602618882 | 576460753377165313 | 288230376688582656
+// end::st_geotile-literal-result[]
+;
+
+geotileLiteralString
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)")
+| EVAL geotile4 = ST_GEOTILE_TO_STRING(ST_GEOTILE(location, 4)),
+ geotile3 = ST_GEOTILE_TO_STRING(ST_GEOTILE(location, 3)),
+ geotile2 = ST_GEOTILE_TO_STRING(ST_GEOTILE(location, 2)),
+ geotile1 = ST_GEOTILE_TO_STRING(ST_GEOTILE(location, 1))
+;
+
+location:geo_point | geotile4:keyword | geotile3:keyword | geotile2:keyword | geotile1:keyword
+POINT (12.6493508684508 55.6285017221528) | 4/8/5 | 3/4/2 | 2/2/1 | 1/1/0
+;
+
+geotileLiteralMv
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)"), precision = [1,2,3,4,5]
+| MV_EXPAND precision
+| EVAL geotile = ST_GEOTILE(location, precision)
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+;
+
+location:geo_point | precision:integer | geotile:long | geotileString:keyword
+POINT (12.6493508684508 55.6285017221528) | 1 | 288230376688582656 | 1/1/0
+POINT (12.6493508684508 55.6285017221528) | 2 | 576460753377165313 | 2/2/1
+POINT (12.6493508684508 55.6285017221528) | 3 | 864691130602618882 | 3/4/2
+POINT (12.6493508684508 55.6285017221528) | 4 | 1152921508901814277 | 4/8/5
+POINT (12.6493508684508 55.6285017221528) | 5 | 1441151889885364234 | 5/17/10
+;
+
+geotileLiteralMvBoundary
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)"), precision = [1,2,3,4,5]
+| MV_EXPAND precision
+| EVAL geotile = ST_GEOTILE_TO_STRING(ST_GEOTILE(location, precision))
+| EVAL cellBoundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+;
+
+location:geo_point | precision:integer | geotile:keyword | cellBoundary:geo_shape
+POINT (12.6493508684508 55.6285017221528) | 1 | 1/1/0 | POLYGON((0.0 0.0, 180.0 0.0, 180.0 85.0511287798066, 0.0 85.0511287798066, 0.0 0.0))
+POINT (12.6493508684508 55.6285017221528) | 2 | 2/2/1 | POLYGON((0.0 0.0, 90.0 0.0, 90.0 66.51326044311186, 0.0 66.51326044311186, 0.0 0.0))
+POINT (12.6493508684508 55.6285017221528) | 3 | 3/4/2 | POLYGON((0.0 40.979898069620134, 45.0 40.979898069620134, 45.0 66.51326044311186, 0.0 66.51326044311186, 0.0 40.979898069620134))
+POINT (12.6493508684508 55.6285017221528) | 4 | 4/8/5 | POLYGON((0.0 40.979898069620134, 22.5 40.979898069620134, 22.5 55.77657301866769, 0.0 55.77657301866769, 0.0 40.979898069620134))
+POINT (12.6493508684508 55.6285017221528) | 5 | 5/17/10 | POLYGON((11.25 48.922499263758255, 22.5 48.922499263758255, 22.5 55.77657301866769, 11.25 55.77657301866769, 11.25 48.922499263758255))
+;
+
+geotileField
+required_capability: spatial_grid
+
+FROM airports
+| WHERE abbrev == "CPH"
+| EVAL geotile = ST_GEOTILE(location, 7)
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| KEEP geotile, geotileString, abbrev, name, location
+;
+
+geotile:long | geotileString:keyword | abbrev:keyword | name:text | location:geo_point
+2017612669569204264 | 7/68/40 | CPH | Copenhagen | POINT (12.6493508684508 55.6285017221528)
+;
+
+gridGeotileStatsBy
+required_capability: spatial_grid
+
+// tag::st_geotile-grid[]
+FROM airports
+| EVAL geotile = ST_GEOTILE(location, 2)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| SORT count DESC, geotileString ASC
+| KEEP count, centroid, geotileString
+// end::st_geotile-grid[]
+;
+
+// tag::st_geotile-grid-result[]
+count:long | centroid:geo_point | geotileString:keyword
+286 | POINT (39.31202001609169 35.149993664386415) | 2/2/1
+197 | POINT (-55.387361375756825 31.952955322292855) | 2/1/1
+136 | POINT (-110.97162496141048 36.87185255084734) | 2/0/1
+106 | POINT (119.35907618669827 25.46263281488791) | 2/3/1
+67 | POINT (-58.031108492373754 -22.624166105151065) | 2/1/2
+46 | POINT (142.95455511274707 -20.581492295427978) | 2/3/2
+34 | POINT (31.38476753634784 -14.64374022804858) | 2/2/2
+8 | POINT (-160.0723083713092 -19.124013530672528) | 2/0/2
+6 | POINT (23.95813101902604 70.17537698848173) | 2/2/0
+3 | POINT (-133.4001641627401 72.06833167467266) | 2/0/0
+2 | POINT (-68.47209956031293 66.77569948369637) | 2/1/0
+// end::st_geotile-grid-result[]
+;
+
+gridGeotileStatsByBounds
+required_capability: spatial_grid
+
+FROM airports
+| EVAL geotile = ST_GEOTILE(location, 3, TO_GEOSHAPE("POLYGON((0.0 30.0, 12.0 30.0, 12.0 60.0, 0.0 60.0, 0.0 30.0))"))
+| WHERE geotile IS NOT NULL
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| EVAL cellBoundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+| SORT count DESC, geotileString ASC
+| KEEP count, centroid, geotileString, cellBoundary
+;
+
+count:long | centroid:geo_point | geotileString:keyword | cellBoundary:geo_shape
+100 | POINT (18.10569669920951 50.40505832391791) | 3/4/2 | POLYGON((0.0 40.979898069620134, 45.0 40.979898069620134, 45.0 66.51326044311186, 0.0 66.51326044311186, 0.0 40.979898069620134))
+79 | POINT (24.516750878736943 23.93036561181085) | 3/4/3 | POLYGON((0.0 0.0, 45.0 0.0, 45.0 40.979898069620134, 0.0 40.979898069620134, 0.0 0.0))
+;
+
+gridGeotileStatsByPointsBounds
+required_capability: spatial_grid
+
+FROM airports
+| EVAL points = ["POINT(0.0 30.0)", "POINT(12.0 60.0)"]
+| EVAL geotile = ST_GEOTILE(location, 3, TO_GEOPOINT(points))
+| WHERE geotile IS NOT NULL
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| EVAL cellBoundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+| KEEP count, centroid, geotileString, cellBoundary
+| SORT count DESC, geotileString ASC
+;
+
+count:long | centroid:geo_point | geotileString:keyword | cellBoundary:geo_shape
+100 | POINT (18.10569669920951 50.40505832391791) | 3/4/2 | POLYGON((0.0 40.979898069620134, 45.0 40.979898069620134, 45.0 66.51326044311186, 0.0 66.51326044311186, 0.0 40.979898069620134))
+79 | POINT (24.516750878736943 23.93036561181085) | 3/4/3 | POLYGON((0.0 0.0, 45.0 0.0, 45.0 40.979898069620134, 0.0 40.979898069620134, 0.0 0.0))
+;
+
+gridGeotileDocsFromCell
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, ST_GEOTILE_TO_GEOSHAPE("3/4/3"))
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+;
+
+count:long | centroid:geo_point
+79 | POINT (24.516750878736943 23.93036561181085)
+;
+
+gridGeotileStatsByWhereUK
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| EVAL geotile = ST_GEOTILE(location, 4)
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| EVAL cellBoundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+| KEEP count, centroid, geotileString, cellBoundary
+| SORT count DESC
+;
+
+count:long | centroid:geo_point | geotileString:keyword | cellBoundary:geo_shape
+12 | POINT (-2.342151787597686 52.9600293841213) | 4/7/5 | POLYGON((-22.5 40.979898069620134, 0.0 40.979898069620134, 0.0 55.77657301866769, -22.5 55.77657301866769, -22.5 40.979898069620134))
+5 | POINT (-3.2097987569868565 57.63667118176818) | 4/7/4 | POLYGON((-22.5 55.77657301866769, 0.0 55.77657301866769, 0.0 66.51326044311186, -22.5 66.51326044311186, -22.5 55.77657301866769))
+;
+
+gridGeotileStatsByBoundsUK
+required_capability: spatial_grid
+
+FROM airports
+| EVAL bounds = ST_ENVELOPE(TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| EVAL geotile = ST_GEOTILE(location, 4, bounds)
+| WHERE geotile IS NOT NULL
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY geotile
+| EVAL geotileString = ST_GEOTILE_TO_STRING(geotile)
+| EVAL cellBoundary = ST_GEOTILE_TO_GEOSHAPE(geotile)
+| KEEP count, centroid, geotileString, cellBoundary
+| SORT count DESC
+;
+
+count:long | centroid:geo_point | geotileString:keyword | cellBoundary:geo_shape
+56 | POINT (10.54233039047436 47.85997457644304) | 4/8/5 | POLYGON((0.0 40.979898069620134, 22.5 40.979898069620134, 22.5 55.77657301866769, 0.0 55.77657301866769, 0.0 40.979898069620134))
+18 | POINT (-3.5578574100509286 51.27018998377025) | 4/7/5 | POLYGON((-22.5 40.979898069620134, 0.0 40.979898069620134, 0.0 55.77657301866769, -22.5 55.77657301866769, -22.5 40.979898069620134))
+11 | POINT (14.310833624648778 59.85619530801407) | 4/8/4 | POLYGON((0.0 55.77657301866769, 22.5 55.77657301866769, 22.5 66.51326044311186, 0.0 66.51326044311186, 0.0 55.77657301866769))
+7 | POINT (-6.466632609122565 59.19681839378817) | 4/7/4 | POLYGON((-22.5 55.77657301866769, 0.0 55.77657301866769, 0.0 66.51326044311186, -22.5 66.51326044311186, -22.5 55.77657301866769))
+;
+
+gridGeotileInStatsBy
+required_capability: spatial_grid
+
+FROM airports
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY ST_GEOTILE(location, 1)
+| SORT count DESC
+| KEEP count, centroid
+| LIMIT 10
+;
+
+count:long | centroid:geo_point
+398 | POINT (60.39961956408642 33.09796363900383)
+338 | POINT (-78.52247301001411 34.49426195088267)
+80 | POINT (95.5373953927774 -18.057947666791733)
+75 | POINT (-68.91550314612687 -22.25081649720669)
+;
+
+gridGeotileInStatsByWhereUK
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY ST_GEOTILE(location, 3)
+| KEEP count, centroid
+| SORT count DESC
+;
+
+count:long | centroid:geo_point
+17 | POINT (-2.597342072712148 54.33551226578214)
+;
+
+###############################################
+# Tests for geo_grid function: ST_GEOHEX
+###############################################
+
+geohexStringToLong
+required_capability: spatial_grid
+
+// tag::geohex_to_long[]
+ROW geohex = "841f059ffffffff"
+| EVAL geohexLong = ST_GEOHEX_TO_LONG(geohex)
+// end::geohex_to_long[]
+;
+
+// tag::geohex_to_long-result[]
+geohex:keyword | geohexLong:long
+841f059ffffffff | 595020895127339007
+// end::geohex_to_long-result[]
+;
+
+geohexLongToString
+required_capability: spatial_grid
+
+// tag::geohex_to_string[]
+ROW geohex = 595020895127339007
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+// end::geohex_to_string[]
+;
+
+// tag::geohex_to_string-result[]
+geohex:long | geohexString:keyword
+595020895127339007 | 841f059ffffffff
+// end::geohex_to_string-result[]
+;
+
+geohexStringToGeoshape
+required_capability: spatial_grid
+
+// tag::geohex_to_geoshape[]
+ROW geohex = "841f059ffffffff"
+| EVAL boundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+// end::geohex_to_geoshape[]
+;
+
+// tag::geohex_to_geoshape-result[]
+geohex:keyword | boundary:geo_shape
+841f059ffffffff | POLYGON ((12.353546327258265 55.80335405461356, 12.2434612967008 55.60502874054935, 12.51733872608954 55.470800201545316, 12.901880149865134 55.53443156197633, 13.014270156228921 55.73262985778208, 12.739822958959069 55.8673267391136, 12.353546327258265 55.80335405461356))
+// end::geohex_to_geoshape-result[]
+;
+
+geohexLiteral
+required_capability: spatial_grid
+
+// tag::st_geohex-literal[]
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)")
+| EVAL geohex4 = ST_GEOHEX(location, 4),
+ geohex3 = ST_GEOHEX(location, 3),
+ geohex2 = ST_GEOHEX(location, 2),
+ geohex1 = ST_GEOHEX(location, 1)
+// end::st_geohex-literal[]
+;
+
+// tag::st_geohex-literal-result[]
+location:geo_point | geohex4:long | geohex3:long | geohex2:long | geohex1:long
+POINT (12.6493508684508 55.6285017221528) | 595020895127339007 | 590517321269772287 | 586013859081355263 | 581514107744681983
+// end::st_geohex-literal-result[]
+;
+
+geohexLiteralString
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)")
+| EVAL geohex4 = ST_GEOHEX_TO_STRING(ST_GEOHEX(location, 4)),
+ geohex3 = ST_GEOHEX_TO_STRING(ST_GEOHEX(location, 3)),
+ geohex2 = ST_GEOHEX_TO_STRING(ST_GEOHEX(location, 2)),
+ geohex1 = ST_GEOHEX_TO_STRING(ST_GEOHEX(location, 1))
+;
+
+location:geo_point | geohex4:keyword | geohex3:keyword | geohex2:keyword | geohex1:keyword
+POINT (12.6493508684508 55.6285017221528) | 841f059ffffffff | 831f05fffffffff | 821f07fffffffff | 811f3ffffffffff
+;
+
+geohexLiteralMv
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)"), precision = [1,2,3,4,5]
+| MV_EXPAND precision
+| EVAL geohex = ST_GEOHEX(location, precision)
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+;
+
+location:geo_point | precision:integer | geohex:long | geohexString:keyword
+POINT (12.6493508684508 55.6285017221528) | 1 | 581514107744681983 | 811f3ffffffffff
+POINT (12.6493508684508 55.6285017221528) | 2 | 586013859081355263 | 821f07fffffffff
+POINT (12.6493508684508 55.6285017221528) | 3 | 590517321269772287 | 831f05fffffffff
+POINT (12.6493508684508 55.6285017221528) | 4 | 595020895127339007 | 841f059ffffffff
+POINT (12.6493508684508 55.6285017221528) | 5 | 599524487238516735 | 851f0583fffffff
+;
+
+geohexLiteralMvBoundary
+required_capability: spatial_grid
+
+ROW location = TO_GEOPOINT("POINT(12.6493508684508 55.6285017221528)"), precision = [1,2,3,4,5]
+| MV_EXPAND precision
+| EVAL geohex = ST_GEOHEX_TO_STRING(ST_GEOHEX(location, precision))
+| EVAL cellBoundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+;
+
+location:geo_point | precision:integer | geohex:keyword | cellBoundary:geo_shape
+POINT (12.6493508684508 55.6285017221528) | 1 | 811f3ffffffffff | POLYGON ((5.523646549290313 55.70676846515226, 6.259687055981993 51.96477015603749, 12.345747364400065 50.55427508726938, 18.289963485942284 52.81905199947076, 18.2752359517578 56.63295271905082, 11.555977692900722 58.13053116585142, 5.523646549290313 55.70676846515226))
+POINT (12.6493508684508 55.6285017221528) | 2 | 821f07fffffffff | POLYGON ((10.06569233741819 55.3948787035456, 9.374530816292083 53.97551370235174, 11.245170492278875 53.03203973205439, 13.839528609232428 53.48724516123688, 14.640206584543728 54.90218190493179, 12.739822958959062 55.8673267391136, 10.06569233741819 55.3948787035456))
+POINT (12.6493508684508 55.6285017221528) | 3 | 831f05fffffffff | POLYGON ((11.860105203878488 55.53944935104936, 11.917786510985744 55.0057489060893, 12.839846498886947 54.79854371636354, 13.717124800902113 55.12308086302377, 13.674892957473272 55.65810153974231, 12.739822958959069 55.8673267391136, 11.860105203878488 55.53944935104936))
+POINT (12.6493508684508 55.6285017221528) | 4 | 841f059ffffffff | POLYGON ((12.353546327258265 55.80335405461356, 12.2434612967008 55.60502874054935, 12.51733872608954 55.470800201545316, 12.901880149865134 55.53443156197633, 13.014270156228921 55.73262985778208, 12.739822958959069 55.8673267391136, 12.353546327258265 55.80335405461356))
+POINT (12.6493508684508 55.6285017221528) | 5 | 851f0583fffffff | POLYGON ((12.494766876566144 55.69864155255122, 12.502309582573728 55.6227995792834, 12.635566668537882 55.59351730049433, 12.761550811198047 55.640035539252175, 12.754326292782508 55.71590456460875, 12.62079911077343 55.745228479933665, 12.494766876566144 55.69864155255122))
+;
+
+geohexField
+required_capability: spatial_grid
+
+FROM airports
+| WHERE abbrev == "CPH"
+| EVAL geohex = ST_GEOHEX(location, 7)
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| KEEP geohex, geohexString, abbrev, name, location
+;
+
+geohex:long | geohexString:keyword | abbrev:keyword | name:text | location:geo_point
+608531685838946303 | 871f05818ffffff | CPH | Copenhagen | POINT (12.6493508684508 55.6285017221528)
+;
+
+gridGeohexStatsBy
+required_capability: spatial_grid
+
+// tag::st_geohex-grid[]
+FROM airports
+| EVAL geohex = ST_GEOHEX(location, 1)
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| WHERE count >= 10
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| KEEP count, centroid, geohexString
+| SORT count DESC, geohexString ASC
+// end::st_geohex-grid[]
+;
+
+// tag::st_geohex-grid-result[]
+count:long | centroid:geo_point | geohexString:keyword
+22 | POINT (7.250850197689777 48.21363834643059) | 811fbffffffffff
+18 | POINT (-80.64959161449224 40.04119813675061) | 812abffffffffff
+17 | POINT (-0.7606179875266903 52.86413913565304) | 81197ffffffffff
+13 | POINT (22.53157936179867 41.98255742864254) | 811efffffffffff
+13 | POINT (78.30096947387435 26.073904778951636) | 813dbffffffffff
+12 | POINT (-76.39781514415517 45.16300531569868) | 812bbffffffffff
+12 | POINT (-100.30120467301458 20.114154297625646) | 8149bffffffffff
+11 | POINT (18.037187419831753 48.66540593306788) | 811e3ffffffffff
+11 | POINT (-83.42379064553164 33.18388901439241) | 8144fffffffffff
+11 | POINT (-99.4237939513881 27.100012352774765) | 8148bffffffffff
+10 | POINT (128.01009018346667 35.8699960866943) | 8130fffffffffff
+// end::st_geohex-grid-result[]
+;
+
+gridGeohexStatsByBounds
+required_capability: spatial_grid
+
+FROM airports
+| EVAL geohex = ST_GEOHEX(location, 1, TO_GEOSHAPE("POLYGON((0.0 30.0, 12.0 30.0, 12.0 60.0, 0.0 60.0, 0.0 30.0))"))
+| WHERE geohex IS NOT NULL
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| EVAL cellBoundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+| SORT count DESC, geohexString ASC
+| KEEP count, centroid, geohexString, cellBoundary
+;
+
+count:long | centroid:geo_point | geohexString:keyword | cellBoundary:geo_shape
+22 | POINT (7.250850197689777 48.21363834643059) | 811fbffffffffff | POLYGON ((1.1885095534434493 49.47027919866873, 2.026568965384636 45.18424868970643, 7.509948481928886 43.786609353945, 12.677317810359858 46.40695745798227, 12.345747364400065 50.55427508726938, 6.259687055981993 51.96477015603749, 3.6300086390995467 50.6104633125695, 1.1885095534434493 49.47027919866873))
+17 | POINT (-0.7606179875266903 52.86413913565304) | 81197ffffffffff | POLYGON ((-0.9315871635106163 57.689497374592854, -6.436337296790312 55.3773904155485, -4.889760342933795 51.22372845966177, 1.1885095534434493 49.47027919866873, 3.6300086390995365 50.610463312569514, 6.259687055981993 51.96477015603749, 5.523646549290304 55.70676846515228, -0.9315871635106163 57.689497374592854))
+7 | POINT (2.475211258445467 41.32352174592337) | 81397ffffffffff | POLYGON ((-2.4177958307002347 42.54047996565121, -1.416234262837718 38.1159967227654, 0.2387159546288331 37.58884624349159, 3.543589356987798 36.59789814478598, 8.045142114241353 39.40213636897177, 7.509948481928886 43.786609353945, 2.026568965384636 45.18424868970643, -2.4177958307002347 42.54047996565121))
+6 | POINT (11.75047050230205 42.351422344800085) | 811ebffffffffff | POLYGON ((7.509948481928886 43.786609353945, 8.045142114241353 39.40213636897177, 13.244313475659823 37.5237123657852, 18.325260281104846 39.988177963363235, 18.314740441319 44.47067958889503, 12.677317810359858 46.40695745798227, 7.509948481928886 43.786609353945))
+5 | POINT (18.766171680763364 59.15833930950612) | 8108bffffffffff | POLYGON ((11.080660058482376 61.54051460002519, 11.555977692900722 58.13053116585142, 18.2752359517578 56.63295271905082, 25.082722326707877 58.4015448703527, 24.517172437523495 62.47811345192472, 17.53544630840839 63.800792653212156, 15.771773841154092 62.88996835796253, 11.080660058482376 61.54051460002519))
+5 | POINT (11.404999259859324 54.510593589395285) | 811f3ffffffffff | POLYGON ((5.523646549290313 55.70676846515226, 6.259687055981993 51.96477015603749, 12.345747364400065 50.55427508726938, 18.289963485942284 52.81905199947076, 18.2752359517578 56.63295271905082, 11.555977692900722 58.13053116585142, 5.523646549290313 55.70676846515226))
+4 | POINT (5.167026452254504 59.81037143385038) | 8109bffffffffff | POLYGON ((4.012620898449951 63.32706132801842, -2.297526087621831 61.54550957788076, -0.9315871635106163 57.689497374592854, 5.523646549290304 55.70676846515228, 11.555977692900722 58.13053116585142, 11.080660058482376 61.54051460002519, 8.644221197607186 61.8908384753262, 4.012620898449951 63.32706132801842))
+4 | POINT (-1.1871178611181676 35.77457194332965) | 81383ffffffffff | POLYGON ((-5.442348460730093 35.12442405176796, -4.341769247156951 30.69897412778907, -1.2408020165138167 29.828776159725596, 0.26094682603113584 29.300374659234986, 4.204173995672654 32.11010693139568, 3.543589356987798 36.59789814478598, 0.2387159546288331 37.58884624349159, -1.416234262837718 38.1159967227654, -5.442348460730093 35.12442405176796))
+3 | POINT (-1.1497433669865131 45.83295159973204) | 81187ffffffffff | POLYGON ((-4.889760342933795 51.22372845966177, -9.724736207832692 48.43009421958323, -8.101589192291552 44.0506310521871, -2.4177958307002347 42.54047996565121, 2.026568965384605 45.18424868970644, 1.1885095534434493 49.47027919866873, -4.889760342933795 51.22372845966177))
+3 | POINT (9.197671310976148 36.29719984252006) | 81387ffffffffff | POLYGON ((3.543589356987798 36.59789814478598, 4.204173995672654 32.11010693139568, 8.974635637589913 30.254840780701706, 13.488580338198277 32.87501547725471, 13.244313475659823 37.5237123657852, 8.045142114241353 39.40213636897177, 3.543589356987798 36.59789814478598))
+1 | POINT (13.144258903339505 32.66916951164603) | 813fbffffffffff | POLYGON ((8.974635637589913 30.254840780701706, 9.380676605201614 25.60370257696876, 13.915183885634578 23.45043771532035, 18.351304577591982 25.86678026136113, 18.343416368196834 30.62734805513404, 13.488580338198277 32.87501547725471, 8.974635637589913 30.254840780701706))
+;
+
+gridGeohexStatsByPointsBounds
+required_capability: spatial_grid
+
+FROM airports
+| EVAL points = ["POINT(0.0 30.0)", "POINT(12.0 60.0)"]
+| EVAL geohex = ST_GEOHEX(location, 1, TO_GEOPOINT(points))
+| WHERE geohex IS NOT NULL
+| STATS
+ count = COUNT(*),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| EVAL cellBoundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+| KEEP count, centroid, geohexString, cellBoundary
+| SORT count DESC, geohexString ASC
+;
+
+count:long | centroid:geo_point | geohexString:keyword | cellBoundary:geo_shape
+22 | POINT (7.250850197689777 48.21363834643059) | 811fbffffffffff | POLYGON ((1.1885095534434493 49.47027919866873, 2.026568965384636 45.18424868970643, 7.509948481928886 43.786609353945, 12.677317810359858 46.40695745798227, 12.345747364400065 50.55427508726938, 6.259687055981993 51.96477015603749, 3.6300086390995467 50.6104633125695, 1.1885095534434493 49.47027919866873))
+17 | POINT (-0.7606179875266903 52.86413913565304) | 81197ffffffffff | POLYGON ((-0.9315871635106163 57.689497374592854, -6.436337296790312 55.3773904155485, -4.889760342933795 51.22372845966177, 1.1885095534434493 49.47027919866873, 3.6300086390995365 50.610463312569514, 6.259687055981993 51.96477015603749, 5.523646549290304 55.70676846515228, -0.9315871635106163 57.689497374592854))
+7 | POINT (2.475211258445467 41.32352174592337) | 81397ffffffffff | POLYGON ((-2.4177958307002347 42.54047996565121, -1.416234262837718 38.1159967227654, 0.2387159546288331 37.58884624349159, 3.543589356987798 36.59789814478598, 8.045142114241353 39.40213636897177, 7.509948481928886 43.786609353945, 2.026568965384636 45.18424868970643, -2.4177958307002347 42.54047996565121))
+6 | POINT (11.75047050230205 42.351422344800085) | 811ebffffffffff | POLYGON ((7.509948481928886 43.786609353945, 8.045142114241353 39.40213636897177, 13.244313475659823 37.5237123657852, 18.325260281104846 39.988177963363235, 18.314740441319 44.47067958889503, 12.677317810359858 46.40695745798227, 7.509948481928886 43.786609353945))
+5 | POINT (18.766171680763364 59.15833930950612) | 8108bffffffffff | POLYGON ((11.080660058482376 61.54051460002519, 11.555977692900722 58.13053116585142, 18.2752359517578 56.63295271905082, 25.082722326707877 58.4015448703527, 24.517172437523495 62.47811345192472, 17.53544630840839 63.800792653212156, 15.771773841154092 62.88996835796253, 11.080660058482376 61.54051460002519))
+5 | POINT (11.404999259859324 54.510593589395285) | 811f3ffffffffff | POLYGON ((5.523646549290313 55.70676846515226, 6.259687055981993 51.96477015603749, 12.345747364400065 50.55427508726938, 18.289963485942284 52.81905199947076, 18.2752359517578 56.63295271905082, 11.555977692900722 58.13053116585142, 5.523646549290313 55.70676846515226))
+4 | POINT (5.167026452254504 59.81037143385038) | 8109bffffffffff | POLYGON ((4.012620898449951 63.32706132801842, -2.297526087621831 61.54550957788076, -0.9315871635106163 57.689497374592854, 5.523646549290304 55.70676846515228, 11.555977692900722 58.13053116585142, 11.080660058482376 61.54051460002519, 8.644221197607186 61.8908384753262, 4.012620898449951 63.32706132801842))
+4 | POINT (-1.1871178611181676 35.77457194332965) | 81383ffffffffff | POLYGON ((-5.442348460730093 35.12442405176796, -4.341769247156951 30.69897412778907, -1.2408020165138167 29.828776159725596, 0.26094682603113584 29.300374659234986, 4.204173995672654 32.11010693139568, 3.543589356987798 36.59789814478598, 0.2387159546288331 37.58884624349159, -1.416234262837718 38.1159967227654, -5.442348460730093 35.12442405176796))
+3 | POINT (-1.1497433669865131 45.83295159973204) | 81187ffffffffff | POLYGON ((-4.889760342933795 51.22372845966177, -9.724736207832692 48.43009421958323, -8.101589192291552 44.0506310521871, -2.4177958307002347 42.54047996565121, 2.026568965384605 45.18424868970644, 1.1885095534434493 49.47027919866873, -4.889760342933795 51.22372845966177))
+3 | POINT (9.197671310976148 36.29719984252006) | 81387ffffffffff | POLYGON ((3.543589356987798 36.59789814478598, 4.204173995672654 32.11010693139568, 8.974635637589913 30.254840780701706, 13.488580338198277 32.87501547725471, 13.244313475659823 37.5237123657852, 8.045142114241353 39.40213636897177, 3.543589356987798 36.59789814478598))
+1 | POINT (13.144258903339505 32.66916951164603) | 813fbffffffffff | POLYGON ((8.974635637589913 30.254840780701706, 9.380676605201614 25.60370257696876, 13.915183885634578 23.45043771532035, 18.351304577591982 25.86678026136113, 18.343416368196834 30.62734805513404, 13.488580338198277 32.87501547725471, 8.974635637589913 30.254840780701706))
+;
+
+// TODO: Fix this test
+//gridGeohexDocsFromCell
+//required_capability: spatial_grid
+//
+//FROM airports
+//| WHERE ST_INTERSECTS(location, ST_GEOHEX_TO_GEOSHAPE("81397ffffffffff"))
+//| STATS
+// count = COUNT(*),
+// centroid = ST_CENTROID_AGG(location)
+//;
+//
+//count:long | centroid:geo_point
+//7 | POINT (2.475211258445467 41.32352174592337)
+//;
+
+gridGeohexStatsByWhereUK
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| EVAL geohex = ST_GEOHEX(location, 1)
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| EVAL cellBoundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+| KEEP count, centroid, geohexString, cellBoundary
+| SORT count DESC, geohexString ASC
+;
+
+count:long | centroid:geo_point | geohexString:keyword | cellBoundary:geo_shape
+13 | POINT (-2.283508819169723 53.28242553254733) | 81197ffffffffff | POLYGON ((-0.9315871635106163 57.689497374592854, -6.436337296790312 55.3773904155485, -4.889760342933795 51.22372845966177, 1.1885095534434493 49.47027919866873, 3.6300086390995365 50.610463312569514, 6.259687055981993 51.96477015603749, 5.523646549290304 55.70676846515228, -0.9315871635106163 57.689497374592854))
+2 | POINT (-3.482485176064074 58.24696456314996) | 81193ffffffffff | POLYGON ((-10.44497754477833 63.09505407752544, -16.37196241724276 60.568693514800756, -13.79164665163863 56.73632442836386, -6.436337296790312 55.3773904155485, -0.9315871635106163 57.689497374592854, -2.297526087621831 61.54550957788076, -10.44497754477833 63.09505407752544))
+1 | POINT (-1.2880607228726149 59.87668995279819) | 8109bffffffffff | POLYGON ((4.012620898449951 63.32706132801842, -2.297526087621831 61.54550957788076, -0.9315871635106163 57.689497374592854, 5.523646549290304 55.70676846515228, 11.555977692900722 58.13053116585142, 11.080660058482376 61.54051460002519, 8.644221197607186 61.8908384753262, 4.012620898449951 63.32706132801842))
+1 | POINT (-6.216169511899352 54.66155751608312) | 81183ffffffffff | POLYGON ((-13.79164665163863 56.73632442836386, -18.841171686388584 53.74020675223833, -16.3823406256948 49.6092693973489, -9.724736207832692 48.43009421958323, -4.889760342933795 51.22372845966177, -6.436337296790312 55.3773904155485, -13.79164665163863 56.73632442836386))
+;
+
+gridGeohexStatsByBoundsUK
+required_capability: spatial_grid
+
+FROM airports
+| EVAL bounds = ST_ENVELOPE(TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| EVAL geohex = ST_GEOHEX(location, 2, bounds)
+| WHERE geohex IS NOT NULL
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY geohex
+| EVAL geohexString = ST_GEOHEX_TO_STRING(geohex)
+| EVAL cellBoundary = ST_GEOHEX_TO_GEOSHAPE(geohex)
+| KEEP count, centroid, geohexString, cellBoundary
+| SORT count DESC, geohexString ASC
+;
+
+count:long | centroid:geo_point | geohexString:keyword | cellBoundary:geo_shape
+5 | POINT (-1.7227098606526852 51.717823022045195) | 82195ffffffffff | POLYGON ((-2.1788859209290505 53.251551050714795, -4.578382413982977 52.792439042763036, -4.889760342933795 51.22372845966177, -2.906527879284175 50.14229216995713, -0.6310947579755295 50.60191112048408, -0.21913743445926337 52.1416808182628, -2.1788859209290505 53.251551050714795))
+4 | POINT (-3.9897833741270006 54.21732849790715) | 821957fffffffff | POLYGON ((-3.9031812914854935 55.840605806464886, -6.436337296790312 55.3773904155485, -6.713499065102683 53.841053788108646, -4.578382413982977 52.792439042763036, -2.1788859209290505 53.251551050714795, -1.7855433613316123 54.76238836624054, -3.9031812914854935 55.840605806464886))
+3 | POINT (-7.885485291481018 52.65633414499462) | 82182ffffffffff | POLYGON ((-6.713499065102683 53.841053788108646, -9.16699332513357 53.28908444969493, -9.362891292036808 51.69686278886942, -7.224493860559402 50.67904691252102, -4.889760342933795 51.22372845966177, -4.578382413982977 52.792439042763036, -6.713499065102683 53.841053788108646))
+3 | POINT (3.0334555450826883 48.842039234004915) | 821fb7fffffffff | POLYGON ((1.1885095534434493 49.47027919866873, 0.7640259623867446 47.90745035042651, 2.458901717447715 46.76373050573097, 4.652435425239358 47.393027403288, 5.163510941569062 48.943763955054884, 3.3847715100589153 49.85377035058955, 1.1885095534434493 49.47027919866873))
+2 | POINT (5.428531668148935 59.585608751513064) | 820987fffffffff | POLYGON ((5.479068693190393 60.92198553471913, 2.767527791540729 60.73321011494055, 2.198216040575142 59.383886486983954, 4.229785449235632 58.25202580757624, 6.766112799653718 58.44709614018101, 7.4384643434389535 59.76718020289119, 5.479068693190393 60.92198553471913))
+2 | POINT (-4.2476349184289575 56.70184155693278) | 82190ffffffffff | POLYGON ((-5.832270115239392 58.353838498476094, -8.508655388670189 57.8811306023458, -8.740899630542538 56.38258811071853, -6.436337296790312 55.3773904155485, -3.9031812914854935 55.840605806464886, -3.536381138969186 57.31728796714347, -5.832270115239392 58.353838498476094))
+2 | POINT (1.4715016074478626 50.863699545152485) | 82194ffffffffff | POLYGON ((2.101313983947425 52.51713397842229, -0.21913743445926337 52.1416808182628, -0.6310947579755295 50.60191112048408, 1.1885095534434493 49.47027919866873, 3.384771510058846 49.85377035058955, 3.8811335596706074 51.360376195950145, 2.101313983947425 52.51713397842229))
+2 | POINT (4.5991105819121 52.129031270742416) | 82196ffffffffff | POLYGON ((4.948742151508294 54.2882866932292, 2.592789031305367 53.99775155222293, 2.101313983947425 52.51713397842229, 3.8811335596706074 51.360376195950145, 6.259687055981993 51.96477015603749, 6.8482289070479005 53.43156035343158, 4.948742151508294 54.2882866932292))
+2 | POINT (-2.5373152876272798 55.49281941493973) | 821977fffffffff | POLYGON ((-0.9315871635106163 57.689497374592854, -3.536381138969186 57.31728796714347, -3.9031812914854935 55.840605806464886, -1.7855433613316123 54.76238836624054, 0.6707588590220377 55.133805943055435, 1.1521975506221078 56.583354389307445, -0.9315871635106163 57.689497374592854))
+1 | POINT (-1.2880607228726149 59.87668995279819) | 8209a7fffffffff | POLYGON ((0.5480202135218006 61.82886471359379, -2.297526087621831 61.54550957788076, -2.7356231170279166 60.17050935552437, -0.4671324618104694 59.1035939329508, 2.198216040575142 59.383886486983954, 2.767527791540729 60.73321011494055, 0.5480202135218006 61.82886471359379))
+1 | POINT (0.15865350142121315 49.36166098807007) | 821867fffffffff | POLYGON ((-0.6310947579755295 50.60191112048408, -2.906527879284175 50.14229216995713, -3.2436562689082553 48.547301491418715, -1.3966081625912774 47.44351195977148, 0.7640259623867446 47.90745035042651, 1.1885095534434493 49.47027919866873, -0.6310947579755295 50.60191112048408))
+1 | POINT (-7.270800014957786 62.06249998882413) | 821927fffffffff | POLYGON ((-4.777634484041529 62.56377272218282, -7.72004205669337 62.172198741500715, -7.999682127502827 60.77664727688846, -5.502334925300172 59.7918551703578, -2.7356231170279166 60.17050935552437, -2.297526087621831 61.54550957788076, -4.777634484041529 62.56377272218282))
+1 | POINT (-2.9013785161077976 58.95442885346711) | 82192ffffffffff | POLYGON ((-2.7356231170279166 60.17050935552437, -5.502334925300172 59.7918551703578, -5.832270115239392 58.353838498476094, -3.536381138969186 57.31728796714347, -0.9315871635106163 57.689497374592854, -0.4671324618104694 59.1035939329508, -2.7356231170279166 60.17050935552437))
+1 | POINT (-1.6598310694098473 53.8690819311887) | 821947fffffffff | POLYGON ((0.6707588590220377 55.133805943055435, -1.7855433613316123 54.76238836624054, -2.1788859209290505 53.251551050714795, -0.21913743445926337 52.1416808182628, 2.101313983947425 52.51713397842229, 2.592789031305367 53.99775155222293, 0.6707588590220377 55.133805943055435))
+;
+
+gridGeohexInStatsByBounds
+required_capability: spatial_grid
+
+FROM airports
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY ST_GEOHEX(location, 2, TO_GEOSHAPE("POLYGON((0.0 30.0, 12.0 30.0, 12.0 60.0, 0.0 60.0, 0.0 30.0))"))
+| WHERE count > 3
+| SORT count DESC
+| KEEP count, centroid
+| LIMIT 10
+;
+
+count:long | centroid:geo_point
+827 | POINT (-0.5615898292227913 22.56591850424922)
+6 | POINT (5.582276992499828 50.72238312335685)
+5 | POINT (8.6918301936239 45.19817395694554)
+;
+
+gridGeohexInStatsByWhereUK
+required_capability: spatial_grid
+
+FROM airports
+| WHERE ST_INTERSECTS(location, TO_GEOSHAPE("POLYGON((1.2305 60.8449, -1.582 61.6899, -10.7227 58.4017, -7.1191 55.3291, -7.9102 54.2139, -5.4492 54.0078, -5.2734 52.3756, -7.8223 49.6676, -5.0977 49.2678, 0.9668 50.5134, 2.5488 52.1065, 2.6367 54.0078, -0.9668 56.4625, 1.2305 60.8449))"))
+| STATS
+ count = COUNT(location),
+ centroid = ST_CENTROID_AGG(location)
+ BY ST_GEOHEX(location, 1)
+| WHERE count > 1
+| KEEP count, centroid
+| SORT count DESC
+;
+
+count:long | centroid:geo_point
+13 | POINT (-2.283508819169723 53.28242553254733)
+2 | POINT (-3.482485176064074 58.24696456314996)
+;
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..dafbf8836e46f
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,140 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldAndFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator in, EvalOperator.ExpressionEvaluator precision,
+ GeoBoundingBox bbox, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), inBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldAndFieldAndLiteral(result, p, inBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in, precision);
+ }
+
+ 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 in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ EvalOperator.ExpressionEvaluator.Factory precision, GeoBoundingBox bbox) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeohashFromFieldAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldAndFieldAndLiteralEvaluator(source, in.get(context), precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndFieldEvaluator.java
new file mode 100644
index 0000000000000..e97975b26aa44
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndFieldEvaluator.java
@@ -0,0 +1,132 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldAndFieldEvaluator(Source source, EvalOperator.ExpressionEvaluator in,
+ EvalOperator.ExpressionEvaluator precision, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), inBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldAndField(result, p, inBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in, precision);
+ }
+
+ 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 in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohashFromFieldAndFieldEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldAndFieldEvaluator(source, in.get(context), precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndLiteralAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndLiteralAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..2e4b8e7d538d8
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndLiteralAndLiteralEvaluator.java
@@ -0,0 +1,119 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldAndLiteralAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final StGeohash.GeoHashBoundedGrid bounds;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldAndLiteralAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator in, StGeohash.GeoHashBoundedGrid bounds,
+ DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.bounds = bounds;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ return eval(page.getPositionCount(), inBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldAndLiteralAndLiteral(result, p, inBlock, this.bounds);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndLiteralAndLiteralEvaluator[" + "in=" + in + ", bounds=" + bounds + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in);
+ }
+
+ 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 in;
+
+ private final StGeohash.GeoHashBoundedGrid bounds;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ StGeohash.GeoHashBoundedGrid bounds) {
+ this.source = source;
+ this.in = in;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public StGeohashFromFieldAndLiteralAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldAndLiteralAndLiteralEvaluator(source, in.get(context), bounds, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndLiteralAndLiteralEvaluator[" + "in=" + in + ", bounds=" + bounds + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..c67d7e4855299
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldAndLiteralEvaluator.java
@@ -0,0 +1,118 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator wkbBlock;
+
+ private final int precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator wkbBlock, int precision, DriverContext driverContext) {
+ this.source = source;
+ this.wkbBlock = wkbBlock;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock wkbBlockBlock = (BytesRefBlock) wkbBlock.eval(page)) {
+ return eval(page.getPositionCount(), wkbBlockBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock wkbBlockBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!wkbBlockBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldAndLiteral(result, p, wkbBlockBlock, this.precision);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndLiteralEvaluator[" + "wkbBlock=" + wkbBlock + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(wkbBlock);
+ }
+
+ 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 wkbBlock;
+
+ private final int precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory wkbBlock,
+ int precision) {
+ this.source = source;
+ this.wkbBlock = wkbBlock;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohashFromFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldAndLiteralEvaluator(source, wkbBlock.get(context), precision, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldAndLiteralEvaluator[" + "wkbBlock=" + wkbBlock + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..c5e2f6a651825
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,139 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, EvalOperator.ExpressionEvaluator precision,
+ GeoBoundingBox bbox, DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldDocValuesAndFieldAndLiteral(result, p, encodedBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded, precision);
+ }
+
+ 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 encoded;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ EvalOperator.ExpressionEvaluator.Factory precision, GeoBoundingBox bbox) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator(source, encoded.get(context), precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndFieldEvaluator.java
new file mode 100644
index 0000000000000..ad24270bba186
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndFieldEvaluator.java
@@ -0,0 +1,132 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldDocValuesAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldDocValuesAndFieldEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, EvalOperator.ExpressionEvaluator precision,
+ DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldDocValuesAndField(result, p, encodedBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndFieldEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded, precision);
+ }
+
+ 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 encoded;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohashFromFieldDocValuesAndFieldEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldDocValuesAndFieldEvaluator(source, encoded.get(context), precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndFieldEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..862b2b90c9b2d
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator.java
@@ -0,0 +1,118 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final StGeohash.GeoHashBoundedGrid bounds;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, StGeohash.GeoHashBoundedGrid bounds,
+ DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.bounds = bounds;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldDocValuesAndLiteralAndLiteral(result, p, encodedBlock, this.bounds);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator[" + "encoded=" + encoded + ", bounds=" + bounds + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded);
+ }
+
+ 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 encoded;
+
+ private final StGeohash.GeoHashBoundedGrid bounds;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ StGeohash.GeoHashBoundedGrid bounds) {
+ this.source = source;
+ this.encoded = encoded;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator(source, encoded.get(context), bounds, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator[" + "encoded=" + encoded + ", bounds=" + bounds + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..a57c2559093bf
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromFieldDocValuesAndLiteralEvaluator.java
@@ -0,0 +1,116 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromFieldDocValuesAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final int precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromFieldDocValuesAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, int precision, DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohash.fromFieldDocValuesAndLiteral(result, p, encodedBlock, this.precision);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded);
+ }
+
+ 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 encoded;
+
+ private final int precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded, int precision) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohashFromFieldDocValuesAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromFieldDocValuesAndLiteralEvaluator(source, encoded.get(context), precision, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromFieldDocValuesAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromLiteralAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromLiteralAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..11b7e9d8e220c
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromLiteralAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,149 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromLiteralAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromLiteralAndFieldAndLiteralEvaluator(Source source, BytesRef in,
+ EvalOperator.ExpressionEvaluator precision, GeoBoundingBox bbox,
+ DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ IntVector precisionVector = precisionBlock.asVector();
+ if (precisionVector == null) {
+ return eval(page.getPositionCount(), precisionBlock);
+ }
+ return eval(page.getPositionCount(), precisionVector);
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ try {
+ result.appendLong(StGeohash.fromLiteralAndFieldAndLiteral(this.in, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntVector precisionVector) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ try {
+ result.appendLong(StGeohash.fromLiteralAndFieldAndLiteral(this.in, precisionVector.getInt(p), this.bbox));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromLiteralAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(precision);
+ }
+
+ 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 BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, BytesRef in, EvalOperator.ExpressionEvaluator.Factory precision,
+ GeoBoundingBox bbox) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeohashFromLiteralAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohashFromLiteralAndFieldAndLiteralEvaluator(source, in, precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromLiteralAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromLiteralAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromLiteralAndFieldEvaluator.java
new file mode 100644
index 0000000000000..8a60d2211ae4a
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashFromLiteralAndFieldEvaluator.java
@@ -0,0 +1,140 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohash}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohashFromLiteralAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohashFromLiteralAndFieldEvaluator(Source source, BytesRef in,
+ EvalOperator.ExpressionEvaluator precision, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ IntVector precisionVector = precisionBlock.asVector();
+ if (precisionVector == null) {
+ return eval(page.getPositionCount(), precisionBlock);
+ }
+ return eval(page.getPositionCount(), precisionVector);
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ try {
+ result.appendLong(StGeohash.fromLiteralAndField(this.in, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p))));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntVector precisionVector) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ try {
+ result.appendLong(StGeohash.fromLiteralAndField(this.in, precisionVector.getInt(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromLiteralAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(precision);
+ }
+
+ 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 BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, BytesRef in, EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohashFromLiteralAndFieldEvaluator get(DriverContext context) {
+ return new StGeohashFromLiteralAndFieldEvaluator(source, in, precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashFromLiteralAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShapeFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShapeFromLongEvaluator.java
new file mode 100644
index 0000000000000..3e95b8a55d7e9
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShapeFromLongEvaluator.java
@@ -0,0 +1,124 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohashToGeoShape}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohashToGeoShapeFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohashToGeoShapeFromLongEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator gridId, DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ LongVector vector = (LongVector) v;
+ int positionCount = v.getPositionCount();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongVector container, int index) {
+ long value = container.getLong(index);
+ return StGeohashToGeoShape.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ LongBlock block = (LongBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongBlock container, int index) {
+ long value = container.getLong(index);
+ return StGeohashToGeoShape.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToGeoShapeFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohashToGeoShapeFromLongEvaluator get(DriverContext context) {
+ return new StGeohashToGeoShapeFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToGeoShapeFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShapeFromStringEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShapeFromStringEvaluator.java
new file mode 100644
index 0000000000000..0e50257a50bed
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShapeFromStringEvaluator.java
@@ -0,0 +1,144 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.OrdinalBytesRefVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohashToGeoShape}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohashToGeoShapeFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohashToGeoShapeFromStringEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator gridId, DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ OrdinalBytesRefVector ordinals = vector.asOrdinals();
+ if (ordinals != null) {
+ return evalOrdinals(ordinals);
+ }
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohashToGeoShape.fromString(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohashToGeoShape.fromString(value);
+ }
+
+ private Block evalOrdinals(OrdinalBytesRefVector v) {
+ int positionCount = v.getDictionaryVector().getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ try (BytesRefVector.Builder builder = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(v.getDictionaryVector(), p, scratchPad));
+ }
+ IntVector ordinals = v.getOrdinalsVector();
+ ordinals.incRef();
+ return new OrdinalBytesRefVector(ordinals, builder.build()).asBlock();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToGeoShapeFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohashToGeoShapeFromStringEvaluator get(DriverContext context) {
+ return new StGeohashToGeoShapeFromStringEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToGeoShapeFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToLongFromStringEvaluator.java
new file mode 100644
index 0000000000000..640a77fe05fb9
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToLongFromStringEvaluator.java
@@ -0,0 +1,126 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohashToLong}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohashToLongFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohashToLongFromStringEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendLong(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohashToLong.fromString(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ long value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendLong(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohashToLong.fromString(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToLongFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohashToLongFromStringEvaluator get(DriverContext context) {
+ return new StGeohashToLongFromStringEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToLongFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToStringFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToStringFromLongEvaluator.java
new file mode 100644
index 0000000000000..3e33f376e4e7e
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToStringFromLongEvaluator.java
@@ -0,0 +1,124 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohashToString}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohashToStringFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohashToStringFromLongEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ LongVector vector = (LongVector) v;
+ int positionCount = v.getPositionCount();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongVector container, int index) {
+ long value = container.getLong(index);
+ return StGeohashToString.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ LongBlock block = (LongBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongBlock container, int index) {
+ long value = container.getLong(index);
+ return StGeohashToString.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToStringFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohashToStringFromLongEvaluator get(DriverContext context) {
+ return new StGeohashToStringFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohashToStringFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..eb956fa8b37b1
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,140 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldAndFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator in, EvalOperator.ExpressionEvaluator precision,
+ GeoBoundingBox bbox, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), inBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldAndFieldAndLiteral(result, p, inBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in, precision);
+ }
+
+ 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 in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ EvalOperator.ExpressionEvaluator.Factory precision, GeoBoundingBox bbox) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeohexFromFieldAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldAndFieldAndLiteralEvaluator(source, in.get(context), precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndFieldEvaluator.java
new file mode 100644
index 0000000000000..36d121396a086
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndFieldEvaluator.java
@@ -0,0 +1,132 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldAndFieldEvaluator(Source source, EvalOperator.ExpressionEvaluator in,
+ EvalOperator.ExpressionEvaluator precision, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), inBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldAndField(result, p, inBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in, precision);
+ }
+
+ 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 in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohexFromFieldAndFieldEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldAndFieldEvaluator(source, in.get(context), precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndLiteralAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndLiteralAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..24e070c46adda
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndLiteralAndLiteralEvaluator.java
@@ -0,0 +1,119 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldAndLiteralAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final StGeohex.GeoHexBoundedGrid bounds;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldAndLiteralAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator in, StGeohex.GeoHexBoundedGrid bounds,
+ DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.bounds = bounds;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ return eval(page.getPositionCount(), inBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldAndLiteralAndLiteral(result, p, inBlock, this.bounds);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndLiteralAndLiteralEvaluator[" + "in=" + in + ", bounds=" + bounds + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in);
+ }
+
+ 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 in;
+
+ private final StGeohex.GeoHexBoundedGrid bounds;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ StGeohex.GeoHexBoundedGrid bounds) {
+ this.source = source;
+ this.in = in;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public StGeohexFromFieldAndLiteralAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldAndLiteralAndLiteralEvaluator(source, in.get(context), bounds, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndLiteralAndLiteralEvaluator[" + "in=" + in + ", bounds=" + bounds + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..5209d32caf308
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldAndLiteralEvaluator.java
@@ -0,0 +1,118 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator wkbBlock;
+
+ private final int precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator wkbBlock, int precision, DriverContext driverContext) {
+ this.source = source;
+ this.wkbBlock = wkbBlock;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock wkbBlockBlock = (BytesRefBlock) wkbBlock.eval(page)) {
+ return eval(page.getPositionCount(), wkbBlockBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock wkbBlockBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!wkbBlockBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldAndLiteral(result, p, wkbBlockBlock, this.precision);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndLiteralEvaluator[" + "wkbBlock=" + wkbBlock + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(wkbBlock);
+ }
+
+ 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 wkbBlock;
+
+ private final int precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory wkbBlock,
+ int precision) {
+ this.source = source;
+ this.wkbBlock = wkbBlock;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohexFromFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldAndLiteralEvaluator(source, wkbBlock.get(context), precision, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldAndLiteralEvaluator[" + "wkbBlock=" + wkbBlock + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..c8b750048d141
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,139 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, EvalOperator.ExpressionEvaluator precision,
+ GeoBoundingBox bbox, DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldDocValuesAndFieldAndLiteral(result, p, encodedBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded, precision);
+ }
+
+ 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 encoded;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ EvalOperator.ExpressionEvaluator.Factory precision, GeoBoundingBox bbox) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator(source, encoded.get(context), precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndFieldEvaluator.java
new file mode 100644
index 0000000000000..947bc22e41136
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndFieldEvaluator.java
@@ -0,0 +1,132 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldDocValuesAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldDocValuesAndFieldEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, EvalOperator.ExpressionEvaluator precision,
+ DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldDocValuesAndField(result, p, encodedBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndFieldEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded, precision);
+ }
+
+ 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 encoded;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohexFromFieldDocValuesAndFieldEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldDocValuesAndFieldEvaluator(source, encoded.get(context), precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndFieldEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..0ac32cbdbedad
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator.java
@@ -0,0 +1,118 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final StGeohex.GeoHexBoundedGrid bounds;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, StGeohex.GeoHexBoundedGrid bounds,
+ DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.bounds = bounds;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldDocValuesAndLiteralAndLiteral(result, p, encodedBlock, this.bounds);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator[" + "encoded=" + encoded + ", bounds=" + bounds + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded);
+ }
+
+ 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 encoded;
+
+ private final StGeohex.GeoHexBoundedGrid bounds;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ StGeohex.GeoHexBoundedGrid bounds) {
+ this.source = source;
+ this.encoded = encoded;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator(source, encoded.get(context), bounds, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator[" + "encoded=" + encoded + ", bounds=" + bounds + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..2b7d48d5f159d
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromFieldDocValuesAndLiteralEvaluator.java
@@ -0,0 +1,116 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromFieldDocValuesAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final int precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromFieldDocValuesAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, int precision, DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeohex.fromFieldDocValuesAndLiteral(result, p, encodedBlock, this.precision);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded);
+ }
+
+ 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 encoded;
+
+ private final int precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded, int precision) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohexFromFieldDocValuesAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromFieldDocValuesAndLiteralEvaluator(source, encoded.get(context), precision, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromFieldDocValuesAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromLiteralAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromLiteralAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..ff03bbf920760
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromLiteralAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,149 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromLiteralAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromLiteralAndFieldAndLiteralEvaluator(Source source, BytesRef in,
+ EvalOperator.ExpressionEvaluator precision, GeoBoundingBox bbox,
+ DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ IntVector precisionVector = precisionBlock.asVector();
+ if (precisionVector == null) {
+ return eval(page.getPositionCount(), precisionBlock);
+ }
+ return eval(page.getPositionCount(), precisionVector);
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ try {
+ result.appendLong(StGeohex.fromLiteralAndFieldAndLiteral(this.in, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntVector precisionVector) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ try {
+ result.appendLong(StGeohex.fromLiteralAndFieldAndLiteral(this.in, precisionVector.getInt(p), this.bbox));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromLiteralAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(precision);
+ }
+
+ 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 BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, BytesRef in, EvalOperator.ExpressionEvaluator.Factory precision,
+ GeoBoundingBox bbox) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeohexFromLiteralAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeohexFromLiteralAndFieldAndLiteralEvaluator(source, in, precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromLiteralAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromLiteralAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromLiteralAndFieldEvaluator.java
new file mode 100644
index 0000000000000..d3daf1b6178da
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexFromLiteralAndFieldEvaluator.java
@@ -0,0 +1,140 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeohex}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeohexFromLiteralAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeohexFromLiteralAndFieldEvaluator(Source source, BytesRef in,
+ EvalOperator.ExpressionEvaluator precision, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ IntVector precisionVector = precisionBlock.asVector();
+ if (precisionVector == null) {
+ return eval(page.getPositionCount(), precisionBlock);
+ }
+ return eval(page.getPositionCount(), precisionVector);
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ try {
+ result.appendLong(StGeohex.fromLiteralAndField(this.in, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p))));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntVector precisionVector) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ try {
+ result.appendLong(StGeohex.fromLiteralAndField(this.in, precisionVector.getInt(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromLiteralAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(precision);
+ }
+
+ 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 BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, BytesRef in, EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeohexFromLiteralAndFieldEvaluator get(DriverContext context) {
+ return new StGeohexFromLiteralAndFieldEvaluator(source, in, precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexFromLiteralAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShapeFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShapeFromLongEvaluator.java
new file mode 100644
index 0000000000000..250c2159e380b
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShapeFromLongEvaluator.java
@@ -0,0 +1,124 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohexToGeoShape}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohexToGeoShapeFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohexToGeoShapeFromLongEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ LongVector vector = (LongVector) v;
+ int positionCount = v.getPositionCount();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongVector container, int index) {
+ long value = container.getLong(index);
+ return StGeohexToGeoShape.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ LongBlock block = (LongBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongBlock container, int index) {
+ long value = container.getLong(index);
+ return StGeohexToGeoShape.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToGeoShapeFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohexToGeoShapeFromLongEvaluator get(DriverContext context) {
+ return new StGeohexToGeoShapeFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToGeoShapeFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShapeFromStringEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShapeFromStringEvaluator.java
new file mode 100644
index 0000000000000..94887bda9c960
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShapeFromStringEvaluator.java
@@ -0,0 +1,144 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.OrdinalBytesRefVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohexToGeoShape}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohexToGeoShapeFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohexToGeoShapeFromStringEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator gridId, DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ OrdinalBytesRefVector ordinals = vector.asOrdinals();
+ if (ordinals != null) {
+ return evalOrdinals(ordinals);
+ }
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohexToGeoShape.fromString(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohexToGeoShape.fromString(value);
+ }
+
+ private Block evalOrdinals(OrdinalBytesRefVector v) {
+ int positionCount = v.getDictionaryVector().getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ try (BytesRefVector.Builder builder = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(v.getDictionaryVector(), p, scratchPad));
+ }
+ IntVector ordinals = v.getOrdinalsVector();
+ ordinals.incRef();
+ return new OrdinalBytesRefVector(ordinals, builder.build()).asBlock();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToGeoShapeFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohexToGeoShapeFromStringEvaluator get(DriverContext context) {
+ return new StGeohexToGeoShapeFromStringEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToGeoShapeFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToLongFromStringEvaluator.java
new file mode 100644
index 0000000000000..815b5c9cb9015
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToLongFromStringEvaluator.java
@@ -0,0 +1,126 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohexToLong}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohexToLongFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohexToLongFromStringEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendLong(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohexToLong.fromString(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ long value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendLong(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeohexToLong.fromString(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToLongFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohexToLongFromStringEvaluator get(DriverContext context) {
+ return new StGeohexToLongFromStringEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToLongFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToStringFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToStringFromLongEvaluator.java
new file mode 100644
index 0000000000000..9806e85ef6a48
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToStringFromLongEvaluator.java
@@ -0,0 +1,124 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeohexToString}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeohexToStringFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeohexToStringFromLongEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ LongVector vector = (LongVector) v;
+ int positionCount = v.getPositionCount();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongVector container, int index) {
+ long value = container.getLong(index);
+ return StGeohexToString.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ LongBlock block = (LongBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongBlock container, int index) {
+ long value = container.getLong(index);
+ return StGeohexToString.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToStringFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeohexToStringFromLongEvaluator get(DriverContext context) {
+ return new StGeohexToStringFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeohexToStringFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..3644945c6600c
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,140 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldAndFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator in, EvalOperator.ExpressionEvaluator precision,
+ GeoBoundingBox bbox, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), inBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldAndFieldAndLiteral(result, p, inBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in, precision);
+ }
+
+ 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 in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ EvalOperator.ExpressionEvaluator.Factory precision, GeoBoundingBox bbox) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeotileFromFieldAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldAndFieldAndLiteralEvaluator(source, in.get(context), precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndFieldEvaluator.java
new file mode 100644
index 0000000000000..c0e9075742d58
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndFieldEvaluator.java
@@ -0,0 +1,132 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldAndFieldEvaluator(Source source, EvalOperator.ExpressionEvaluator in,
+ EvalOperator.ExpressionEvaluator precision, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), inBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldAndField(result, p, inBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in, precision);
+ }
+
+ 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 in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeotileFromFieldAndFieldEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldAndFieldEvaluator(source, in.get(context), precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndLiteralAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndLiteralAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..13a8da4ad0c9c
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndLiteralAndLiteralEvaluator.java
@@ -0,0 +1,119 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldAndLiteralAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator in;
+
+ private final StGeotile.GeoTileBoundedGrid bounds;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldAndLiteralAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator in, StGeotile.GeoTileBoundedGrid bounds,
+ DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.bounds = bounds;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock inBlock = (BytesRefBlock) in.eval(page)) {
+ return eval(page.getPositionCount(), inBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock inBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!inBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldAndLiteralAndLiteral(result, p, inBlock, this.bounds);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndLiteralAndLiteralEvaluator[" + "in=" + in + ", bounds=" + bounds + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(in);
+ }
+
+ 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 in;
+
+ private final StGeotile.GeoTileBoundedGrid bounds;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory in,
+ StGeotile.GeoTileBoundedGrid bounds) {
+ this.source = source;
+ this.in = in;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public StGeotileFromFieldAndLiteralAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldAndLiteralAndLiteralEvaluator(source, in.get(context), bounds, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndLiteralAndLiteralEvaluator[" + "in=" + in + ", bounds=" + bounds + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..f5b6f4b503373
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldAndLiteralEvaluator.java
@@ -0,0 +1,118 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator wkbBlock;
+
+ private final int precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator wkbBlock, int precision, DriverContext driverContext) {
+ this.source = source;
+ this.wkbBlock = wkbBlock;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (BytesRefBlock wkbBlockBlock = (BytesRefBlock) wkbBlock.eval(page)) {
+ return eval(page.getPositionCount(), wkbBlockBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, BytesRefBlock wkbBlockBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!wkbBlockBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldAndLiteral(result, p, wkbBlockBlock, this.precision);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndLiteralEvaluator[" + "wkbBlock=" + wkbBlock + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(wkbBlock);
+ }
+
+ 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 wkbBlock;
+
+ private final int precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory wkbBlock,
+ int precision) {
+ this.source = source;
+ this.wkbBlock = wkbBlock;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeotileFromFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldAndLiteralEvaluator(source, wkbBlock.get(context), precision, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldAndLiteralEvaluator[" + "wkbBlock=" + wkbBlock + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..9c621960577ec
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,139 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, EvalOperator.ExpressionEvaluator precision,
+ GeoBoundingBox bbox, DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldDocValuesAndFieldAndLiteral(result, p, encodedBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded, precision);
+ }
+
+ 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 encoded;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ EvalOperator.ExpressionEvaluator.Factory precision, GeoBoundingBox bbox) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator(source, encoded.get(context), precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndFieldEvaluator.java
new file mode 100644
index 0000000000000..0db8ced570f03
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndFieldEvaluator.java
@@ -0,0 +1,132 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldDocValuesAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldDocValuesAndFieldEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, EvalOperator.ExpressionEvaluator precision,
+ DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock, precisionBlock);
+ }
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldDocValuesAndField(result, p, encodedBlock, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndFieldEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded, precision);
+ }
+
+ 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 encoded;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeotileFromFieldDocValuesAndFieldEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldDocValuesAndFieldEvaluator(source, encoded.get(context), precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndFieldEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..c6dc1d23b9f3d
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator.java
@@ -0,0 +1,118 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final StGeotile.GeoTileBoundedGrid bounds;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, StGeotile.GeoTileBoundedGrid bounds,
+ DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.bounds = bounds;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldDocValuesAndLiteralAndLiteral(result, p, encodedBlock, this.bounds);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator[" + "encoded=" + encoded + ", bounds=" + bounds + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded);
+ }
+
+ 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 encoded;
+
+ private final StGeotile.GeoTileBoundedGrid bounds;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded,
+ StGeotile.GeoTileBoundedGrid bounds) {
+ this.source = source;
+ this.encoded = encoded;
+ this.bounds = bounds;
+ }
+
+ @Override
+ public StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator(source, encoded.get(context), bounds, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator[" + "encoded=" + encoded + ", bounds=" + bounds + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..93acf7bd2badc
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromFieldDocValuesAndLiteralEvaluator.java
@@ -0,0 +1,116 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromFieldDocValuesAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator encoded;
+
+ private final int precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromFieldDocValuesAndLiteralEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator encoded, int precision, DriverContext driverContext) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (LongBlock encodedBlock = (LongBlock) encoded.eval(page)) {
+ return eval(page.getPositionCount(), encodedBlock);
+ }
+ }
+
+ public LongBlock eval(int positionCount, LongBlock encodedBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ boolean allBlocksAreNulls = true;
+ if (!encodedBlock.isNull(p)) {
+ allBlocksAreNulls = false;
+ }
+ if (allBlocksAreNulls) {
+ result.appendNull();
+ continue position;
+ }
+ try {
+ StGeotile.fromFieldDocValuesAndLiteral(result, p, encodedBlock, this.precision);
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(encoded);
+ }
+
+ 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 encoded;
+
+ private final int precision;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory encoded, int precision) {
+ this.source = source;
+ this.encoded = encoded;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeotileFromFieldDocValuesAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromFieldDocValuesAndLiteralEvaluator(source, encoded.get(context), precision, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromFieldDocValuesAndLiteralEvaluator[" + "encoded=" + encoded + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromLiteralAndFieldAndLiteralEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromLiteralAndFieldAndLiteralEvaluator.java
new file mode 100644
index 0000000000000..f626323fdc4f9
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromLiteralAndFieldAndLiteralEvaluator.java
@@ -0,0 +1,149 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromLiteralAndFieldAndLiteralEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final GeoBoundingBox bbox;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromLiteralAndFieldAndLiteralEvaluator(Source source, BytesRef in,
+ EvalOperator.ExpressionEvaluator precision, GeoBoundingBox bbox,
+ DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ IntVector precisionVector = precisionBlock.asVector();
+ if (precisionVector == null) {
+ return eval(page.getPositionCount(), precisionBlock);
+ }
+ return eval(page.getPositionCount(), precisionVector);
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ try {
+ result.appendLong(StGeotile.fromLiteralAndFieldAndLiteral(this.in, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p)), this.bbox));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntVector precisionVector) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ try {
+ result.appendLong(StGeotile.fromLiteralAndFieldAndLiteral(this.in, precisionVector.getInt(p), this.bbox));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromLiteralAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(precision);
+ }
+
+ 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 BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ private final GeoBoundingBox bbox;
+
+ public Factory(Source source, BytesRef in, EvalOperator.ExpressionEvaluator.Factory precision,
+ GeoBoundingBox bbox) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.bbox = bbox;
+ }
+
+ @Override
+ public StGeotileFromLiteralAndFieldAndLiteralEvaluator get(DriverContext context) {
+ return new StGeotileFromLiteralAndFieldAndLiteralEvaluator(source, in, precision.get(context), bbox, context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromLiteralAndFieldAndLiteralEvaluator[" + "in=" + in + ", precision=" + precision + ", bbox=" + bbox + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromLiteralAndFieldEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromLiteralAndFieldEvaluator.java
new file mode 100644
index 0000000000000..8a9534e8095d6
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileFromLiteralAndFieldEvaluator.java
@@ -0,0 +1,140 @@
+// 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.spatial;
+
+import java.lang.IllegalArgumentException;
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.IntBlock;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.LongBlock;
+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 StGeotile}.
+ * This class is generated. Edit {@code EvaluatorImplementer} instead.
+ */
+public final class StGeotileFromLiteralAndFieldEvaluator implements EvalOperator.ExpressionEvaluator {
+ private final Source source;
+
+ private final BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator precision;
+
+ private final DriverContext driverContext;
+
+ private Warnings warnings;
+
+ public StGeotileFromLiteralAndFieldEvaluator(Source source, BytesRef in,
+ EvalOperator.ExpressionEvaluator precision, DriverContext driverContext) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ this.driverContext = driverContext;
+ }
+
+ @Override
+ public Block eval(Page page) {
+ try (IntBlock precisionBlock = (IntBlock) precision.eval(page)) {
+ IntVector precisionVector = precisionBlock.asVector();
+ if (precisionVector == null) {
+ return eval(page.getPositionCount(), precisionBlock);
+ }
+ return eval(page.getPositionCount(), precisionVector);
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntBlock precisionBlock) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ if (precisionBlock.isNull(p)) {
+ result.appendNull();
+ continue position;
+ }
+ if (precisionBlock.getValueCount(p) != 1) {
+ if (precisionBlock.getValueCount(p) > 1) {
+ warnings().registerException(new IllegalArgumentException("single-value function encountered multi-value"));
+ }
+ result.appendNull();
+ continue position;
+ }
+ try {
+ result.appendLong(StGeotile.fromLiteralAndField(this.in, precisionBlock.getInt(precisionBlock.getFirstValueIndex(p))));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ public LongBlock eval(int positionCount, IntVector precisionVector) {
+ try(LongBlock.Builder result = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ position: for (int p = 0; p < positionCount; p++) {
+ try {
+ result.appendLong(StGeotile.fromLiteralAndField(this.in, precisionVector.getInt(p)));
+ } catch (IllegalArgumentException e) {
+ warnings().registerException(e);
+ result.appendNull();
+ }
+ }
+ return result.build();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromLiteralAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(precision);
+ }
+
+ 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 BytesRef in;
+
+ private final EvalOperator.ExpressionEvaluator.Factory precision;
+
+ public Factory(Source source, BytesRef in, EvalOperator.ExpressionEvaluator.Factory precision) {
+ this.source = source;
+ this.in = in;
+ this.precision = precision;
+ }
+
+ @Override
+ public StGeotileFromLiteralAndFieldEvaluator get(DriverContext context) {
+ return new StGeotileFromLiteralAndFieldEvaluator(source, in, precision.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileFromLiteralAndFieldEvaluator[" + "in=" + in + ", precision=" + precision + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShapeFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShapeFromLongEvaluator.java
new file mode 100644
index 0000000000000..e625fb7443aab
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShapeFromLongEvaluator.java
@@ -0,0 +1,124 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeotileToGeoShape}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeotileToGeoShapeFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeotileToGeoShapeFromLongEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator gridId, DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ LongVector vector = (LongVector) v;
+ int positionCount = v.getPositionCount();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongVector container, int index) {
+ long value = container.getLong(index);
+ return StGeotileToGeoShape.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ LongBlock block = (LongBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongBlock container, int index) {
+ long value = container.getLong(index);
+ return StGeotileToGeoShape.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToGeoShapeFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeotileToGeoShapeFromLongEvaluator get(DriverContext context) {
+ return new StGeotileToGeoShapeFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToGeoShapeFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShapeFromStringEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShapeFromStringEvaluator.java
new file mode 100644
index 0000000000000..801bf228e6adf
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShapeFromStringEvaluator.java
@@ -0,0 +1,144 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.IntVector;
+import org.elasticsearch.compute.data.OrdinalBytesRefVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeotileToGeoShape}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeotileToGeoShapeFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeotileToGeoShapeFromStringEvaluator(Source source,
+ EvalOperator.ExpressionEvaluator gridId, DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ OrdinalBytesRefVector ordinals = vector.asOrdinals();
+ if (ordinals != null) {
+ return evalOrdinals(ordinals);
+ }
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeotileToGeoShape.fromString(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeotileToGeoShape.fromString(value);
+ }
+
+ private Block evalOrdinals(OrdinalBytesRefVector v) {
+ int positionCount = v.getDictionaryVector().getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ try (BytesRefVector.Builder builder = driverContext.blockFactory().newBytesRefVectorBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(v.getDictionaryVector(), p, scratchPad));
+ }
+ IntVector ordinals = v.getOrdinalsVector();
+ ordinals.incRef();
+ return new OrdinalBytesRefVector(ordinals, builder.build()).asBlock();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToGeoShapeFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeotileToGeoShapeFromStringEvaluator get(DriverContext context) {
+ return new StGeotileToGeoShapeFromStringEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToGeoShapeFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLongFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLongFromLongEvaluator.java
new file mode 100644
index 0000000000000..7fdf4159a9513
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLongFromLongEvaluator.java
@@ -0,0 +1,126 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeotileToLong}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeotileToLongFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeotileToLongFromLongEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendLong(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeotileToLong.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ long value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendLong(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeotileToLong.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToLongFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeotileToLongFromLongEvaluator get(DriverContext context) {
+ return new StGeotileToLongFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToLongFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLongFromStringEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLongFromStringEvaluator.java
new file mode 100644
index 0000000000000..b99abc16e62f8
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLongFromStringEvaluator.java
@@ -0,0 +1,126 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.BytesRefVector;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeotileToLong}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeotileToLongFromStringEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeotileToLongFromStringEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ BytesRefVector vector = (BytesRefVector) v;
+ int positionCount = v.getPositionCount();
+ BytesRef scratchPad = new BytesRef();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantLongBlockWith(evalValue(vector, 0, scratchPad), positionCount);
+ }
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendLong(evalValue(vector, p, scratchPad));
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefVector container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeotileToLong.fromString(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ BytesRefBlock block = (BytesRefBlock) b;
+ int positionCount = block.getPositionCount();
+ try (LongBlock.Builder builder = driverContext.blockFactory().newLongBlockBuilder(positionCount)) {
+ BytesRef scratchPad = new BytesRef();
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ long value = evalValue(block, i, scratchPad);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendLong(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private long evalValue(BytesRefBlock container, int index, BytesRef scratchPad) {
+ BytesRef value = container.getBytesRef(index, scratchPad);
+ return StGeotileToLong.fromString(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToLongFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeotileToLongFromStringEvaluator get(DriverContext context) {
+ return new StGeotileToLongFromStringEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToLongFromStringEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToStringFromLongEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToStringFromLongEvaluator.java
new file mode 100644
index 0000000000000..aece0da62cd23
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToStringFromLongEvaluator.java
@@ -0,0 +1,124 @@
+// 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.spatial;
+
+import java.lang.Override;
+import java.lang.String;
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.compute.data.Block;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.data.LongVector;
+import org.elasticsearch.compute.data.Vector;
+import org.elasticsearch.compute.operator.DriverContext;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.core.Releasables;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction;
+
+/**
+ * {@link EvalOperator.ExpressionEvaluator} implementation for {@link StGeotileToString}.
+ * This class is generated. Edit {@code ConvertEvaluatorImplementer} instead.
+ */
+public final class StGeotileToStringFromLongEvaluator extends AbstractConvertFunction.AbstractEvaluator {
+ private final EvalOperator.ExpressionEvaluator gridId;
+
+ public StGeotileToStringFromLongEvaluator(Source source, EvalOperator.ExpressionEvaluator gridId,
+ DriverContext driverContext) {
+ super(driverContext, source);
+ this.gridId = gridId;
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator next() {
+ return gridId;
+ }
+
+ @Override
+ public Block evalVector(Vector v) {
+ LongVector vector = (LongVector) v;
+ int positionCount = v.getPositionCount();
+ if (vector.isConstant()) {
+ return driverContext.blockFactory().newConstantBytesRefBlockWith(evalValue(vector, 0), positionCount);
+ }
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ builder.appendBytesRef(evalValue(vector, p));
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongVector container, int index) {
+ long value = container.getLong(index);
+ return StGeotileToString.fromLong(value);
+ }
+
+ @Override
+ public Block evalBlock(Block b) {
+ LongBlock block = (LongBlock) b;
+ int positionCount = block.getPositionCount();
+ try (BytesRefBlock.Builder builder = driverContext.blockFactory().newBytesRefBlockBuilder(positionCount)) {
+ for (int p = 0; p < positionCount; p++) {
+ int valueCount = block.getValueCount(p);
+ int start = block.getFirstValueIndex(p);
+ int end = start + valueCount;
+ boolean positionOpened = false;
+ boolean valuesAppended = false;
+ for (int i = start; i < end; i++) {
+ BytesRef value = evalValue(block, i);
+ if (positionOpened == false && valueCount > 1) {
+ builder.beginPositionEntry();
+ positionOpened = true;
+ }
+ builder.appendBytesRef(value);
+ valuesAppended = true;
+ }
+ if (valuesAppended == false) {
+ builder.appendNull();
+ } else if (positionOpened) {
+ builder.endPositionEntry();
+ }
+ }
+ return builder.build();
+ }
+ }
+
+ private BytesRef evalValue(LongBlock container, int index) {
+ long value = container.getLong(index);
+ return StGeotileToString.fromLong(value);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToStringFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+
+ @Override
+ public void close() {
+ Releasables.closeExpectNoException(gridId);
+ }
+
+ public static class Factory implements EvalOperator.ExpressionEvaluator.Factory {
+ private final Source source;
+
+ private final EvalOperator.ExpressionEvaluator.Factory gridId;
+
+ public Factory(Source source, EvalOperator.ExpressionEvaluator.Factory gridId) {
+ this.source = source;
+ this.gridId = gridId;
+ }
+
+ @Override
+ public StGeotileToStringFromLongEvaluator get(DriverContext context) {
+ return new StGeotileToStringFromLongEvaluator(source, gridId.get(context), context);
+ }
+
+ @Override
+ public String toString() {
+ return "StGeotileToStringFromLongEvaluator[" + "gridId=" + gridId + "]";
+ }
+ }
+}
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 07990a72e99cc..9866c1fcf899e 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
@@ -363,6 +363,11 @@ public enum Cap {
*/
ST_ENVELOPE,
+ /**
+ * Support ST_GEOHASH, ST_GEOTILE and ST_GEOHEX functions
+ */
+ SPATIAL_GRID,
+
/**
* Fix to GROK and DISSECT that allows extracting attributes with the same name as the input
* https://github.com/elastic/elasticsearch/issues/110184
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalGeometry.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalGeometry.java
new file mode 100644
index 0000000000000..be43f576c7fdd
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalGeometry.java
@@ -0,0 +1,227 @@
+/*
+ * 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.common.spatial;
+
+import org.apache.lucene.geo.Component2D;
+import org.apache.lucene.geo.GeoUtils;
+import org.apache.lucene.geo.LatLonGeometry;
+import org.apache.lucene.index.PointValues;
+import org.apache.lucene.spatial3d.geom.GeoArea;
+import org.apache.lucene.spatial3d.geom.GeoAreaFactory;
+import org.apache.lucene.spatial3d.geom.GeoPolygon;
+import org.apache.lucene.spatial3d.geom.LatLonBounds;
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.elasticsearch.h3.H3;
+
+/**
+ * Implementation of a lucene {@link LatLonGeometry} that covers the extent of a provided H3 bin.
+ * Note that H3 bin are polygons on the sphere.
+ * TODO: This class is a copy of the same class in org.elasticsearch.xpack.spatial.common, we should find a common location for it.
+ */
+class H3SphericalGeometry extends LatLonGeometry {
+
+ private final long h3;
+
+ H3SphericalGeometry(long h3) {
+ this.h3 = h3;
+ }
+
+ @Override
+ protected Component2D toComponent2D() {
+ return new H3Polygon2D(h3);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o instanceof H3SphericalGeometry geom) {
+ return h3 == geom.h3;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Long.hashCode(h3);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("H3 : ");
+ sb.append("\"");
+ sb.append(h3);
+ sb.append("\"");
+ return sb.toString();
+ }
+
+ private static class H3Polygon2D implements Component2D {
+
+ // We want to make are edges a bit bigger because spatial3d and h3 edges do not fully agree in
+ // membership of points around he edges.
+ private static final double BBOX_EDGE_DELTA = 1e-4;
+ private final long h3;
+ private final int res;
+ private final GeoPolygon hexagon;
+ private final double minX, maxX, minY, maxY;
+
+ private H3Polygon2D(long h3) {
+ this.h3 = h3;
+ this.res = H3.getResolution(h3);
+ this.hexagon = H3SphericalUtil.toGeoPolygon(h3);
+ final LatLonBounds bounds = new LatLonBounds();
+ this.hexagon.getBounds(bounds);
+ final double minY = bounds.checkNoBottomLatitudeBound() ? GeoUtils.MIN_LAT_INCL : Math.toDegrees(bounds.getMinLatitude());
+ final double maxY = bounds.checkNoTopLatitudeBound() ? GeoUtils.MAX_LAT_INCL : Math.toDegrees(bounds.getMaxLatitude());
+ final double minX;
+ final double maxX;
+ if (bounds.checkNoLongitudeBound() || bounds.getLeftLongitude() > bounds.getRightLongitude()) {
+ minX = GeoUtils.MIN_LON_INCL;
+ maxX = GeoUtils.MAX_LON_INCL;
+ } else {
+ minX = Math.toDegrees(bounds.getLeftLongitude());
+ maxX = Math.toDegrees(bounds.getRightLongitude());
+ }
+ // Unfortunately, h3 bin edges are fuzzy and cannot be represented easily. We need to buffer
+ // the bounding boxes to make sure we don't reject valid points
+ this.minX = Math.max(GeoUtils.MIN_LON_INCL, minX - BBOX_EDGE_DELTA);
+ this.maxX = Math.min(GeoUtils.MAX_LON_INCL, maxX + BBOX_EDGE_DELTA);
+ this.minY = Math.max(GeoUtils.MIN_LAT_INCL, minY - BBOX_EDGE_DELTA);
+ this.maxY = Math.min(GeoUtils.MAX_LAT_INCL, maxY + BBOX_EDGE_DELTA);
+
+ }
+
+ @Override
+ public double getMinX() {
+ return minX;
+ }
+
+ @Override
+ public double getMaxX() {
+ return maxX;
+ }
+
+ @Override
+ public double getMinY() {
+ return minY;
+ }
+
+ @Override
+ public double getMaxY() {
+ return maxY;
+ }
+
+ @Override
+ public boolean contains(double x, double y) {
+ return h3 == H3.geoToH3(y, x, res);
+ }
+
+ @Override
+ public PointValues.Relation relate(double minX, double maxX, double minY, double maxY) {
+ if (minX > this.maxX || maxX < this.minX || maxY < this.minY || minY > this.maxY) {
+ return PointValues.Relation.CELL_OUTSIDE_QUERY;
+ }
+ // h3 edges are fuzzy, therefore to avoid issues when bounding box are around the edges,
+ // we just buffer slightly the bounding box to check if it is inside the h3 bin, otherwise
+ // return crosses.
+ final GeoArea box = GeoAreaFactory.makeGeoArea(
+ PlanetModel.SPHERE,
+ Math.toRadians(Math.min(GeoUtils.MAX_LAT_INCL, maxY + BBOX_EDGE_DELTA)),
+ Math.toRadians(Math.max(GeoUtils.MIN_LAT_INCL, minY - BBOX_EDGE_DELTA)),
+ Math.toRadians(Math.max(GeoUtils.MIN_LON_INCL, minX - BBOX_EDGE_DELTA)),
+ Math.toRadians(Math.min(GeoUtils.MAX_LON_INCL, maxX + BBOX_EDGE_DELTA))
+ );
+ return switch (box.getRelationship(hexagon)) {
+ case GeoArea.CONTAINS -> PointValues.Relation.CELL_INSIDE_QUERY;
+ case GeoArea.DISJOINT -> PointValues.Relation.CELL_OUTSIDE_QUERY;
+ default -> PointValues.Relation.CELL_CROSSES_QUERY;
+ };
+ }
+
+ @Override
+ public boolean intersectsLine(double minX, double maxX, double minY, double maxY, double aX, double aY, double bX, double bY) {
+ throw new UnsupportedOperationException("intersectsLine not implemented in H3Polygon2D");
+ }
+
+ @Override
+ public boolean intersectsTriangle(
+ double minX,
+ double maxX,
+ double minY,
+ double maxY,
+ double aX,
+ double aY,
+ double bX,
+ double bY,
+ double cX,
+ double cY
+ ) {
+ throw new UnsupportedOperationException("intersectsTriangle not implemented in H3Polygon2D");
+ }
+
+ @Override
+ public boolean containsLine(double minX, double maxX, double minY, double maxY, double aX, double aY, double bX, double bY) {
+ throw new UnsupportedOperationException("containsLine not implemented in H3Polygon2D");
+ }
+
+ @Override
+ public boolean containsTriangle(
+ double minX,
+ double maxX,
+ double minY,
+ double maxY,
+ double aX,
+ double aY,
+ double bX,
+ double bY,
+ double cX,
+ double cY
+ ) {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public WithinRelation withinPoint(double x, double y) {
+ return contains(x, y) ? WithinRelation.NOTWITHIN : WithinRelation.DISJOINT;
+ }
+
+ @Override
+ public WithinRelation withinLine(
+ double minX,
+ double maxX,
+ double minY,
+ double maxY,
+ double aX,
+ double aY,
+ boolean ab,
+ double bX,
+ double bY
+ ) {
+ throw new UnsupportedOperationException("withinLine not implemented in H3Polygon2D");
+ }
+
+ @Override
+ public WithinRelation withinTriangle(
+ double minX,
+ double maxX,
+ double minY,
+ double maxY,
+ double aX,
+ double aY,
+ boolean ab,
+ double bX,
+ double bY,
+ boolean bc,
+ double cX,
+ double cY,
+ boolean ca
+ ) {
+ throw new UnsupportedOperationException("withinTriangle not implemented in H3Polygon2D");
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalUtil.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalUtil.java
new file mode 100644
index 0000000000000..ee63b8cfb18d6
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalUtil.java
@@ -0,0 +1,136 @@
+/*
+ * 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.common.spatial;
+
+import org.apache.lucene.geo.LatLonGeometry;
+import org.apache.lucene.spatial3d.geom.GeoPoint;
+import org.apache.lucene.spatial3d.geom.GeoPolygon;
+import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
+import org.apache.lucene.spatial3d.geom.PlanetModel;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.h3.CellBoundary;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.h3.LatLng;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiPredicate;
+
+/**
+ * Utility class for generating H3 spherical objects.
+ * TODO: This class is a copy of the same class in org.elasticsearch.xpack.spatial.common, we should find a common location for it.
+ */
+public final class H3SphericalUtil {
+
+ private static final BiPredicate MIN_COMPARATOR = (e1, e2) -> e1.getLatRad() < e2.getLatRad();
+
+ private static final BiPredicate MAX_COMPARATOR = (e1, e2) -> e1.getLatRad() > e2.getLatRad();
+
+ /**
+ * Computes the bounding box of the provided h3 cell considering edges to be great circles and
+ * stores then in the provided {@link GeoBoundingBox}.
+ */
+ public static void computeGeoBounds(long h3, GeoBoundingBox boundingBox) {
+ final CellBoundary boundary = H3.h3ToGeoBoundary(h3);
+ final int res = H3.getResolution(h3);
+ if (h3 == H3.northPolarH3(res)) {
+ // specialize north pole
+ computeNorthPoleBounds(boundary, boundingBox);
+ } else if (h3 == H3.southPolarH3(res)) {
+ // specialize south pole
+ computeSouthPoleBounds(boundary, boundingBox);
+ } else {
+ // generic case
+ computeBounds(boundary, boundingBox);
+ }
+ }
+
+ private static void computeNorthPoleBounds(CellBoundary boundary, GeoBoundingBox boundingBox) {
+ double minLat = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < boundary.numPoints(); i++) {
+ minLat = Math.min(minLat, boundary.getLatLon(i).getLatRad());
+ }
+ boundingBox.topLeft().reset(90, -180d);
+ boundingBox.bottomRight().reset(Math.toDegrees(minLat), 180d);
+ }
+
+ private static void computeSouthPoleBounds(CellBoundary boundary, GeoBoundingBox boundingBox) {
+ double maxLat = Double.NEGATIVE_INFINITY;
+ for (int i = 0; i < boundary.numPoints(); i++) {
+ maxLat = Math.max(maxLat, boundary.getLatLon(i).getLatRad());
+ }
+ boundingBox.topLeft().reset(Math.toDegrees(maxLat), -180d);
+ boundingBox.bottomRight().reset(-90, 180d);
+ }
+
+ private static void computeBounds(CellBoundary boundary, GeoBoundingBox boundingBox) {
+ // This algorithm is based on the bounding box for great circle edges in
+ // https://trs.jpl.nasa.gov/bitstream/handle/2014/41271/07-0286.pdf
+ double minLat = Double.POSITIVE_INFINITY;
+ int minLatPos = -1;
+ double minLon = Double.POSITIVE_INFINITY;
+ double maxLat = Double.NEGATIVE_INFINITY;
+ int maxLatPos = -1;
+ double maxLon = Double.NEGATIVE_INFINITY;
+ double maxNegLon = Double.NEGATIVE_INFINITY;
+ double minPosLon = Double.POSITIVE_INFINITY;
+ for (int i = 0; i < boundary.numPoints(); i++) {
+ final double lon = boundary.getLatLon(i).getLonRad();
+ final double lat = boundary.getLatLon(i).getLatRad();
+ if (lat < minLat) {
+ minLat = lat;
+ minLatPos = i;
+ }
+ if (lat > maxLat) {
+ maxLat = lat;
+ maxLatPos = i;
+ }
+ minLon = Math.min(minLon, lon);
+ maxLon = Math.max(maxLon, lon);
+ if (lon < 0) {
+ maxNegLon = Math.max(maxNegLon, lon);
+ } else {
+ minPosLon = Math.min(minPosLon, lon);
+ }
+ }
+ if (minLat < 0) {
+ // we only correct the min latitude if negative
+ minLat = boundary.getLatLon(minLatPos).greatCircleMinLatitude(computeEdge(boundary, minLatPos, MIN_COMPARATOR));
+ }
+ if (maxLat > 0) {
+ // we only correct the max latitude if positive
+ maxLat = boundary.getLatLon(maxLatPos).greatCircleMaxLatitude(computeEdge(boundary, maxLatPos, MAX_COMPARATOR));
+ }
+ // the min / max longitude is computed the same way as in cartesian, being careful with polygons crossing the dateline
+ final boolean crossesDateline = maxLon - minLon > Math.PI;
+ boundingBox.topLeft().reset(Math.toDegrees(maxLat), crossesDateline ? Math.toDegrees(minPosLon) : Math.toDegrees(minLon));
+ boundingBox.bottomRight().reset(Math.toDegrees(minLat), crossesDateline ? Math.toDegrees(maxNegLon) : Math.toDegrees(maxLon));
+ }
+
+ private static LatLng computeEdge(CellBoundary boundary, int pos, BiPredicate comparator) {
+ final LatLng end1 = boundary.getLatLon((pos + 1) % boundary.numPoints());
+ final LatLng end2 = boundary.getLatLon(pos == 0 ? boundary.numPoints() - 1 : pos - 1);
+ return comparator.test(end1, end2) ? end1 : end2;
+ }
+
+ /** Return the {@link GeoPolygon} representing the provided H3 bin */
+ public static GeoPolygon toGeoPolygon(long h3) {
+ final CellBoundary boundary = H3.h3ToGeoBoundary(h3);
+ List points = new ArrayList<>(boundary.numPoints());
+ for (int i = 0; i < boundary.numPoints(); i++) {
+ LatLng latLng = boundary.getLatLon(i);
+ points.add(new GeoPoint(PlanetModel.SPHERE, latLng.getLatRad(), latLng.getLonRad()));
+ }
+ return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
+ }
+
+ /** Return the {@link LatLonGeometry} representing the provided H3 bin */
+ public static LatLonGeometry getLatLonGeometry(long h3) {
+ return new H3SphericalGeometry(h3);
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java
index 4ffa778b8287a..6b520ee4c034d 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/ExpressionWritables.java
@@ -58,6 +58,18 @@
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialWithin;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StDistance;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StEnvelope;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohash;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohashToGeoShape;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohashToLong;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohashToString;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohex;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohexToGeoShape;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohexToLong;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohexToString;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotile;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToGeoShape;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToLong;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToString;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StX;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StXMax;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StXMin;
@@ -212,7 +224,25 @@ public static List unaryScalars() {
}
private static List spatials() {
- return List.of(SpatialContains.ENTRY, SpatialDisjoint.ENTRY, SpatialIntersects.ENTRY, SpatialWithin.ENTRY, StDistance.ENTRY);
+ return List.of(
+ SpatialContains.ENTRY,
+ SpatialDisjoint.ENTRY,
+ SpatialIntersects.ENTRY,
+ SpatialWithin.ENTRY,
+ StDistance.ENTRY,
+ StGeohash.ENTRY,
+ StGeohashToString.ENTRY,
+ StGeohashToLong.ENTRY,
+ StGeohashToGeoShape.ENTRY,
+ StGeotile.ENTRY,
+ StGeotileToString.ENTRY,
+ StGeotileToLong.ENTRY,
+ StGeotileToGeoShape.ENTRY,
+ StGeohex.ENTRY,
+ StGeohexToString.ENTRY,
+ StGeohexToLong.ENTRY,
+ StGeohexToGeoShape.ENTRY
+ );
}
private static List arithmetics() {
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 b2d85d809c058..95d2b284152df 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
@@ -131,6 +131,18 @@
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialWithin;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StDistance;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StEnvelope;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohash;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohashToGeoShape;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohashToLong;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohashToString;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohex;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohexToGeoShape;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohexToLong;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeohexToString;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotile;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToGeoShape;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToLong;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToString;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StX;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StXMax;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StXMin;
@@ -380,7 +392,19 @@ private static FunctionDefinition[][] functions() {
def(StYMax.class, StYMax::new, "st_ymax"),
def(StYMin.class, StYMin::new, "st_ymin"),
def(StX.class, StX::new, "st_x"),
- def(StY.class, StY::new, "st_y") },
+ def(StY.class, StY::new, "st_y"),
+ def(StGeohash.class, StGeohash::new, "st_geohash"),
+ def(StGeohashToGeoShape.class, StGeohashToGeoShape::new, "st_geohash_to_geoshape"),
+ def(StGeohashToLong.class, StGeohashToLong::new, "st_geohash_to_long"),
+ def(StGeohashToString.class, StGeohashToString::new, "st_geohash_to_string"),
+ def(StGeotile.class, StGeotile::new, "st_geotile"),
+ def(StGeotileToGeoShape.class, StGeotileToGeoShape::new, "st_geotile_to_geoshape"),
+ def(StGeotileToLong.class, StGeotileToLong::new, "st_geotile_to_long"),
+ def(StGeotileToString.class, StGeotileToString::new, "st_geotile_to_string"),
+ def(StGeohex.class, StGeohex::new, "st_geohex"),
+ def(StGeohexToGeoShape.class, StGeohexToGeoShape::new, "st_geohex_to_geoshape"),
+ def(StGeohexToLong.class, StGeohexToLong::new, "st_geohex_to_long"),
+ def(StGeohexToString.class, StGeohexToString::new, "st_geohex_to_string") },
// conditional
new FunctionDefinition[] { def(Case.class, Case::new, "case") },
// null
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java
index 3b869c0200cb9..cdd55ca24d86f 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/AbstractConvertFunction.java
@@ -100,7 +100,7 @@ static String supportedTypesNames(Set types) {
}
@FunctionalInterface
- interface BuildFactory {
+ public interface BuildFactory {
ExpressionEvaluator.Factory build(Source source, ExpressionEvaluator.Factory field);
}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/GeoHexBoundedPredicate.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/GeoHexBoundedPredicate.java
new file mode 100644
index 0000000000000..500a218e956f9
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/GeoHexBoundedPredicate.java
@@ -0,0 +1,47 @@
+/*
+ * 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.spatial;
+
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.xpack.esql.common.spatial.H3SphericalUtil;
+
+/**
+ * A class that checks if a hexagon intersects with a bounding box.
+ * TODO: This class is a copy of the inner class GeoHexPredicate inside GeoHexCellIdSource, we should find a common location for it.
+ */
+public class GeoHexBoundedPredicate {
+
+ private final boolean crossesDateline;
+ private final GeoBoundingBox bbox, scratch;
+
+ GeoHexBoundedPredicate(GeoBoundingBox bbox) {
+ this.crossesDateline = bbox.right() < bbox.left();
+ this.bbox = bbox;
+ scratch = new GeoBoundingBox(new org.elasticsearch.common.geo.GeoPoint(), new org.elasticsearch.common.geo.GeoPoint());
+ }
+
+ public boolean validHex(long hex) {
+ H3SphericalUtil.computeGeoBounds(hex, scratch);
+ if (bbox.top() > scratch.bottom() && bbox.bottom() < scratch.top()) {
+ if (scratch.left() > scratch.right()) {
+ return intersects(-180, scratch.right()) || intersects(scratch.left(), 180);
+ } else {
+ return intersects(scratch.left(), scratch.right());
+ }
+ }
+ return false;
+ }
+
+ private boolean intersects(double minLon, double maxLon) {
+ if (crossesDateline) {
+ return bbox.left() < maxLon || bbox.right() > minLon;
+ } else {
+ return bbox.left() < maxLon && bbox.right() > minLon;
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridFunction.java
new file mode 100644
index 0000000000000..a6b91599fcb99
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridFunction.java
@@ -0,0 +1,353 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.common.geo.GeoPoint;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.common.io.stream.StreamOutput;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.geometry.Point;
+import org.elasticsearch.geometry.Rectangle;
+import org.elasticsearch.geometry.utils.SpatialEnvelopeVisitor;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.function.scalar.ScalarFunction;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.util.PlanStreamInput;
+import org.elasticsearch.xpack.esql.expression.function.OptionalArgument;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST;
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND;
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.THIRD;
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType;
+import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isWholeNumber;
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_POINT;
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
+import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.GEO;
+
+/**
+ * Spatial functions that take one spatial argument, one parameter and one optional bounds can inherit from this class.
+ * Obvious choices are: StGeohash, StGeotile and StGeohex.
+ */
+public abstract class SpatialGridFunction extends ScalarFunction implements OptionalArgument {
+ protected final Expression spatialField;
+ protected final Expression parameter;
+ protected final Expression bounds;
+ protected final boolean spatialDocsValues;
+
+ protected SpatialGridFunction(
+ Source source,
+ Expression spatialField,
+ Expression parameter,
+ Expression bounds,
+ boolean spatialDocsValues
+ ) {
+ super(source, bounds == null ? Arrays.asList(spatialField, parameter) : Arrays.asList(spatialField, parameter, bounds));
+ this.spatialField = spatialField;
+ this.parameter = parameter;
+ this.bounds = bounds;
+ this.spatialDocsValues = spatialDocsValues;
+ }
+
+ protected SpatialGridFunction(StreamInput in, boolean spatialDocsValues) throws IOException {
+ this(
+ Source.readFrom((StreamInput & PlanStreamInput) in),
+ in.readNamedWriteable(Expression.class),
+ in.readNamedWriteable(Expression.class),
+ in.readOptionalNamedWriteable(Expression.class),
+ spatialDocsValues
+ );
+ }
+
+ @Override
+ public void writeTo(StreamOutput out) throws IOException {
+ source().writeTo(out);
+ out.writeNamedWriteable(spatialField);
+ out.writeNamedWriteable(parameter);
+ out.writeOptionalNamedWriteable(bounds);
+ }
+
+ /**
+ * Mark the function as expecting the specified field to arrive as doc-values.
+ * This only applies to geo_point and cartesian_point types.
+ */
+ public abstract SpatialGridFunction withDocValues(boolean useDocValues);
+
+ @Override
+ protected TypeResolution resolveType() {
+ if (childrenResolved() == false) {
+ return new TypeResolution("Unresolved children");
+ }
+
+ TypeResolution resolution = isGeoPoint(spatialField(), sourceText());
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+
+ resolution = isWholeNumber(parameter(), sourceText(), SECOND);
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+
+ if (bounds() != null) {
+ resolution = isGeo(bounds(), sourceText());
+ if (resolution.unresolved()) {
+ return resolution;
+ }
+ }
+
+ return TypeResolution.TYPE_RESOLVED;
+ }
+
+ protected static Expression.TypeResolution isGeoPoint(Expression e, String operationName) {
+ return isType(e, t -> t.equals(GEO_POINT), operationName, FIRST, GEO_POINT.typeName());
+ }
+
+ protected static Expression.TypeResolution isGeo(Expression e, String operationName) {
+ return isType(e, t -> t.equals(GEO_SHAPE) || t.equals(GEO_POINT), operationName, THIRD, GEO_SHAPE.typeName(), GEO_POINT.typeName());
+ }
+
+ protected static Rectangle asRectangle(BytesRef boundsBytesRef) {
+ var geometry = GEO.wkbToGeometry(boundsBytesRef);
+ if (geometry instanceof Rectangle rectangle) {
+ return rectangle;
+ }
+ var envelope = SpatialEnvelopeVisitor.visitGeo(geometry, SpatialEnvelopeVisitor.WrapLongitude.WRAP);
+ if (envelope.isPresent()) {
+ return envelope.get();
+ }
+ throw new IllegalArgumentException("Cannot determine envelope of bounds geometry");
+ }
+
+ protected static Rectangle asRectangle(List> list) {
+ var visitor = new SpatialEnvelopeVisitor(new SpatialEnvelopeVisitor.GeoPointVisitor(SpatialEnvelopeVisitor.WrapLongitude.WRAP));
+ for (Object o : list) {
+ if (o instanceof BytesRef bytesRef) {
+ var geometry = GEO.wkbToGeometry(bytesRef);
+ geometry.visit(visitor);
+ } else {
+ throw new IllegalArgumentException("Cannot determine envelope of bounds geometry of type " + o.getClass().getSimpleName());
+ }
+ }
+ return visitor.getResult();
+ }
+
+ protected static GeoBoundingBox asGeoBoundingBox(Object bounds) {
+ if (bounds instanceof BytesRef boundsBytesRef) {
+ return asGeoBoundingBox(asRectangle(boundsBytesRef));
+ } else if (bounds instanceof List> list) {
+ return asGeoBoundingBox(asRectangle(list));
+ }
+ throw new IllegalArgumentException("Cannot determine envelope of bounds geometry of type " + bounds.getClass().getSimpleName());
+ }
+
+ protected static GeoBoundingBox asGeoBoundingBox(Rectangle rectangle) {
+ return new GeoBoundingBox(
+ new GeoPoint(rectangle.getMaxLat(), rectangle.getMinLon()),
+ new GeoPoint(rectangle.getMinLat(), rectangle.getMaxLon())
+ );
+ }
+
+ protected static boolean inBounds(Point point, Rectangle bounds) {
+ // TODO: consider bounds across the dateline
+ return point.getX() >= bounds.getMinX()
+ && point.getY() >= bounds.getMinY()
+ && point.getX() <= bounds.getMaxX()
+ && point.getY() <= bounds.getMaxY();
+ }
+
+ @Override
+ public int hashCode() {
+ // NB: the hashcode is currently used for key generation so
+ // to avoid clashes between aggs with the same arguments, add the class name as variation
+ return Objects.hash(getClass(), children(), spatialDocsValues);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (super.equals(obj)) {
+ SpatialGridFunction other = (SpatialGridFunction) obj;
+ return Objects.equals(other.children(), children()) && Objects.equals(other.spatialDocsValues, spatialDocsValues);
+ }
+ return false;
+ }
+
+ public boolean spatialDocsValues() {
+ return spatialDocsValues;
+ }
+
+ @Override
+ public final SpatialGridFunction replaceChildren(List newChildren) {
+ Expression newSpatialField = newChildren.get(0);
+ Expression newParameter = newChildren.get(1);
+ Expression newBounds = newChildren.size() > 2 ? newChildren.get(2) : null;
+
+ return spatialField.equals(newSpatialField)
+ && parameter.equals(newParameter)
+ && (bounds == null && newBounds == null || bounds != null && bounds.equals(newBounds))
+ ? this
+ : replaceChildren(newSpatialField, newParameter, newBounds);
+ }
+
+ protected abstract SpatialGridFunction replaceChildren(Expression newSpatialField, Expression newParameter, Expression newBounds);
+
+ public Expression spatialField() {
+ return spatialField;
+ }
+
+ public Expression parameter() {
+ return parameter;
+ }
+
+ public Expression bounds() {
+ return bounds;
+ }
+
+ @Override
+ public boolean foldable() {
+ return spatialField.foldable() && parameter.foldable() && (bounds == null || bounds.foldable());
+ }
+
+ protected static void addGrids(LongBlock.Builder results, List gridIds) {
+ if (gridIds.isEmpty()) {
+ results.appendNull();
+ } else if (gridIds.size() == 1) {
+ results.appendLong(gridIds.getFirst());
+ } else {
+ results.beginPositionEntry();
+ for (long gridId : gridIds) {
+ results.appendLong(gridId);
+ }
+ results.endPositionEntry();
+ }
+ }
+
+ protected interface UnboundedGrid {
+ long calculateGridId(Point point, int precision);
+ }
+
+ protected interface BoundedGrid {
+ long calculateGridId(Point point);
+
+ int precision();
+ }
+
+ protected static void fromWKB(
+ LongBlock.Builder results,
+ int position,
+ BytesRefBlock wkbBlock,
+ int precision,
+ UnboundedGrid unboundedGrid
+ ) {
+ int valueCount = wkbBlock.getValueCount(position);
+ if (valueCount < 1) {
+ results.appendNull();
+ } else {
+ final BytesRef scratch = new BytesRef();
+ final int firstValueIndex = wkbBlock.getFirstValueIndex(position);
+ if (valueCount == 1) {
+ results.appendLong(
+ unboundedGrid.calculateGridId(GEO.wkbAsPoint(wkbBlock.getBytesRef(firstValueIndex, scratch)), precision)
+ );
+ } else {
+ results.beginPositionEntry();
+ for (int i = 0; i < valueCount; i++) {
+ results.appendLong(
+ unboundedGrid.calculateGridId(GEO.wkbAsPoint(wkbBlock.getBytesRef(firstValueIndex + i, scratch)), precision)
+ );
+ }
+ results.endPositionEntry();
+ }
+ }
+ }
+
+ protected static void fromEncodedLong(
+ LongBlock.Builder results,
+ int position,
+ LongBlock encoded,
+ int precision,
+ UnboundedGrid unboundedGrid
+ ) {
+ int valueCount = encoded.getValueCount(position);
+ if (valueCount < 1) {
+ results.appendNull();
+ } else {
+ final int firstValueIndex = encoded.getFirstValueIndex(position);
+ if (valueCount == 1) {
+ results.appendLong(unboundedGrid.calculateGridId(GEO.longAsPoint(encoded.getLong(firstValueIndex)), precision));
+ } else {
+ results.beginPositionEntry();
+ for (int i = 0; i < valueCount; i++) {
+ results.appendLong(unboundedGrid.calculateGridId(GEO.longAsPoint(encoded.getLong(firstValueIndex + i)), precision));
+ }
+ results.endPositionEntry();
+ }
+ }
+ }
+
+ protected static void fromWKB(LongBlock.Builder results, int position, BytesRefBlock wkbBlock, BoundedGrid bounds) {
+ int valueCount = wkbBlock.getValueCount(position);
+ if (valueCount < 1) {
+ results.appendNull();
+ } else {
+ final BytesRef scratch = new BytesRef();
+ final int firstValueIndex = wkbBlock.getFirstValueIndex(position);
+ if (valueCount == 1) {
+ long grid = bounds.calculateGridId(GEO.wkbAsPoint(wkbBlock.getBytesRef(firstValueIndex, scratch)));
+ if (grid < 0) {
+ results.appendNull();
+ } else {
+ results.appendLong(grid);
+ }
+ } else {
+ var gridIds = new ArrayList(valueCount);
+ for (int i = 0; i < valueCount; i++) {
+ var grid = bounds.calculateGridId(GEO.wkbAsPoint(wkbBlock.getBytesRef(firstValueIndex + i, scratch)));
+ if (grid >= 0) {
+ gridIds.add(grid);
+ }
+ }
+ addGrids(results, gridIds);
+ }
+ }
+ }
+
+ protected static void fromEncodedLong(LongBlock.Builder results, int position, LongBlock encoded, BoundedGrid bounds) {
+ int valueCount = encoded.getValueCount(position);
+ if (valueCount < 1) {
+ results.appendNull();
+ } else {
+ final int firstValueIndex = encoded.getFirstValueIndex(position);
+ if (valueCount == 1) {
+ long grid = bounds.calculateGridId(GEO.longAsPoint(encoded.getLong(firstValueIndex)));
+ if (grid < 0) {
+ results.appendNull();
+ } else {
+ results.appendLong(grid);
+ }
+ } else {
+ var gridIds = new ArrayList(valueCount);
+ for (int i = 0; i < valueCount; i++) {
+ var grid = bounds.calculateGridId(GEO.longAsPoint(encoded.getLong(firstValueIndex + i)));
+ if (grid >= 0) {
+ gridIds.add(grid);
+ }
+ }
+ addGrids(results, gridIds);
+ }
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohash.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohash.java
new file mode 100644
index 0000000000000..174f5f9b25207
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohash.java
@@ -0,0 +1,297 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.Evaluator;
+import org.elasticsearch.compute.ann.Fixed;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.geometry.Point;
+import org.elasticsearch.geometry.utils.Geohash;
+import org.elasticsearch.search.aggregations.bucket.geogrid.GeoHashBoundedPredicate;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.FoldContext;
+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.evaluator.mapper.EvaluatorMapper;
+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 java.io.IOException;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.GEO;
+
+/**
+ * Calculates the geohash of geo_point geometries.
+ */
+public class StGeohash extends SpatialGridFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohash",
+ StGeohash::new
+ );
+
+ /**
+ * When checking grid cells with bounds, we need to check if the cell is valid (intersects with the bounds).
+ * This uses GeoHashBoundedPredicate to check if the grid cell is valid.
+ */
+ protected static class GeoHashBoundedGrid implements BoundedGrid {
+ private final int precision;
+ private final GeoHashBoundedPredicate bounds;
+
+ public GeoHashBoundedGrid(int precision, GeoBoundingBox bbox) {
+ this.precision = checkPrecisionRange(precision);
+ this.bounds = new GeoHashBoundedPredicate(precision, bbox);
+ }
+
+ public long calculateGridId(Point point) {
+ String geohash = Geohash.stringEncode(point.getX(), point.getY(), precision);
+ if (bounds.validHash(geohash)) {
+ return Geohash.longEncode(geohash);
+ }
+ // TODO: Are negative values allowed in geohash long encoding?
+ return -1;
+ }
+
+ @Override
+ public int precision() {
+ return precision;
+ }
+ }
+
+ /**
+ * For unbounded grids, we don't need to check if the grid cell is valid,
+ * just calculate the encoded long intersecting the point at that precision.
+ */
+ protected static final UnboundedGrid unboundedGrid = (point, precision) -> Geohash.longEncode(
+ point.getX(),
+ point.getY(),
+ checkPrecisionRange(precision)
+ );
+
+ private static int checkPrecisionRange(int precision) {
+ if (precision < 1 || precision > Geohash.PRECISION) {
+ throw new IllegalArgumentException(
+ "Invalid geohash precision of " + precision + ". Must be between 1 and " + Geohash.PRECISION + "."
+ );
+ }
+ return precision;
+ }
+
+ @FunctionInfo(
+ returnType = "long",
+ description = """
+ Calculates the `geohash` of the supplied geo_point at the specified precision.
+ The result is long encoded. Use [ST_GEOHASH_TO_STRING](#esql-st_geohash_to_string) to convert the result to a string.
+ Or use [ST_GEOHASH_TO_GEOSHAPE](#esql-st_geohash_to_geoshape) to convert either the long or string `geohash` to a
+
+ These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query)
+ and the [`geohash_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geohashgrid-aggregation).""",
+ examples = @Example(file = "spatial-grid", tag = "st_geohash-grid")
+ )
+ public StGeohash(
+ Source source,
+ @Param(
+ name = "geometry",
+ type = { "geo_point" },
+ description = "Expression of type `geo_point`. If `null`, the function returns `null`."
+ ) Expression field,
+ @Param(name = "precision", type = { "integer" }, description = """
+ Expression of type `integer`. If `null`, the function returns `null`.
+ Valid values are between [1 and 12](https://en.wikipedia.org/wiki/Geohash).""") Expression precision,
+ @Param(name = "bounds", type = { "geo_shape", "geo_point" }, description = """
+ Optional bounds to filter the grid tiles, either a `geo_shape` or an array of at least two `geo_point`s.
+ The envelope of the `geo_shape` is used as bounds.""", optional = true) Expression bounds
+ ) {
+ this(source, field, precision, bounds, false);
+ }
+
+ private StGeohash(Source source, Expression field, Expression precision, Expression bounds, boolean spatialDocValues) {
+ super(source, field, precision, bounds, spatialDocValues);
+ }
+
+ private StGeohash(StreamInput in) throws IOException {
+ super(in, false);
+ }
+
+ @Override
+ public SpatialGridFunction withDocValues(boolean useDocValues) {
+ // Only update the docValues flags if the field is found in the attributes
+ boolean docValues = this.spatialDocsValues || useDocValues;
+ return new StGeohash(source(), spatialField, parameter, bounds, docValues);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public DataType dataType() {
+ return LONG;
+ }
+
+ @Override
+ protected SpatialGridFunction replaceChildren(Expression newSpatialField, Expression newParameter, Expression newBounds) {
+ return new StGeohash(source(), newSpatialField, newParameter, newBounds);
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohash::new, spatialField, parameter, bounds);
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ if (bounds != null) {
+ if (bounds.foldable() == false) {
+ throw new IllegalArgumentException("bounds must be foldable");
+ }
+ GeoBoundingBox bbox = asGeoBoundingBox(bounds.fold(toEvaluator.foldCtx()));
+ if (spatialField().foldable()) {
+ // Assume right is not foldable, since that would be dealt with in isFoldable() and fold()
+ var point = (BytesRef) spatialField.fold(toEvaluator.foldCtx());
+ return new StGeohashFromLiteralAndFieldAndLiteralEvaluator.Factory(source(), point, toEvaluator.apply(parameter()), bbox);
+ } else if (parameter().foldable()) {
+ // Assume left is not foldable, since that would be dealt with in isFoldable() and fold()
+ int precision = (int) parameter.fold(toEvaluator.foldCtx());
+ GeoHashBoundedGrid bounds = new GeoHashBoundedGrid(precision, bbox);
+ return spatialDocsValues
+ ? new StGeohashFromFieldDocValuesAndLiteralAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField()),
+ bounds
+ )
+ : new StGeohashFromFieldAndLiteralAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField), bounds);
+ } else {
+ // Both arguments come from index fields
+ return spatialDocsValues
+ ? new StGeohashFromFieldDocValuesAndFieldAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter),
+ bbox
+ )
+ : new StGeohashFromFieldAndFieldAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter),
+ bbox
+ );
+ }
+ } else {
+ if (spatialField().foldable()) {
+ // Assume right is not foldable, since that would be dealt with in isFoldable() and fold()
+ var point = (BytesRef) spatialField.fold(toEvaluator.foldCtx());
+ return new StGeohashFromLiteralAndFieldEvaluator.Factory(source(), point, toEvaluator.apply(parameter()));
+ } else if (parameter().foldable()) {
+ // Assume left is not foldable, since that would be dealt with in isFoldable() and fold()
+ int precision = checkPrecisionRange((int) parameter.fold(toEvaluator.foldCtx()));
+ return spatialDocsValues
+ ? new StGeohashFromFieldDocValuesAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField()), precision)
+ : new StGeohashFromFieldAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField), precision);
+ } else {
+ // Both arguments come from index fields
+ return spatialDocsValues
+ ? new StGeohashFromFieldDocValuesAndFieldEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter)
+ )
+ : new StGeohashFromFieldAndFieldEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter)
+ );
+ }
+ }
+ }
+
+ @Override
+ public Object fold(FoldContext ctx) {
+ var point = (BytesRef) spatialField().fold(ctx);
+ int precision = (int) parameter().fold(ctx);
+ if (bounds() == null) {
+ return fromLiteralAndField(point, precision);
+ } else {
+ return fromLiteralAndFieldAndLiteral(point, precision, asGeoBoundingBox((BytesRef) bounds().fold(ctx)));
+ }
+ }
+
+ @Evaluator(extraName = "FromFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndLiteral(LongBlock.Builder results, int p, BytesRefBlock wkbBlock, @Fixed int precision) {
+ fromWKB(results, p, wkbBlock, precision, unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndLiteral(LongBlock.Builder results, int p, LongBlock encoded, @Fixed int precision) {
+ fromEncodedLong(results, p, encoded, precision, unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldAndField", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndField(LongBlock.Builder results, int p, BytesRefBlock in, int precision) {
+ fromWKB(results, p, in, checkPrecisionRange(precision), unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndField", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndField(LongBlock.Builder results, int p, LongBlock encoded, int precision) {
+ fromEncodedLong(results, p, encoded, checkPrecisionRange(precision), unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromLiteralAndField", warnExceptions = { IllegalArgumentException.class })
+ static long fromLiteralAndField(@Fixed BytesRef in, int precision) {
+ return unboundedGrid.calculateGridId(GEO.wkbAsPoint(in), precision);
+ }
+
+ @Evaluator(extraName = "FromFieldAndLiteralAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndLiteralAndLiteral(LongBlock.Builder results, int p, BytesRefBlock in, @Fixed GeoHashBoundedGrid bounds) {
+ fromWKB(results, p, in, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndLiteralAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndLiteralAndLiteral(
+ LongBlock.Builder results,
+ int p,
+ LongBlock encoded,
+ @Fixed GeoHashBoundedGrid bounds
+ ) {
+ fromEncodedLong(results, p, encoded, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndFieldAndLiteral(LongBlock.Builder results, int p, BytesRefBlock in, int precision, @Fixed GeoBoundingBox bbox) {
+ GeoHashBoundedGrid bounds = new GeoHashBoundedGrid(precision, bbox);
+ fromWKB(results, p, in, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndFieldAndLiteral(
+ LongBlock.Builder results,
+ int p,
+ LongBlock encoded,
+ int precision,
+ @Fixed GeoBoundingBox bbox
+ ) {
+ GeoHashBoundedGrid bounds = new GeoHashBoundedGrid(precision, bbox);
+ fromEncodedLong(results, p, encoded, bounds);
+ }
+
+ @Evaluator(extraName = "FromLiteralAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static long fromLiteralAndFieldAndLiteral(@Fixed BytesRef in, int precision, @Fixed GeoBoundingBox bbox) {
+ GeoHashBoundedGrid bounds = new GeoHashBoundedGrid(precision, bbox);
+ return bounds.calculateGridId(GEO.wkbAsPoint(in));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShape.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShape.java
new file mode 100644
index 0000000000000..b205ca194dbc9
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToGeoShape.java
@@ -0,0 +1,103 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.geometry.utils.Geohash;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.StGeotileToGeoShape.fromRectangle;
+
+public class StGeohashToGeoShape extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohashToGeoShape",
+ StGeohashToGeoShape::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(KEYWORD, StGeohashToGeoShapeFromStringEvaluator.Factory::new),
+ Map.entry(LONG, StGeohashToGeoShapeFromLongEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "geo_shape",
+ description = """
+ Converts an input value to a `geo_shape` value.
+ The input values are expected to be the grid-ids of geohash grids, in either long or string format.""",
+ examples = @Example(file = "spatial-grid", tag = "geohash_to_geoshape"),
+ depthOffset = 1 // make it appear as a subsection of ST_GEOHASH
+ )
+ public StGeohashToGeoShape(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeohashToGeoShape(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return GEO_SHAPE;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeohashToGeoShape(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohashToGeoShape::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromString")
+ static BytesRef fromString(BytesRef gridId) {
+ return fromRectangle(Geohash.toBoundingBox(gridId.utf8ToString()));
+ }
+
+ @ConvertEvaluator(extraName = "FromLong")
+ static BytesRef fromLong(long gridId) {
+ return fromRectangle(Geohash.toBoundingBox(Geohash.stringEncode(gridId)));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToLong.java
new file mode 100644
index 0000000000000..717dfea9724ec
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToLong.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.geometry.utils.Geohash;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeohashToLong extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohashToLong",
+ StGeohashToLong::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(LONG, (source, fieldEval) -> fieldEval),
+ Map.entry(KEYWORD, StGeohashToLongFromStringEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "long",
+ description = "Converts an input value representing a geohash grid-ID in string format into a long.",
+ examples = { @Example(file = "spatial-grid", tag = "geohash_to_long") },
+ depthOffset = 1 // make it appear as a subsection of ST_GEOHASH
+ )
+ public StGeohashToLong(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeohashToLong(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return LONG;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeohashToLong(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohashToLong::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromString")
+ static long fromString(BytesRef gridId) {
+ return Geohash.longEncode(gridId.utf8ToString());
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToString.java
new file mode 100644
index 0000000000000..d3c6f7697e0c9
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohashToString.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.geometry.utils.Geohash;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeohashToString extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohashToString",
+ StGeohashToString::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(KEYWORD, (source, fieldEval) -> fieldEval),
+ Map.entry(LONG, StGeohashToStringFromLongEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "keyword",
+ description = "Converts an input value representing a geohash grid-ID in long format into a string.",
+ examples = { @Example(file = "spatial-grid", tag = "geohash_to_string") },
+ depthOffset = 1 // make it appear as a subsection of ST_GEOHASH
+ )
+ public StGeohashToString(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geohash grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeohashToString(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return KEYWORD;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeohashToString(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohashToString::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromLong")
+ static BytesRef fromLong(long gridId) {
+ return new BytesRef(Geohash.stringEncode(gridId));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohex.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohex.java
new file mode 100644
index 0000000000000..cbd16c4c461aa
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohex.java
@@ -0,0 +1,294 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.Evaluator;
+import org.elasticsearch.compute.ann.Fixed;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.geometry.Point;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.FoldContext;
+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.evaluator.mapper.EvaluatorMapper;
+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 java.io.IOException;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.GEO;
+
+/**
+ * Calculates the geohex of geo_point geometries.
+ */
+public class StGeohex extends SpatialGridFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "StGeohex", StGeohex::new);
+
+ /**
+ * When checking grid cells with bounds, we need to check if the cell is valid (intersects with the bounds).
+ * This uses GeoHexBoundedPredicate to check if the cell is valid.
+ */
+ protected static class GeoHexBoundedGrid implements BoundedGrid {
+ private final int precision;
+ private final GeoHexBoundedPredicate bounds;
+
+ public GeoHexBoundedGrid(int precision, GeoBoundingBox bbox) {
+ this.precision = checkPrecisionRange(precision);
+ this.bounds = new GeoHexBoundedPredicate(bbox);
+ }
+
+ public long calculateGridId(Point point) {
+ // For points, filtering the point is as good as filtering the tile
+ long geohex = H3.geoToH3(point.getLat(), point.getLon(), precision);
+ if (bounds.validHex(geohex)) {
+ return geohex;
+ }
+ // TODO: Are we sure negative numbers are not valid
+ return -1L;
+ }
+
+ @Override
+ public int precision() {
+ return precision;
+ }
+ }
+
+ /**
+ * For unbounded grids, we don't need to check if the tile is valid,
+ * just calculate the encoded long intersecting the point at that precision.
+ */
+ protected static final UnboundedGrid unboundedGrid = (point, precision) -> H3.geoToH3(
+ point.getLat(),
+ point.getLon(),
+ checkPrecisionRange(precision)
+ );
+
+ private static int checkPrecisionRange(int precision) {
+ if (precision < 1 || precision > H3.MAX_H3_RES) {
+ throw new IllegalArgumentException(
+ "Invalid geohex precision of " + precision + ". Must be between 0 and " + H3.MAX_H3_RES + "."
+ );
+ }
+ return precision;
+ }
+
+ @FunctionInfo(
+ returnType = "long",
+ description = """
+ Calculates the `geohex`, the H3 cell-id, of the supplied geo_point at the specified precision.
+ The result is long encoded. Use [ST_GEOHEX_TO_STRING](#esql-st_geohex_to_string) to convert the result to a string.
+ Or use [ST_GEOHEX_TO_GEOSHAPE](#esql-st_geohex_to_geoshape) to convert either the long or string `geohex` to a
+ POLYGON geo_shape.
+
+ These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query)
+ and the [`geohex_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geohexgrid-aggregation).""",
+ examples = @Example(file = "spatial-grid", tag = "st_geohex-grid")
+ )
+ public StGeohex(
+ Source source,
+ @Param(
+ name = "geometry",
+ type = { "geo_point" },
+ description = "Expression of type `geo_point`. If `null`, the function returns `null`."
+ ) Expression field,
+ @Param(name = "precision", type = { "integer" }, description = """
+ Expression of type `integer`. If `null`, the function returns `null`.
+ Valid values are between [0 and 15](https://h3geo.org/docs/core-library/restable/).""") Expression precision,
+ @Param(name = "bounds", type = { "geo_shape", "geo_point" }, description = """
+ Optional bounds to filter the grid tiles, either a `geo_shape` or an array of at least two `geo_point`s.
+ The envelope of the `geo_shape` is used as bounds.""", optional = true) Expression bounds
+ ) {
+ this(source, field, precision, bounds, false);
+ }
+
+ private StGeohex(Source source, Expression field, Expression precision, Expression bounds, boolean spatialDocValues) {
+ super(source, field, precision, bounds, spatialDocValues);
+ }
+
+ private StGeohex(StreamInput in) throws IOException {
+ super(in, false);
+ }
+
+ @Override
+ public SpatialGridFunction withDocValues(boolean useDocValues) {
+ // Only update the docValues flags if the field is found in the attributes
+ boolean docValues = this.spatialDocsValues || useDocValues;
+ return new StGeohex(source(), spatialField, parameter, bounds, docValues);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public DataType dataType() {
+ return LONG;
+ }
+
+ @Override
+ protected SpatialGridFunction replaceChildren(Expression newSpatialField, Expression newParameter, Expression newBounds) {
+ return new StGeohex(source(), newSpatialField, newParameter, newBounds);
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohex::new, spatialField, parameter, bounds);
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ if (bounds != null) {
+ if (bounds.foldable() == false) {
+ throw new IllegalArgumentException("bounds must be foldable");
+ }
+ GeoBoundingBox bbox = asGeoBoundingBox(bounds.fold(toEvaluator.foldCtx()));
+ if (spatialField().foldable()) {
+ // Assume right is not foldable, since that would be dealt with in isFoldable() and fold()
+ var point = (BytesRef) spatialField.fold(toEvaluator.foldCtx());
+ return new StGeohexFromLiteralAndFieldAndLiteralEvaluator.Factory(source(), point, toEvaluator.apply(parameter()), bbox);
+ } else if (parameter().foldable()) {
+ // Assume left is not foldable, since that would be dealt with in isFoldable() and fold()
+ int precision = (int) parameter.fold(toEvaluator.foldCtx());
+ GeoHexBoundedGrid bounds = new GeoHexBoundedGrid(precision, bbox);
+ return spatialDocsValues
+ ? new StGeohexFromFieldDocValuesAndLiteralAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField()),
+ bounds
+ )
+ : new StGeohexFromFieldAndLiteralAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField), bounds);
+ } else {
+ // Both arguments come from index fields
+ return spatialDocsValues
+ ? new StGeohexFromFieldDocValuesAndFieldAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter),
+ bbox
+ )
+ : new StGeohexFromFieldAndFieldAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter),
+ bbox
+ );
+ }
+ } else {
+ if (spatialField().foldable()) {
+ // Assume right is not foldable, since that would be dealt with in isFoldable() and fold()
+ var point = (BytesRef) spatialField.fold(toEvaluator.foldCtx());
+ return new StGeohexFromLiteralAndFieldEvaluator.Factory(source(), point, toEvaluator.apply(parameter()));
+ } else if (parameter().foldable()) {
+ // Assume left is not foldable, since that would be dealt with in isFoldable() and fold()
+ int precision = checkPrecisionRange((int) parameter.fold(toEvaluator.foldCtx()));
+ return spatialDocsValues
+ ? new StGeohexFromFieldDocValuesAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField()), precision)
+ : new StGeohexFromFieldAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField), precision);
+ } else {
+ // Both arguments come from index fields
+ return spatialDocsValues
+ ? new StGeohexFromFieldDocValuesAndFieldEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter)
+ )
+ : new StGeohexFromFieldAndFieldEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter)
+ );
+ }
+ }
+ }
+
+ @Override
+ public Object fold(FoldContext ctx) {
+ var point = (BytesRef) spatialField().fold(ctx);
+ int precision = checkPrecisionRange((int) parameter().fold(ctx));
+ if (bounds() == null) {
+ return fromLiteralAndField(point, precision);
+ } else {
+ return fromLiteralAndFieldAndLiteral(point, precision, asGeoBoundingBox((BytesRef) bounds().fold(ctx)));
+ }
+ }
+
+ @Evaluator(extraName = "FromFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndLiteral(LongBlock.Builder results, int p, BytesRefBlock wkbBlock, @Fixed int precision) {
+ fromWKB(results, p, wkbBlock, precision, unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndLiteral(LongBlock.Builder results, int p, LongBlock encoded, @Fixed int precision) {
+ fromEncodedLong(results, p, encoded, precision, unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldAndField", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndField(LongBlock.Builder results, int p, BytesRefBlock in, int precision) {
+ fromWKB(results, p, in, checkPrecisionRange(precision), unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndField", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndField(LongBlock.Builder results, int p, LongBlock encoded, int precision) {
+ fromEncodedLong(results, p, encoded, checkPrecisionRange(precision), unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromLiteralAndField", warnExceptions = { IllegalArgumentException.class })
+ static long fromLiteralAndField(@Fixed BytesRef in, int precision) {
+ return unboundedGrid.calculateGridId(GEO.wkbAsPoint(in), precision);
+ }
+
+ @Evaluator(extraName = "FromFieldAndLiteralAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndLiteralAndLiteral(LongBlock.Builder results, int p, BytesRefBlock in, @Fixed GeoHexBoundedGrid bounds) {
+ fromWKB(results, p, in, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndLiteralAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndLiteralAndLiteral(
+ LongBlock.Builder results,
+ int p,
+ LongBlock encoded,
+ @Fixed GeoHexBoundedGrid bounds
+ ) {
+ fromEncodedLong(results, p, encoded, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndFieldAndLiteral(LongBlock.Builder results, int p, BytesRefBlock in, int precision, @Fixed GeoBoundingBox bbox) {
+ GeoHexBoundedGrid bounds = new GeoHexBoundedGrid(precision, bbox);
+ fromWKB(results, p, in, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndFieldAndLiteral(
+ LongBlock.Builder results,
+ int p,
+ LongBlock encoded,
+ int precision,
+ @Fixed GeoBoundingBox bbox
+ ) {
+ GeoHexBoundedGrid bounds = new GeoHexBoundedGrid(precision, bbox);
+ fromEncodedLong(results, p, encoded, bounds);
+ }
+
+ @Evaluator(extraName = "FromLiteralAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static long fromLiteralAndFieldAndLiteral(@Fixed BytesRef in, int precision, @Fixed GeoBoundingBox bbox) {
+ GeoHexBoundedGrid bounds = new GeoHexBoundedGrid(precision, bbox);
+ return bounds.calculateGridId(GEO.wkbAsPoint(in));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShape.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShape.java
new file mode 100644
index 0000000000000..816fadb6061c2
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToGeoShape.java
@@ -0,0 +1,122 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.geometry.LinearRing;
+import org.elasticsearch.geometry.Polygon;
+import org.elasticsearch.h3.CellBoundary;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.h3.LatLng;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeohexToGeoShape extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohexToGeoShape",
+ StGeohexToGeoShape::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(KEYWORD, StGeohexToGeoShapeFromStringEvaluator.Factory::new),
+ Map.entry(LONG, StGeohexToGeoShapeFromLongEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "geo_shape",
+ description = """
+ Converts an input value to a `geo_shape` value.
+ The input values are expected to be the grid-ids of H3 grids, in either long or string format.""",
+ examples = @Example(file = "spatial-grid", tag = "geohex_to_geoshape"),
+ depthOffset = 1 // make it appear as a subsection of ST_GEOHEX
+ )
+ public StGeohexToGeoShape(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input H3 grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeohexToGeoShape(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return GEO_SHAPE;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeohexToGeoShape(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohexToGeoShape::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromString")
+ static BytesRef fromString(BytesRef gridId) {
+ return fromCellBoundary(H3.h3ToGeoBoundary(gridId.utf8ToString()));
+ }
+
+ @ConvertEvaluator(extraName = "FromLong")
+ static BytesRef fromLong(long gridId) {
+ return fromCellBoundary(H3.h3ToGeoBoundary(gridId));
+ }
+
+ private static BytesRef fromCellBoundary(CellBoundary cell) {
+ double[] x = new double[cell.numPoints() + 1];
+ double[] y = new double[cell.numPoints() + 1];
+ for (int i = 0; i < cell.numPoints(); i++) {
+ LatLng vertex = cell.getLatLon(i);
+ x[i] = vertex.getLonDeg();
+ y[i] = vertex.getLatDeg();
+ }
+ x[cell.numPoints()] = x[0];
+ y[cell.numPoints()] = y[0];
+ LinearRing ring = new LinearRing(x, y);
+ Polygon polygon = new Polygon(ring);
+ return SpatialCoordinateTypes.GEO.asWkb(polygon);
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToLong.java
new file mode 100644
index 0000000000000..abb357a683109
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToLong.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeohexToLong extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohexToLong",
+ StGeohexToLong::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(LONG, (source, fieldEval) -> fieldEval),
+ Map.entry(KEYWORD, StGeohexToLongFromStringEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "long",
+ description = "Converts an input value representing a geohex grid-ID in string format into a long.",
+ examples = { @Example(file = "spatial-grid", tag = "geohex_to_long") },
+ depthOffset = 1 // make it appear as a subsection of ST_GEOHEX
+ )
+ public StGeohexToLong(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geohex grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeohexToLong(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return LONG;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeohexToLong(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohexToLong::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromString")
+ static long fromString(BytesRef gridId) {
+ return H3.stringToH3(gridId.utf8ToString());
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToString.java
new file mode 100644
index 0000000000000..9632fe983a66b
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeohexToString.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeohexToString extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeohexToString",
+ StGeohexToString::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(KEYWORD, (source, fieldEval) -> fieldEval),
+ Map.entry(LONG, StGeohexToStringFromLongEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "keyword",
+ description = "Converts an input value representing a Geohex grid-ID in long format into a string.",
+ examples = { @Example(file = "spatial-grid", tag = "geohex_to_string") },
+ depthOffset = 1 // make it appear as a subsection of ST_GEOHEX
+ )
+ public StGeohexToString(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input Geohex grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeohexToString(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return KEYWORD;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeohexToString(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeohexToString::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromLong")
+ static BytesRef fromLong(long gridId) {
+ return new BytesRef(H3.h3ToString(gridId));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotile.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotile.java
new file mode 100644
index 0000000000000..231191d7485e2
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotile.java
@@ -0,0 +1,292 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.Evaluator;
+import org.elasticsearch.compute.ann.Fixed;
+import org.elasticsearch.compute.data.BytesRefBlock;
+import org.elasticsearch.compute.data.LongBlock;
+import org.elasticsearch.compute.operator.EvalOperator;
+import org.elasticsearch.geometry.Point;
+import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileBoundedPredicate;
+import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.expression.FoldContext;
+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.evaluator.mapper.EvaluatorMapper;
+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 java.io.IOException;
+
+import static org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils.checkPrecisionRange;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes.GEO;
+
+/**
+ * Calculates the geotile of geo_point geometries.
+ */
+public class StGeotile extends SpatialGridFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeotile",
+ StGeotile::new
+ );
+
+ /**
+ * When checking tiles with bounds, we need to check if the tile is valid (intersects with the bounds).
+ * This uses GeoTileBoundedPredicate to check if the tile is valid.
+ */
+ protected static class GeoTileBoundedGrid implements BoundedGrid {
+ private final int precision;
+ private final GeoTileBoundedPredicate bounds;
+
+ public GeoTileBoundedGrid(int precision, GeoBoundingBox bbox) {
+ this.precision = checkPrecisionRange(precision);
+ this.bounds = new GeoTileBoundedPredicate(precision, bbox);
+ }
+
+ public long calculateGridId(Point point) {
+ final int tiles = 1 << precision;
+ final int x = GeoTileUtils.getXTile(point.getX(), tiles);
+ final int y = GeoTileUtils.getYTile(point.getY(), tiles);
+ if (bounds.validTile(x, y, precision)) {
+ return GeoTileUtils.longEncodeTiles(precision, x, y);
+ }
+ // TODO: Are we sure negative numbers are not valid
+ return -1L;
+ }
+
+ @Override
+ public int precision() {
+ return precision;
+ }
+ }
+
+ /**
+ * For unbounded grids, we don't need to check if the tile is valid,
+ * just calculate the encoded long intersecting the point at that precision.
+ */
+ protected static final UnboundedGrid unboundedGrid = (point, precision) -> GeoTileUtils.longEncode(
+ point.getX(),
+ point.getY(),
+ checkPrecisionRange(precision)
+ );
+
+ @FunctionInfo(
+ returnType = "long",
+ description = """
+ Calculates the `geotile` of the supplied geo_point at the specified precision.
+ The result is long encoded. Use [ST_GEOTILE_TO_STRING](#esql-st_geotile_to_string) to convert the result to a string.
+ Or use [ST_GEOTILE_TO_GEOSHAPE](#esql-st_geotile_to_geoshape) to convert either the long or string `geotile` to a
+ POLYGON geo_shape.
+
+ These functions are related to the [`geo_grid` query](/reference/query-languages/query-dsl/query-dsl-geo-grid-query)
+ and the [`geotile_grid` aggregation](/reference/aggregations/search-aggregations-bucket-geotilegrid-aggregation).""",
+ examples = @Example(file = "spatial-grid", tag = "st_geotile-grid")
+ )
+ public StGeotile(
+ Source source,
+ @Param(
+ name = "geometry",
+ type = { "geo_point" },
+ description = "Expression of type `geo_point`. If `null`, the function returns `null`."
+ ) Expression field,
+ @Param(name = "precision", type = { "integer" }, description = """
+ Expression of type `integer`. If `null`, the function returns `null`.
+ Valid values are between [0 and 29](https://wiki.openstreetmap.org/wiki/Zoom_levels).""") Expression precision,
+ @Param(name = "bounds", type = { "geo_shape", "geo_point" }, description = """
+ Optional bounds to filter the grid tiles, either a `geo_shape` or an array of at least two `geo_point`s.
+ The envelope of the `geo_shape` is used as bounds.""", optional = true) Expression bounds
+ ) {
+ this(source, field, precision, bounds, false);
+ }
+
+ private StGeotile(Source source, Expression field, Expression precision, Expression bounds, boolean spatialDocValues) {
+ super(source, field, precision, bounds, spatialDocValues);
+ }
+
+ private StGeotile(StreamInput in) throws IOException {
+ super(in, false);
+ }
+
+ @Override
+ public SpatialGridFunction withDocValues(boolean useDocValues) {
+ // Only update the docValues flags if the field is found in the attributes
+ boolean docValues = this.spatialDocsValues || useDocValues;
+ return new StGeotile(source(), spatialField, parameter, bounds, docValues);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ public DataType dataType() {
+ return LONG;
+ }
+
+ @Override
+ protected SpatialGridFunction replaceChildren(Expression newSpatialField, Expression newParameter, Expression newBounds) {
+ return new StGeotile(source(), newSpatialField, newParameter, newBounds);
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeotile::new, spatialField, parameter, bounds);
+ }
+
+ @Override
+ public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
+ if (bounds != null) {
+ if (bounds.foldable() == false) {
+ throw new IllegalArgumentException("bounds must be foldable");
+ }
+ GeoBoundingBox bbox = asGeoBoundingBox(bounds.fold(toEvaluator.foldCtx()));
+ if (spatialField().foldable()) {
+ // Assume right is not foldable, since that would be dealt with in isFoldable() and fold()
+ var point = (BytesRef) spatialField.fold(toEvaluator.foldCtx());
+ return new StGeotileFromLiteralAndFieldAndLiteralEvaluator.Factory(source(), point, toEvaluator.apply(parameter()), bbox);
+ } else if (parameter().foldable()) {
+ // Assume left is not foldable, since that would be dealt with in isFoldable() and fold()
+ int precision = (int) parameter.fold(toEvaluator.foldCtx());
+ GeoTileBoundedGrid bounds = new GeoTileBoundedGrid(precision, bbox);
+ return spatialDocsValues
+ ? new StGeotileFromFieldDocValuesAndLiteralAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField()),
+ bounds
+ )
+ : new StGeotileFromFieldAndLiteralAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField), bounds);
+ } else {
+ // Both arguments come from index fields
+ return spatialDocsValues
+ ? new StGeotileFromFieldDocValuesAndFieldAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter),
+ bbox
+ )
+ : new StGeotileFromFieldAndFieldAndLiteralEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter),
+ bbox
+ );
+ }
+ } else {
+ if (spatialField().foldable()) {
+ // Assume right is not foldable, since that would be dealt with in isFoldable() and fold()
+ var point = (BytesRef) spatialField.fold(toEvaluator.foldCtx());
+ return new StGeotileFromLiteralAndFieldEvaluator.Factory(source(), point, toEvaluator.apply(parameter()));
+ } else if (parameter().foldable()) {
+ // Assume left is not foldable, since that would be dealt with in isFoldable() and fold()
+ int precision = checkPrecisionRange((int) parameter.fold(toEvaluator.foldCtx()));
+ return spatialDocsValues
+ ? new StGeotileFromFieldDocValuesAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField()), precision)
+ : new StGeotileFromFieldAndLiteralEvaluator.Factory(source(), toEvaluator.apply(spatialField), precision);
+ } else {
+ // Both arguments come from index fields
+ return spatialDocsValues
+ ? new StGeotileFromFieldDocValuesAndFieldEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter)
+ )
+ : new StGeotileFromFieldAndFieldEvaluator.Factory(
+ source(),
+ toEvaluator.apply(spatialField),
+ toEvaluator.apply(parameter)
+ );
+ }
+ }
+ }
+
+ @Override
+ public Object fold(FoldContext ctx) {
+ var point = (BytesRef) spatialField().fold(ctx);
+ int precision = checkPrecisionRange((int) parameter().fold(ctx));
+ if (bounds() == null) {
+ return fromLiteralAndField(point, precision);
+ } else {
+ return fromLiteralAndFieldAndLiteral(point, precision, asGeoBoundingBox((BytesRef) bounds().fold(ctx)));
+ }
+ }
+
+ @Evaluator(extraName = "FromFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndLiteral(LongBlock.Builder results, int p, BytesRefBlock wkbBlock, @Fixed int precision) {
+ fromWKB(results, p, wkbBlock, precision, unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndLiteral(LongBlock.Builder results, int p, LongBlock encoded, @Fixed int precision) {
+ fromEncodedLong(results, p, encoded, precision, unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldAndField", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndField(LongBlock.Builder results, int p, BytesRefBlock in, int precision) {
+ fromWKB(results, p, in, checkPrecisionRange(precision), unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndField", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndField(LongBlock.Builder results, int p, LongBlock encoded, int precision) {
+ fromEncodedLong(results, p, encoded, checkPrecisionRange(precision), unboundedGrid);
+ }
+
+ @Evaluator(extraName = "FromLiteralAndField", warnExceptions = { IllegalArgumentException.class })
+ static long fromLiteralAndField(@Fixed BytesRef in, int precision) {
+ return unboundedGrid.calculateGridId(GEO.wkbAsPoint(in), precision);
+ }
+
+ @Evaluator(extraName = "FromFieldAndLiteralAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndLiteralAndLiteral(LongBlock.Builder results, int p, BytesRefBlock in, @Fixed GeoTileBoundedGrid bounds) {
+ fromWKB(results, p, in, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndLiteralAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndLiteralAndLiteral(
+ LongBlock.Builder results,
+ int p,
+ LongBlock encoded,
+ @Fixed GeoTileBoundedGrid bounds
+ ) {
+ fromEncodedLong(results, p, encoded, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldAndFieldAndLiteral(LongBlock.Builder results, int p, BytesRefBlock in, int precision, @Fixed GeoBoundingBox bbox) {
+ GeoTileBoundedGrid bounds = new GeoTileBoundedGrid(precision, bbox);
+ fromWKB(results, p, in, bounds);
+ }
+
+ @Evaluator(extraName = "FromFieldDocValuesAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static void fromFieldDocValuesAndFieldAndLiteral(
+ LongBlock.Builder results,
+ int p,
+ LongBlock encoded,
+ int precision,
+ @Fixed GeoBoundingBox bbox
+ ) {
+ GeoTileBoundedGrid bounds = new GeoTileBoundedGrid(precision, bbox);
+ fromEncodedLong(results, p, encoded, bounds);
+ }
+
+ @Evaluator(extraName = "FromLiteralAndFieldAndLiteral", warnExceptions = { IllegalArgumentException.class })
+ static long fromLiteralAndFieldAndLiteral(@Fixed BytesRef in, int precision, @Fixed GeoBoundingBox bbox) {
+ GeoTileBoundedGrid bounds = new GeoTileBoundedGrid(precision, bbox);
+ return bounds.calculateGridId(GEO.wkbAsPoint(in));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShape.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShape.java
new file mode 100644
index 0000000000000..862a33d82ad79
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToGeoShape.java
@@ -0,0 +1,114 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.geometry.LinearRing;
+import org.elasticsearch.geometry.Polygon;
+import org.elasticsearch.geometry.Rectangle;
+import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.core.util.SpatialCoordinateTypes;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_SHAPE;
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeotileToGeoShape extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeotileToGeoShape",
+ StGeotileToGeoShape::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(KEYWORD, StGeotileToGeoShapeFromStringEvaluator.Factory::new),
+ Map.entry(LONG, StGeotileToGeoShapeFromLongEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "geo_shape",
+ description = """
+ Converts an input value to a `geo_shape` value.
+ The input values are expected to be the grid-ids of geotile grids, in either long or string format.""",
+ examples = @Example(file = "spatial-grid", tag = "geotile_to_geoshape"),
+ depthOffset = 1 // make it appear as a subsection of ST_GEOTILE
+ )
+ public StGeotileToGeoShape(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeotileToGeoShape(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return GEO_SHAPE;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeotileToGeoShape(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeotileToGeoShape::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromString")
+ static BytesRef fromString(BytesRef gridId) {
+ return fromRectangle(GeoTileUtils.toBoundingBox(gridId.utf8ToString()));
+ }
+
+ @ConvertEvaluator(extraName = "FromLong")
+ static BytesRef fromLong(long gridId) {
+ return fromRectangle(GeoTileUtils.toBoundingBox(gridId));
+ }
+
+ static BytesRef fromRectangle(Rectangle bbox) {
+ double[] x = new double[] { bbox.getMinX(), bbox.getMaxX(), bbox.getMaxX(), bbox.getMinX(), bbox.getMinX() };
+ double[] y = new double[] { bbox.getMinY(), bbox.getMinY(), bbox.getMaxY(), bbox.getMaxY(), bbox.getMinY() };
+ LinearRing ring = new LinearRing(x, y);
+ Polygon polygon = new Polygon(ring);
+ return SpatialCoordinateTypes.GEO.asWkb(polygon);
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLong.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLong.java
new file mode 100644
index 0000000000000..fd52f273a4f76
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToLong.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeotileToLong extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeotileToLong",
+ StGeotileToLong::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(LONG, (source, fieldEval) -> fieldEval),
+ Map.entry(KEYWORD, StGeotileToLongFromStringEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "long",
+ description = "Converts an input value representing a geotile grid-ID in string format into a long.",
+ examples = { @Example(file = "spatial-grid", tag = "geotile_to_long") },
+ depthOffset = 1 // make it appear as a subsection of ST_GEOTILE
+ )
+ public StGeotileToLong(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeotileToLong(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return LONG;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeotileToLong(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeotileToLong::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromString")
+ static long fromString(BytesRef gridId) {
+ return GeoTileUtils.longEncode(gridId.utf8ToString());
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToString.java
new file mode 100644
index 0000000000000..1a2d2b88afc52
--- /dev/null
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StGeotileToString.java
@@ -0,0 +1,94 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
+import org.elasticsearch.common.io.stream.StreamInput;
+import org.elasticsearch.compute.ann.ConvertEvaluator;
+import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
+import org.elasticsearch.xpack.esql.core.expression.Expression;
+import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
+import org.elasticsearch.xpack.esql.core.tree.Source;
+import org.elasticsearch.xpack.esql.core.type.DataType;
+import org.elasticsearch.xpack.esql.evaluator.mapper.EvaluatorMapper;
+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.convert.AbstractConvertFunction;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD;
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+
+public class StGeotileToString extends AbstractConvertFunction implements EvaluatorMapper {
+ public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
+ Expression.class,
+ "StGeotileToString",
+ StGeotileToString::new
+ );
+
+ private static final Map EVALUATORS = Map.ofEntries(
+ Map.entry(KEYWORD, (source, fieldEval) -> fieldEval),
+ Map.entry(LONG, StGeotileToStringFromLongEvaluator.Factory::new)
+ );
+
+ @FunctionInfo(
+ returnType = "keyword",
+ description = "Converts an input value representing a geotile grid-ID in long format into a string.",
+ examples = { @Example(file = "spatial-grid", tag = "geotile_to_string") },
+ depthOffset = 1 // make it appear as a subsection of ST_GEOTILE
+ )
+ public StGeotileToString(
+ Source source,
+ @Param(
+ name = "grid_id",
+ type = { "keyword", "long" },
+ description = "Input geotile grid-id. The input can be a single- or multi-valued column or an expression."
+ ) Expression v
+ ) {
+ super(source, v);
+ }
+
+ private StGeotileToString(StreamInput in) throws IOException {
+ super(in);
+ }
+
+ @Override
+ public String getWriteableName() {
+ return ENTRY.name;
+ }
+
+ @Override
+ protected Map factories() {
+ return EVALUATORS;
+ }
+
+ @Override
+ public DataType dataType() {
+ return KEYWORD;
+ }
+
+ @Override
+ public Expression replaceChildren(List newChildren) {
+ return new StGeotileToString(source(), newChildren.get(0));
+ }
+
+ @Override
+ protected NodeInfo extends Expression> info() {
+ return NodeInfo.create(this, StGeotileToString::new, field());
+ }
+
+ @ConvertEvaluator(extraName = "FromLong")
+ static BytesRef fromLong(long gridId) {
+ return new BytesRef(GeoTileUtils.stringEncode(gridId));
+ }
+}
diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/SpatialDocValuesExtraction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/SpatialDocValuesExtraction.java
index d70153258871e..d55c0f9f17384 100644
--- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/SpatialDocValuesExtraction.java
+++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/SpatialDocValuesExtraction.java
@@ -16,6 +16,7 @@
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.function.aggregate.SpatialAggregateFunction;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.BinarySpatialFunction;
+import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialGridFunction;
import org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesFunction;
import org.elasticsearch.xpack.esql.optimizer.LocalPhysicalOptimizerContext;
import org.elasticsearch.xpack.esql.optimizer.PhysicalOptimizerRules;
@@ -110,7 +111,10 @@ && allowedForDocValues(fieldAttribute, ctx.searchStats(), agg, foundAttributes))
if (exec instanceof EvalExec evalExec) {
List fields = evalExec.fields();
List changed = fields.stream()
- .map(f -> (Alias) f.transformDown(BinarySpatialFunction.class, s -> withDocValues(s, foundAttributes)))
+ .map(
+ f -> (Alias) f.transformDown(BinarySpatialFunction.class, s -> withDocValues(s, foundAttributes))
+ .transformDown(SpatialGridFunction.class, s -> withDocValues(s, foundAttributes))
+ )
.toList();
if (changed.equals(fields) == false) {
exec = new EvalExec(exec.source(), exec.child(), changed);
@@ -119,7 +123,9 @@ && allowedForDocValues(fieldAttribute, ctx.searchStats(), agg, foundAttributes))
if (exec instanceof FilterExec filterExec) {
// Note that ST_CENTROID does not support shapes, but SpatialRelatesFunction does, so when we extend the centroid
// to support shapes, we need to consider loading shape doc-values for both centroid and relates (ST_INTERSECTS)
- var condition = filterExec.condition().transformDown(BinarySpatialFunction.class, s -> withDocValues(s, foundAttributes));
+ var condition = filterExec.condition()
+ .transformDown(BinarySpatialFunction.class, s -> withDocValues(s, foundAttributes))
+ .transformDown(SpatialGridFunction.class, s -> withDocValues(s, foundAttributes));
if (filterExec.condition().equals(condition) == false) {
exec = new FilterExec(filterExec.source(), filterExec.child(), condition);
}
@@ -149,6 +155,12 @@ private BinarySpatialFunction withDocValues(BinarySpatialFunction spatial, Set foundAttributes) {
+ // Only update the docValues flags if the field is found in the attributes
+ boolean found = foundField(spatial.spatialField(), foundAttributes);
+ return found ? spatial.withDocValues(found) : spatial;
+ }
+
private boolean hasFieldAttribute(BinarySpatialFunction spatial, Set foundAttributes) {
return foundField(spatial.left(), foundAttributes) || foundField(spatial.right(), foundAttributes);
}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalGeometryTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalGeometryTests.java
new file mode 100644
index 0000000000000..2431d61752345
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalGeometryTests.java
@@ -0,0 +1,96 @@
+/*
+ * 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.common.spatial;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.LatLonPoint;
+import org.apache.lucene.document.ShapeField;
+import org.apache.lucene.geo.GeoEncodingUtils;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.SerialMergeScheduler;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.store.Directory;
+import org.elasticsearch.core.IOUtils;
+import org.elasticsearch.geo.GeometryTestUtils;
+import org.elasticsearch.geometry.Point;
+import org.elasticsearch.h3.CellBoundary;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.h3.LatLng;
+import org.elasticsearch.test.ESTestCase;
+
+/**
+ * TODO: This class is a copy of the same class in org.elasticsearch.xpack.spatial.common, we should find a common location for it.
+ */
+public class H3SphericalGeometryTests extends ESTestCase {
+
+ private static final String FIELD_NAME = "field";
+
+ public void testIndexPoints() throws Exception {
+ Point queryPoint = GeometryTestUtils.randomPoint();
+ long[] hexes = new long[H3.MAX_H3_RES + 1];
+ for (int res = 0; res < hexes.length; res++) {
+ hexes[res] = H3.geoToH3(queryPoint.getLat(), queryPoint.getLon(), res);
+ }
+ IndexWriterConfig iwc = newIndexWriterConfig();
+ // Else seeds may not reproduce:
+ iwc.setMergeScheduler(new SerialMergeScheduler());
+ // Else we can get O(N^2) merging:
+ iwc.setMaxBufferedDocs(10);
+ Directory dir = newDirectory();
+ // RandomIndexWriter is too slow here:
+ int[] counts = new int[H3.MAX_H3_RES + 1];
+ IndexWriter w = new IndexWriter(dir, iwc);
+ for (long hex : hexes) {
+ CellBoundary cellBoundary = H3.h3ToGeoBoundary(hex);
+ for (int i = 0; i < cellBoundary.numPoints(); i++) {
+ Document doc = new Document();
+ LatLng latLng = cellBoundary.getLatLon(i);
+ doc.add(new LatLonPoint(FIELD_NAME, latLng.getLatDeg(), latLng.getLonDeg()));
+ w.addDocument(doc);
+ computeCounts(hexes, latLng.getLonDeg(), latLng.getLatDeg(), counts);
+ }
+
+ }
+ final int numDocs = randomIntBetween(1000, 2000);
+ for (int id = 0; id < numDocs; id++) {
+ Document doc = new Document();
+ Point point = GeometryTestUtils.randomPoint();
+ doc.add(new LatLonPoint(FIELD_NAME, point.getLat(), point.getLon()));
+ w.addDocument(doc);
+ computeCounts(hexes, point.getLon(), point.getLat(), counts);
+ }
+
+ if (random().nextBoolean()) {
+ w.forceMerge(1);
+ }
+ final IndexReader r = DirectoryReader.open(w);
+ w.close();
+
+ IndexSearcher s = newSearcher(r);
+ for (int i = 0; i < H3.MAX_H3_RES + 1; i++) {
+ H3SphericalGeometry geometry = new H3SphericalGeometry(hexes[i]);
+ Query indexQuery = LatLonPoint.newGeometryQuery(FIELD_NAME, ShapeField.QueryRelation.INTERSECTS, geometry);
+ assertEquals(counts[i], s.count(indexQuery));
+ }
+ IOUtils.close(r, dir);
+ }
+
+ private void computeCounts(long[] hexes, double lon, double lat, int[] counts) {
+ double qLat = GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat));
+ double qLon = GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon));
+ for (int res = 0; res < hexes.length; res++) {
+ if (hexes[res] == H3.geoToH3(qLat, qLon, res)) {
+ counts[res]++;
+ }
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalUtilTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalUtilTests.java
new file mode 100644
index 0000000000000..51537b11687e7
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/common/spatial/H3SphericalUtilTests.java
@@ -0,0 +1,67 @@
+/*
+ * 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.common.spatial;
+
+import org.apache.lucene.spatial3d.geom.GeoPolygon;
+import org.apache.lucene.spatial3d.geom.LatLonBounds;
+import org.apache.lucene.tests.geo.GeoTestUtil;
+import org.elasticsearch.common.geo.GeoBoundingBox;
+import org.elasticsearch.common.geo.GeoPoint;
+import org.elasticsearch.h3.H3;
+import org.elasticsearch.test.ESTestCase;
+import org.elasticsearch.xpack.spatial.common.H3SphericalUtil;
+
+/**
+ * TODO: This class is a copy of the same class in org.elasticsearch.xpack.spatial.common, we should find a common location for it.
+ */
+public class H3SphericalUtilTests extends ESTestCase {
+
+ private static final double LAT_DELTA = 1e-7;
+ private static final double LON_DELTA = 1e-5;
+
+ public void testRandomBounds() {
+ GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
+ for (int res = 0; res < H3.MAX_H3_RES; res++) {
+ final long h3 = H3.geoToH3(GeoTestUtil.nextLatitude(), GeoTestUtil.nextLongitude(), res);
+ assertBounds(h3, boundingBox);
+ }
+ }
+
+ public void testBoundsLevel0() {
+ GeoBoundingBox boundingBox = new GeoBoundingBox(new GeoPoint(), new GeoPoint());
+ for (long h3 : H3.getLongRes0Cells()) {
+ assertBounds(h3, boundingBox);
+ }
+ }
+
+ private void assertBounds(long h3, GeoBoundingBox boundingBox) {
+ org.elasticsearch.xpack.spatial.common.H3SphericalUtil.computeGeoBounds(h3, boundingBox);
+ GeoPolygon polygon = H3SphericalUtil.toGeoPolygon(h3);
+ LatLonBounds bounds = new LatLonBounds();
+ polygon.getBounds(bounds);
+ if (bounds.checkNoLongitudeBound()) {
+ assertEquals(-180d, boundingBox.left(), LON_DELTA);
+ assertEquals(180d, boundingBox.right(), LON_DELTA);
+ } else {
+ assertEquals(Math.toDegrees(bounds.getLeftLongitude()), boundingBox.left(), LON_DELTA);
+ assertEquals(Math.toDegrees(bounds.getRightLongitude()), boundingBox.right(), LON_DELTA);
+ }
+
+ if (bounds.checkNoTopLatitudeBound()) {
+ assertEquals(90d, boundingBox.top(), LAT_DELTA);
+ } else {
+ assertEquals(Math.toDegrees(bounds.getMaxLatitude()), boundingBox.top(), LAT_DELTA);
+ }
+
+ if (bounds.checkNoBottomLatitudeBound()) {
+ assertEquals(-90d, boundingBox.bottom(), LAT_DELTA);
+ } else {
+ assertEquals(Math.toDegrees(bounds.getMinLatitude()), boundingBox.bottom(), LAT_DELTA);
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridFunctionTestCase.java
new file mode 100644
index 0000000000000..3678ecd65df1f
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridFunctionTestCase.java
@@ -0,0 +1,90 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+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 java.util.List;
+import java.util.function.BiFunction;
+
+import static org.elasticsearch.xpack.esql.core.type.DataType.LONG;
+import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatialGeo;
+import static org.hamcrest.Matchers.equalTo;
+
+public abstract class SpatialGridFunctionTestCase extends AbstractScalarFunctionTestCase {
+
+ private static String getFunctionClassName() {
+ Class> testClass = getTestClass();
+ String testClassName = testClass.getSimpleName();
+ return testClassName.replace("Tests", "");
+ }
+
+ protected static void addTestCaseSuppliers(
+ List suppliers,
+ DataType[] dataTypes,
+ BiFunction expectedValue
+ ) {
+ for (DataType spatialType : dataTypes) {
+ DataType shapeType = isSpatialGeo(spatialType) ? DataType.GEO_SHAPE : DataType.CARTESIAN_SHAPE;
+ TestCaseSupplier.TypedDataSupplier geometrySupplier = testCaseSupplier(spatialType, true);
+ suppliers.add(new TestCaseSupplier("with precision", List.of(spatialType, DataType.INTEGER), () -> {
+ TestCaseSupplier.TypedData geoTypedData = geometrySupplier.get();
+ BytesRef geometry = (BytesRef) geoTypedData.data();
+ int precision = between(1, 8);
+ TestCaseSupplier.TypedData precisionData = new TestCaseSupplier.TypedData(precision, DataType.INTEGER, "precision");
+ return new TestCaseSupplier.TestCase(
+ List.of(geoTypedData, precisionData),
+ getFunctionClassName() + "FromFieldAndFieldEvaluator[in=Attribute[channel=0], precision=Attribute[channel=1]]",
+ LONG,
+ equalTo(expectedValue.apply(geometry, precision))
+ );
+ }));
+ /*
+ TODO: Implement non-foldable bounds
+ suppliers.add(new TestCaseSupplier("with precision and bounds", List.of(spatialType, DataType.INTEGER, shapeType), () -> {
+ TestCaseSupplier.TypedData geoTypedData = geometrySupplier.get();
+ BytesRef geometry = (BytesRef) geoTypedData.data();
+ int precision = between(1, 8);
+ TestCaseSupplier.TypedData precisionData = new TestCaseSupplier.TypedData(precision, DataType.INTEGER, "precision");
+ Rectangle bounds = new Rectangle(-30, 30, 30, -30);
+ BytesRef boundsBytesRef = GEO.asWkb(bounds);
+ TestCaseSupplier.TypedData boundsData = new TestCaseSupplier.TypedData(boundsBytesRef, shapeType, "bounds");
+ return new TestCaseSupplier.TestCase(
+ List.of(geoTypedData, precisionData, boundsData),
+ getFunctionClassName()
+ + "FromFieldAndFieldAndLiteralEvaluator["
+ + "in=Attribute[channel=0], precision=Attribute[channel=1], bounds=Attribute[channel=2]]",
+ LONG,
+ equalTo(expectedValue.apply(geometry, precision))
+ );
+ }));
+ */
+ }
+ }
+
+ public static TestCaseSupplier.TypedDataSupplier testCaseSupplier(DataType dataType, boolean pointsOnly) {
+ if (pointsOnly) {
+ return switch (dataType.esType()) {
+ case "geo_point" -> TestCaseSupplier.geoPointCases(() -> false).get(0);
+ case "cartesian_point" -> TestCaseSupplier.cartesianPointCases(() -> false).get(0);
+ default -> throw new IllegalArgumentException("Unsupported datatype for " + functionName() + ": " + dataType);
+ };
+ } else {
+ return switch (dataType.esType()) {
+ case "geo_point" -> TestCaseSupplier.geoPointCases(() -> false).get(0);
+ case "geo_shape" -> TestCaseSupplier.geoShapeCases(() -> false).get(0);
+ case "cartesian_point" -> TestCaseSupplier.cartesianPointCases(() -> false).get(0);
+ case "cartesian_shape" -> TestCaseSupplier.cartesianShapeCases(() -> false).get(0);
+ default -> throw new IllegalArgumentException("Unsupported datatype for " + functionName() + ": " + dataType);
+ };
+ }
+ }
+}
diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridTypeConversionTestCases.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridTypeConversionTestCases.java
new file mode 100644
index 0000000000000..c69269cbf7a83
--- /dev/null
+++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialGridTypeConversionTestCases.java
@@ -0,0 +1,54 @@
+/*
+ * 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.spatial;
+
+import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.test.ESTestCase;
+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 java.util.List;
+import java.util.function.Function;
+
+import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.geoPointCases;
+
+public abstract class SpatialGridTypeConversionTestCases extends AbstractScalarFunctionTestCase {
+ public static void forUnaryGeoPoint(
+ DataType type,
+ List suppliers,
+ String expectedEvaluatorToString,
+ DataType expectedType,
+ Function geometryToGeotile,
+ Function