Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hugegraph-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Implementation-Version>0.36.0.0</Implementation-Version>
<Implementation-Version>0.37.0.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -82,7 +84,7 @@ public String get(@Context GraphManager manager,
ShortestPathTraverser traverser = new ShortestPathTraverser(g);
List<Id> path = traverser.shortestPath(sourceId, targetId, dir,
edgeLabel, depth, degree,
capacity);
skipDegree, capacity);
return manager.serializer(g).writeIds("path", path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,21 +43,22 @@ public ShortestPathTraverser(HugeGraph graph) {

public List<Id> 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);
}

Id labelId = this.getEdgeLabelId(label);
Traverser traverser = new Traverser(sourceV, targetV, dir, labelId,
degree, capacity);
degree, skipDegree, capacity);
List<Id> path;
while (true) {
// Found, reach max depth or reach capacity, stop searching
Expand All @@ -73,6 +76,25 @@ public List<Id> 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
Expand All @@ -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;
}
Expand All @@ -101,16 +125,21 @@ public Traverser(Id sourceV, Id targetV, Directions dir,
*/
public List<Id> forward() {
Map<Id, Node> newVertices = newMap();
long degree = this.skipDegree > 0L ? this.skipDegree : this.degree;
// Traversal vertices of previous level
for (Node v : this.sources.values()) {
Iterator<Edge> 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));
}

Expand Down Expand Up @@ -140,17 +169,22 @@ public List<Id> forward() {
*/
public List<Id> backward() {
Map<Id, Node> 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<Edge> 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));
}

Expand All @@ -174,5 +208,30 @@ public List<Id> backward() {

return PATH_NONE;
}

private Iterator<Edge> skipSuperNodeIfNeeded(Iterator<Edge> edges) {
if (this.skipDegree <= 0L) {
return edges;
}
List<Edge> 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<Edge> edges = edgesOfVertex(vertex, direction,
this.label, this.skipDegree);
return IteratorUtils.count(edges) >= this.skipDegree;
}
}
}