diff --git a/hugegraph-api/pom.xml b/hugegraph-api/pom.xml
index 98dc09a54e..8ea074ee5e 100644
--- a/hugegraph-api/pom.xml
+++ b/hugegraph-api/pom.xml
@@ -86,7 +86,7 @@
- 0.36.0.0
+ 0.37.0.0
diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/ShortestPathAPI.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/ShortestPathAPI.java
index 841a177436..c702e0e2dd 100644
--- a/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/ShortestPathAPI.java
+++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/api/traversers/ShortestPathAPI.java
@@ -41,11 +41,11 @@
import com.baidu.hugegraph.server.RestServer;
import com.baidu.hugegraph.traversal.algorithm.ShortestPathTraverser;
import com.baidu.hugegraph.type.define.Directions;
+import com.baidu.hugegraph.util.E;
import com.baidu.hugegraph.util.Log;
import com.codahale.metrics.annotation.Timed;
-import static com.baidu.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
-import static com.baidu.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_DEGREE;
+import static com.baidu.hugegraph.traversal.algorithm.HugeTraverser.*;
@Path("graphs/{graph}/traversers/shortestpath")
@Singleton
@@ -65,13 +65,15 @@ public String get(@Context GraphManager manager,
@QueryParam("max_depth") int depth,
@QueryParam("max_degree")
@DefaultValue(DEFAULT_DEGREE) long degree,
+ @QueryParam("skip_degree")
+ @DefaultValue("0") long skipDegree,
@QueryParam("capacity")
@DefaultValue(DEFAULT_CAPACITY) long capacity) {
LOG.debug("Graph [{}] get shortest path from '{}', to '{}' with " +
"direction {}, edge label {}, max depth '{}', " +
- "max degree '{}' and capacity '{}'",
+ "max degree '{}', skipped degree '{}' and capacity '{}'",
graph, source, target, direction, edgeLabel, depth,
- degree, capacity);
+ degree, skipDegree, capacity);
Id sourceId = VertexAPI.checkAndParseVertexId(source);
Id targetId = VertexAPI.checkAndParseVertexId(target);
@@ -82,7 +84,7 @@ public String get(@Context GraphManager manager,
ShortestPathTraverser traverser = new ShortestPathTraverser(g);
List path = traverser.shortestPath(sourceId, targetId, dir,
edgeLabel, depth, degree,
- capacity);
+ skipDegree, capacity);
return manager.serializer(g).writeIds("path", path);
}
}
diff --git a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java
index a0dd5e71c6..ed51000a36 100644
--- a/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java
+++ b/hugegraph-api/src/main/java/com/baidu/hugegraph/version/ApiVersion.java
@@ -82,10 +82,11 @@ public final class ApiVersion {
* [0.34] Issue-307: Let VertexAPI use simplified property serializer
* [0.35] Issue-287: Support pagination when do index query
* [0.36] Issue-360: Support paging for scan api
+ * [0.37] Issue-391: Add skip_super_node for shortest path
*/
// The second parameter of Version.of() is for IDE running without JAR
- public static final Version VERSION = Version.of(ApiVersion.class, "0.36");
+ public static final Version VERSION = Version.of(ApiVersion.class, "0.37");
public static final void check() {
// Check version of hugegraph-core. Firstly do check from version 0.3
diff --git a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java
index f283808ffc..8bc692c1e4 100644
--- a/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java
+++ b/hugegraph-core/src/main/java/com/baidu/hugegraph/traversal/algorithm/ShortestPathTraverser.java
@@ -19,12 +19,14 @@
package com.baidu.hugegraph.traversal.algorithm;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import com.baidu.hugegraph.HugeGraph;
import com.baidu.hugegraph.backend.id.Id;
@@ -41,13 +43,14 @@ public ShortestPathTraverser(HugeGraph graph) {
public List shortestPath(Id sourceV, Id targetV, Directions dir,
String label, int depth, long degree,
- long capacity) {
+ long skipDegree, long capacity) {
E.checkNotNull(sourceV, "source vertex id");
E.checkNotNull(targetV, "target vertex id");
E.checkNotNull(dir, "direction");
checkPositive(depth, "max depth");
checkDegree(degree);
checkCapacity(capacity);
+ checkSkipDegree(skipDegree, degree, capacity);
if (sourceV.equals(targetV)) {
return ImmutableList.of(sourceV);
@@ -55,7 +58,7 @@ public List shortestPath(Id sourceV, Id targetV, Directions dir,
Id labelId = this.getEdgeLabelId(label);
Traverser traverser = new Traverser(sourceV, targetV, dir, labelId,
- degree, capacity);
+ degree, skipDegree, capacity);
List path;
while (true) {
// Found, reach max depth or reach capacity, stop searching
@@ -73,6 +76,25 @@ public List shortestPath(Id sourceV, Id targetV, Directions dir,
return path;
}
+ private static void checkSkipDegree(long skipDegree, long degree,
+ long capacity) {
+ E.checkArgument(skipDegree >= 0L,
+ "The skipped degree must be >= 0, but got '%s'",
+ skipDegree);
+ if (capacity != NO_LIMIT) {
+ E.checkArgument(degree != NO_LIMIT && degree < capacity,
+ "The degree must be < capacity");
+ E.checkArgument(skipDegree < capacity,
+ "The skipped degree must be < capacity");
+ }
+ if (skipDegree > 0L) {
+ E.checkArgument(degree != NO_LIMIT && skipDegree >= degree,
+ "The skipped degree must be >= degree, " +
+ "but got skipped degree '%s' and degree '%s'",
+ skipDegree, degree);
+ }
+ }
+
private class Traverser {
// TODO: change Map to Set to reduce memory cost
@@ -82,16 +104,18 @@ private class Traverser {
private final Directions direction;
private final Id label;
private final long degree;
+ private final long skipDegree;
private final long capacity;
private long size;
- public Traverser(Id sourceV, Id targetV, Directions dir,
- Id label, long degree, long capacity) {
+ public Traverser(Id sourceV, Id targetV, Directions dir, Id label,
+ long degree, long skipDegree, long capacity) {
this.sources.put(sourceV, new Node(sourceV));
this.targets.put(targetV, new Node(targetV));
this.direction = dir;
this.label = label;
this.degree = degree;
+ this.skipDegree = skipDegree;
this.capacity = capacity;
this.size = 0L;
}
@@ -101,16 +125,21 @@ public Traverser(Id sourceV, Id targetV, Directions dir,
*/
public List forward() {
Map newVertices = newMap();
+ long degree = this.skipDegree > 0L ? this.skipDegree : this.degree;
// Traversal vertices of previous level
for (Node v : this.sources.values()) {
Iterator edges = edgesOfVertex(v.id(), this.direction,
- this.label, this.degree);
+ this.label, degree);
+ edges = this.skipSuperNodeIfNeeded(edges);
while (edges.hasNext()) {
HugeEdge edge = (HugeEdge) edges.next();
Id target = edge.id().otherVertexId();
// If cross point exists, shortest path found, concat them
if (this.targets.containsKey(target)) {
+ if (this.superNode(target, this.direction)) {
+ continue;
+ }
return v.joinPath(this.targets.get(target));
}
@@ -140,17 +169,22 @@ public List forward() {
*/
public List backward() {
Map newVertices = newMap();
+ long degree = this.skipDegree > 0L ? this.skipDegree : this.degree;
Directions opposite = this.direction.opposite();
// Traversal vertices of previous level
for (Node v : this.targets.values()) {
Iterator edges = edgesOfVertex(v.id(), opposite,
- this.label, this.degree);
+ this.label, degree);
+ edges = this.skipSuperNodeIfNeeded(edges);
while (edges.hasNext()) {
HugeEdge edge = (HugeEdge) edges.next();
Id target = edge.id().otherVertexId();
// If cross point exists, shortest path found, concat them
if (this.sources.containsKey(target)) {
+ if (this.superNode(target, opposite)) {
+ continue;
+ }
return v.joinPath(this.sources.get(target));
}
@@ -174,5 +208,30 @@ public List backward() {
return PATH_NONE;
}
+
+ private Iterator skipSuperNodeIfNeeded(Iterator edges) {
+ if (this.skipDegree <= 0L) {
+ return edges;
+ }
+ List edgeList = new ArrayList<>();
+ for (int i = 1; edges.hasNext(); i++) {
+ if (i <= this.degree) {
+ edgeList.add(edges.next());
+ }
+ if (i >= this.skipDegree) {
+ return Collections.emptyIterator();
+ }
+ }
+ return edgeList.iterator();
+ }
+
+ private boolean superNode(Id vertex, Directions direction) {
+ if (this.skipDegree <= 0L) {
+ return false;
+ }
+ Iterator edges = edgesOfVertex(vertex, direction,
+ this.label, this.skipDegree);
+ return IteratorUtils.count(edges) >= this.skipDegree;
+ }
}
}