diff --git a/docs/changelog/133546.yaml b/docs/changelog/133546.yaml new file mode 100644 index 0000000000000..80a8690ace2b8 --- /dev/null +++ b/docs/changelog/133546.yaml @@ -0,0 +1,5 @@ +pr: 133546 +summary: "Support geohash, geotile and geohex grid types in ST_INTERSECTS and ST_DISJOINT" +area: "ES|QL" +type: enhancement +issues: [] diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_disjoint.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_disjoint.md index ea2f95d29fead..4d0e35831157c 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_disjoint.md +++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_disjoint.md @@ -3,8 +3,8 @@ **Parameters** `geomA` -: Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. +: Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. `geomB` -: Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters. +: Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters. diff --git a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_intersects.md b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_intersects.md index ea2f95d29fead..4d0e35831157c 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/parameters/st_intersects.md +++ b/docs/reference/query-languages/esql/_snippets/functions/parameters/st_intersects.md @@ -3,8 +3,8 @@ **Parameters** `geomA` -: Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. +: Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. `geomB` -: Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters. +: Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters. diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_disjoint.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_disjoint.md index b26626ad71ffa..858e2a29d6404 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/types/st_disjoint.md +++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_disjoint.md @@ -10,6 +10,12 @@ | cartesian_shape | cartesian_shape | boolean | | geo_point | geo_point | boolean | | geo_point | geo_shape | boolean | +| geo_point | geohash | boolean | +| geo_point | geohex | boolean | +| geo_point | geotile | boolean | | geo_shape | geo_point | boolean | | geo_shape | geo_shape | boolean | +| geohash | geo_point | boolean | +| geohex | geo_point | boolean | +| geotile | geo_point | boolean | diff --git a/docs/reference/query-languages/esql/_snippets/functions/types/st_intersects.md b/docs/reference/query-languages/esql/_snippets/functions/types/st_intersects.md index b26626ad71ffa..858e2a29d6404 100644 --- a/docs/reference/query-languages/esql/_snippets/functions/types/st_intersects.md +++ b/docs/reference/query-languages/esql/_snippets/functions/types/st_intersects.md @@ -10,6 +10,12 @@ | cartesian_shape | cartesian_shape | boolean | | geo_point | geo_point | boolean | | geo_point | geo_shape | boolean | +| geo_point | geohash | boolean | +| geo_point | geohex | boolean | +| geo_point | geotile | boolean | | geo_shape | geo_point | boolean | | geo_shape | geo_shape | boolean | +| geohash | geo_point | boolean | +| geohex | geo_point | boolean | +| geotile | geo_point | boolean | diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_disjoint.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_disjoint.json index c86eb75ceaf52..a0fd759e954d1 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/st_disjoint.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_disjoint.json @@ -10,13 +10,13 @@ "name" : "geomA", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -28,13 +28,13 @@ "name" : "geomA", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -46,13 +46,13 @@ "name" : "geomA", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -64,13 +64,13 @@ "name" : "geomA", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -82,13 +82,13 @@ "name" : "geomA", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -100,13 +100,67 @@ "name" : "geomA", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geohash", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geohex", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geotile", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -118,13 +172,13 @@ "name" : "geomA", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -136,13 +190,67 @@ "name" : "geomA", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geohash", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geohex", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geotile", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, diff --git a/docs/reference/query-languages/esql/kibana/definition/functions/st_intersects.json b/docs/reference/query-languages/esql/kibana/definition/functions/st_intersects.json index cf90c9f67fda0..4073c12e75c23 100644 --- a/docs/reference/query-languages/esql/kibana/definition/functions/st_intersects.json +++ b/docs/reference/query-languages/esql/kibana/definition/functions/st_intersects.json @@ -10,13 +10,13 @@ "name" : "geomA", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -28,13 +28,13 @@ "name" : "geomA", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -46,13 +46,13 @@ "name" : "geomA", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -64,13 +64,13 @@ "name" : "geomA", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "cartesian_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -82,13 +82,13 @@ "name" : "geomA", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -100,13 +100,67 @@ "name" : "geomA", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geohash", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geohex", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geotile", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -118,13 +172,13 @@ "name" : "geomA", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_point", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, @@ -136,13 +190,67 @@ "name" : "geomA", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." }, { "name" : "geomB", "type" : "geo_shape", "optional" : false, - "description" : "Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geohash", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geohex", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." + } + ], + "variadic" : false, + "returnType" : "boolean" + }, + { + "params" : [ + { + "name" : "geomA", + "type" : "geotile", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`." + }, + { + "name" : "geomB", + "type" : "geo_point", + "optional" : false, + "description" : "Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) or a geo-grid value (`geohash`, `geotile`, `geohex`). If `null`, the function returns `null`. The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_*` and `cartesian_*` parameters." } ], "variadic" : false, diff --git a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java index 6546e86b7b70a..51907b4da0f96 100644 --- a/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java +++ b/x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java @@ -147,7 +147,7 @@ protected void shouldSkipTest(String testName) throws IOException { hasCapabilities(adminClient(), List.of(ENABLE_LOOKUP_JOIN_ON_REMOTE.capabilityName())) ); } - // Unmapped fields require a coorect capability response from every cluster, which isn't currently implemented. + // Unmapped fields require a correct capability response from every cluster, which isn't currently implemented. assumeFalse("UNMAPPED FIELDS not yet supported in CCS", testCase.requiredCapabilities.contains(UNMAPPED_FIELDS.capabilityName())); // Tests that use capabilities not supported in CCS assumeFalse( 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 index f0dc76e4c8e87..89a2adc512f44 100644 --- 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 @@ -320,6 +320,62 @@ count:long | centroid:geo_point | geohashString:keywo 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_intersects + +FROM airports +| WHERE ST_INTERSECTS(location, TO_GEOHASH("u1")) +| STATS + count = COUNT(*), + centroid = ST_CENTROID_AGG(location) +; + +count:long | centroid:geo_point +8 | POINT (6.351574736181647 51.8981519783847) +; + +gridIntersectsDisjointGeohash +required_capability: spatial_grid_intersects + +FROM airports +| EVAL intersects = ST_INTERSECTS(location, "u1"::geohash) +| EVAL disjoint = ST_DISJOINT(location, "u1"::geohash) +| STATS count = COUNT(*) BY intersects, disjoint +| SORT count ASC +; + +count:long | intersects:boolean | disjoint:boolean +8 | true | false +883 | false | true +; + +gridGeohashLiteralFromCell +required_capability: spatial_grid_intersects + +ROW location = ["POINT (6.360728044651057 47.94084087577894)", "POINT (6.351574736181647 51.8981519783847)", "POINT (5.268637698941997 42.747250193330856)"] +| EVAL location = TO_GEOPOINT(location) +| MV_EXPAND location +| WHERE ST_INTERSECTS(location, "u1"::geohash) +; + +location:geo_point +POINT (6.351574736181647 51.8981519783847) +; + +gridGeohashLiteralFromCellDisjoint +required_capability: spatial_grid_intersects + +ROW location = ["POINT (6.360728044651057 47.94084087577894)", "POINT (6.351574736181647 51.8981519783847)", "POINT (5.268637698941997 42.747250193330856)"] +| EVAL location = location::geo_point +| MV_EXPAND location +| WHERE ST_DISJOINT(location, "u1"::geohash) +; + +location:geo_point +POINT (6.360728044651057 47.94084087577894) +POINT (5.268637698941997 42.747250193330856) +; + gridGeohashStatsByWhereUK required_capability: spatial_grid_types @@ -688,6 +744,62 @@ count:long | centroid:geo_point | geotileString:keywor 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_intersects + +FROM airports +| WHERE ST_INTERSECTS(location, TO_GEOTILE("3/4/3")) +| STATS + count = COUNT(*), + centroid = ST_CENTROID_AGG(location) +; + +count:long | centroid:geo_point +79 | POINT (24.516750878736943 23.93036561181085) +; + +gridIntersectsDisjointGeotile +required_capability: spatial_grid_intersects + +FROM airports +| EVAL intersects = ST_INTERSECTS(location, "3/4/3"::geotile) +| EVAL disjoint = ST_DISJOINT(location, "3/4/3"::geotile) +| STATS count = COUNT(*) BY intersects, disjoint +| SORT count ASC +; + +count:long | intersects:boolean | disjoint:boolean +79 | true | false +812 | false | true +; + +gridGeotileLiteralFromCell +required_capability: spatial_grid_intersects + +ROW location = ["POINT (6.360728044651057 47.94084087577894)", "POINT (24.516750878736943 23.93036561181085)", "POINT (5.268637698941997 42.747250193330856)"] +| EVAL location = TO_GEOPOINT(location) +| MV_EXPAND location +| WHERE ST_INTERSECTS(location, "3/4/3"::geotile) +; + +location:geo_point +POINT (24.516750878736943 23.93036561181085) +; + +gridGeotileLiteralFromCellDisjoint +required_capability: spatial_grid_intersects + +ROW location = ["POINT (6.360728044651057 47.94084087577894)", "POINT (24.516750878736943 23.93036561181085)", "POINT (5.268637698941997 42.747250193330856)"] +| EVAL location = location::geo_point +| MV_EXPAND location +| WHERE ST_DISJOINT(location, "3/4/3"::geotile) +; + +location:geo_point +POINT (6.360728044651057 47.94084087577894) +POINT (5.268637698941997 42.747250193330856) +; + gridGeotileStatsByWhereUK required_capability: spatial_grid_types @@ -1123,6 +1235,62 @@ POINT (13.1442589810713 32.6691695504993) | 813fbffffffffff | 813fbffffffffff POINT (118.12696884672 24.537192570557) | 8141bffffffffff | null | POLYGON ((121.34751445935747 26.200276060455465, 120.50339385995798 29.892985338693542, 116.26130043917948 31.68090796798786, 112.74092235558841 29.367231998154967, 113.70376749169584 25.199133186716526, 117.99047716641066 23.5089519597363, 119.64146543031175 24.728661050965922, 121.34751445935747 26.200276060455465)) ; +gridGeohexDocsFromCell +required_capability: spatial_grid_intersects + +FROM airports +| WHERE ST_INTERSECTS(location, TO_GEOHEX("81397ffffffffff")) +| STATS + count = COUNT(*), + centroid = ST_CENTROID_AGG(location) +; + +count:long | centroid:geo_point +7 | POINT (2.475211258445467 41.32352174592337) +; + +gridIntersectsDisjointGeohex +required_capability: spatial_grid_intersects + +FROM airports +| EVAL intersects = ST_INTERSECTS(location, "81397ffffffffff"::geohex) +| EVAL disjoint = ST_DISJOINT(location, "81397ffffffffff"::geohex) +| STATS count = COUNT(*) BY intersects, disjoint +| SORT count ASC +; + +count:long | intersects:boolean | disjoint:boolean +7 | true | false +884 | false | true +; + +gridGeohexLiteralFromCell +required_capability: spatial_grid_intersects + +ROW location = ["POINT (6.360728044651057 47.94084087577894)", "POINT (2.475211258445467 41.32352174592337)", "POINT (5.268637698941997 42.747250193330856)"] +| EVAL location = TO_GEOPOINT(location) +| MV_EXPAND location +| WHERE ST_INTERSECTS(location, "81397ffffffffff"::geohex) +; + +location:geo_point +POINT (2.475211258445467 41.32352174592337) +POINT (5.268637698941997 42.747250193330856) +; + +gridGeohexLiteralFromCellDisjoint +required_capability: spatial_grid_intersects + +ROW location = ["POINT (6.360728044651057 47.94084087577894)", "POINT (2.475211258445467 41.32352174592337)", "POINT (5.268637698941997 42.747250193330856)"] +| EVAL location = TO_GEOPOINT(location) +| MV_EXPAND location +| WHERE ST_DISJOINT(location, "81397ffffffffff"::geohex) +; + +location:geo_point +POINT (6.360728044651057 47.94084087577894) +; + gridGeohexStatsByWhereUK required_capability: spatial_grid_types diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator.java index 9464495a05178..1236e6a977382 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.LongBlock; @@ -24,6 +25,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator encodedPoints; @@ -53,6 +56,13 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += encodedPoints.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, LongBlock encodedPointsBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { position: for (int p = 0; p < positionCount; p++) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator.java index 23a6a0016867b..5f950e7c59319 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.LongBlock; @@ -24,6 +25,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator encodedPoints; @@ -55,6 +58,14 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += encodedPoints.baseRamBytesUsed(); + baseRamBytesUsed += gridIds.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, LongBlock encodedPointsBlock, LongBlock gridIdsBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndConstantGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndConstantGridEvaluator.java index aa8b09b9e8420..e8b4e99314a63 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndConstantGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndConstantGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; @@ -24,6 +25,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialDisjointGeoSourceAndConstantGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialDisjointGeoSourceAndConstantGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator wkb; @@ -53,6 +56,13 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += wkb.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, BytesRefBlock wkbBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { position: for (int p = 0; p < positionCount; p++) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndSourceGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndSourceGridEvaluator.java index 556c647edb695..ce62c9fcf1737 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndSourceGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointGeoSourceAndSourceGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; @@ -25,6 +26,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialDisjointGeoSourceAndSourceGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialDisjointGeoSourceAndSourceGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator wkb; @@ -56,6 +59,14 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += wkb.baseRamBytesUsed(); + baseRamBytesUsed += gridId.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, BytesRefBlock wkbBlock, LongBlock gridIdBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { position: for (int p = 0; p < positionCount; p++) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator.java index e134546b8e35c..dacf4ddb6234d 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.LongBlock; @@ -24,6 +25,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator encodedPoints; @@ -53,6 +56,13 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += encodedPoints.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, LongBlock encodedPointsBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { position: for (int p = 0; p < positionCount; p++) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator.java index df4058bca87b4..5e52697c98277 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.LongBlock; @@ -24,6 +25,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator encodedPoints; @@ -55,6 +58,14 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += encodedPoints.baseRamBytesUsed(); + baseRamBytesUsed += gridIds.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, LongBlock encodedPointsBlock, LongBlock gridIdsBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndConstantGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndConstantGridEvaluator.java index 23a6fb1cda3be..3fa41279ec5a5 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndConstantGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndConstantGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; @@ -24,6 +25,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialIntersectsGeoSourceAndConstantGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialIntersectsGeoSourceAndConstantGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator wkb; @@ -53,6 +56,13 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += wkb.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, BytesRefBlock wkbBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { position: for (int p = 0; p < positionCount; p++) { diff --git a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndSourceGridEvaluator.java b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndSourceGridEvaluator.java index 8d2c55a82cec6..7b5948f81e9bb 100644 --- a/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndSourceGridEvaluator.java +++ b/x-pack/plugin/esql/src/main/generated/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsGeoSourceAndSourceGridEvaluator.java @@ -8,6 +8,7 @@ import java.lang.IllegalArgumentException; import java.lang.Override; import java.lang.String; +import org.apache.lucene.util.RamUsageEstimator; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; @@ -25,6 +26,8 @@ * This class is generated. Edit {@code EvaluatorImplementer} instead. */ public final class SpatialIntersectsGeoSourceAndSourceGridEvaluator implements EvalOperator.ExpressionEvaluator { + private static final long BASE_RAM_BYTES_USED = RamUsageEstimator.shallowSizeOfInstance(SpatialIntersectsGeoSourceAndSourceGridEvaluator.class); + private final Source source; private final EvalOperator.ExpressionEvaluator wkb; @@ -56,6 +59,14 @@ public Block eval(Page page) { } } + @Override + public long baseRamBytesUsed() { + long baseRamBytesUsed = BASE_RAM_BYTES_USED; + baseRamBytesUsed += wkb.baseRamBytesUsed(); + baseRamBytesUsed += gridId.baseRamBytesUsed(); + return baseRamBytesUsed; + } + public BooleanBlock eval(int positionCount, BytesRefBlock wkbBlock, LongBlock gridIdBlock) { try(BooleanBlock.Builder result = driverContext.blockFactory().newBooleanBlockBuilder(positionCount)) { position: for (int p = 0; p < positionCount; p++) { 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 68a21bf4d753b..b0156204b1c72 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 @@ -421,6 +421,11 @@ public enum Cap { */ SPATIAL_GRID_TYPES(Build.current().isSnapshot()), + /** + * Support geohash, geotile and geohex in ST_INTERSECTS and ST_DISJOINT. Done in #133546 + */ + SPATIAL_GRID_INTERSECTS(Build.current().isSnapshot()), + /** * 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/expression/function/scalar/spatial/BinarySpatialFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunction.java index 437973705f343..c9b85a5681e7d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunction.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.util.Objects; +import java.util.function.Predicate; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; @@ -55,15 +56,17 @@ protected BinarySpatialFunction( Expression right, boolean leftDocValues, boolean rightDocValues, - boolean pointsOnly + boolean pointsOnly, + boolean supportsGrid ) { super(source, left, right); this.leftDocValues = leftDocValues; this.rightDocValues = rightDocValues; - this.spatialTypeResolver = new SpatialTypeResolver(this, pointsOnly); + this.spatialTypeResolver = new SpatialTypeResolver(this, pointsOnly, supportsGrid); } - protected BinarySpatialFunction(StreamInput in, boolean leftDocValues, boolean rightDocValues, boolean pointsOnly) throws IOException { + protected BinarySpatialFunction(StreamInput in, boolean leftDocValues, boolean rightDocValues, boolean pointsOnly, boolean supportsGrid) + throws IOException { // The doc-values fields are only used on data nodes local planning, and therefor never serialized this( in.getTransportVersion().onOrAfter(TransportVersions.ESQL_SERIALIZE_SOURCE_FUNCTIONS_WARNINGS) @@ -73,7 +76,8 @@ protected BinarySpatialFunction(StreamInput in, boolean leftDocValues, boolean r in.readNamedWriteable(Expression.class), leftDocValues, rightDocValues, - pointsOnly + pointsOnly, + supportsGrid ); } @@ -119,10 +123,12 @@ protected TypeResolution resolveType() { static class SpatialTypeResolver { private final SpatialEvaluatorFactory.SpatialSourceResolution supplier; private final boolean pointsOnly; + private final boolean supportsGrid; - SpatialTypeResolver(SpatialEvaluatorFactory.SpatialSourceResolution supplier, boolean pointsOnly) { + SpatialTypeResolver(SpatialEvaluatorFactory.SpatialSourceResolution supplier, boolean pointsOnly, boolean supportsGrid) { this.supplier = supplier; this.pointsOnly = pointsOnly; + this.supportsGrid = supportsGrid; } public Expression left() { @@ -147,10 +153,16 @@ protected TypeResolution resolveType() { } } - protected Expression.TypeResolution isSpatial(Expression e, TypeResolutions.ParamOrdinal paramOrd) { + protected Expression.TypeResolution isCompatibleSpatial(Expression e, TypeResolutions.ParamOrdinal paramOrd) { return pointsOnly ? EsqlTypeResolutions.isSpatialPoint(e, sourceText(), paramOrd) - : EsqlTypeResolutions.isSpatial(e, sourceText(), paramOrd); + : (supportsGrid + ? EsqlTypeResolutions.isSpatialOrGrid(e, sourceText(), paramOrd) + : EsqlTypeResolutions.isSpatial(e, sourceText(), paramOrd)); + } + + protected Expression.TypeResolution isGeoPoint(Expression e, TypeResolutions.ParamOrdinal paramOrd) { + return isType(e, GEO_POINT::equals, sourceText(), paramOrd, GEO_POINT.typeName()); } private TypeResolution resolveType( @@ -159,8 +171,8 @@ private TypeResolution resolveType( TypeResolutions.ParamOrdinal leftOrdinal, TypeResolutions.ParamOrdinal rightOrdinal ) { - TypeResolution leftResolution = isSpatial(leftExpression, leftOrdinal); - TypeResolution rightResolution = isSpatial(rightExpression, rightOrdinal); + TypeResolution leftResolution = isCompatibleSpatial(leftExpression, leftOrdinal); + TypeResolution rightResolution = isCompatibleSpatial(rightExpression, rightOrdinal); if (leftResolution.resolved()) { return resolveType(leftExpression, rightExpression, rightOrdinal); } else if (rightResolution.resolved()) { @@ -176,9 +188,17 @@ protected TypeResolution resolveType( TypeResolutions.ParamOrdinal otherParamOrdinal ) { if (isNull(spatialExpression.dataType())) { - return isSpatial(otherExpression, otherParamOrdinal); + return isCompatibleSpatial(otherExpression, otherParamOrdinal); } TypeResolution resolution = isSameSpatialType(spatialExpression.dataType(), otherExpression, sourceText(), otherParamOrdinal); + // TODO Remove these grid checks once we support geo_shape relation to geoGrid + // but retain a rule to disallow grid-grid relations + if (resolution.resolved() && DataType.isGeoGrid(spatialExpression.dataType())) { + resolution = isGeoPoint(otherExpression, otherParamOrdinal); + } + if (resolution.resolved() && DataType.isGeoGrid(otherExpression.dataType())) { + resolution = isGeoPoint(spatialExpression, otherParamOrdinal == FIRST ? SECOND : FIRST); + } if (resolution.unresolved()) { return resolution; } @@ -192,15 +212,12 @@ protected TypeResolution isSameSpatialType( String operationName, TypeResolutions.ParamOrdinal paramOrd ) { - return pointsOnly - ? isType(expression, dt -> dt == spatialDataType, operationName, paramOrd, compatibleTypeNames(spatialDataType)) - : isType( - expression, - dt -> DataType.isSpatial(dt) && spatialCRSCompatible(spatialDataType, dt), - operationName, - paramOrd, - compatibleTypeNames(spatialDataType) - ); + Predicate isSpatialType = pointsOnly + ? dt -> dt == spatialDataType + : (supportsGrid + ? dt -> DataType.isSpatialOrGrid(dt) && spatialCRSCompatible(spatialDataType, dt) + : dt -> DataType.isSpatial(dt) && spatialCRSCompatible(spatialDataType, dt)); + return isType(expression, isSpatialType, operationName, paramOrd, compatibleTypeNames(spatialDataType)); } } @@ -248,7 +265,7 @@ public enum SpatialCrsType { public static SpatialCrsType fromDataType(DataType dataType) { return DataType.isSpatialGeo(dataType) ? SpatialCrsType.GEO - : DataType.isSpatial(dataType) ? SpatialCrsType.CARTESIAN + : DataType.isSpatialOrGrid(dataType) ? SpatialCrsType.CARTESIAN : SpatialCrsType.UNSPECIFIED; } } @@ -278,8 +295,8 @@ public TranslationAware.Translatable translatable(LucenePushdownPredicates pushd // The use of foldable here instead of SpatialEvaluatorFieldKey.isConstant is intentional to match the behavior of the // Lucene pushdown code in EsqlTranslationHandler::SpatialRelatesTranslator // We could enhance both places to support ReferenceAttributes that refer to constants, but that is a larger change - return isPushableSpatialAttribute(left(), pushdownPredicates) && right().foldable() - || isPushableSpatialAttribute(right(), pushdownPredicates) && left().foldable() + return isPushableSpatialAttribute(left(), pushdownPredicates) && isPushableLiteralAttribute(right()) + || isPushableSpatialAttribute(right(), pushdownPredicates) && isPushableLiteralAttribute(left()) ? TranslationAware.Translatable.YES : TranslationAware.Translatable.NO; @@ -288,4 +305,9 @@ public TranslationAware.Translatable translatable(LucenePushdownPredicates pushd private static boolean isPushableSpatialAttribute(Expression exp, LucenePushdownPredicates p) { return exp instanceof FieldAttribute fa && DataType.isSpatial(fa.dataType()) && fa.getExactInfo().hasExact() && p.isIndexed(fa); } + + private static boolean isPushableLiteralAttribute(Expression exp) { + // TODO: Support pushdown of geo-grid queries where the constant is a geo-grid-id literal + return DataType.isSpatial(exp.dataType()) && exp.foldable(); + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjoint.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjoint.java index 3e16fa163fcd6..e4d2fbaa78151 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjoint.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjoint.java @@ -38,10 +38,15 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_POINT; import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_SHAPE; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHASH; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHEX; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEOTILE; 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.expression.Foldables.valueOf; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asGeometryDocValueReader; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asLuceneComponent2D; +import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.makeGeometryFromLiteral; /** * This is the primary class for supporting the function ST_DISJOINT. @@ -81,24 +86,34 @@ public class SpatialDisjoint extends SpatialRelatesFunction { ) public SpatialDisjoint( Source source, - @Param(name = "geomA", type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape" }, description = """ - Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. - If `null`, the function returns `null`.""") Expression left, - @Param(name = "geomB", type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape" }, description = """ - Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. - If `null`, the function returns `null`. - The second parameter must also have the same coordinate system as the first. - This means it is not possible to combine `geo_*` and `cartesian_*` parameters.""") Expression right + @Param( + name = "geomA", + type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape", "geohash", "geotile", "geohex" }, + description = """ + Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) + or a geo-grid value (`geohash`, `geotile`, `geohex`). + If `null`, the function returns `null`.""" + ) Expression left, + @Param( + name = "geomB", + type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape", "geohash", "geotile", "geohex" }, + description = """ + Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) + or a geo-grid value (`geohash`, `geotile`, `geohex`). + If `null`, the function returns `null`. + The second parameter must also have the same coordinate system as the first. + This means it is not possible to combine `geo_*` and `cartesian_*` parameters.""" + ) Expression right ) { this(source, left, right, false, false); } private SpatialDisjoint(Source source, Expression left, Expression right, boolean leftDocValues, boolean rightDocValues) { - super(source, left, right, leftDocValues, rightDocValues); + super(source, left, right, leftDocValues, rightDocValues, true); } private SpatialDisjoint(StreamInput in) throws IOException { - super(in, false, false); + super(in, false, false, true); } @Override @@ -132,6 +147,11 @@ protected NodeInfo info() { @Override public Object fold(FoldContext ctx) { try { + if (DataType.isGeoGrid(left().dataType())) { + return foldGeoGrid(ctx, right(), left(), left().dataType()); + } else if (DataType.isGeoGrid(right().dataType())) { + return foldGeoGrid(ctx, left(), right(), right().dataType()); + } GeometryDocValueReader docValueReader = asGeometryDocValueReader(ctx, crsType(), left()); Component2D component2D = asLuceneComponent2D(ctx, crsType(), right()); return (crsType() == SpatialCrsType.GEO) @@ -142,6 +162,11 @@ public Object fold(FoldContext ctx) { } } + private Object foldGeoGrid(FoldContext ctx, Expression spatialExp, Expression gridExp, DataType gridType) throws IOException { + long gridId = (Long) valueOf(ctx, gridExp); + return GEO.compareGeometryAndGrid(makeGeometryFromLiteral(ctx, spatialExp), gridId, gridType); + } + @Override Map> evaluatorRules() { return evaluatorMap; @@ -211,6 +236,78 @@ public Object fold(FoldContext ctx) { } } } + + // Support geo_point and geo-grid types + for (DataType gridType : new DataType[] { GEOHASH, GEOTILE, GEOHEX }) { + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSourceAndConstant(GEO_POINT, gridType), + new SpatialEvaluatorFactory.SpatialEvaluatorWithConstantGridFactory( + (s, w, g) -> new SpatialDisjointGeoSourceAndConstantGridEvaluator.Factory(s, w, g, gridType) + ) + ); + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSourceAndConstant(GEO_POINT, gridType).withLeftDocValues(), + new SpatialEvaluatorFactory.SpatialEvaluatorWithConstantGridFactory( + (s, w, g) -> new SpatialDisjointGeoPointDocValuesAndConstantGridEvaluator.Factory(s, w, g, gridType) + ) + ); + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSources(GEO_POINT, gridType), + new SpatialEvaluatorFactory.SpatialEvaluatorFactoryWithFields( + (s, w, g) -> new SpatialDisjointGeoSourceAndSourceGridEvaluator.Factory(s, w, g, gridType) + ) + ); + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSources(GEO_POINT, gridType).withLeftDocValues(), + new SpatialEvaluatorFactory.SpatialEvaluatorFactoryWithFields( + (s, w, g) -> new SpatialDisjointGeoPointDocValuesAndSourceGridEvaluator.Factory(s, w, g, gridType) + ) + ); + } + } + + @Evaluator(extraName = "GeoSourceAndConstantGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoSourceAndConstantGrid( + BooleanBlock.Builder results, + int p, + BytesRefBlock wkb, + @Fixed long gridId, + @Fixed DataType gridType + ) throws IOException { + GEO.processSourceAndConstantGrid(results, p, wkb, gridId, gridType); + } + + @Evaluator(extraName = "GeoPointDocValuesAndConstantGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoPointDocValuesAndConstantGrid( + BooleanBlock.Builder results, + int p, + LongBlock encodedPoints, + @Fixed long gridId, + @Fixed DataType gridType + ) throws IOException { + GEO.processGeoPointDocValuesAndConstantGrid(results, p, encodedPoints, gridId, gridType); + } + + @Evaluator(extraName = "GeoSourceAndSourceGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoSourceAndSourceGrid( + BooleanBlock.Builder results, + int p, + BytesRefBlock wkb, + LongBlock gridId, + @Fixed DataType gridType + ) throws IOException { + GEO.processSourceAndSourceGrid(results, p, wkb, gridId, gridType); + } + + @Evaluator(extraName = "GeoPointDocValuesAndSourceGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoPointDocValuesAndSourceGrid( + BooleanBlock.Builder results, + int p, + LongBlock encodedPoints, + LongBlock gridIds, + @Fixed DataType gridType + ) throws IOException { + GEO.processGeoPointDocValuesAndSourceGrid(results, p, encodedPoints, gridIds, gridType); } @Evaluator(extraName = "GeoSourceAndConstant", warnExceptions = { IllegalArgumentException.class, IOException.class }) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialEvaluatorFactory.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialEvaluatorFactory.java index dcd53075cf69c..8cda0414474eb 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialEvaluatorFactory.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialEvaluatorFactory.java @@ -17,6 +17,7 @@ import java.util.Map; +import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asLong; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asLuceneComponent2D; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asLuceneComponent2Ds; @@ -179,6 +180,26 @@ public EvalOperator.ExpressionEvaluator.Factory get(SpatialSourceSupplier s, Eva } } + /** + * This evaluator factory is used when the right hand side is a geo-grid constant or literal, + * and the left is sourced from the index, or from previous evaluators. + */ + protected static class SpatialEvaluatorWithConstantGridFactory extends SpatialEvaluatorFactory< + EvalOperator.ExpressionEvaluator.Factory, + Long> { + + SpatialEvaluatorWithConstantGridFactory( + TriFunction factoryCreator + ) { + super(factoryCreator); + } + + @Override + public EvalOperator.ExpressionEvaluator.Factory get(SpatialSourceSupplier s, EvaluatorMapper.ToEvaluator toEvaluator) { + return factoryCreator.apply(s.source(), toEvaluator.apply(s.left()), asLong(toEvaluator.foldCtx(), s.right())); + } + } + /** * This evaluator factory is used when the right hand side is a constant or literal, * and the left is sourced from the index, or from previous evaluators. diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersects.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersects.java index 601550cd173bb..9b87a5efb1510 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersects.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersects.java @@ -38,10 +38,15 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_POINT; import static org.elasticsearch.xpack.esql.core.type.DataType.CARTESIAN_SHAPE; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHASH; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHEX; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEOTILE; 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.expression.Foldables.valueOf; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asGeometryDocValueReader; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asLuceneComponent2D; +import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.makeGeometryFromLiteral; /** * This is the primary class for supporting the function ST_INTERSECTS. @@ -79,24 +84,34 @@ public class SpatialIntersects extends SpatialRelatesFunction { In mathematical terms: ST_Intersects(A, B) ⇔ A ⋂ B ≠ ∅""", examples = @Example(file = "spatial", tag = "st_intersects-airports")) public SpatialIntersects( Source source, - @Param(name = "geomA", type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape" }, description = """ - Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. - If `null`, the function returns `null`.""") Expression left, - @Param(name = "geomB", type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape" }, description = """ - Expression of type `geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`. - If `null`, the function returns `null`. - The second parameter must also have the same coordinate system as the first. - This means it is not possible to combine `geo_*` and `cartesian_*` parameters.""") Expression right + @Param( + name = "geomA", + type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape", "geohash", "geotile", "geohex" }, + description = """ + Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) + or a geo-grid value (`geohash`, `geotile`, `geohex`). + If `null`, the function returns `null`.""" + ) Expression left, + @Param( + name = "geomB", + type = { "geo_point", "cartesian_point", "geo_shape", "cartesian_shape", "geohash", "geotile", "geohex" }, + description = """ + Expression that is either a geometry (`geo_point`, `cartesian_point`, `geo_shape` or `cartesian_shape`) + or a geo-grid value (`geohash`, `geotile`, `geohex`). + If `null`, the function returns `null`. + The second parameter must also have the same coordinate system as the first. + This means it is not possible to combine `geo_*` and `cartesian_*` parameters.""" + ) Expression right ) { this(source, left, right, false, false); } private SpatialIntersects(Source source, Expression left, Expression right, boolean leftDocValues, boolean rightDocValues) { - super(source, left, right, leftDocValues, rightDocValues); + super(source, left, right, leftDocValues, rightDocValues, true); } private SpatialIntersects(StreamInput in) throws IOException { - super(in, false, false); + super(in, false, false, true); } @Override @@ -130,6 +145,11 @@ protected NodeInfo info() { @Override public Object fold(FoldContext ctx) { try { + if (DataType.isGeoGrid(left().dataType())) { + return foldGeoGrid(ctx, right(), left(), left().dataType()); + } else if (DataType.isGeoGrid(right().dataType())) { + return foldGeoGrid(ctx, left(), right(), right().dataType()); + } GeometryDocValueReader docValueReader = asGeometryDocValueReader(ctx, crsType(), left()); Component2D component2D = asLuceneComponent2D(ctx, crsType(), right()); return (crsType() == SpatialCrsType.GEO) @@ -140,6 +160,11 @@ public Object fold(FoldContext ctx) { } } + private Object foldGeoGrid(FoldContext ctx, Expression spatialExp, Expression gridExp, DataType gridType) throws IOException { + long gridId = (Long) valueOf(ctx, gridExp); + return GEO.compareGeometryAndGrid(makeGeometryFromLiteral(ctx, spatialExp), gridId, gridType); + } + @Override Map> evaluatorRules() { return evaluatorMap; @@ -209,6 +234,78 @@ public Object fold(FoldContext ctx) { } } } + + // Support geo_point and geo-grid types + for (DataType gridType : new DataType[] { GEOHASH, GEOTILE, GEOHEX }) { + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSourceAndConstant(GEO_POINT, gridType), + new SpatialEvaluatorFactory.SpatialEvaluatorWithConstantGridFactory( + (s, w, g) -> new SpatialIntersectsGeoSourceAndConstantGridEvaluator.Factory(s, w, g, gridType) + ) + ); + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSourceAndConstant(GEO_POINT, gridType).withLeftDocValues(), + new SpatialEvaluatorFactory.SpatialEvaluatorWithConstantGridFactory( + (s, w, g) -> new SpatialIntersectsGeoPointDocValuesAndConstantGridEvaluator.Factory(s, w, g, gridType) + ) + ); + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSources(GEO_POINT, gridType), + new SpatialEvaluatorFactory.SpatialEvaluatorFactoryWithFields( + (s, w, g) -> new SpatialIntersectsGeoSourceAndSourceGridEvaluator.Factory(s, w, g, gridType) + ) + ); + evaluatorMap.put( + SpatialEvaluatorFactory.SpatialEvaluatorKey.fromSources(GEO_POINT, gridType).withLeftDocValues(), + new SpatialEvaluatorFactory.SpatialEvaluatorFactoryWithFields( + (s, w, g) -> new SpatialIntersectsGeoPointDocValuesAndSourceGridEvaluator.Factory(s, w, g, gridType) + ) + ); + } + } + + @Evaluator(extraName = "GeoSourceAndConstantGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoSourceAndConstantGrid( + BooleanBlock.Builder results, + int p, + BytesRefBlock wkb, + @Fixed long gridId, + @Fixed DataType gridType + ) throws IOException { + GEO.processSourceAndConstantGrid(results, p, wkb, gridId, gridType); + } + + @Evaluator(extraName = "GeoPointDocValuesAndConstantGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoPointDocValuesAndConstantGrid( + BooleanBlock.Builder results, + int p, + LongBlock encodedPoints, + @Fixed long gridId, + @Fixed DataType gridType + ) throws IOException { + GEO.processGeoPointDocValuesAndConstantGrid(results, p, encodedPoints, gridId, gridType); + } + + @Evaluator(extraName = "GeoSourceAndSourceGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoSourceAndSourceGrid( + BooleanBlock.Builder results, + int p, + BytesRefBlock wkb, + LongBlock gridId, + @Fixed DataType gridType + ) throws IOException { + GEO.processSourceAndSourceGrid(results, p, wkb, gridId, gridType); + } + + @Evaluator(extraName = "GeoPointDocValuesAndSourceGrid", warnExceptions = { IllegalArgumentException.class, IOException.class }) + static void processGeoPointDocValuesAndSourceGrid( + BooleanBlock.Builder results, + int p, + LongBlock encodedPoints, + LongBlock gridIds, + @Fixed DataType gridType + ) throws IOException { + GEO.processGeoPointDocValuesAndSourceGrid(results, p, encodedPoints, gridIds, gridType); } @Evaluator(extraName = "GeoSourceAndConstant", warnExceptions = { IllegalArgumentException.class, IOException.class }) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java index 1aa6faea83860..92b5e666a4236 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java @@ -12,16 +12,19 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.geo.ShapeRelation; import org.elasticsearch.common.io.stream.StreamInput; -import org.elasticsearch.compute.ann.Fixed; import org.elasticsearch.compute.data.BooleanBlock; import org.elasticsearch.compute.data.BytesRefBlock; import org.elasticsearch.compute.data.LongBlock; import org.elasticsearch.compute.operator.EvalOperator; import org.elasticsearch.geometry.Geometry; +import org.elasticsearch.geometry.Point; +import org.elasticsearch.geometry.utils.Geohash; +import org.elasticsearch.h3.H3; import org.elasticsearch.index.mapper.ShapeIndexer; import org.elasticsearch.lucene.spatial.Component2DVisitor; import org.elasticsearch.lucene.spatial.CoordinateEncoder; import org.elasticsearch.lucene.spatial.GeometryDocValueReader; +import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils; import org.elasticsearch.xpack.esql.capabilities.TranslationAware; import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException; import org.elasticsearch.xpack.esql.core.expression.Expression; @@ -41,6 +44,7 @@ import java.io.IOException; import java.util.Map; +import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asGeometry; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asGeometryDocValueReader; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesUtils.asLuceneComponent2D; @@ -52,11 +56,27 @@ public abstract class SpatialRelatesFunction extends BinarySpatialFunction SurrogateExpression { protected SpatialRelatesFunction(Source source, Expression left, Expression right, boolean leftDocValues, boolean rightDocValues) { - super(source, left, right, leftDocValues, rightDocValues, false); + super(source, left, right, leftDocValues, rightDocValues, false, false); + } + + protected SpatialRelatesFunction( + Source source, + Expression left, + Expression right, + boolean leftDocValues, + boolean rightDocValues, + boolean supportsGrid + ) { + super(source, left, right, leftDocValues, rightDocValues, false, supportsGrid); } protected SpatialRelatesFunction(StreamInput in, boolean leftDocValues, boolean rightDocValues) throws IOException { - super(in, leftDocValues, rightDocValues, false); + super(in, leftDocValues, rightDocValues, false, false); + } + + protected SpatialRelatesFunction(StreamInput in, boolean leftDocValues, boolean rightDocValues, boolean supportsGrid) + throws IOException { + super(in, leftDocValues, rightDocValues, false, supportsGrid); } public abstract ShapeRelation queryRelation(); @@ -116,7 +136,98 @@ protected boolean geometryRelatesGeometry(GeometryDocValueReader reader, Compone return visitor.matches(); } - protected void processSourceAndConstant(BooleanBlock.Builder builder, int position, BytesRefBlock left, @Fixed Component2D right) + protected void processSourceAndConstantGrid( + BooleanBlock.Builder builder, + int position, + BytesRefBlock wkb, + long gridId, + DataType gridType + ) { + if (wkb.getValueCount(position) < 1) { + builder.appendNull(); + } else { + builder.appendBoolean(compareGeometryAndGrid(asGeometry(wkb, position), gridId, gridType)); + } + } + + protected void processSourceAndSourceGrid( + BooleanBlock.Builder builder, + int position, + BytesRefBlock wkb, + LongBlock gridIds, + DataType gridType + ) { + if (wkb.getValueCount(position) < 1 || gridIds.getValueCount(position) < 1) { + builder.appendNull(); + } else { + builder.appendBoolean(compareGeometryAndGrid(asGeometry(wkb, position), gridIds.getLong(position), gridType)); + } + } + + protected boolean compareGeometryAndGrid(Geometry geometry, long gridId, DataType gridType) { + if (geometry instanceof Point point) { + long geoGridId = getGridId(point, gridId, gridType); + return switch (queryRelation) { + case INTERSECTS -> gridId == geoGridId; + case DISJOINT -> gridId != geoGridId; + default -> throw new IllegalArgumentException("Unsupported grid relation: " + queryRelation); + }; + } else { + throw new IllegalArgumentException( + "Unsupported grid intersection geometry type: " + geometry.getClass().getSimpleName() + "; expected Point" + ); + } + } + + protected void processGeoPointDocValuesAndConstantGrid( + BooleanBlock.Builder builder, + int position, + LongBlock encodedPoint, + long gridId, + DataType gridType + ) { + if (encodedPoint.getValueCount(position) < 1) { + builder.appendNull(); + } else { + final Point point = spatialCoordinateType.longAsPoint(encodedPoint.getLong(position)); + long geoGridId = getGridId(point, gridId, gridType); + builder.appendBoolean(gridId == geoGridId); + } + } + + protected void processGeoPointDocValuesAndSourceGrid( + BooleanBlock.Builder builder, + int position, + LongBlock encodedPoint, + LongBlock gridIds, + DataType gridType + ) { + if (encodedPoint.getValueCount(position) < 1 || gridIds.getValueCount(position) < 1) { + builder.appendNull(); + } else { + final Point point = spatialCoordinateType.longAsPoint(encodedPoint.getLong(position)); + final long gridId = gridIds.getLong(position); + long geoGridId = getGridId(point, gridId, gridType); + builder.appendBoolean(gridId == geoGridId); + } + } + + private long getGridId(Point point, long gridId, DataType gridType) { + return switch (gridType) { + case GEOHASH -> Geohash.longEncode(point.getX(), point.getY(), Geohash.stringEncode(gridId).length()); + case GEOTILE -> GeoTileUtils.longEncode( + point.getX(), + point.getY(), + Integer.parseInt(GeoTileUtils.stringEncode(gridId).split("/")[0]) + ); + case GEOHEX -> H3.geoToH3(point.getY(), point.getX(), H3.getResolution(gridId)); + default -> throw new IllegalArgumentException( + "Unsupported grid type: " + gridType + "; expected GEOHASH, GEOTILE, or GEOHEX" + ); + }; + } + + protected void processSourceAndConstant(BooleanBlock.Builder builder, int position, BytesRefBlock left, Component2D right) throws IOException { if (left.getValueCount(position) < 1) { builder.appendNull(); @@ -141,7 +252,7 @@ protected void processPointDocValuesAndConstant( BooleanBlock.Builder builder, int position, LongBlock leftValue, - @Fixed Component2D rightValue + Component2D rightValue ) throws IOException { if (leftValue.getValueCount(position) < 1) { builder.appendNull(); @@ -212,6 +323,7 @@ private Query translate(TranslatorHandler handler, Expression spatialExpression, String name = handler.nameOf(attribute); try { + // TODO: Support geo-grid query pushdown Geometry shape = SpatialRelatesUtils.makeGeometryFromLiteral(constantExpression); return new SpatialRelatesQuery(source(), name, queryRelation(), shape, attribute.dataType()); } catch (IllegalArgumentException e) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java index 0d323ad8cb856..89fccfc34faa4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java @@ -43,6 +43,20 @@ public class SpatialRelatesUtils { + /** Converts a {@link Expression} into a {@link Long}. */ + static Long asLong(FoldContext ctx, Expression expression) { + Object value = valueOf(ctx, expression); + if (value instanceof Long longValue) { + return longValue; + } else if (value instanceof Integer intValue) { + return intValue.longValue(); + } else { + throw new IllegalArgumentException( + "Unsupported combination of literal [" + value.getClass().getSimpleName() + "] of type [" + expression.dataType() + "]" + ); + } + } + /** Converts a {@link Expression} into a {@link Component2D}. */ static Component2D asLuceneComponent2D(FoldContext ctx, BinarySpatialFunction.SpatialCrsType crsType, Expression expression) { return asLuceneComponent2D(crsType, makeGeometryFromLiteral(ctx, expression)); @@ -154,7 +168,7 @@ static GeometryDocValueReader asGeometryDocValueReader( return asGeometryDocValueReader(encoder, shapeIndexer, asGeometry(valueBlock, position)); } - private static Geometry asGeometry(BytesRefBlock valueBlock, int position) { + static Geometry asGeometry(BytesRefBlock valueBlock, int position) { final BytesRef scratch = new BytesRef(); final int firstValueIndex = valueBlock.getFirstValueIndex(position); final int valueCount = valueBlock.getValueCount(position); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistance.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistance.java index f0c25e3289cc1..e318d79848cc4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistance.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistance.java @@ -241,15 +241,15 @@ public StDistance( The second parameter must also have the same coordinate system as the first. This means it is not possible to combine `geo_point` and `cartesian_point` parameters.""") Expression right ) { - super(source, left, right, false, false, true); + super(source, left, right, false, false, true, false); } protected StDistance(Source source, Expression left, Expression right, boolean leftDocValues, boolean rightDocValues) { - super(source, left, right, leftDocValues, rightDocValues, true); + super(source, left, right, leftDocValues, rightDocValues, true, false); } private StDistance(StreamInput in) throws IOException { - super(in, false, false, true); + super(in, false, false, true, false); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunctionTestCase.java index 1f53ea4d9a72c..ab0ef54599077 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/BinarySpatialFunctionTestCase.java @@ -10,6 +10,7 @@ import joptsimple.internal.Strings; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.geometry.Geometry; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; import org.elasticsearch.xpack.esql.core.type.DataType; @@ -31,8 +32,8 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHASH; import static org.elasticsearch.xpack.esql.core.type.DataType.GEOHEX; import static org.elasticsearch.xpack.esql.core.type.DataType.GEOTILE; -import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatial; import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatialGeo; +import static org.elasticsearch.xpack.esql.core.type.DataType.isSpatialOrGrid; import static org.elasticsearch.xpack.esql.core.type.DataType.isString; import static org.elasticsearch.xpack.esql.expression.function.scalar.spatial.SpatialRelatesFunction.compatibleTypeNames; import static org.hamcrest.Matchers.equalTo; @@ -100,6 +101,39 @@ protected static void addSpatialCombinations( } } + /** + * Binary spatial functions that take two spatial arguments, one of which is a gridId, + * should use this to generate combinations of test cases. + */ + protected static void addSpatialGridCombinations(List suppliers, DataType[] dataTypes, DataType returnType) { + for (DataType leftType : dataTypes) { + TestCaseSupplier.TypedDataSupplier leftDataSupplier = testCaseSupplier(leftType, true); + for (DataType rightType : new DataType[] { DataType.GEOHASH, DataType.GEOTILE, DataType.GEOHEX }) { + if (typeCompatible(leftType, rightType)) { + TestCaseSupplier.TypedDataSupplier rightDataSupplier = testCaseSupplier(rightType, false); + suppliers.add( + TestCaseSupplier.testCaseSupplier( + leftDataSupplier, + rightDataSupplier, + BinarySpatialFunctionTestCase::spatialEvaluatorString, + returnType, + (l, r) -> expected(l, leftType, r, rightType) + ) + ); + suppliers.add( + TestCaseSupplier.testCaseSupplier( + rightDataSupplier, + leftDataSupplier, + BinarySpatialFunctionTestCase::spatialEvaluatorString, + returnType, + (l, r) -> expected(l, rightType, r, leftType) + ) + ); + } + } + } + } + /** * Build the expected error message for an invalid type signature. * For two args, this assumes they are both spatial. @@ -109,7 +143,8 @@ protected static String typeErrorMessage( boolean includeOrdinal, List> validPerPosition, List types, - boolean pointsOnly + boolean pointsOnly, + boolean supportsGrid ) { boolean argInvalid = false; List badArgPositions = new ArrayList<>(); @@ -133,15 +168,22 @@ protected static String typeErrorMessage( if (badArgPositions.size() == 1) { int badArgPosition = badArgPositions.get(0); int goodArgPosition = badArgPosition == 0 ? 1 : 0; - if (isSpatial(types.get(goodArgPosition)) == false) { - return oneInvalid(badArgPosition, -1, includeOrdinal, types, pointsOnly); + if (DataType.isGeoGrid(types.get(goodArgPosition))) { + // When the valid position is a grid, the other type can only be points + return oneInvalid(badArgPosition, goodArgPosition, includeOrdinal, types, true, supportsGrid); + } else if (isSpatialOrGrid(types.get(goodArgPosition)) == false) { + return oneInvalid(badArgPosition, -1, includeOrdinal, types, pointsOnly, supportsGrid); } else { - return oneInvalid(badArgPosition, goodArgPosition, includeOrdinal, types, pointsOnly); + return oneInvalid(badArgPosition, goodArgPosition, includeOrdinal, types, pointsOnly, supportsGrid); } } else if (argInvalid && badArgPositions.size() != 2) { return invalidArg(types.get(2)); + } else if (supportsGrid && DataType.isGeoGrid(types.get(0))) { + return invalidGrid(1, types, true); + } else if (supportsGrid && DataType.isGeoGrid(types.get(1))) { + return invalidGrid(0, types, true); } else { - return oneInvalid(0, -1, includeOrdinal, types, pointsOnly); + return oneInvalid(0, -1, includeOrdinal, types, pointsOnly, supportsGrid); } } @@ -157,14 +199,26 @@ private static String invalidArg(DataType invalidType) { ); } + private static String invalidGrid(int badArgPosition, List types, boolean pointsOnly) { + String expectedType = pointsOnly ? "geo_point" : "geo_point or geo_shape"; + String ordinal = TypeResolutions.ParamOrdinal.fromIndex(badArgPosition).name().toLowerCase(Locale.ROOT) + " "; + String name = types.get(badArgPosition).typeName(); + return ordinal + "argument of [source] must be [" + expectedType + "], found value [" + name + "] type [" + name + "]"; + } + private static String oneInvalid( int badArgPosition, int goodArgPosition, boolean includeOrdinal, List types, - boolean pointsOnly + boolean pointsOnly, + boolean supportsGrid ) { - String expected = pointsOnly ? "geo_point or cartesian_point" : "geo_point, cartesian_point, geo_shape or cartesian_shape"; + String expected = pointsOnly + ? "geo_point or cartesian_point" + : (supportsGrid + ? "geo_point, cartesian_point, geo_shape, cartesian_shape, geohash, geotile or geohex" + : "geo_point, cartesian_point, geo_shape or cartesian_shape"); String ordinal = includeOrdinal ? TypeResolutions.ParamOrdinal.fromIndex(badArgPosition).name().toLowerCase(Locale.ROOT) + " " : ""; String expectedType = goodArgPosition >= 0 ? compatibleTypes(types.get(goodArgPosition)) : expected; String name = types.get(badArgPosition).typeName(); @@ -179,17 +233,33 @@ protected static Object expected(Object left, DataType leftType, Object right, D if (typeCompatible(leftType, rightType) == false) { return null; } - // TODO cast objects to right type and check intersection - BytesRef leftWKB = asGeometryWKB(left, leftType); - BytesRef rightWKB = asGeometryWKB(right, rightType); BinarySpatialFunction.BinarySpatialComparator spatialRelations = spatialRelations(left, leftType, right, rightType); try { - return spatialRelations.compare(leftWKB, rightWKB); + if (DataType.isGeoGrid(leftType)) { + return expectedGrid(spatialRelations, asGeometryWKB(right, rightType), left, leftType); + } else if (DataType.isGeoGrid(rightType)) { + return expectedGrid(spatialRelations, asGeometryWKB(left, leftType), right, rightType); + } else { + BytesRef leftWKB = asGeometryWKB(left, leftType); + BytesRef rightWKB = asGeometryWKB(right, rightType); + return spatialRelations.compare(leftWKB, rightWKB); + } } catch (IOException e) { throw new RuntimeException(e); } } + private static boolean expectedGrid( + BinarySpatialFunction.BinarySpatialComparator spatialRelations, + BytesRef wkb, + Object grid, + DataType gridType + ) { + Geometry geometry = SpatialCoordinateTypes.UNSPECIFIED.wkbToGeometry(wkb); + long gridId = Long.parseLong(grid.toString()); + return ((SpatialRelatesFunction.SpatialRelations) spatialRelations).compareGeometryAndGrid(geometry, gridId, gridType); + } + /** * When two spatial arguments are processed and then compared with a third argument, * we need to process this argument too, before producing the final result. @@ -227,7 +297,7 @@ private static BinarySpatialFunction.BinarySpatialComparator spatialRelations ) { if (isSpatialGeo(leftType) || isSpatialGeo(rightType)) { return getRelationsField("GEO"); - } else if (isSpatial(leftType) || isSpatial(rightType)) { + } else if (isSpatialOrGrid(leftType) || isSpatialOrGrid(rightType)) { return getRelationsField("CARTESIAN"); } else { throw new IllegalArgumentException( @@ -255,7 +325,7 @@ protected static BytesRef asGeometryWKB(Object object, DataType dataType) { } protected static boolean typeCompatible(DataType leftType, DataType rightType) { - if (isSpatial(leftType) && isSpatial(rightType)) { + if (isSpatialOrGrid(leftType) && isSpatialOrGrid(rightType)) { // Both must be GEO_* or both must be CARTESIAN_* return countGeo(leftType, rightType) != 1; } @@ -263,9 +333,9 @@ protected static boolean typeCompatible(DataType leftType, DataType rightType) { } private static DataType pickSpatialType(DataType leftType, DataType rightType) { - if (isSpatial(leftType)) { + if (isSpatialOrGrid(leftType)) { return leftType; - } else if (isSpatial(rightType)) { + } else if (isSpatialOrGrid(rightType)) { return rightType; } else { throw new IllegalArgumentException("Invalid spatial types: " + leftType + " and " + rightType); @@ -274,6 +344,12 @@ private static DataType pickSpatialType(DataType leftType, DataType rightType) { private static Matcher spatialEvaluatorString(DataType leftType, DataType rightType) { String crsType = isSpatialGeo(pickSpatialType(leftType, rightType)) ? "Geo" : "Cartesian"; + if (DataType.isGeoGrid(leftType) || DataType.isGeoGrid(rightType)) { + int[] c = DataType.isGeoGrid(leftType) ? new int[] { 1, 0 } : new int[] { 0, 1 }; + DataType gridType = DataType.isGeoGrid(leftType) ? leftType : rightType; + String channelText = "wkb=Attribute[channel=" + c[0] + "], gridId=Attribute[channel=" + c[1] + "], gridType=" + gridType; + return equalTo(getFunctionClassName() + crsType + "SourceAndSourceGridEvaluator[" + channelText + "]"); + } String channels = channelsText("left", "right"); return equalTo(getFunctionClassName() + crsType + "SourceAndSourceEvaluator[" + channels + "]"); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointTests.java index b3feac5619c16..2c1ccb347d8bc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialDisjointTests.java @@ -18,8 +18,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.function.Supplier; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_POINT; + @FunctionName("st_disjoint") public class SpatialDisjointTests extends SpatialRelatesFunctionTestCase { public SpatialDisjointTests(@Name("TestCase") Supplier testCaseSupplier) { @@ -29,6 +32,7 @@ public SpatialDisjointTests(@Name("TestCase") Supplier parameters() { List suppliers = new ArrayList<>(); + SpatialRelatesFunctionTestCase.addSpatialGridCombinations(suppliers, GEO_POINT); DataType[] geoDataTypes = { DataType.GEO_POINT, DataType.GEO_SHAPE }; SpatialRelatesFunctionTestCase.addSpatialCombinations(suppliers, geoDataTypes); DataType[] cartesianDataTypes = { DataType.CARTESIAN_POINT, DataType.CARTESIAN_SHAPE }; @@ -42,4 +46,8 @@ public static Iterable parameters() { protected Expression build(Source source, List args) { return new SpatialDisjoint(source, args.get(0), args.get(1)); } + + protected static String typeErrorMessage(boolean includeOrdinal, List> validPerPosition, List types) { + return typeErrorMessage(includeOrdinal, validPerPosition, types, false, true); + } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsTests.java index ccf94bf6d2760..d651402214a10 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialIntersectsTests.java @@ -18,8 +18,11 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.function.Supplier; +import static org.elasticsearch.xpack.esql.core.type.DataType.GEO_POINT; + @FunctionName("st_intersects") public class SpatialIntersectsTests extends SpatialRelatesFunctionTestCase { public SpatialIntersectsTests(@Name("TestCase") Supplier testCaseSupplier) { @@ -29,7 +32,8 @@ public SpatialIntersectsTests(@Name("TestCase") Supplier parameters() { List suppliers = new ArrayList<>(); - DataType[] geoDataTypes = { DataType.GEO_POINT, DataType.GEO_SHAPE }; + SpatialRelatesFunctionTestCase.addSpatialGridCombinations(suppliers, GEO_POINT); + DataType[] geoDataTypes = { GEO_POINT, DataType.GEO_SHAPE }; SpatialRelatesFunctionTestCase.addSpatialCombinations(suppliers, geoDataTypes); DataType[] cartesianDataTypes = { DataType.CARTESIAN_POINT, DataType.CARTESIAN_SHAPE }; SpatialRelatesFunctionTestCase.addSpatialCombinations(suppliers, cartesianDataTypes); @@ -42,4 +46,8 @@ public static Iterable parameters() { protected Expression build(Source source, List args) { return new SpatialIntersects(source, args.get(0), args.get(1)); } + + protected static String typeErrorMessage(boolean includeOrdinal, List> validPerPosition, List types) { + return typeErrorMessage(includeOrdinal, validPerPosition, types, false, true); + } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunctionTestCase.java index 53ed472a4d43f..756ebc08bab0a 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunctionTestCase.java @@ -15,11 +15,15 @@ public abstract class SpatialRelatesFunctionTestCase extends BinarySpatialFunctionTestCase { - protected static void addSpatialCombinations(List suppliers, DataType[] dataTypes) { + protected static void addSpatialCombinations(List suppliers, DataType... dataTypes) { addSpatialCombinations(suppliers, dataTypes, DataType.BOOLEAN, false); } + protected static void addSpatialGridCombinations(List suppliers, DataType... dataTypes) { + addSpatialGridCombinations(suppliers, dataTypes, DataType.BOOLEAN); + } + protected static String typeErrorMessage(boolean includeOrdinal, List> validPerPosition, List types) { - return typeErrorMessage(includeOrdinal, validPerPosition, types, false); + return typeErrorMessage(includeOrdinal, validPerPosition, types, false, false); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistanceTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistanceTests.java index c78977918fc5e..06958b94a492f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistanceTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/StDistanceTests.java @@ -49,6 +49,6 @@ protected static void addSpatialCombinations(List suppliers, D } protected static String typeErrorMessage(boolean includeOrdinal, List> validPerPosition, List types) { - return typeErrorMessage(includeOrdinal, validPerPosition, types, true); + return typeErrorMessage(includeOrdinal, validPerPosition, types, true, false); } }