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 @@ -101,7 +101,7 @@
</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Implementation-Version>0.28.0.0</Implementation-Version>
<Implementation-Version>0.29.0.0</Implementation-Version>
</manifestEntries>
</archive>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,4 @@ public String get(@Context GraphManager manager,
edgeLabel, maxDepth, degree, capacity, limit);
return manager.serializer(g).writePaths("paths", paths, false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.baidu.hugegraph.api.traversers;

import java.util.List;

import javax.inject.Singleton;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;

import org.slf4j.Logger;

import com.baidu.hugegraph.HugeGraph;
import com.baidu.hugegraph.api.API;
import com.baidu.hugegraph.api.graph.EdgeAPI;
import com.baidu.hugegraph.api.graph.VertexAPI;
import com.baidu.hugegraph.backend.id.Id;
import com.baidu.hugegraph.core.GraphManager;
import com.baidu.hugegraph.server.RestServer;
import com.baidu.hugegraph.traversal.optimize.HugeTraverser;
import com.baidu.hugegraph.type.define.Directions;
import com.baidu.hugegraph.util.Log;
import com.codahale.metrics.annotation.Timed;

@Path("graphs/{graph}/traversers/rays")
@Singleton
public class Rays extends API {

private static final Logger LOG = Log.logger(RestServer.class);

@GET
@Timed
@Produces(APPLICATION_JSON_WITH_CHARSET)
public String get(@Context GraphManager manager,
@PathParam("graph") String graph,
@QueryParam("source") String sourceV,
@QueryParam("direction") String direction,
@QueryParam("label") String edgeLabel,
@QueryParam("depth") int depth,
@QueryParam("degree") @DefaultValue("-1") long degree,
@QueryParam("capacity") @DefaultValue("-1") long capacity,
@QueryParam("limit") @DefaultValue("-1") long limit) {
LOG.debug("Graph [{}] get rays paths from '{}' with " +
"direction '{}', edge label '{}', depth '{}', " +
"degree '{}' and limit '{}'",
graph, sourceV, direction, edgeLabel, depth, degree, limit);

Id source = VertexAPI.checkAndParseVertexId(sourceV);
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));

HugeGraph g = graph(manager, graph);

HugeTraverser traverser = new HugeTraverser(g);
List<HugeTraverser.Path> paths = traverser.rays(source, dir, edgeLabel,
depth, degree, capacity,
limit);
return manager.serializer(g).writePaths("rays", paths, false);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2017 HugeGraph Authors
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/

package com.baidu.hugegraph.api.traversers;

import java.util.List;

import javax.inject.Singleton;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;

import org.slf4j.Logger;

import com.baidu.hugegraph.HugeGraph;
import com.baidu.hugegraph.api.API;
import com.baidu.hugegraph.api.graph.EdgeAPI;
import com.baidu.hugegraph.api.graph.VertexAPI;
import com.baidu.hugegraph.backend.id.Id;
import com.baidu.hugegraph.core.GraphManager;
import com.baidu.hugegraph.server.RestServer;
import com.baidu.hugegraph.traversal.optimize.HugeTraverser;
import com.baidu.hugegraph.type.define.Directions;
import com.baidu.hugegraph.util.Log;
import com.codahale.metrics.annotation.Timed;

@Path("graphs/{graph}/traversers/rings")
@Singleton
public class Rings extends API {

private static final Logger LOG = Log.logger(RestServer.class);

@GET
@Timed
@Produces(APPLICATION_JSON_WITH_CHARSET)
public String get(@Context GraphManager manager,
@PathParam("graph") String graph,
@QueryParam("source") String sourceV,
@QueryParam("direction") String direction,
@QueryParam("label") String edgeLabel,
@QueryParam("depth") int depth,
@QueryParam("degree") @DefaultValue("-1") long degree,
@QueryParam("capacity") @DefaultValue("-1") long capacity,
@QueryParam("limit") @DefaultValue("-1") long limit) {
LOG.debug("Graph [{}] get rings paths reachable from '{}' with " +
"direction '{}', edge label '{}', depth '{}', " +
"degree '{}' and limit '{}'",
graph, sourceV, direction, edgeLabel, depth, degree, limit);

Id source = VertexAPI.checkAndParseVertexId(sourceV);
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));

HugeGraph g = graph(manager, graph);

HugeTraverser traverser = new HugeTraverser(g);
List<HugeTraverser.Path> paths = traverser.rings(source, dir, edgeLabel,
depth, degree,
capacity, limit);
return manager.serializer(g).writePaths("rings", paths, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,11 @@ public final class ApiVersion {
*
* version 0.8:
* [0.28] Issue-153: Add task-cancel API
* [0.29] Issue-39: Add rays and 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.28");
public static final Version VERSION = Version.of(ApiVersion.class, "0.29");

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 @@ -138,6 +138,45 @@ public Set<Path> paths(Id sourceV, Directions sourceDir,
return paths;
}

public List<Path> rays(Id sourceV, Directions dir, String label,
int maxDepth, long degree, long capacity,
long limit) {
return this.subGraphPaths(sourceV, dir, label, maxDepth, degree,
capacity, limit, false);
}

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

private List<Path> subGraphPaths(Id sourceV, Directions dir, String label,
int maxDepth, long degree, long capacity,
long limit, boolean rings) {
E.checkNotNull(sourceV, "source vertex id");
E.checkNotNull(dir, "direction");
checkPositive(maxDepth, "Paths max depth");
checkDegree(degree);
checkCapacity(capacity);
checkLimit(limit);

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

public Set<Id> kout(Id sourceV, Directions dir, String label,
int depth, boolean nearest,
long degree, long capacity, long limit) {
Expand Down Expand Up @@ -408,7 +447,7 @@ public List<Id> backward() {
return PATH_NONE;
}

public boolean reachCapacity() {
private boolean reachCapacity() {
if (this.capacity == NO_LIMIT || this.size < this.capacity) {
return false;
}
Expand Down Expand Up @@ -539,11 +578,11 @@ public List<Path> backward(Directions direction) {
return paths;
}

public int accessedNodes() {
private int accessedNodes() {
return this.sourcesAll.size() + this.targetsAll.size();
}

public boolean reachLimit() {
private boolean reachLimit() {
if (this.capacity != NO_LIMIT &&
this.accessedNodes() > this.capacity) {
return true;
Expand All @@ -555,6 +594,101 @@ public boolean reachLimit() {
}
}

private class SubGraphTraverser {

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

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

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

/**
* Search forward from source
*/
public List<Path> forward(Directions direction) {
List<Path> paths = new ArrayList<>();
MultivaluedMap<Id, Node> newVertices = newMultivalueMap();
Iterator<Edge> edges;
// Traversal vertices of previous level
for (List<Node> nodes : this.sources.values()) {
for (Node n : nodes) {
edges = edgesOfVertex(n.id(), direction,
this.label, this.degree);
if (!edges.hasNext()) {
// Reach the end, rays found
if (this.rings) {
continue;
}
// Store rays
paths.add(new Path(null, n.path()));
this.pathCount++;
if (reachLimit()) {
return paths;
}
continue;
}
while (edges.hasNext()) {
HugeEdge edge = (HugeEdge) edges.next();
Id target = edge.id().otherVertexId();
this.accessedVertices.add(target);

if (!n.contains(target)) {
// Add node to next start-nodes
newVertices.add(target, new Node(target, n));
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));
this.pathCount++;
if (reachLimit()) {
return paths;
}
}
}
}
}
// Re-init sources
this.sources = newVertices;

return paths;
}

private boolean reachLimit() {
if (this.capacity != NO_LIMIT &&
this.accessedVertices.size() > this.capacity) {
return true;
}
if (this.limit == NO_LIMIT || this.pathCount < this.limit) {
return false;
}
return true;
}

private boolean finished() {
return this.sources.isEmpty();
}
}

private static class Node {

private Id id;
Expand Down