forked from hugegraph/hugegraph
Add source_in_ring args for rings API (#528)
fixed: #523 Change-Id: If8c5cc4d8678f847ce4a5f0388e56aae465d3ea6
This commit is contained in:
parent
f7a619f971
commit
8d23665a40
|
|
@ -86,7 +86,7 @@
|
|||
</addDefaultSpecificationEntries>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Implementation-Version>0.39.0.0</Implementation-Version>
|
||||
<Implementation-Version>0.40.0.0</Implementation-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
|
|
|||
|
|
@ -72,8 +72,9 @@ public class RaysAPI extends API {
|
|||
@DefaultValue(DEFAULT_PATHS_LIMIT) long limit) {
|
||||
LOG.debug("Graph [{}] get rays paths from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"max degree '{}' and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, degree, limit);
|
||||
"max degree '{}', capacity '{}' and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, degree,
|
||||
capacity, limit);
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ public class RingsAPI extends API {
|
|||
@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")
|
||||
|
|
@ -72,8 +74,10 @@ public class RingsAPI extends API {
|
|||
@DefaultValue(DEFAULT_PATHS_LIMIT) long limit) {
|
||||
LOG.debug("Graph [{}] get rings paths reachable from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"max degree '{}' and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, degree, limit);
|
||||
"source in ring '{}', max degree '{}', capacity '{}' " +
|
||||
"and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, sourceInRing,
|
||||
degree, capacity, limit);
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -82,8 +86,9 @@ public class RingsAPI extends API {
|
|||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,10 +87,11 @@ public final class ApiVersion {
|
|||
*
|
||||
* version 0.10:
|
||||
* [0.39] Issue-522: Add profile RESTful API
|
||||
* [0.40] Issue-523: Add source_in_ring 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.39");
|
||||
public static final Version VERSION = Version.of(ApiVersion.class, "0.40");
|
||||
|
||||
public static final void check() {
|
||||
// Check version of hugegraph-core. Firstly do check from version 0.3
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import java.util.HashSet;
|
|||
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;
|
||||
|
|
@ -379,7 +380,8 @@ public class HugeTraverser {
|
|||
return false;
|
||||
}
|
||||
Node other = (Node) object;
|
||||
return this.id.equals(other.id);
|
||||
return Objects.equals(this.id, other.id) &&
|
||||
Objects.equals(this.parent, other.parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -28,6 +29,7 @@ import java.util.Set;
|
|||
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;
|
||||
|
|
@ -44,18 +46,20 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
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");
|
||||
|
|
@ -64,13 +68,13 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
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;
|
||||
}
|
||||
|
|
@ -78,27 +82,47 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
return paths;
|
||||
}
|
||||
|
||||
private static boolean hasMultiEdges(List<Edge> edges, Id target) {
|
||||
int count = 0;
|
||||
for (Edge edge : edges) {
|
||||
if (((HugeEdge) edge).id().otherVertexId().equals(target)) {
|
||||
if (++count > 1) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +136,10 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
// 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);
|
||||
// Record edgeList to determine if multiple edges exist
|
||||
List<Edge> edgeList = IteratorUtils.list(edgesOfVertex(
|
||||
vid, direction, this.label, this.degree));
|
||||
edges = edgeList.iterator();
|
||||
|
||||
if (!edges.hasNext()) {
|
||||
// Reach the end, rays found
|
||||
|
|
@ -128,33 +155,80 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int neighborCount = 0;
|
||||
Set<Id> currentNeighbors = new HashSet<>();
|
||||
while (edges.hasNext()) {
|
||||
neighborCount++;
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
// Avoid deduplicate 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 it's fake ring like:
|
||||
// path is pattern: A->B<-A && A is only neighbor of B
|
||||
boolean uniqueEdge = neighborCount == 1 &&
|
||||
!edges.hasNext();
|
||||
boolean bothBack = target.equals(node.parent().id()) &&
|
||||
direction == Directions.BOTH;
|
||||
if (!this.rings && bothBack && uniqueEdge) {
|
||||
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 (hasMultiEdges(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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue