Skip to content

Commit

Permalink
moved aStartWithPoint to apoc-full
Browse files Browse the repository at this point in the history
  • Loading branch information
vga91 committed Apr 20, 2022
1 parent 48d18de commit e4e6854
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 54 deletions.
20 changes: 1 addition & 19 deletions core/src/main/java/apoc/algo/PathFinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,6 @@ private double[] getCoordinates(Node node) {
@Context
public Transaction tx;

@Procedure
@Description("apoc.algo.aStarWithPoint(startNode, endNode, 'relTypesAndDirs', 'distance','pointProp') - " +
"equivalent to apoc.algo.aStar but accept a Point type as a pointProperty instead of Number types as latitude and longitude properties")
public Stream<WeightedPathResult> aStarWithPoint(
@Name("startNode") Node startNode,
@Name("endNode") Node endNode,
@Name("relationshipTypesAndDirections") String relTypesAndDirs,
@Name("weightPropertyName") String weightPropertyName,
@Name("pointPropertyName") String pointPropertyName) {

PathFinder<WeightedPath> algo = GraphAlgoFactory.aStar(
new BasicEvaluationContext(tx, db),
buildPathExpander(relTypesAndDirs),
CommonEvaluators.doubleCostEvaluator(weightPropertyName),
new GeoEstimateEvaluatorPointCustom(pointPropertyName));
return WeightedPathResult.streamWeightedPathResult(startNode, endNode, algo);
}

@Procedure
@Description("apoc.algo.aStar(startNode, endNode, 'KNOWS|<WORKS_WITH|IS_MANAGER_OF>', 'distance','lat','lon') " +
"YIELD path, weight - run A* with relationship property name as cost function")
Expand Down Expand Up @@ -198,7 +180,7 @@ public Stream<WeightedPathResult> dijkstraWithDefaultWeight(
return WeightedPathResult.streamWeightedPathResult(startNode, endNode, algo);
}

private PathExpander<Double> buildPathExpander(String relationshipsAndDirections) {
public static PathExpander<Double> buildPathExpander(String relationshipsAndDirections) {
PathExpanderBuilder builder = PathExpanderBuilder.empty();
for (Pair<RelationshipType, Direction> pair : RelationshipTypeAndDirections
.parse(relationshipsAndDirections)) {
Expand Down
37 changes: 3 additions & 34 deletions core/src/test/java/apoc/algo/PathFindingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import java.util.List;
import java.util.Map;

import static apoc.algo.AlgoUtil.SETUP_GEO;
import static apoc.algo.AlgoUtil.assertAStarResult;
import static apoc.util.TestUtil.testCall;
import static apoc.util.TestUtil.testResult;
import static apoc.util.Util.map;
Expand Down Expand Up @@ -46,15 +48,6 @@ public class PathFindingTest {
"(b)-[:ROAD {d:20}]->(c), " +
"(c)-[:ROAD {d:30}]->(d), " +
"(a)-[:ROAD {d:20}]->(c) ";
private static final String SETUP_GEO = "CREATE (b:City {name:'Berlin', coords: point({latitude:52.52464,longitude:13.40514}), lat:52.52464,lon:13.40514})\n" +
"CREATE (m:City {name:'München', coords: point({latitude:48.1374,longitude:11.5755, height: 1}), lat:48.1374,lon:11.5755})\n" +
"CREATE (f:City {name:'Frankfurt',coords: point({latitude:50.1167,longitude:8.68333, height: 1}), lat:50.1167,lon:8.68333})\n" +
"CREATE (h:City {name:'Hamburg', coords: point({latitude:53.554423,longitude:9.994583, height: 1}), lat:53.554423,lon:9.994583})\n" +
"CREATE (b)-[:DIRECT {dist:255.64*1000}]->(h)\n" +
"CREATE (b)-[:DIRECT {dist:504.47*1000}]->(m)\n" +
"CREATE (b)-[:DIRECT {dist:424.12*1000}]->(f)\n" +
"CREATE (f)-[:DIRECT {dist:304.28*1000}]->(m)\n" +
"CREATE (f)-[:DIRECT {dist:393.15*1000}]->(h)";

@Rule
public DbmsRule db = new ImpermanentDbmsRule();
Expand All @@ -76,17 +69,6 @@ public void testAStar() {
);
}

@Test
public void testAStarWithPoint() {
db.executeTransactionally(SETUP_GEO);
testResult(db,
"MATCH (from:City {name:'München'}), (to:City {name:'Hamburg'}) " +
"CALL apoc.algo.aStarWithPoint(from, to, 'DIRECT', 'dist', 'coords') yield path, weight " +
"RETURN path, weight" ,
this::assertAStarResult
);
}

@Test
public void testAStarConfig() {
db.executeTransactionally(SETUP_GEO);
Expand All @@ -105,23 +87,10 @@ public void testAStarConfigWithPoint() {
"MATCH (from:City {name:'München'}), (to:City {name:'Hamburg'}) " +
"CALL apoc.algo.aStarConfig(from, to, 'DIRECT', {pointPropName:'coords', weight:'dist', default:100}) yield path, weight " +
"RETURN path, weight" ,
this::assertAStarResult
AlgoUtil::assertAStarResult
);
}

private void assertAStarResult(Result r) {
assertEquals(true, r.hasNext());
Map<String, Object> row = r.next();
assertEquals(697, ((Number)row.get("weight")).intValue()/1000) ;
Path path = (Path) row.get("path");
assertEquals(2, path.length()) ; // 3nodes, 2 rels
List<Node> nodes = Iterables.asList(path.nodes());
assertEquals("München", nodes.get(0).getProperty("name")) ;
assertEquals("Frankfurt", nodes.get(1).getProperty("name")) ;
assertEquals("Hamburg", nodes.get(2).getProperty("name")) ;
assertEquals(false,r.hasNext());
}

@Test
public void testDijkstra() {
db.executeTransactionally(SETUP_SIMPLE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ APOC exposes some built in path-finding functions that Neo4j brings along.
| apoc.algo.dijkstra(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 'distance') YIELD path, weight | run dijkstra with relationship property name as cost function
| apoc.algo.dijkstraWithDefaultWeight(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 'distance', 10) YIELD path, weight | run dijkstra with relationship property name as cost function and a default weight if the property does not exist
| apoc.algo.aStar(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 'distance','lat','lon') YIELD path, weight | run A* with relationship property name as cost function
| apoc.algo.aStarWithPoint(startNode, endNode, 'relTypesAndDirs', 'weightPropertyName','pointPropertyName') - equivalent to apoc.algo.aStar but accept a Point type as a pointProperty instead of Number types as latitude and longitude properties
| label:apoc-full[] apoc.algo.aStarWithPoint(startNode, endNode, 'relTypesAndDirs', 'weightPropertyName','pointPropertyName') - equivalent to apoc.algo.aStar but accept a Point type as a pointProperty instead of Number types as latitude and longitude properties
| apoc.algo.aStarConfig(startNode, endNode, 'KNOWS|<WORKS_WITH|IS_MANAGER_OF>', {weight:'dist',default:10, x:'lon',y:'lat',pointPropName:'point'}) YIELD path, weight - run A* with relationship property name as cost function
| apoc.algo.allSimplePaths(startNode, endNode, 'KNOWS\|<WORKS_WITH\|IS_MANAGER_OF>', 5) YIELD path, weight | run allSimplePaths with relationships given and maxNodes
| apoc.stats.degrees(relTypesDirections) yield type, direction, total, min, max, mean, p50, p75, p90, p95, p99, p999 | compute degree distribution in parallel
Expand Down
49 changes: 49 additions & 0 deletions full/src/main/java/apoc/algo/PathFindingFull.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package apoc.algo;

import apoc.Extended;
import apoc.result.WeightedPathResult;
import org.neo4j.graphalgo.BasicEvaluationContext;
import org.neo4j.graphalgo.CommonEvaluators;
import org.neo4j.graphalgo.GraphAlgoFactory;
import org.neo4j.graphalgo.PathFinder;
import org.neo4j.graphalgo.WeightedPath;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;

import java.util.stream.Stream;

import static apoc.algo.PathFinding.buildPathExpander;

@Extended
public class PathFindingFull {

@Context
public GraphDatabaseService db;

@Context
public Transaction tx;

@Procedure
@Description("apoc.algo.aStarWithPoint(startNode, endNode, 'relTypesAndDirs', 'distance','pointProp') - " +
"equivalent to apoc.algo.aStar but accept a Point type as a pointProperty instead of Number types as latitude and longitude properties")
public Stream<WeightedPathResult> aStarWithPoint(
@Name("startNode") Node startNode,
@Name("endNode") Node endNode,
@Name("relationshipTypesAndDirections") String relTypesAndDirs,
@Name("weightPropertyName") String weightPropertyName,
@Name("pointPropertyName") String pointPropertyName) {

PathFinder<WeightedPath> algo = GraphAlgoFactory.aStar(
new BasicEvaluationContext(tx, db),
buildPathExpander(relTypesAndDirs),
CommonEvaluators.doubleCostEvaluator(weightPropertyName),
new PathFinding.GeoEstimateEvaluatorPointCustom(pointPropertyName));
return WeightedPathResult.streamWeightedPathResult(startNode, endNode, algo);
}

}
1 change: 1 addition & 0 deletions full/src/main/resources/extended.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
apoc.algo.aStarWithPoint
apoc.bolt.execute
apoc.bolt.load
apoc.bolt.load.fromLocal
Expand Down
34 changes: 34 additions & 0 deletions full/src/test/java/apoc/algo/PathFindingFullTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package apoc.algo;

import apoc.util.TestUtil;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.neo4j.test.rule.DbmsRule;
import org.neo4j.test.rule.ImpermanentDbmsRule;

import static apoc.algo.AlgoUtil.SETUP_GEO;
import static apoc.util.TestUtil.testResult;

public class PathFindingFullTest {

@ClassRule
public static DbmsRule db = new ImpermanentDbmsRule();


@BeforeClass
public static void setUp() throws Exception {
TestUtil.registerProcedure(db, PathFindingFull.class);
}

@Test
public void testAStarWithPoint() {
db.executeTransactionally(SETUP_GEO);
testResult(db,
"MATCH (from:City {name:'München'}), (to:City {name:'Hamburg'}) " +
"CALL apoc.algo.aStarWithPoint(from, to, 'DIRECT', 'dist', 'coords') yield path, weight " +
"RETURN path, weight" ,
AlgoUtil::assertAStarResult
);
}
}
37 changes: 37 additions & 0 deletions test-utils/src/main/java/apoc/algo/AlgoUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package apoc.algo;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Result;
import org.neo4j.internal.helpers.collection.Iterables;

import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;

public class AlgoUtil {
public static final String SETUP_GEO = "CREATE (b:City {name:'Berlin', coords: point({latitude:52.52464,longitude:13.40514}), lat:52.52464,lon:13.40514})\n" +
"CREATE (m:City {name:'München', coords: point({latitude:48.1374,longitude:11.5755, height: 1}), lat:48.1374,lon:11.5755})\n" +
"CREATE (f:City {name:'Frankfurt',coords: point({latitude:50.1167,longitude:8.68333, height: 1}), lat:50.1167,lon:8.68333})\n" +
"CREATE (h:City {name:'Hamburg', coords: point({latitude:53.554423,longitude:9.994583, height: 1}), lat:53.554423,lon:9.994583})\n" +
"CREATE (b)-[:DIRECT {dist:255.64*1000}]->(h)\n" +
"CREATE (b)-[:DIRECT {dist:504.47*1000}]->(m)\n" +
"CREATE (b)-[:DIRECT {dist:424.12*1000}]->(f)\n" +
"CREATE (f)-[:DIRECT {dist:304.28*1000}]->(m)\n" +
"CREATE (f)-[:DIRECT {dist:393.15*1000}]->(h)";


public static void assertAStarResult(Result r) {
assertEquals(true, r.hasNext());
Map<String, Object> row = r.next();
assertEquals(697, ((Number)row.get("weight")).intValue()/1000) ;
Path path = (Path) row.get("path");
assertEquals(2, path.length()) ; // 3nodes, 2 rels
List<Node> nodes = Iterables.asList(path.nodes());
assertEquals("München", nodes.get(0).getProperty("name")) ;
assertEquals("Frankfurt", nodes.get(1).getProperty("name")) ;
assertEquals("Hamburg", nodes.get(2).getProperty("name")) ;
assertEquals(false,r.hasNext());
}
}

0 comments on commit e4e6854

Please sign in to comment.