Skip to content

Commit

Permalink
Add sourceInRing args for rings
Browse files Browse the repository at this point in the history
fixed: #523

Change-Id: If8c5cc4d8678f847ce4a5f0388e56aae465d3ea6
  • Loading branch information
zhoney committed May 28, 2019
1 parent 9188fb2 commit cb48d13
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 28 deletions.
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.38.0.0</Implementation-Version>
<Implementation-Version>0.39.0.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public String get(@Context GraphManager manager,
@DefaultValue(DEFAULT_PATHS_LIMIT) long limit) {
LOG.debug("Graph [{}] get rays paths from '{}' with " +
"direction '{}', edge label '{}', max depth '{}', " +
"max degree '{}' and limit '{}'",
"max degree '{}', capacity '{}' and limit '{}'",
graph, sourceV, direction, edgeLabel, depth, degree, limit);

Id source = VertexAPI.checkAndParseVertexId(sourceV);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public String get(@Context GraphManager manager,
@QueryParam("direction") String direction,
@QueryParam("label") String edgeLabel,
@QueryParam("max_depth") int depth,
@QueryParam("source_in_ring")
@DefaultValue("true") boolean sourceInRing,
@QueryParam("max_degree")
@DefaultValue(DEFAULT_DEGREE) long degree,
@QueryParam("capacity")
Expand All @@ -72,7 +74,8 @@ public String get(@Context GraphManager manager,
@DefaultValue(DEFAULT_PATHS_LIMIT) long limit) {
LOG.debug("Graph [{}] get rings paths reachable from '{}' with " +
"direction '{}', edge label '{}', max depth '{}', " +
"max degree '{}' and limit '{}'",
"source in ring '{}', max degree '{}', capacity '{}' " +
"and limit '{}'",
graph, sourceV, direction, edgeLabel, depth, degree, limit);

Id source = VertexAPI.checkAndParseVertexId(sourceV);
Expand All @@ -82,8 +85,9 @@ public String get(@Context GraphManager manager,

SubGraphTraverser traverser = new SubGraphTraverser(g);
List<HugeTraverser.Path> paths = traverser.rings(source, dir, edgeLabel,
depth, degree,
capacity, limit);
depth, sourceInRing,
degree, capacity,
limit);
return manager.serializer(g).writePaths("rings", paths, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ public final class ApiVersion {
* [0.36] Issue-360: Support paging for scan api
* [0.37] Issue-391: Add skip_super_node for shortest path
* [0.38] Issue-274: Add personalrank and neighborrank RESTful API
* [0.39] Issue-523: Add sourceInRing args for rings RESTful API
*/

// The second parameter of Version.of() is for IDE running without JAR
public static final Version VERSION = Version.of(ApiVersion.class, "0.38");
public static final Version VERSION = Version.of(ApiVersion.class, "0.39");

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 @@ -27,6 +27,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.ws.rs.core.MultivaluedHashMap;
Expand Down Expand Up @@ -379,7 +380,8 @@ public boolean equals(Object object) {
return false;
}
Node other = (Node) object;
return this.id.equals(other.id);
return this.id.equals(other.id) &&
Objects.equals(this.parent, other.parent);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package com.baidu.hugegraph.traversal.algorithm;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
Expand All @@ -28,6 +29,7 @@
import javax.ws.rs.core.MultivaluedMap;

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 @@ -44,18 +46,20 @@ public SubGraphTraverser(HugeGraph graph) {
public List<Path> rays(Id sourceV, Directions dir, String label,
int depth, long degree, long capacity, long limit) {
return this.subGraphPaths(sourceV, dir, label, depth, degree,
capacity, limit, false);
capacity, limit, false, false);
}

public List<Path> rings(Id sourceV, Directions dir, String label,
int depth, long degree, long capacity, long limit) {
public List<Path> rings(Id sourceV, Directions dir, String label, int depth,
boolean sourceInRing, long degree, long capacity,
long limit) {
return this.subGraphPaths(sourceV, dir, label, depth, degree,
capacity, limit, true);
capacity, limit, true, sourceInRing);
}

private List<Path> subGraphPaths(Id sourceV, Directions dir, String label,
int depth, long degree, long capacity,
long limit, boolean rings) {
long limit, boolean rings,
boolean sourceInRing) {
E.checkNotNull(sourceV, "source vertex id");
E.checkNotNull(dir, "direction");
checkPositive(depth, "max depth");
Expand All @@ -64,41 +68,61 @@ private List<Path> subGraphPaths(Id sourceV, Directions dir, String label,
checkLimit(limit);

Id labelId = this.getEdgeLabelId(label);
Traverser traverser = new Traverser(sourceV, labelId, degree,
capacity, limit, rings);
Traverser traverser = new Traverser(sourceV, labelId, depth, degree,
capacity, limit, rings,
sourceInRing);
List<Path> paths = new ArrayList<>();
while (true) {
paths.addAll(traverser.forward(dir));
boolean reachDepth = rings ? --depth <= 0 : depth-- <= 0;
if (reachDepth || traverser.reachLimit() ||
if (--depth <= 0 || traverser.reachLimit() ||
traverser.finished()) {
break;
}
}
return paths;
}

private static boolean multiEdges(List<Edge> edges, Id target) {
int count = 0;
for (Edge edge : edges) {
if (((HugeEdge) edge).id().otherVertexId().equals(target)) {
if (++count >= 2) {
return true;
}
}
}
assert count == 1;
return false;
}

private class Traverser {

private final Id source;
private MultivaluedMap<Id, Node> sources = newMultivalueMap();
private Set<Id> accessedVertices = newSet();

private final Id label;
private int depth;
private final long degree;
private final long capacity;
private final long limit;
private final boolean rings;
private final boolean sourceInRing;
private long pathCount;

public Traverser(Id sourceV, Id label, long degree,
long capacity, long limit, boolean rings) {
public Traverser(Id sourceV, Id label, int depth, long degree,
long capacity, long limit, boolean rings,
boolean sourceInRing) {
this.source = sourceV;
this.sources.add(sourceV, new Node(sourceV));
this.accessedVertices.add(sourceV);
this.label = label;
this.depth = depth;
this.degree = degree;
this.capacity = capacity;
this.limit = limit;
this.rings = rings;
this.sourceInRing = sourceInRing;
this.pathCount = 0L;
}

Expand All @@ -112,7 +136,9 @@ public List<Path> forward(Directions direction) {
// Traversal vertices of previous level
for (Map.Entry<Id, List<Node>> entry : this.sources.entrySet()) {
Id vid = entry.getKey();
edges = edgesOfVertex(vid, direction, this.label, this.degree);
List<Edge> edgeList = IteratorUtils.list(edgesOfVertex(
vid, direction, this.label, this.degree));
edges = edgeList.iterator();

if (!edges.hasNext()) {
// Reach the end, rays found
Expand All @@ -128,33 +154,78 @@ public List<Path> forward(Directions direction) {
}
}
}

int neighborCount = 0;
Set<Id> currentNeighbors = new HashSet<>();
while (edges.hasNext()) {
neighborCount++;
HugeEdge edge = (HugeEdge) edges.next();
Id target = edge.id().otherVertexId();
// Avoid dedup path
if (currentNeighbors.contains(target)) {
continue;
}
currentNeighbors.add(target);
this.accessedVertices.add(target);
for (Node n : entry.getValue()) {
if (!n.contains(target)) {
for (Node node : entry.getValue()) {
// No ring, continue
if (!node.contains(target)) {
// Add node to next start-nodes
newVertices.add(target, new Node(target, n));
newVertices.add(target, new Node(target, node));
continue;
}
// Rings found and expect rings
if (this.rings) {
assert n.contains(target);
List<Id> prePath = n.path();
prePath.add(target);
paths.add(new Path(null, prePath));

// Rays found if fake ring like:
// path is pattern: A->B<-A && A is only neighbor of B
if (!this.rings && target.equals(node.parent().id()) &&
neighborCount == 1 && !edges.hasNext() &&
direction == Directions.BOTH) {
paths.add(new Path(null, node.path()));
this.pathCount++;
if (reachLimit()) {
return paths;
}
}

// Actual rings found
if (this.rings) {
boolean ringsFound = false;
// 1. sourceInRing is false, or
// 2. sourceInRing is true and target == source
if (!sourceInRing || target.equals(this.source)) {
if (!target.equals(node.parent().id())) {
ringsFound = true;
} else if (direction != Directions.BOTH) {
ringsFound = true;
} else if (multiEdges(edgeList, target)) {
ringsFound = true;
}
}

if (ringsFound) {
List<Id> path = node.path();
path.add(target);
paths.add(new Path(null, path));
this.pathCount++;
if (reachLimit()) {
return paths;
}
}
}
}
}
}
// Re-init sources
this.sources = newVertices;

if (!rings && --this.depth <= 0) {
for (List<Node> list : newVertices.values()) {
for (Node n : list) {
paths.add(new Path(null, n.path()));
}
}
}

return paths;
}

Expand Down

0 comments on commit cb48d13

Please sign in to comment.