diff --git a/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledConversionUtils.java b/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledConversionUtils.java index 227a10f2fe8e5..aeba512796bcc 100644 --- a/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledConversionUtils.java +++ b/community/cypher/cypher/src/main/java/org/neo4j/cypher/internal/codegen/CompiledConversionUtils.java @@ -49,6 +49,9 @@ import org.neo4j.values.SequenceValue; import org.neo4j.values.storable.ArrayValue; import org.neo4j.values.storable.BooleanValue; +import org.neo4j.values.storable.DurationValue; +import org.neo4j.values.storable.PointValue; +import org.neo4j.values.storable.TemporalValue; import org.neo4j.values.storable.Values; import org.neo4j.values.virtual.ListValue; import org.neo4j.values.virtual.MapValue; @@ -651,6 +654,19 @@ public static Object mapGetProperty( Object object, String key ) Map map = (Map) object; return map.get( key ); } + if ( object instanceof TemporalValue ) + { + return ((TemporalValue) object).get( key ); + } + if ( object instanceof DurationValue ) + { + return ((DurationValue) object).get( key ); + } + if ( object instanceof PointValue ) + { + return ((PointValue) object).get( key ); + } + // NOTE: VirtualNodeValue and VirtualRelationshipValue will fall through to here // To handle these we would need specialized cursor code throw new CypherTypeException( String.format( "Type mismatch: expected a map but was %s", object ), null ); diff --git a/community/values/src/main/java/org/neo4j/values/storable/PointValue.java b/community/values/src/main/java/org/neo4j/values/storable/PointValue.java index d9c8b07efe9d6..278cfee1daa73 100644 --- a/community/values/src/main/java/org/neo4j/values/storable/PointValue.java +++ b/community/values/src/main/java/org/neo4j/values/storable/PointValue.java @@ -418,7 +418,7 @@ else if ( crs == CoordinateReferenceSystem.WGS84_3D ) /** * For accessors from cypher. */ - public AnyValue get( String fieldName ) + public Value get( String fieldName ) { switch ( fieldName.toLowerCase() ) { diff --git a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala index e805325df8e31..64cca072d808b 100644 --- a/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala +++ b/enterprise/cypher/acceptance-spec-suite/src/test/scala/org/neo4j/internal/cypher/acceptance/SpatialFunctionsAcceptanceTest.scala @@ -588,35 +588,54 @@ class SpatialFunctionsAcceptanceTest extends ExecutionEngineFunSuite with Cypher failWithError(equalityConfig + Configs.Procs, query, Seq("Collections containing point values with different CRS can not be stored in properties.")) } - // FIXME accessors should also work in compiled runtime. Split query into two so that compiled picks it up. test("accessors on 2D cartesian points") { - val result = executeWith(latestPointConfig, "WITH point({x: 1, y: 2}) AS p RETURN p.x, p.y, p.crs, p.srid") - result.toList should be(List(Map("p.x" -> 1.0, "p.y" -> 2.0, "p.crs" -> "cartesian", "p.srid" -> 7203))) + // given + graph.execute("CREATE (:P {p : point({x: 1, y: 2})})") + + // when + val result = executeWith(Configs.All - Configs.OldAndRule, "MATCH (n:P) WITH n.p AS p RETURN p.x, p.y, p.crs, p.srid") - failWithError(latestPointConfig + Configs.Procs, "WITH point({x: 1, y: 2}) AS p RETURN p.latitude", Seq("Field: latitude is not available")) - failWithError(latestPointConfig + Configs.Procs, "WITH point({x: 1, y: 2}) AS p RETURN p.z", Seq("Field: z is not available")) + // then + result.toList should be(List(Map("p.x" -> 1.0, "p.y" -> 2.0, "p.crs" -> "cartesian", "p.srid" -> 7203))) + failWithError(Configs.All - Configs.OldAndRule + Configs.Procs, "MATCH (n:P) WITH n.p AS p RETURN p.latitude", Seq("Field: latitude is not available")) + failWithError(Configs.All - Configs.OldAndRule + Configs.Procs, "MATCH (n:P) WITH n.p AS p RETURN p.z", Seq("Field: z is not available")) } test("accessors on 3D cartesian points") { - val result = executeWith(latestPointConfig, "WITH point({x: 1, y: 2, z:3}) AS p RETURN p.x, p.y, p.z, p.crs, p.srid") - result.toList should be(List(Map("p.x" -> 1.0, "p.y" -> 2.0, "p.z" -> 3.0, "p.crs" -> "cartesian-3d", "p.srid" -> 9157))) + // given + graph.execute("CREATE (:P {p : point({x: 1, y: 2, z:3})})") + + // when + val result = executeWith(Configs.All - Configs.OldAndRule, "MATCH (n:P) WITH n.p AS p RETURN p.x, p.y, p.z, p.crs, p.srid") - failWithError(latestPointConfig + Configs.Procs, "WITH point({x: 1, y: 2, z:3}) AS p RETURN p.latitude", Seq("Field: latitude is not available")) + // then + result.toList should be(List(Map("p.x" -> 1.0, "p.y" -> 2.0, "p.z" -> 3.0, "p.crs" -> "cartesian-3d", "p.srid" -> 9157))) + failWithError(Configs.All - Configs.OldAndRule + Configs.Procs, "MATCH (n:P) WITH n.p AS p RETURN p.latitude", Seq("Field: latitude is not available")) } test("accessors on 2D geographic points") { - val result = executeWith(latestPointConfig, "WITH point({longitude: 1, latitude: 2}) AS p RETURN p.longitude, p.latitude, p.crs, p.x, p.y, p.srid") - result.toList should be(List(Map("p.longitude" -> 1.0, "p.latitude" -> 2.0, "p.crs" -> "wgs-84", "p.x" -> 1.0, "p.y" -> 2.0, "p.srid" -> 4326))) + // given + graph.execute("CREATE (:P {p : point({longitude: 1, latitude: 2})})") + + // when + val result = executeWith(Configs.All - Configs.OldAndRule, "MATCH (n:P) WITH n.p AS p RETURN p.longitude, p.latitude, p.crs, p.x, p.y, p.srid") - failWithError(latestPointConfig + Configs.Procs, "WITH point({x: 1, y: 2}) AS p RETURN p.height", Seq("Field: height is not available")) - failWithError(latestPointConfig + Configs.Procs, "WITH point({x: 1, y: 2}) AS p RETURN p.z", Seq("Field: z is not available")) + // then + result.toList should be(List(Map("p.longitude" -> 1.0, "p.latitude" -> 2.0, "p.crs" -> "wgs-84", "p.x" -> 1.0, "p.y" -> 2.0, "p.srid" -> 4326))) + failWithError(Configs.All - Configs.OldAndRule + Configs.Procs, "MATCH (n:P) WITH n.p AS p RETURN p.height", Seq("Field: height is not available")) + failWithError(Configs.All - Configs.OldAndRule + Configs.Procs, "MATCH (n:P) WITH n.p AS p RETURN p.z", Seq("Field: z is not available")) } test("accessors on 3D geographic points") { - val result = executeWith(latestPointConfig, - "WITH point({longitude: 1, latitude: 2, height:3}) AS p RETURN p.longitude, p.latitude, p.height, p.crs, p.x, p.y, p.z, p.srid") + // given + graph.execute("CREATE (:P {p : point({longitude: 1, latitude: 2, height:3})})") + + // when + val result = executeWith(Configs.All - Configs.OldAndRule, + "MATCH (n:P) WITH n.p AS p RETURN p.longitude, p.latitude, p.height, p.crs, p.x, p.y, p.z, p.srid") + + // then result.toList should be(List(Map("p.longitude" -> 1.0, "p.latitude" -> 2.0, "p.height" -> 3.0, "p.crs" -> "wgs-84-3d", "p.srid" -> 4979, "p.x" -> 1.0, "p.y" -> 2.0, "p.z" -> 3.0))) } - }