Browse Source

Support points as input types to the TO_GEOSHAPE and TO_CARTESIANSHAPE functions (#104799)

* Support points as input types to TO_GEOSHAPE and TO_CARTESIAN_SHAPE

* Update docs for TO_GEOSHAPE and TO_GEOPOINT
Craig Taverner 1 year ago
parent
commit
b0f978f41e

+ 1 - 1
docs/reference/esql/functions/to_cartesianshape.asciidoc

@@ -13,7 +13,7 @@ TO_CARTESIANSHAPE(v)
 
 `v`::
 Input value. The input can be a single- or multi-valued column or an expression.
-The input type must be a string or a `cartesian_shape`.
+The input type must be a string, a `cartesian_shape` or a `cartesian_point`.
 
 *Description*
 

+ 1 - 1
docs/reference/esql/functions/to_geoshape.asciidoc

@@ -13,7 +13,7 @@ TO_GEOPOINT(v)
 
 `v`::
 Input value. The input can be a single- or multi-valued column or an expression.
-The input type must be a string or a `geo_shape`.
+The input type must be a string, a `geo_shape` or a `geo_point`.
 
 *Description*
 

+ 1 - 0
docs/reference/esql/functions/types/to_cartesianshape.asciidoc

@@ -1,6 +1,7 @@
 [%header.monospaced.styled,format=dsv,separator=|]
 |===
 v | result
+cartesian_point | cartesian_shape
 cartesian_shape | cartesian_shape
 keyword | cartesian_shape
 text | cartesian_shape

+ 1 - 0
docs/reference/esql/functions/types/to_geoshape.asciidoc

@@ -1,6 +1,7 @@
 [%header.monospaced.styled,format=dsv,separator=|]
 |===
 v | result
+geo_point | geo_shape
 geo_shape | geo_shape
 keyword | geo_shape
 text | geo_shape

+ 6 - 6
x-pack/plugin/esql/qa/testFixtures/src/main/resources/show.csv-spec

@@ -75,15 +75,15 @@ tanh                     |"double tanh(n:double|integer|long|unsigned_long)"
 tau                      |double tau()                                                 | null                    | null             | null                                               |double                    | "The ratio of a circle’s circumference to its radius."                      | null                 | false | false
 to_bool                  |"boolean to_bool(v:boolean|keyword|text|double|long|unsigned_long|integer)"                      |v   |"boolean|keyword|text|double|long|unsigned_long|integer"                                                   |                                                    |boolean                          | "Converts an input value to a boolean value."                                                                                      |false                       |false           | false
 to_boolean               |"boolean to_boolean(v:boolean|keyword|text|double|long|unsigned_long|integer)"                   |v   |"boolean|keyword|text|double|long|unsigned_long|integer"                                                   |                                                    |boolean                          | "Converts an input value to a boolean value."                                                                                      |false                       |false           | false
-to_cartesianpoint        |"cartesian_point to_cartesianpoint(v:cartesian_point|keyword|text)"                         |v   |"cartesian_point|keyword|text"                              |                                                    |cartesian_point                  | "Converts an input value to a point value."                        |false                 |false | false
-to_cartesianshape        |"cartesian_shape to_cartesianshape(v:cartesian_shape|keyword|text)"                         |v   |"cartesian_shape|keyword|text"                              |                                                    |cartesian_shape                  | "Converts an input value to a shape value."                        |false                 |false | false
+to_cartesianpoint        |"cartesian_point to_cartesianpoint(v:cartesian_point|keyword|text)"                              |v   |"cartesian_point|keyword|text"                                                                             |                                                    |cartesian_point                  | "Converts an input value to a point value."                        |false                 |false | false
+to_cartesianshape        |"cartesian_shape to_cartesianshape(v:cartesian_point|cartesian_shape|keyword|text)"              |v   |"cartesian_point|cartesian_shape|keyword|text"                                                             |                                                    |cartesian_shape                  | "Converts an input value to a shape value."                        |false                 |false | false
 to_datetime              |"date to_datetime(v:date|keyword|text|double|long|unsigned_long|integer)"                        |v   |"date|keyword|text|double|long|unsigned_long|integer"                                                      |                                                    |date                             | "Converts an input value to a date value."                                                                                      |false                       |false           | false
 to_dbl                   |"double to_dbl(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"                   |v   |"boolean|date|keyword|text|double|long|unsigned_long|integer"                                              |                                                    |double                           | "Converts an input value to a double value."                                                                                      |false                       |false           | false
 to_degrees               |"double to_degrees(v:double|integer|long|unsigned_long)"                                         |v   |"double|integer|long|unsigned_long"                                                                        |                                                    |double                           | "Converts a number in radians to degrees."                                                                                      |false                       |false           | false
 to_double                |"double to_double(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"                |v   |"boolean|date|keyword|text|double|long|unsigned_long|integer"                                              |                                                    |double                           | "Converts an input value to a double value."                                                                                      |false                       |false           | false
 to_dt                    |"date to_dt(v:date|keyword|text|double|long|unsigned_long|integer)"                              |v   |"date|keyword|text|double|long|unsigned_long|integer"                                                      |                                                    |date                             | "Converts an input value to a date value."                                                                                      |false                       |false           | false
-to_geopoint              |"geo_point to_geopoint(v:geo_point|keyword|text)"                                           |v   |"geo_point|keyword|text"                                                                              |                                                    |geo_point                        | "Converts an input value to a geo_point value."                                                                                     |false                       |false           | false
-to_geoshape              |"geo_shape to_geoshape(v:geo_shape|keyword|text)"                                           |v   |"geo_shape|keyword|text"                                                                              |                                                    |geo_shape                        | "Converts an input value to a geo_shape value."                                                                                     |false                       |false           | false
+to_geopoint              |"geo_point to_geopoint(v:geo_point|keyword|text)"                                                |v   |"geo_point|keyword|text"                                                                                   |                                                    |geo_point                        | "Converts an input value to a geo_point value."                                                                                     |false                       |false           | false
+to_geoshape              |"geo_shape to_geoshape(v:geo_point|geo_shape|keyword|text)"                                      |v   |"geo_point|geo_shape|keyword|text"                                                                         |                                                    |geo_shape                        | "Converts an input value to a geo_shape value."                                                                                     |false                       |false           | false
 to_int                   |"integer to_int(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"                  |v   |"boolean|date|keyword|text|double|long|unsigned_long|integer"                                              |                                                    |integer                          | "Converts an input value to an integer value."                                                                                      |false                       |false           | false
 to_integer               |"integer to_integer(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"              |v   |"boolean|date|keyword|text|double|long|unsigned_long|integer"                                              |                                                    |integer                          | "Converts an input value to an integer value."                                                                                      |false                       |false           | false
 to_ip                    |"ip to_ip(v:ip|keyword|text)"                                                                    |v   |"ip|keyword|text"                                                                                          |                                                    |ip                               | "Converts an input string to an IP value."                                                                                      |false                       |false           | false
@@ -172,14 +172,14 @@ double tau()
 "boolean to_bool(v:boolean|keyword|text|double|long|unsigned_long|integer)"
 "boolean to_boolean(v:boolean|keyword|text|double|long|unsigned_long|integer)"
 "cartesian_point to_cartesianpoint(v:cartesian_point|keyword|text)"   
-"cartesian_shape to_cartesianshape(v:cartesian_shape|keyword|text)"                                                                  
+"cartesian_shape to_cartesianshape(v:cartesian_point|cartesian_shape|keyword|text)"                                                                  
 "date to_datetime(v:date|keyword|text|double|long|unsigned_long|integer)"
 "double to_dbl(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"
 "double to_degrees(v:double|integer|long|unsigned_long)"
 "double to_double(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"
 "date to_dt(v:date|keyword|text|double|long|unsigned_long|integer)"  
 "geo_point to_geopoint(v:geo_point|keyword|text)"       
-"geo_shape to_geoshape(v:geo_shape|keyword|text)"                                                                  
+"geo_shape to_geoshape(v:geo_point|geo_shape|keyword|text)"                                                                  
 "integer to_int(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"
 "integer to_integer(v:boolean|date|keyword|text|double|long|unsigned_long|integer)"
 "ip to_ip(v:ip|keyword|text)"

+ 46 - 1
x-pack/plugin/esql/qa/testFixtures/src/main/resources/spatial_shapes.csv-spec

@@ -23,14 +23,37 @@ wkt:keyword
 ["POLYGON ((30 10\, 40 40\, 20 40\, 10 20\, 30 10))", "POINT(75.8092915005895 22.727749187571)"] |[POLYGON ((30 10\, 40 40\, 20 40\, 10 20\, 30 10)), POINT(75.8092915005895 22.727749187571)]
 ;
 
+convertFromStringViaPoint#[skip:-8.12.99, reason: spatial type geo_shape only added in 8.13]
+ROW wkt = "POINT (30 10)"
+| EVAL point = TO_GEOPOINT(wkt)
+| EVAL shape = TO_GEOSHAPE(point)
+;
+
+wkt:keyword     | point:geo_point | shape:geo_shape
+"POINT (30 10)" | POINT (30 10)   | POINT (30 10)
+;
+
 # need to work out how to upload WKT
-simpleLoad#[skip:-8.12.99, reason: spatial type geo_shape  only added in 8.13]
+simpleLoad#[skip:-8.12.99, reason: spatial type geo_shape only added in 8.13]
 FROM countries_bbox | WHERE id == "ISL";
 
 id:keyword|	name:keyword|	shape:geo_shape
 ISL|Iceland|BBOX(-24.538400, -13.499446, 66.536100, 63.390000)
 ;
 
+simpleLoadPointsAsShapes#[skip:-8.12.99, reason: spatial type geo_shape only added in 8.13]
+FROM airports
+| WHERE abbrev == "CPH" OR abbrev == "VLC"
+| SORT abbrev
+| EVAL location = TO_GEOSHAPE(location), city_location = TO_GEOSHAPE(city_location)
+| KEEP abbrev, name, location, country, city, city_location
+;
+
+abbrev:keyword | name:text    | location:geo_shape                         | country:keyword | city:keyword | city_location:geo_shape
+"CPH"          | "Copenhagen" | POINT(12.6493508684508 55.6285017221528)   | "Denmark"       | "Copenhagen" | POINT(12.5683 55.6761)
+"VLC"          | "Valencia"   | POINT(-0.473474930771676 39.4914597884489) | "Spain"         | "Paterna"    | POINT(-0.4406 39.5028)
+;
+
 geo_shapeEquals#[skip:-8.12.99, reason: spatial type geo_shape only added in 8.13]
 
 ROW wkt = ["POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", "POINT(75.8092915005895 22.727749187571)"]
@@ -97,6 +120,16 @@ wkt:keyword
 ["POLYGON ((3339584.72 1118889.97\, 4452779.63 4865942.27\, 2226389.81 4865942.27\, 1113194.90 2273030.92\, 3339584.72 1118889.97))", "POINT(7580.93 2272.77)"] |[POLYGON ((3339584.72 1118889.97\, 4452779.63 4865942.27\, 2226389.81 4865942.27\, 1113194.90 2273030.92\, 3339584.72 1118889.97)), POINT(7580.93 2272.77)]
 ;
 
+convertCartesianFromStringViaPoint#[skip:-8.12.99, reason: spatial type cartesian_shape only added in 8.13]
+ROW wkt = "POINT (3010 -1010)"
+| EVAL point = TO_CARTESIANPOINT(wkt)
+| EVAL shape = TO_CARTESIANSHAPE(point)
+;
+
+wkt:keyword          | point:cartesian_point | shape:cartesian_shape
+"POINT (3010 -1010)" | POINT (3010 -1010)    | POINT (3010 -1010)
+;
+
 # need to work out how to upload WKT
 simpleCartesianShapeLoad#[skip:-8.12.99, reason: spatial type cartesian_shape only added in 8.13]
 FROM countries_bbox_web | WHERE id == "ISL";
@@ -105,6 +138,18 @@ id:keyword|	name:keyword|shape:cartesian_shape
 ISL|Iceland|BBOX(-2731602.192501422, -1502751.454502109, 1.0025136653899286E7, 9196525.03584683)
 ;
 
+simpleLoadCartesianPointsAsShapes#[skip:-8.12.99, reason: spatial type cartesian_shape only added in 8.13]
+FROM airports_web
+| WHERE abbrev == "CPH" OR abbrev == "VLC"
+| SORT abbrev
+| EVAL location = TO_CARTESIANSHAPE(location)
+;
+
+abbrev:keyword | name:text    | scalerank:integer | type:keyword | location:cartesian_shape
+"CPH"          | "Copenhagen" | 3                 | "major"      | POINT(1408119.2975413958 7484813.53657096)
+"VLC"          | "Valencia"   | 8                 | "mid"        | POINT(-52706.98819688343 4792315.469321795)
+;
+
 cartesianshapeEquals#[skip:-8.12.99, reason: spatial type cartesian_shape only added in 8.13]
 ROW wkt = ["POLYGON ((3339584.72 1118889.97, 4452779.63 4865942.27, 2226389.81 4865942.27, 1113194.90 2273030.92, 3339584.72 1118889.97))", "POINT(7580.93 2272.77)"]
 | MV_EXPAND wkt

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

@@ -19,6 +19,7 @@ import org.elasticsearch.xpack.ql.type.DataType;
 import java.util.List;
 import java.util.Map;
 
+import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.CARTESIAN_POINT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.CARTESIAN_SHAPE;
 import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD;
 import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT;
@@ -27,13 +28,17 @@ import static org.elasticsearch.xpack.ql.util.SpatialCoordinateTypes.CARTESIAN;
 public class ToCartesianShape extends AbstractConvertFunction {
 
     private static final Map<DataType, BuildFactory> EVALUATORS = Map.ofEntries(
+        Map.entry(CARTESIAN_POINT, (fieldEval, source) -> fieldEval),
         Map.entry(CARTESIAN_SHAPE, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToCartesianShapeFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToCartesianShapeFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(returnType = "cartesian_shape", description = "Converts an input value to a shape value.")
-    public ToCartesianShape(Source source, @Param(name = "v", type = { "cartesian_shape", "keyword", "text" }) Expression field) {
+    public ToCartesianShape(
+        Source source,
+        @Param(name = "v", type = { "cartesian_point", "cartesian_shape", "keyword", "text" }) Expression field
+    ) {
         super(source, field);
     }
 

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

@@ -19,6 +19,7 @@ import org.elasticsearch.xpack.ql.type.DataType;
 import java.util.List;
 import java.util.Map;
 
+import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.GEO_POINT;
 import static org.elasticsearch.xpack.esql.type.EsqlDataTypes.GEO_SHAPE;
 import static org.elasticsearch.xpack.ql.type.DataTypes.KEYWORD;
 import static org.elasticsearch.xpack.ql.type.DataTypes.TEXT;
@@ -27,13 +28,14 @@ import static org.elasticsearch.xpack.ql.util.SpatialCoordinateTypes.GEO;
 public class ToGeoShape extends AbstractConvertFunction {
 
     private static final Map<DataType, BuildFactory> EVALUATORS = Map.ofEntries(
+        Map.entry(GEO_POINT, (fieldEval, source) -> fieldEval),
         Map.entry(GEO_SHAPE, (fieldEval, source) -> fieldEval),
         Map.entry(KEYWORD, ToGeoShapeFromStringEvaluator.Factory::new),
         Map.entry(TEXT, ToGeoShapeFromStringEvaluator.Factory::new)
     );
 
     @FunctionInfo(returnType = "geo_shape", description = "Converts an input value to a geo_shape value.")
-    public ToGeoShape(Source source, @Param(name = "v", type = { "geo_shape", "keyword", "text" }) Expression field) {
+    public ToGeoShape(Source source, @Param(name = "v", type = { "geo_point", "geo_shape", "keyword", "text" }) Expression field) {
         super(source, field);
     }
 

+ 8 - 2
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java

@@ -912,8 +912,14 @@ public abstract class AbstractFunctionTestCase extends ESTestCase {
         ),
         Map.entry(Set.of(EsqlDataTypes.GEO_POINT, DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL), "geo_point or string"),
         Map.entry(Set.of(EsqlDataTypes.CARTESIAN_POINT, DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL), "cartesian_point or string"),
-        Map.entry(Set.of(EsqlDataTypes.GEO_SHAPE, DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL), "geo_shape or string"),
-        Map.entry(Set.of(EsqlDataTypes.CARTESIAN_SHAPE, DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL), "cartesian_shape or string")
+        Map.entry(
+            Set.of(EsqlDataTypes.GEO_POINT, EsqlDataTypes.GEO_SHAPE, DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL),
+            "geo_point or geo_shape or string"
+        ),
+        Map.entry(
+            Set.of(EsqlDataTypes.CARTESIAN_POINT, EsqlDataTypes.CARTESIAN_SHAPE, DataTypes.KEYWORD, DataTypes.TEXT, DataTypes.NULL),
+            "cartesian_point or cartesian_shape or string"
+        )
     );
 
     // TODO: generate this message dynamically, a la AbstractConvertFunction#supportedTypesNames()?

+ 2 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToCartesianShapeTests.java

@@ -38,8 +38,9 @@ public class ToCartesianShapeTests extends AbstractFunctionTestCase {
         final Function<String, String> evaluatorName = s -> "ToCartesianShape" + s + "Evaluator[field=" + attribute + "]";
         final List<TestCaseSupplier> suppliers = new ArrayList<>();
 
+        TestCaseSupplier.forUnaryCartesianPoint(suppliers, attribute, EsqlDataTypes.CARTESIAN_SHAPE, v -> v, List.of());
         TestCaseSupplier.forUnaryCartesianShape(suppliers, attribute, EsqlDataTypes.CARTESIAN_SHAPE, v -> v, List.of());
-        // random strings that don't look like a geo point
+        // random strings that don't look like a cartesian shape
         TestCaseSupplier.forUnaryStrings(
             suppliers,
             evaluatorName.apply("FromString"),

+ 2 - 1
x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToGeoShapeTests.java

@@ -38,8 +38,9 @@ public class ToGeoShapeTests extends AbstractFunctionTestCase {
         final Function<String, String> evaluatorName = s -> "ToGeoShape" + s + "Evaluator[field=" + attribute + "]";
         final List<TestCaseSupplier> suppliers = new ArrayList<>();
 
+        TestCaseSupplier.forUnaryGeoPoint(suppliers, attribute, EsqlDataTypes.GEO_SHAPE, v -> v, List.of());
         TestCaseSupplier.forUnaryGeoShape(suppliers, attribute, EsqlDataTypes.GEO_SHAPE, v -> v, List.of());
-        // random strings that don't look like a geo point
+        // random strings that don't look like a geo shape
         TestCaseSupplier.forUnaryStrings(
             suppliers,
             evaluatorName.apply("FromString"),