From 7164ba75563cbac159c4ad0e383084c267c15ed0 Mon Sep 17 00:00:00 2001 From: Rafael Biehler Date: Mon, 13 Jan 2025 14:54:09 +0100 Subject: [PATCH] NeoUtil: Handle conversion of point function to and from Neo4j --- .../db/adapter/neo4j/util/NeoUtil.java | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoUtil.java b/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoUtil.java index a2e16464f1..82e2992068 100644 --- a/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoUtil.java +++ b/plugins/neo4j-adapter/src/main/java/org/polypheny/db/adapter/neo4j/util/NeoUtil.java @@ -31,14 +31,19 @@ import lombok.NonNull; import org.apache.calcite.linq4j.function.Function1; import org.apache.commons.lang3.NotImplementedException; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.PrecisionModel; import org.neo4j.driver.Record; import org.neo4j.driver.Value; import org.neo4j.driver.internal.value.FloatValue; import org.neo4j.driver.internal.value.IntegerValue; import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.internal.value.PointValue; import org.neo4j.driver.internal.value.StringValue; import org.neo4j.driver.types.Node; import org.neo4j.driver.types.Path; +import org.neo4j.driver.types.Point; import org.neo4j.driver.types.Relationship; import org.polypheny.db.adapter.neo4j.types.NestedPolyType; import org.polypheny.db.algebra.constant.Kind; @@ -121,13 +126,35 @@ static Function1 getUnnullableTypeFunction( NestedPolyType typ case NODE -> o -> asPolyNode( o.asNode() ); case EDGE -> o -> asPolyEdge( o.asRelationship() ); case PATH -> o -> asPolyPath( o.asPath() ); - case GEOMETRY -> o -> PolyGeometry.of( o.asString() ); + case GEOMETRY -> NeoUtil::asPolyGeometry; default -> throw new GenericRuntimeException( String.format( "Object of type %s was not transformable.", type ) ); }; } + static PolyGeometry asPolyGeometry( Value value ){ + if ( value instanceof PointValue pointValue ) { + Point point = pointValue.asPoint(); + + // These are the only real SRIDs that Neo4j uses, the others are only used + // internally for the cartesian 2D / 3D case, which we will represent as 0. + int srid = 0; + if (point.srid() == 4326 || point.srid() == 4979) { + srid = point.srid(); + } + + GeometryFactory geometryFactory = new GeometryFactory( new PrecisionModel(), srid ); + Coordinate coordinate = new Coordinate(point.x(), point.y()); + if (!Double.isNaN( point.z() )){ + coordinate.setZ( point.z() ); + } + return PolyGeometry.of(geometryFactory.createPoint( coordinate )); + } + + throw new GenericRuntimeException( String.format( "Could not transform object of type %s to PolyGeometry", value.type() ) ); + } + static PolyPath asPolyPath( Path path ) { Iterator nodeIter = path.nodes().iterator(); Iterator edgeIter = path.relationships().iterator(); @@ -343,6 +370,7 @@ static Function1, String> getOpAsNeo( OperatorName operatorName, Li case AVG -> o -> String.format( "avg(%s)", o.get( 0 ) ); case MIN -> o -> String.format( "min(%s)", o.get( 0 ) ); case MAX -> o -> String.format( "max(%s)", o.get( 0 ) ); + case CYPHER_POINT -> handlePoint( operatorName, operands, returnType ); default -> null; }; @@ -359,6 +387,26 @@ private static Function1, String> handleCast( List operand return o -> o.get( 0 ); } + static Function1, String> handlePoint( OperatorName operatorName, List operands, AlgDataType returnType ) { + // return point( { argName1: argValue1, argName2: argValue2 } ) + List arguments = new ArrayList<>(); + for (int i = 0; i < operands.size(); i += 2) { + RexNode argName = operands.get(i); + RexNode argValue = operands.get(i + 1); + + if (argName instanceof RexLiteral argNameLiteral && argValue instanceof RexLiteral argValueLiteral){ + if (argNameLiteral.value == null){ + // Unknown value, we are done. + break; + } + String arg = argNameLiteral.value.toString() + ":" + argValueLiteral.value.toString(); + arguments.add(arg); + } + } + + return o -> "point({" + String.join( ",", arguments) + "})"; + } + static Function1, String> handleDivide( OperatorName operatorName, List operands, AlgDataType returnType ) { if ( PolyType.APPROX_TYPES.contains( returnType.getPolyType() ) ) { return o -> String.format( "toFloat(%s) / %s", o.get( 0 ), o.get( 1 ) );