forked from hugegraph/hugegraph-sync
feat(api&core): in oltp apis, add statistics info and support full info about vertices and edges (#2262)
* chore: improve gitignore file * feat: add ApiMeasure to collect runtime data ApiMeasure will count the number of vertices and edges traversed at runtime, and the time the api takes to execute * feat: Add ApiMeasure to JsonSerializer and Modify the Serializer interface * JsonSerializer: return measure information in api response * Serializer: fit the feature that returns complete information about vertices and edges * refactor: format code based on hugegraph-style.xml * feat: Add statistics information in all oltp restful apis response and Support full information about vertices and edges Statistics information: * add vertexIterCounter and edgeIterCounter in HugeTraverser.java to track traversed vertices and edges at run time * modify all oltp restful apis to add statistics information in response Full information about vertices and edges: * add 'with_vertex' and 'with_edge' parameter option in apis * modify oltp apis to support vertex and edge information in api response * add EdgeRecord in HugeTraverser.java to record edges at run time and generate the edge information returned in api response * modify Path and PathSet in HugeTraverser.java to support full edge information storage * modify all traversers to support track of edge information at run time * fix: numeric cast * fix: Jaccard Similarity api test * fix: adjust the code style and naming convention * Empty commit * Empty commit * fix: 1. change System.currentTimeMillis() to System.nanoTime(); 2. modify addCount() * fix: rollback change in .gitignore * fix: rollback ServerOptions.java code style * fix: rollback API.java code style and add exception in else branch * fix: fix code style * fix: name style & code style * rename edgeRecord to edgeResults * fix Request class code style in SameNeighborsAPI.java
This commit is contained in:
parent
c13b4e2b3a
commit
c7054e0704
|
|
@ -22,41 +22,39 @@ import java.util.Map;
|
|||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.lang.mutable.MutableLong;
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.apache.hugegraph.metrics.MetricsUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import jakarta.ws.rs.ForbiddenException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.NotSupportedException;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.define.Checkable;
|
||||
import org.apache.hugegraph.metrics.MetricsUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.Meter;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class API {
|
||||
|
||||
protected static final Logger LOG = Log.logger(API.class);
|
||||
|
||||
public static final String CHARSET = "UTF-8";
|
||||
|
||||
public static final String TEXT_PLAIN = MediaType.TEXT_PLAIN;
|
||||
public static final String APPLICATION_JSON = MediaType.APPLICATION_JSON;
|
||||
public static final String APPLICATION_JSON_WITH_CHARSET =
|
||||
APPLICATION_JSON + ";charset=" + CHARSET;
|
||||
public static final String JSON = MediaType.APPLICATION_JSON_TYPE
|
||||
.getSubtype();
|
||||
|
||||
public static final String ACTION_APPEND = "append";
|
||||
public static final String ACTION_ELIMINATE = "eliminate";
|
||||
public static final String ACTION_CLEAR = "clear";
|
||||
|
||||
protected static final Logger LOG = Log.logger(API.class);
|
||||
private static final Meter SUCCEED_METER =
|
||||
MetricsUtil.registerMeter(API.class, "commit-succeed");
|
||||
private static final Meter ILLEGAL_ARG_ERROR_METER =
|
||||
|
|
@ -69,8 +67,7 @@ public class API {
|
|||
public static HugeGraph graph(GraphManager manager, String graph) {
|
||||
HugeGraph g = manager.graph(graph);
|
||||
if (g == null) {
|
||||
throw new NotFoundException(String.format(
|
||||
"Graph '%s' does not exist", graph));
|
||||
throw new NotFoundException(String.format("Graph '%s' does not exist", graph));
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
|
@ -140,8 +137,7 @@ public class API {
|
|||
body.checkUpdate();
|
||||
}
|
||||
|
||||
protected static void checkCreatingBody(
|
||||
Collection<? extends Checkable> bodies) {
|
||||
protected static void checkCreatingBody(Collection<? extends Checkable> bodies) {
|
||||
E.checkArgumentNotNull(bodies, "The request body can't be empty");
|
||||
for (Checkable body : bodies) {
|
||||
E.checkArgument(body != null,
|
||||
|
|
@ -150,8 +146,7 @@ public class API {
|
|||
}
|
||||
}
|
||||
|
||||
protected static void checkUpdatingBody(
|
||||
Collection<? extends Checkable> bodies) {
|
||||
protected static void checkUpdatingBody(Collection<? extends Checkable> bodies) {
|
||||
E.checkArgumentNotNull(bodies, "The request body can't be empty");
|
||||
for (Checkable body : bodies) {
|
||||
E.checkArgumentNotNull(body,
|
||||
|
|
@ -186,8 +181,58 @@ public class API {
|
|||
} else if (action.equals(ACTION_ELIMINATE)) {
|
||||
return false;
|
||||
} else {
|
||||
throw new NotSupportedException(
|
||||
String.format("Not support action '%s'", action));
|
||||
throw new NotSupportedException(String.format("Not support action '%s'", action));
|
||||
}
|
||||
}
|
||||
|
||||
public static class ApiMeasurer {
|
||||
|
||||
public static final String EDGE_ITER = "edge_iterations";
|
||||
public static final String VERTICE_ITER = "vertice_iterations";
|
||||
public static final String COST = "cost(ns)";
|
||||
private final long timeStart;
|
||||
private final Map<String, Object> measures;
|
||||
|
||||
public ApiMeasurer() {
|
||||
this.timeStart = System.nanoTime();
|
||||
this.measures = InsertionOrderUtil.newMap();
|
||||
}
|
||||
|
||||
public Map<String, Object> measures() {
|
||||
measures.put(COST, System.nanoTime() - timeStart);
|
||||
return measures;
|
||||
}
|
||||
|
||||
public void put(String key, String value) {
|
||||
this.measures.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, long value) {
|
||||
this.measures.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, int value) {
|
||||
this.measures.put(key, value);
|
||||
}
|
||||
|
||||
protected void addCount(String key, long value) {
|
||||
Object current = measures.get(key);
|
||||
if (current == null) {
|
||||
measures.put(key, new MutableLong(value));
|
||||
} else if (current instanceof MutableLong) {
|
||||
((MutableLong) measures.computeIfAbsent(key, MutableLong::new)).add(value);
|
||||
} else if (current instanceof Long) {
|
||||
Long currentLong = (Long) current;
|
||||
measures.put(key, new MutableLong(currentLong + value));
|
||||
} else {
|
||||
throw new NotSupportedException("addCount() method's 'value' datatype must be " +
|
||||
"Long or MutableLong");
|
||||
}
|
||||
}
|
||||
|
||||
public void addIterCount(long verticeIters, long edgeIters) {
|
||||
this.addCount(EDGE_ITER, edgeIters);
|
||||
this.addCount(VERTICE_ITER, verticeIters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,19 +20,10 @@ package org.apache.hugegraph.api.traversers;
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
|
|
@ -44,9 +35,22 @@ import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
|||
import org.apache.hugegraph.traversal.algorithm.ShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
@Path("graphs/{graph}/traversers/allshortestpaths")
|
||||
@Singleton
|
||||
@Tag(name = "AllShortestPathsAPI")
|
||||
|
|
@ -68,13 +72,20 @@ public class AllShortestPathsAPI extends API {
|
|||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("with_vertex")
|
||||
@DefaultValue("false") boolean withVertex,
|
||||
@QueryParam("with_edge")
|
||||
@DefaultValue("false") boolean withEdge,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity) {
|
||||
LOG.debug("Graph [{}] get shortest path from '{}', to '{}' with " +
|
||||
"direction {}, edge label {}, max depth '{}', " +
|
||||
"max degree '{}', skipped degree '{}' and capacity '{}'",
|
||||
"max degree '{}', skipped degree '{}', capacity '{}', " +
|
||||
"with_vertex '{}' and with_edge '{}'",
|
||||
graph, source, target, direction, edgeLabel, depth,
|
||||
maxDegree, skipDegree, capacity);
|
||||
maxDegree, skipDegree, capacity, withVertex, withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
|
|
@ -85,9 +96,35 @@ public class AllShortestPathsAPI extends API {
|
|||
ShortestPathTraverser traverser = new ShortestPathTraverser(g);
|
||||
List<String> edgeLabels = edgeLabel == null ? ImmutableList.of() :
|
||||
ImmutableList.of(edgeLabel);
|
||||
HugeTraverser.PathSet paths = traverser.allShortestPaths(
|
||||
sourceId, targetId, dir, edgeLabels,
|
||||
depth, maxDegree, skipDegree, capacity);
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
HugeTraverser.PathSet paths = traverser.allShortestPaths(sourceId, targetId, dir,
|
||||
edgeLabels, depth, maxDegree,
|
||||
skipDegree, capacity);
|
||||
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = paths.getEdges();
|
||||
if (withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("paths", paths, false,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,18 +21,6 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAP
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
|
|
@ -43,8 +31,20 @@ import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
|||
import org.apache.hugegraph.traversal.algorithm.PathsTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
@Path("graphs/{graph}/traversers/crosspoints")
|
||||
@Singleton
|
||||
@Tag(name = "CrosspointsAPI")
|
||||
|
|
@ -74,6 +74,7 @@ public class CrosspointsAPI extends API {
|
|||
graph, source, target, direction, edgeLabel,
|
||||
depth, maxDegree, capacity, limit);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -84,6 +85,9 @@ public class CrosspointsAPI extends API {
|
|||
dir, edgeLabel, depth,
|
||||
maxDegree, capacity,
|
||||
limit);
|
||||
return manager.serializer(g).writePaths("crosspoints", paths, true);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("crosspoints", paths, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,30 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.CustomizedCrosspointsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
@ -37,23 +55,6 @@ import jakarta.ws.rs.PathParam;
|
|||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.CustomizedCrosspointsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/customizedcrosspoints")
|
||||
@Singleton
|
||||
@Tag(name = "CustomizedCrosspointsAPI")
|
||||
|
|
@ -61,6 +62,21 @@ public class CustomizedCrosspointsAPI extends API {
|
|||
|
||||
private static final Logger LOG = Log.logger(CustomizedCrosspointsAPI.class);
|
||||
|
||||
private static List<CustomizedCrosspointsTraverser.PathPattern> pathPatterns(
|
||||
HugeGraph graph, CrosspointsRequest request) {
|
||||
int stepSize = request.pathPatterns.size();
|
||||
List<CustomizedCrosspointsTraverser.PathPattern> pathPatterns = new ArrayList<>(stepSize);
|
||||
for (PathPattern pattern : request.pathPatterns) {
|
||||
CustomizedCrosspointsTraverser.PathPattern pathPattern =
|
||||
new CustomizedCrosspointsTraverser.PathPattern();
|
||||
for (Step step : pattern.steps) {
|
||||
pathPattern.add(step.jsonToStep(graph));
|
||||
}
|
||||
pathPatterns.add(pathPattern);
|
||||
}
|
||||
return pathPatterns;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
|
|
@ -78,55 +94,56 @@ public class CustomizedCrosspointsAPI extends API {
|
|||
"The steps of crosspoints request can't be empty");
|
||||
|
||||
LOG.debug("Graph [{}] get customized crosspoints from source vertex " +
|
||||
"'{}', with path_pattern '{}', with_path '{}', with_vertex " +
|
||||
"'{}', capacity '{}' and limit '{}'", graph, request.sources,
|
||||
request.pathPatterns, request.withPath, request.withVertex,
|
||||
request.capacity, request.limit);
|
||||
"'{}', with path_pattern '{}', with path '{}', with_vertex " +
|
||||
"'{}', capacity '{}', limit '{}' and with_edge '{}'",
|
||||
graph, request.sources, request.pathPatterns, request.withPath,
|
||||
request.withVertex, request.capacity, request.limit, request.withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
List<CustomizedCrosspointsTraverser.PathPattern> patterns;
|
||||
patterns = pathPatterns(g, request);
|
||||
|
||||
CustomizedCrosspointsTraverser traverser =
|
||||
new CustomizedCrosspointsTraverser(g);
|
||||
CustomizedCrosspointsTraverser.CrosspointsPaths paths;
|
||||
paths = traverser.crosspointsPaths(sources, patterns, request.capacity,
|
||||
request.limit);
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writeCrosspoints(paths, iter,
|
||||
request.withPath);
|
||||
}
|
||||
Set<Id> ids = new HashSet<>();
|
||||
new CustomizedCrosspointsTraverser(g);
|
||||
|
||||
List<CustomizedCrosspointsTraverser.PathPattern> patterns = pathPatterns(g, request);
|
||||
CustomizedCrosspointsTraverser.CrosspointsPaths paths =
|
||||
traverser.crosspointsPaths(sources, patterns, request.capacity, request.limit);
|
||||
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths.paths()) {
|
||||
ids.addAll(p.vertices());
|
||||
for (HugeTraverser.Path path : paths.paths()) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
} else {
|
||||
ids = paths.crosspoints();
|
||||
vertexIds = paths.crosspoints();
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
return manager.serializer(g).writeCrosspoints(paths, iter,
|
||||
request.withPath);
|
||||
}
|
||||
|
||||
private static List<CustomizedCrosspointsTraverser.PathPattern>
|
||||
pathPatterns(HugeGraph graph, CrosspointsRequest request) {
|
||||
int stepSize = request.pathPatterns.size();
|
||||
List<CustomizedCrosspointsTraverser.PathPattern> pathPatterns;
|
||||
pathPatterns = new ArrayList<>(stepSize);
|
||||
for (PathPattern pattern : request.pathPatterns) {
|
||||
CustomizedCrosspointsTraverser.PathPattern pathPattern;
|
||||
pathPattern = new CustomizedCrosspointsTraverser.PathPattern();
|
||||
for (Step step : pattern.steps) {
|
||||
pathPattern.add(step.jsonToStep(graph));
|
||||
Iterator<?> iterEdge = Collections.emptyIterator();
|
||||
if (request.withPath) {
|
||||
Set<Edge> edges = traverser.edgeResults().getEdges(paths.paths());
|
||||
if (request.withEdge) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
pathPatterns.add(pathPattern);
|
||||
}
|
||||
return pathPatterns;
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeCrosspoints(paths, iterVertex,
|
||||
iterEdge, request.withPath);
|
||||
}
|
||||
|
||||
private static class CrosspointsRequest {
|
||||
|
|
@ -143,14 +160,16 @@ public class CustomizedCrosspointsAPI extends API {
|
|||
public boolean withPath = false;
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("CrosspointsRequest{sourceVertex=%s," +
|
||||
"pathPatterns=%s,withPath=%s,withVertex=%s," +
|
||||
"capacity=%s,limit=%s}", this.sources,
|
||||
this.pathPatterns, this.withPath,
|
||||
this.withVertex, this.capacity, this.limit);
|
||||
"capacity=%s,limit=%s,withEdge=%s}", this.sources,
|
||||
this.pathPatterns, this.withPath, this.withVertex,
|
||||
this.capacity, this.limit, this.withEdge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,24 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.CustomizePathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.WeightedEdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
@ -39,24 +57,6 @@ import jakarta.ws.rs.PathParam;
|
|||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.CustomizePathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.WeightedEdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/customizedpaths")
|
||||
@Singleton
|
||||
@Tag(name = "CustomizedPathsAPI")
|
||||
|
|
@ -64,6 +64,16 @@ public class CustomizedPathsAPI extends API {
|
|||
|
||||
private static final Logger LOG = Log.logger(CustomizedPathsAPI.class);
|
||||
|
||||
private static List<WeightedEdgeStep> step(HugeGraph graph,
|
||||
PathRequest request) {
|
||||
int stepSize = request.steps.size();
|
||||
List<WeightedEdgeStep> steps = new ArrayList<>(stepSize);
|
||||
for (Step step : request.steps) {
|
||||
steps.add(step.jsonToStep(graph));
|
||||
}
|
||||
return steps;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
|
|
@ -81,10 +91,12 @@ public class CustomizedPathsAPI extends API {
|
|||
}
|
||||
|
||||
LOG.debug("Graph [{}] get customized paths from source vertex '{}', " +
|
||||
"with steps '{}', sort by '{}', capacity '{}', limit '{}' " +
|
||||
"and with_vertex '{}'", graph, request.sources, request.steps,
|
||||
"with steps '{}', sort by '{}', capacity '{}', limit '{}', " +
|
||||
"with_vertex '{}' and with_edge '{}'", graph, request.sources, request.steps,
|
||||
request.sortBy, request.capacity, request.limit,
|
||||
request.withVertex);
|
||||
request.withVertex, request.withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
|
|
@ -95,6 +107,8 @@ public class CustomizedPathsAPI extends API {
|
|||
List<HugeTraverser.Path> paths;
|
||||
paths = traverser.customizedPaths(sources, steps, sorted,
|
||||
request.capacity, request.limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
if (sorted) {
|
||||
boolean incr = request.sortBy == SortBy.INCR;
|
||||
|
|
@ -102,29 +116,35 @@ public class CustomizedPathsAPI extends API {
|
|||
request.limit);
|
||||
}
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = traverser.edgeResults().getEdges(paths);
|
||||
if (request.withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("paths", paths, false,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
|
||||
private static List<WeightedEdgeStep> step(HugeGraph graph,
|
||||
PathRequest req) {
|
||||
int stepSize = req.steps.size();
|
||||
List<WeightedEdgeStep> steps = new ArrayList<>(stepSize);
|
||||
for (Step step : req.steps) {
|
||||
steps.add(step.jsonToStep(graph));
|
||||
}
|
||||
return steps;
|
||||
private enum SortBy {
|
||||
INCR,
|
||||
DECR,
|
||||
NONE
|
||||
}
|
||||
|
||||
private static class PathRequest {
|
||||
|
|
@ -142,13 +162,16 @@ public class CustomizedPathsAPI extends API {
|
|||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathRequest{sourceVertex=%s,steps=%s," +
|
||||
"sortBy=%s,capacity=%s,limit=%s," +
|
||||
"withVertex=%s}", this.sources, this.steps,
|
||||
"withVertex=%s,withEdge=%s}", this.sources, this.steps,
|
||||
this.sortBy, this.capacity, this.limit,
|
||||
this.withVertex);
|
||||
this.withVertex, this.withEdge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,10 +213,4 @@ public class CustomizedPathsAPI extends API {
|
|||
this.defaultWeight, this.sample);
|
||||
}
|
||||
}
|
||||
|
||||
private enum SortBy {
|
||||
INCR,
|
||||
DECR,
|
||||
NONE
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,22 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PAG
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.backend.store.Shard;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
|
|
@ -32,22 +48,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.backend.store.Shard;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/edges")
|
||||
@Singleton
|
||||
@Tag(name = "EdgesAPI")
|
||||
|
|
|
|||
|
|
@ -23,6 +23,23 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PAT
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.NO_LIMIT;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.FusiformSimilarityTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.FusiformSimilarityTraverser.SimilarsMap;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
|
|
@ -33,22 +50,6 @@ import jakarta.ws.rs.PathParam;
|
|||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.FusiformSimilarityTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.FusiformSimilarityTraverser.SimilarsMap;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/fusiformsimilarity")
|
||||
@Singleton
|
||||
@Tag(name = "FusiformSimilarityAPI")
|
||||
|
|
@ -64,7 +65,7 @@ public class FusiformSimilarityAPI extends API {
|
|||
@PathParam("graph") String graph,
|
||||
FusiformSimilarityRequest request) {
|
||||
E.checkArgumentNotNull(request, "The fusiform similarity " +
|
||||
"request body can't be null");
|
||||
"request body can't be null");
|
||||
E.checkArgumentNotNull(request.sources,
|
||||
"The sources of fusiform similarity " +
|
||||
"request can't be null");
|
||||
|
|
@ -94,28 +95,37 @@ public class FusiformSimilarityAPI extends API {
|
|||
request.minNeighbors, request.alpha, request.minSimilars,
|
||||
request.groupProperty, request.minGroups);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
E.checkArgument(sources != null && sources.hasNext(),
|
||||
"The source vertices can't be empty");
|
||||
|
||||
FusiformSimilarityTraverser traverser =
|
||||
new FusiformSimilarityTraverser(g);
|
||||
FusiformSimilarityTraverser traverser = new FusiformSimilarityTraverser(g);
|
||||
SimilarsMap result = traverser.fusiformSimilarity(
|
||||
sources, request.direction, request.label,
|
||||
request.minNeighbors, request.alpha,
|
||||
request.minSimilars, request.top,
|
||||
request.groupProperty, request.minGroups,
|
||||
request.maxDegree, request.capacity,
|
||||
request.limit, request.withIntermediary);
|
||||
sources, request.direction, request.label,
|
||||
request.minNeighbors, request.alpha,
|
||||
request.minSimilars, request.top,
|
||||
request.groupProperty, request.minGroups,
|
||||
request.maxDegree, request.capacity,
|
||||
request.limit, request.withIntermediary);
|
||||
|
||||
CloseableIterator.closeIterator(sources);
|
||||
|
||||
Iterator<Vertex> iterator = QueryResults.emptyIterator();
|
||||
if (request.withVertex && !result.isEmpty()) {
|
||||
iterator = g.vertices(result.vertices().toArray());
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = result.vertices();
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
return manager.serializer(g).writeSimilars(result, iterator);
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeSimilars(result, iterVertex);
|
||||
}
|
||||
|
||||
private static class FusiformSimilarityRequest {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,28 @@
|
|||
package org.apache.hugegraph.api.traversers;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_LIMIT;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.JaccardSimilarTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
@ -35,24 +52,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.JaccardSimilarTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
@Path("graphs/{graph}/traversers/jaccardsimilarity")
|
||||
@Singleton
|
||||
@Tag(name = "JaccardSimilarityAPI")
|
||||
|
|
@ -75,6 +74,8 @@ public class JaccardSimilarityAPI extends TraverserAPI {
|
|||
"with direction {}, edge label {} and max degree '{}'",
|
||||
graph, vertex, other, direction, edgeLabel, maxDegree);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(vertex);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(other);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -82,12 +83,15 @@ public class JaccardSimilarityAPI extends TraverserAPI {
|
|||
HugeGraph g = graph(manager, graph);
|
||||
double similarity;
|
||||
try (JaccardSimilarTraverser traverser =
|
||||
new JaccardSimilarTraverser(g)) {
|
||||
new JaccardSimilarTraverser(g)) {
|
||||
similarity = traverser.jaccardSimilarity(sourceId, targetId, dir,
|
||||
edgeLabel, maxDegree);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
return JsonUtil.toJson(ImmutableMap.of("jaccard_similarity",
|
||||
similarity));
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeMap(ImmutableMap.of("jaccard_similarity", similarity));
|
||||
}
|
||||
|
||||
@POST
|
||||
|
|
@ -110,6 +114,8 @@ public class JaccardSimilarityAPI extends TraverserAPI {
|
|||
graph, request.vertex, request.step,
|
||||
request.top, request.capacity);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Id sourceId = HugeVertex.getIdValue(request.vertex);
|
||||
|
||||
|
|
@ -117,11 +123,14 @@ public class JaccardSimilarityAPI extends TraverserAPI {
|
|||
|
||||
Map<Id, Double> results;
|
||||
try (JaccardSimilarTraverser traverser =
|
||||
new JaccardSimilarTraverser(g)) {
|
||||
new JaccardSimilarTraverser(g)) {
|
||||
results = traverser.jaccardSimilars(sourceId, step, request.top,
|
||||
request.capacity);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
return manager.serializer(g).writeMap(results);
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeMap(ImmutableMap.of("jaccard_similarity", results));
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELE
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.NO_LIMIT;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
|
@ -40,12 +41,13 @@ import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
|||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
|
|
@ -75,6 +77,8 @@ public class KneighborAPI extends TraverserAPI {
|
|||
@QueryParam("direction") String direction,
|
||||
@QueryParam("label") String edgeLabel,
|
||||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("count_only")
|
||||
@DefaultValue("false") boolean countOnly,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("limit")
|
||||
|
|
@ -85,6 +89,8 @@ public class KneighborAPI extends TraverserAPI {
|
|||
graph, sourceV, direction, edgeLabel, depth,
|
||||
maxDegree, limit);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
|
|
@ -94,8 +100,14 @@ public class KneighborAPI extends TraverserAPI {
|
|||
try (KneighborTraverser traverser = new KneighborTraverser(g)) {
|
||||
ids = traverser.kneighbor(source, dir, edgeLabel,
|
||||
depth, maxDegree, limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
return manager.serializer(g).writeList("vertices", ids);
|
||||
if (countOnly) {
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeMap(ImmutableMap.of("vertices_size", ids.size()));
|
||||
}
|
||||
return manager.serializer(g, measure.measures()).writeList("vertices", ids);
|
||||
}
|
||||
|
||||
@POST
|
||||
|
|
@ -111,15 +123,18 @@ public class KneighborAPI extends TraverserAPI {
|
|||
E.checkArgument(request.step != null,
|
||||
"The steps of request can't be null");
|
||||
if (request.countOnly) {
|
||||
E.checkArgument(!request.withVertex && !request.withPath,
|
||||
"Can't return vertex or path when count only");
|
||||
E.checkArgument(!request.withVertex && !request.withPath && !request.withEdge,
|
||||
"Can't return vertex, edge or path when count only");
|
||||
}
|
||||
|
||||
LOG.debug("Graph [{}] get customized kneighbor from source vertex " +
|
||||
"'{}', with step '{}', limit '{}', count_only '{}', " +
|
||||
"with_vertex '{}' and with_path '{}'",
|
||||
"with_vertex '{}', with_path '{}' and with_edge '{}'",
|
||||
graph, request.source, request.step, request.limit,
|
||||
request.countOnly, request.withVertex, request.withPath);
|
||||
request.countOnly, request.withVertex, request.withPath,
|
||||
request.withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Id sourceId = HugeVertex.getIdValue(request.source);
|
||||
|
|
@ -131,6 +146,8 @@ public class KneighborAPI extends TraverserAPI {
|
|||
results = traverser.customizedKneighbor(sourceId, step,
|
||||
request.maxDepth,
|
||||
request.limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
|
||||
long size = results.size();
|
||||
|
|
@ -144,20 +161,41 @@ public class KneighborAPI extends TraverserAPI {
|
|||
if (request.withPath) {
|
||||
paths.addAll(results.paths(request.limit));
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (request.withVertex && !request.countOnly) {
|
||||
Set<Id> ids = new HashSet<>(neighbors);
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
|
||||
if (request.countOnly) {
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeNodesWithPath("kneighbor", neighbors, size, paths,
|
||||
QueryResults.emptyIterator(),
|
||||
QueryResults.emptyIterator());
|
||||
}
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>(neighbors);
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
vertexIds.addAll(p.vertices());
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeNodesWithPath("kneighbor", neighbors,
|
||||
size, paths, iter);
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge = Collections.emptyIterator();
|
||||
if (request.withPath) {
|
||||
Set<Edge> edges = results.edgeResults().getEdges(paths);
|
||||
if (request.withEdge) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeNodesWithPath("kneighbor", neighbors,
|
||||
size, paths, iterVertex, iterEdge);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
|
@ -176,14 +214,16 @@ public class KneighborAPI extends TraverserAPI {
|
|||
public boolean withVertex = false;
|
||||
@JsonProperty("with_path")
|
||||
public boolean withPath = false;
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathRequest{source=%s,step=%s,maxDepth=%s" +
|
||||
"limit=%s,countOnly=%s,withVertex=%s," +
|
||||
"withPath=%s}", this.source, this.step,
|
||||
"withPath=%s,withEdge=%s}", this.source, this.step,
|
||||
this.maxDepth, this.limit, this.countOnly,
|
||||
this.withVertex, this.withPath);
|
||||
this.withVertex, this.withPath, this.withEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELE
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.NO_LIMIT;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
|
@ -41,12 +42,13 @@ import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
|||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
|
|
@ -78,6 +80,8 @@ public class KoutAPI extends TraverserAPI {
|
|||
@QueryParam("max_depth") int depth,
|
||||
@QueryParam("nearest")
|
||||
@DefaultValue("true") boolean nearest,
|
||||
@QueryParam("count_only")
|
||||
@DefaultValue("false") boolean count_only,
|
||||
@QueryParam("max_degree")
|
||||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("capacity")
|
||||
|
|
@ -87,8 +91,10 @@ public class KoutAPI extends TraverserAPI {
|
|||
LOG.debug("Graph [{}] get k-out from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', nearest " +
|
||||
"'{}', max degree '{}', capacity '{}' and limit '{}'",
|
||||
graph, source, direction, edgeLabel, depth, nearest,
|
||||
maxDegree, capacity, limit);
|
||||
graph, source, direction, edgeLabel, depth,
|
||||
nearest, maxDegree, capacity, limit);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -99,8 +105,15 @@ public class KoutAPI extends TraverserAPI {
|
|||
try (KoutTraverser traverser = new KoutTraverser(g)) {
|
||||
ids = traverser.kout(sourceId, dir, edgeLabel, depth,
|
||||
nearest, maxDegree, capacity, limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
return manager.serializer(g).writeList("vertices", ids);
|
||||
|
||||
if (count_only) {
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeMap(ImmutableMap.of("vertices_size", ids.size()));
|
||||
}
|
||||
return manager.serializer(g, measure.measures()).writeList("vertices", ids);
|
||||
}
|
||||
|
||||
@POST
|
||||
|
|
@ -116,23 +129,25 @@ public class KoutAPI extends TraverserAPI {
|
|||
E.checkArgument(request.step != null,
|
||||
"The steps of request can't be null");
|
||||
if (request.countOnly) {
|
||||
E.checkArgument(!request.withVertex && !request.withPath,
|
||||
"Can't return vertex or path when count only");
|
||||
E.checkArgument(!request.withVertex && !request.withPath && !request.withEdge,
|
||||
"Can't return vertex, edge or path when count only");
|
||||
}
|
||||
|
||||
LOG.debug("Graph [{}] get customized kout from source vertex '{}', " +
|
||||
"with step '{}', max_depth '{}', nearest '{}', " +
|
||||
"count_only '{}', capacity '{}', limit '{}', " +
|
||||
"with_vertex '{}' and with_path '{}'",
|
||||
"with_vertex '{}', with_path '{}' and with_edge '{}'",
|
||||
graph, request.source, request.step, request.maxDepth,
|
||||
request.nearest, request.countOnly, request.capacity,
|
||||
request.limit, request.withVertex, request.withPath);
|
||||
request.limit, request.withVertex, request.withPath,
|
||||
request.withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Id sourceId = HugeVertex.getIdValue(request.source);
|
||||
|
||||
EdgeStep step = step(g, request.step);
|
||||
|
||||
KoutRecords results;
|
||||
try (KoutTraverser traverser = new KoutTraverser(g)) {
|
||||
results = traverser.customizedKout(sourceId, step,
|
||||
|
|
@ -140,8 +155,9 @@ public class KoutAPI extends TraverserAPI {
|
|||
request.nearest,
|
||||
request.capacity,
|
||||
request.limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
|
||||
long size = results.size();
|
||||
if (request.limit != NO_LIMIT && size > request.limit) {
|
||||
size = request.limit;
|
||||
|
|
@ -154,20 +170,40 @@ public class KoutAPI extends TraverserAPI {
|
|||
paths.addAll(results.paths(request.limit));
|
||||
}
|
||||
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (request.withVertex && !request.countOnly) {
|
||||
Set<Id> ids = new HashSet<>(neighbors);
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
}
|
||||
}
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
if (request.countOnly) {
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeNodesWithPath("kneighbor", neighbors, size, paths,
|
||||
QueryResults.emptyIterator(),
|
||||
QueryResults.emptyIterator());
|
||||
}
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>(neighbors);
|
||||
if (request.withPath) {
|
||||
for (HugeTraverser.Path p : results.paths(request.limit)) {
|
||||
vertexIds.addAll(p.vertices());
|
||||
}
|
||||
}
|
||||
return manager.serializer(g).writeNodesWithPath("kout", neighbors,
|
||||
size, paths, iter);
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge = Collections.emptyIterator();
|
||||
if (request.withPath) {
|
||||
Set<Edge> edges = results.edgeResults().getEdges(paths);
|
||||
if (request.withEdge) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeNodesWithPath("kout", neighbors, size, paths,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
|
@ -190,15 +226,18 @@ public class KoutAPI extends TraverserAPI {
|
|||
public boolean withVertex = false;
|
||||
@JsonProperty("with_path")
|
||||
public boolean withPath = false;
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("KoutRequest{source=%s,step=%s,maxDepth=%s" +
|
||||
"nearest=%s,countOnly=%s,capacity=%s," +
|
||||
"limit=%s,withVertex=%s,withPath=%s}",
|
||||
this.source, this.step, this.maxDepth,
|
||||
this.nearest, this.countOnly, this.capacity,
|
||||
this.limit, this.withVertex, this.withPath);
|
||||
"limit=%s,withVertex=%s,withPath=%s," +
|
||||
"withEdge=%s}", this.source, this.step,
|
||||
this.maxDepth, this.nearest, this.countOnly,
|
||||
this.capacity, this.limit, this.withVertex,
|
||||
this.withPath, this.withEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,21 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.MultiNodeShortestPathTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
@ -33,21 +48,6 @@ import jakarta.ws.rs.PathParam;
|
|||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.MultiNodeShortestPathTraverser;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/multinodeshortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "MultiNodeShortestPathAPI")
|
||||
|
|
@ -74,32 +74,48 @@ public class MultiNodeShortestPathAPI extends TraverserAPI {
|
|||
graph, request.vertices, request.step, request.maxDepth,
|
||||
request.capacity, request.withVertex);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> vertices = request.vertices.vertices(g);
|
||||
|
||||
EdgeStep step = step(g, request.step);
|
||||
|
||||
List<HugeTraverser.Path> paths;
|
||||
MultiNodeShortestPathTraverser.WrappedListPath wrappedListPath;
|
||||
try (MultiNodeShortestPathTraverser traverser =
|
||||
new MultiNodeShortestPathTraverser(g)) {
|
||||
paths = traverser.multiNodeShortestPath(vertices, step,
|
||||
request.maxDepth,
|
||||
request.capacity);
|
||||
new MultiNodeShortestPathTraverser(g)) {
|
||||
wrappedListPath = traverser.multiNodeShortestPath(vertices, step,
|
||||
request.maxDepth,
|
||||
request.capacity);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
}
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
List<HugeTraverser.Path> paths = wrappedListPath.paths();
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = wrappedListPath.edges();
|
||||
if (request.withEdge && !edges.isEmpty()) {
|
||||
iterEdge = wrappedListPath.edges().iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("paths", paths,
|
||||
false, iterVertex, iterEdge);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
|
@ -114,13 +130,15 @@ public class MultiNodeShortestPathAPI extends TraverserAPI {
|
|||
public long capacity = Long.parseLong(DEFAULT_CAPACITY);
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Request{vertices=%s,step=%s,maxDepth=%s" +
|
||||
"capacity=%s,withVertex=%s}",
|
||||
"capacity=%s,withVertex=%s,withEdge=%s}",
|
||||
this.vertices, this.step, this.maxDepth,
|
||||
this.capacity, this.withVertex);
|
||||
this.capacity, this.withVertex, this.withEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,25 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.CollectionPathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.PathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
@ -39,25 +58,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.CollectionPathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.PathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/paths")
|
||||
@Singleton
|
||||
@Tag(name = "PathsAPI")
|
||||
|
|
@ -87,6 +87,8 @@ public class PathsAPI extends TraverserAPI {
|
|||
graph, source, target, direction, edgeLabel, depth,
|
||||
maxDegree, capacity, limit);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -97,7 +99,10 @@ public class PathsAPI extends TraverserAPI {
|
|||
dir.opposite(), edgeLabel,
|
||||
depth, maxDegree, capacity,
|
||||
limit);
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("paths", paths, false);
|
||||
}
|
||||
|
||||
@POST
|
||||
|
|
@ -120,10 +125,12 @@ public class PathsAPI extends TraverserAPI {
|
|||
|
||||
LOG.debug("Graph [{}] get paths from source vertices '{}', target " +
|
||||
"vertices '{}', with step '{}', max depth '{}', " +
|
||||
"capacity '{}', limit '{}' and with_vertex '{}'",
|
||||
"capacity '{}', limit '{}', with_vertex '{}' and with_edge '{}'",
|
||||
graph, request.sources, request.targets, request.step,
|
||||
request.depth, request.capacity, request.limit,
|
||||
request.withVertex);
|
||||
request.withVertex, request.withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
|
|
@ -131,24 +138,38 @@ public class PathsAPI extends TraverserAPI {
|
|||
EdgeStep step = step(g, request.step);
|
||||
|
||||
CollectionPathsTraverser traverser = new CollectionPathsTraverser(g);
|
||||
Collection<HugeTraverser.Path> paths;
|
||||
paths = traverser.paths(sources, targets, step, request.depth,
|
||||
request.nearest, request.capacity,
|
||||
request.limit);
|
||||
CollectionPathsTraverser.WrappedPathCollection
|
||||
wrappedPathCollection = traverser.paths(sources, targets,
|
||||
step, request.depth,
|
||||
request.nearest, request.capacity,
|
||||
request.limit);
|
||||
Collection<HugeTraverser.Path> paths = wrappedPathCollection.paths();
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = wrappedPathCollection.edges();
|
||||
if (request.withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("paths", paths, false,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
|
@ -170,14 +191,17 @@ public class PathsAPI extends TraverserAPI {
|
|||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PathRequest{sources=%s,targets=%s,step=%s," +
|
||||
"maxDepth=%s,nearest=%s,capacity=%s," +
|
||||
"limit=%s,withVertex=%s}", this.sources,
|
||||
this.targets, this.step, this.depth,
|
||||
this.nearest, this.capacity,
|
||||
this.limit, this.withVertex);
|
||||
"limit=%s,withVertex=%s,withEdge=%s}",
|
||||
this.sources, this.targets, this.step,
|
||||
this.depth, this.nearest, this.capacity,
|
||||
this.limit, this.withVertex, this.withEdge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,25 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAP
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SubGraphTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
|
|
@ -31,20 +50,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SubGraphTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/rays")
|
||||
@Singleton
|
||||
@Tag(name = "RaysAPI")
|
||||
|
|
@ -66,12 +71,17 @@ public class RaysAPI extends API {
|
|||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit) {
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit,
|
||||
@QueryParam("with_vertex")
|
||||
@DefaultValue("false") boolean withVertex,
|
||||
@QueryParam("with_edge")
|
||||
@DefaultValue("false") boolean withEdge) {
|
||||
LOG.debug("Graph [{}] get rays paths from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"max degree '{}', capacity '{}' and limit '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, maxDegree,
|
||||
capacity, limit);
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -80,8 +90,33 @@ public class RaysAPI extends API {
|
|||
|
||||
SubGraphTraverser traverser = new SubGraphTraverser(g);
|
||||
HugeTraverser.PathSet paths = traverser.rays(source, dir, edgeLabel,
|
||||
depth, maxDegree,
|
||||
capacity, limit);
|
||||
return manager.serializer(g).writePaths("rays", paths, false);
|
||||
depth, maxDegree, capacity,
|
||||
limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = paths.getEdges();
|
||||
if (withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("rays", paths, false,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,25 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAP
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SubGraphTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
|
|
@ -31,20 +50,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SubGraphTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/rings")
|
||||
@Singleton
|
||||
@Tag(name = "RingsAPI")
|
||||
|
|
@ -68,14 +73,19 @@ public class RingsAPI extends API {
|
|||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit) {
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit,
|
||||
@QueryParam("with_vertex")
|
||||
@DefaultValue("false") boolean withVertex,
|
||||
@QueryParam("with_edge")
|
||||
@DefaultValue("false") boolean withEdge) {
|
||||
LOG.debug("Graph [{}] get rings paths reachable from '{}' with " +
|
||||
"direction '{}', edge label '{}', max depth '{}', " +
|
||||
"source in ring '{}', max degree '{}', capacity '{}' " +
|
||||
"and limit '{}'",
|
||||
"source in ring '{}', max degree '{}', capacity '{}', " +
|
||||
"limit '{}', with_vertex '{}' and with_edge '{}'",
|
||||
graph, sourceV, direction, edgeLabel, depth, sourceInRing,
|
||||
maxDegree, capacity, limit);
|
||||
maxDegree, capacity, limit, withVertex, withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
Id source = VertexAPI.checkAndParseVertexId(sourceV);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
||||
|
|
@ -83,8 +93,34 @@ public class RingsAPI extends API {
|
|||
|
||||
SubGraphTraverser traverser = new SubGraphTraverser(g);
|
||||
HugeTraverser.PathSet paths = traverser.rings(source, dir, edgeLabel,
|
||||
depth, sourceInRing,
|
||||
maxDegree, capacity, limit);
|
||||
return manager.serializer(g).writePaths("rings", paths, false);
|
||||
depth, sourceInRing, maxDegree,
|
||||
capacity, limit);
|
||||
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = paths.getEdges();
|
||||
if (withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("rings", paths, false,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,30 +20,39 @@ package org.apache.hugegraph.api.traversers;
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_ELEMENTS_LIMIT;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.SameNeighborTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
@Path("graphs/{graph}/traversers/sameneighbors")
|
||||
@Singleton
|
||||
|
|
@ -69,6 +78,8 @@ public class SameNeighborsAPI extends API {
|
|||
"direction {}, edge label {}, max degree '{}' and limit '{}'",
|
||||
graph, vertex, other, direction, edgeLabel, maxDegree, limit);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(vertex);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(other);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -77,6 +88,77 @@ public class SameNeighborsAPI extends API {
|
|||
SameNeighborTraverser traverser = new SameNeighborTraverser(g);
|
||||
Set<Id> neighbors = traverser.sameNeighbors(sourceId, targetId, dir,
|
||||
edgeLabel, maxDegree, limit);
|
||||
return manager.serializer(g).writeList("same_neighbors", neighbors);
|
||||
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeList("same_neighbors", neighbors);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Produces(APPLICATION_JSON_WITH_CHARSET)
|
||||
public String sameNeighbors(@Context GraphManager manager,
|
||||
@PathParam("graph") String graph,
|
||||
Request request) {
|
||||
LOG.debug("Graph [{}] get same neighbors among batch, '{}'", graph, request.toString());
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(request.direction));
|
||||
HugeGraph g = graph(manager, graph);
|
||||
SameNeighborTraverser traverser = new SameNeighborTraverser(g);
|
||||
|
||||
List<Object> vertexList = request.vertexList;
|
||||
E.checkArgument(vertexList.size() >= 2, "vertex_list size can't " +
|
||||
"be less than 2");
|
||||
|
||||
List<Id> vertexIds = new ArrayList<>();
|
||||
for (Object obj : vertexList) {
|
||||
vertexIds.add(HugeVertex.getIdValue(obj));
|
||||
}
|
||||
|
||||
Set<Id> neighbors = traverser.sameNeighbors(vertexIds, dir, request.labels,
|
||||
request.maxDegree, request.limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> ids = new HashSet<>(neighbors);
|
||||
ids.addAll(vertexIds);
|
||||
if (request.withVertex && !ids.isEmpty()) {
|
||||
iterVertex = g.vertices(ids.toArray());
|
||||
} else {
|
||||
iterVertex = ids.iterator();
|
||||
}
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeMap(ImmutableMap.of("same_neighbors", neighbors,
|
||||
"vertices", iterVertex));
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
||||
@JsonProperty("max_degree")
|
||||
public long maxDegree = Long.parseLong(DEFAULT_MAX_DEGREE);
|
||||
@JsonProperty("limit")
|
||||
public int limit = Integer.parseInt(DEFAULT_ELEMENTS_LIMIT);
|
||||
@JsonProperty("vertex_list")
|
||||
private List<Object> vertexList;
|
||||
@JsonProperty("direction")
|
||||
private String direction;
|
||||
@JsonProperty("labels")
|
||||
private List<String> labels;
|
||||
@JsonProperty("with_vertex")
|
||||
private boolean withVertex = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("SameNeighborsBatchRequest{vertex_list=%s," +
|
||||
"direction=%s,label=%s,max_degree=%d," +
|
||||
"limit=%d,with_vertex=%s",
|
||||
this.vertexList, this.direction, this.labels,
|
||||
this.maxDegree, this.limit, this.withVertex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,26 @@ package org.apache.hugegraph.api.traversers;
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAPACITY;
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.ShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
|
|
@ -32,21 +51,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.ShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@Path("graphs/{graph}/traversers/shortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "ShortestPathAPI")
|
||||
|
|
@ -68,13 +72,21 @@ public class ShortestPathAPI extends API {
|
|||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("with_vertex")
|
||||
@DefaultValue("false") boolean withVertex,
|
||||
@QueryParam("with_edge")
|
||||
@DefaultValue("false") boolean withEdge,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity) {
|
||||
LOG.debug("Graph [{}] get shortest path from '{}', to '{}' with " +
|
||||
"direction {}, edge label {}, max depth '{}', " +
|
||||
"max degree '{}', skipped maxDegree '{}' and capacity '{}'",
|
||||
"max degree '{}', skipped maxDegree '{}', capacity '{}', " +
|
||||
"with_vertex '{}' and with_edge '{}'",
|
||||
graph, source, target, direction, edgeLabel, depth,
|
||||
maxDegree, skipDegree, capacity);
|
||||
maxDegree, skipDegree, capacity, withVertex, withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -89,6 +101,29 @@ public class ShortestPathAPI extends API {
|
|||
dir, edgeLabels, depth,
|
||||
maxDegree, skipDegree,
|
||||
capacity);
|
||||
return manager.serializer(g).writeList("path", path.vertices());
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
List<Id> vertexIds = path.vertices();
|
||||
if (withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(path.vertices().size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = path.getEdges();
|
||||
if (withEdge) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeMap(ImmutableMap.of("path", path.vertices(),
|
||||
"vertices", iterVertex,
|
||||
"edges", iterEdge));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,22 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PATHS_LIMIT;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
|
|
@ -33,22 +49,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.WeightedPaths;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/singlesourceshortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "SingleSourceShortestPathAPI")
|
||||
|
|
@ -69,16 +69,22 @@ public class SingleSourceShortestPathAPI extends API {
|
|||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("with_vertex")
|
||||
@DefaultValue("false") boolean withVertex,
|
||||
@QueryParam("with_edge")
|
||||
@DefaultValue("false") boolean withEdge,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("limit")
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit,
|
||||
@QueryParam("with_vertex") boolean withVertex) {
|
||||
@DefaultValue(DEFAULT_PATHS_LIMIT) int limit) {
|
||||
LOG.debug("Graph [{}] get single source shortest path from '{}' " +
|
||||
"with direction {}, edge label {}, weight property {}, " +
|
||||
"max degree '{}', limit '{}' and with vertex '{}'",
|
||||
"max degree '{}', capacity '{}', limit '{}', " +
|
||||
"with_vertex '{}' and with_edge '{}'",
|
||||
graph, source, direction, edgeLabel,
|
||||
weight, maxDegree, withVertex);
|
||||
weight, maxDegree, capacity, limit, withVertex, withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -86,14 +92,31 @@ public class SingleSourceShortestPathAPI extends API {
|
|||
HugeGraph g = graph(manager, graph);
|
||||
SingleSourceShortestPathTraverser traverser =
|
||||
new SingleSourceShortestPathTraverser(g);
|
||||
WeightedPaths paths = traverser.singleSourceShortestPaths(
|
||||
sourceId, dir, edgeLabel, weight,
|
||||
maxDegree, skipDegree, capacity, limit);
|
||||
Iterator<Vertex> iterator = QueryResults.emptyIterator();
|
||||
assert paths != null;
|
||||
if (!paths.isEmpty() && withVertex) {
|
||||
iterator = g.vertices(paths.vertices().toArray());
|
||||
SingleSourceShortestPathTraverser.WeightedPaths paths =
|
||||
traverser.singleSourceShortestPaths(
|
||||
sourceId, dir, edgeLabel, weight,
|
||||
maxDegree, skipDegree, capacity, limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = paths.vertices();
|
||||
if (withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
return manager.serializer(g).writeWeightedPaths(paths, iterator);
|
||||
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = paths.getEdges();
|
||||
if (withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeWeightedPaths(paths, iterVertex, iterEdge);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,21 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.TemplatePathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.RepeatEdgeStep;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
|
|
@ -35,21 +50,6 @@ import jakarta.ws.rs.PathParam;
|
|||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.TemplatePathsTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.RepeatEdgeStep;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
@Path("graphs/{graph}/traversers/templatepaths")
|
||||
@Singleton
|
||||
@Tag(name = "TemplatePathsAPI")
|
||||
|
|
@ -57,6 +57,22 @@ public class TemplatePathsAPI extends TraverserAPI {
|
|||
|
||||
private static final Logger LOG = Log.logger(TemplatePathsAPI.class);
|
||||
|
||||
private static List<RepeatEdgeStep> steps(HugeGraph g,
|
||||
List<TemplatePathStep> steps) {
|
||||
List<RepeatEdgeStep> edgeSteps = new ArrayList<>(steps.size());
|
||||
for (TemplatePathStep step : steps) {
|
||||
edgeSteps.add(repeatEdgeStep(g, step));
|
||||
}
|
||||
return edgeSteps;
|
||||
}
|
||||
|
||||
private static RepeatEdgeStep repeatEdgeStep(HugeGraph graph,
|
||||
TemplatePathStep step) {
|
||||
return new RepeatEdgeStep(graph, step.direction, step.labels,
|
||||
step.properties, step.maxDegree,
|
||||
step.skipDegree, step.maxTimes);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Timed
|
||||
@Consumes(APPLICATION_JSON)
|
||||
|
|
@ -74,9 +90,11 @@ public class TemplatePathsAPI extends TraverserAPI {
|
|||
|
||||
LOG.debug("Graph [{}] get template paths from source vertices '{}', " +
|
||||
"target vertices '{}', with steps '{}', " +
|
||||
"capacity '{}', limit '{}' and with_vertex '{}'",
|
||||
"capacity '{}', limit '{}', with_vertex '{}' and with_edge '{}'",
|
||||
graph, request.sources, request.targets, request.steps,
|
||||
request.capacity, request.limit, request.withVertex);
|
||||
request.capacity, request.limit, request.withVertex, request.withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
|
||||
HugeGraph g = graph(manager, graph);
|
||||
Iterator<Vertex> sources = request.sources.vertices(g);
|
||||
|
|
@ -84,40 +102,38 @@ public class TemplatePathsAPI extends TraverserAPI {
|
|||
List<RepeatEdgeStep> steps = steps(g, request.steps);
|
||||
|
||||
TemplatePathsTraverser traverser = new TemplatePathsTraverser(g);
|
||||
Set<HugeTraverser.Path> paths;
|
||||
paths = traverser.templatePaths(sources, targets, steps,
|
||||
TemplatePathsTraverser.WrappedPathSet wrappedPathSet =
|
||||
traverser.templatePaths(sources, targets, steps,
|
||||
request.withRing, request.capacity,
|
||||
request.limit);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
if (!request.withVertex) {
|
||||
return manager.serializer(g).writePaths("paths", paths, false);
|
||||
Set<HugeTraverser.Path> paths = wrappedPathSet.paths();
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
Set<Id> vertexIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
vertexIds.addAll(path.vertices());
|
||||
}
|
||||
if (request.withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Set<Id> ids = new HashSet<>();
|
||||
for (HugeTraverser.Path p : paths) {
|
||||
ids.addAll(p.vertices());
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = wrappedPathSet.edges();
|
||||
if (request.withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
Iterator<Vertex> iter = QueryResults.emptyIterator();
|
||||
if (!ids.isEmpty()) {
|
||||
iter = g.vertices(ids.toArray());
|
||||
}
|
||||
return manager.serializer(g).writePaths("paths", paths, false, iter);
|
||||
}
|
||||
|
||||
private static List<RepeatEdgeStep> steps(HugeGraph g,
|
||||
List<TemplatePathStep> steps) {
|
||||
List<RepeatEdgeStep> edgeSteps = new ArrayList<>(steps.size());
|
||||
for (TemplatePathStep step : steps) {
|
||||
edgeSteps.add(repeatEdgeStep(g, step));
|
||||
}
|
||||
return edgeSteps;
|
||||
}
|
||||
|
||||
private static RepeatEdgeStep repeatEdgeStep(HugeGraph graph,
|
||||
TemplatePathStep step) {
|
||||
return new RepeatEdgeStep(graph, step.direction, step.labels,
|
||||
step.properties, step.maxDegree,
|
||||
step.skipDegree, step.maxTimes);
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writePaths("paths", paths, false,
|
||||
iterVertex, iterEdge);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
|
|
@ -136,15 +152,17 @@ public class TemplatePathsAPI extends TraverserAPI {
|
|||
public int limit = Integer.parseInt(DEFAULT_PATHS_LIMIT);
|
||||
@JsonProperty("with_vertex")
|
||||
public boolean withVertex = false;
|
||||
@JsonProperty("with_edge")
|
||||
public boolean withEdge = false;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("TemplatePathsRequest{sources=%s,targets=%s," +
|
||||
"steps=%s,withRing=%s,capacity=%s,limit=%s," +
|
||||
"withVertex=%s}",
|
||||
"withVertex=%s,withEdge=%s}",
|
||||
this.sources, this.targets, this.steps,
|
||||
this.withRing, this.capacity, this.limit,
|
||||
this.withVertex);
|
||||
this.withVertex, this.withEdge);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,22 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_PAG
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.backend.store.Shard;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
|
|
@ -32,22 +48,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.filter.CompressInterceptor.Compress;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.ConditionQuery;
|
||||
import org.apache.hugegraph.backend.store.Shard;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/vertices")
|
||||
@Singleton
|
||||
@Tag(name = "VerticesAPI")
|
||||
|
|
|
|||
|
|
@ -21,6 +21,25 @@ import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_CAP
|
|||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.DEFAULT_MAX_DEGREE;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.inject.Singleton;
|
||||
|
|
@ -32,23 +51,6 @@ import jakarta.ws.rs.Produces;
|
|||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.hugegraph.core.GraphManager;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.api.graph.EdgeAPI;
|
||||
import org.apache.hugegraph.api.graph.VertexAPI;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.NodeWithWeight;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
|
||||
@Path("graphs/{graph}/traversers/weightedshortestpath")
|
||||
@Singleton
|
||||
@Tag(name = "WeightedShortestPathAPI")
|
||||
|
|
@ -70,16 +72,20 @@ public class WeightedShortestPathAPI extends API {
|
|||
@DefaultValue(DEFAULT_MAX_DEGREE) long maxDegree,
|
||||
@QueryParam("skip_degree")
|
||||
@DefaultValue("0") long skipDegree,
|
||||
@QueryParam("with_vertex")
|
||||
@DefaultValue("false") boolean withVertex,
|
||||
@QueryParam("with_edge")
|
||||
@DefaultValue("false") boolean withEdge,
|
||||
@QueryParam("capacity")
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity,
|
||||
@QueryParam("with_vertex") boolean withVertex) {
|
||||
@DefaultValue(DEFAULT_CAPACITY) long capacity) {
|
||||
LOG.debug("Graph [{}] get weighted shortest path between '{}' and " +
|
||||
"'{}' with direction {}, edge label {}, weight property {}, " +
|
||||
"max degree '{}', skip degree '{}', capacity '{}', " +
|
||||
"and with vertex '{}'",
|
||||
"with_vertex '{}' and with_edge '{}'",
|
||||
graph, source, target, direction, edgeLabel, weight,
|
||||
maxDegree, skipDegree, capacity, withVertex);
|
||||
maxDegree, skipDegree, capacity, withVertex, withEdge);
|
||||
|
||||
ApiMeasurer measure = new ApiMeasurer();
|
||||
Id sourceId = VertexAPI.checkAndParseVertexId(source);
|
||||
Id targetId = VertexAPI.checkAndParseVertexId(target);
|
||||
Directions dir = Directions.convert(EdgeAPI.parseDirection(direction));
|
||||
|
|
@ -89,14 +95,38 @@ public class WeightedShortestPathAPI extends API {
|
|||
SingleSourceShortestPathTraverser traverser =
|
||||
new SingleSourceShortestPathTraverser(g);
|
||||
|
||||
NodeWithWeight path = traverser.weightedShortestPath(
|
||||
sourceId, targetId, dir, edgeLabel, weight,
|
||||
maxDegree, skipDegree, capacity);
|
||||
Iterator<Vertex> iterator = QueryResults.emptyIterator();
|
||||
if (path != null && withVertex) {
|
||||
assert !path.node().path().isEmpty();
|
||||
iterator = g.vertices(path.node().path().toArray());
|
||||
SingleSourceShortestPathTraverser.NodeWithWeight node =
|
||||
traverser.weightedShortestPath(sourceId, targetId,
|
||||
dir, edgeLabel, weight,
|
||||
maxDegree, skipDegree, capacity);
|
||||
measure.addIterCount(traverser.vertexIterCounter.get(),
|
||||
traverser.edgeIterCounter.get());
|
||||
|
||||
if (node == null) {
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeWeightedPath(null,
|
||||
QueryResults.emptyIterator(),
|
||||
QueryResults.emptyIterator());
|
||||
}
|
||||
return manager.serializer(g).writeWeightedPath(path, iterator);
|
||||
|
||||
Iterator<?> iterVertex;
|
||||
List<Id> vertexIds = node.node().path();
|
||||
if (withVertex && !vertexIds.isEmpty()) {
|
||||
iterVertex = g.vertices(vertexIds.toArray());
|
||||
measure.addIterCount(vertexIds.size(), 0L);
|
||||
} else {
|
||||
iterVertex = vertexIds.iterator();
|
||||
}
|
||||
|
||||
Iterator<?> iterEdge;
|
||||
Set<Edge> edges = node.getEdges();
|
||||
if (withEdge && !edges.isEmpty()) {
|
||||
iterEdge = edges.iterator();
|
||||
} else {
|
||||
iterEdge = HugeTraverser.EdgeRecord.getEdgeIds(edges).iterator();
|
||||
}
|
||||
|
||||
return manager.serializer(g, measure.measures())
|
||||
.writeWeightedPath(node, iterVertex, iterEdge);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,4 +264,4 @@ public class ServerOptions extends OptionHolder {
|
|||
disallowEmpty(),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -224,6 +224,10 @@ public final class GraphManager {
|
|||
return JsonSerializer.instance();
|
||||
}
|
||||
|
||||
public Serializer serializer(Graph g, Map<String, Object> apiMeasure) {
|
||||
return JsonSerializer.instance(apiMeasure);
|
||||
}
|
||||
|
||||
public void rollbackAll() {
|
||||
for (Graph graph : this.graphs.values()) {
|
||||
if (graph.features().graph().supportsTransactions() &&
|
||||
|
|
|
|||
|
|
@ -24,11 +24,6 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
|
||||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.api.API;
|
||||
import org.apache.hugegraph.auth.SchemaDefine.AuthElement;
|
||||
|
|
@ -47,25 +42,44 @@ import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverse
|
|||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.WeightedPaths;
|
||||
import org.apache.hugegraph.traversal.optimize.TraversalUtil;
|
||||
import org.apache.hugegraph.util.JsonUtil;
|
||||
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class JsonSerializer implements Serializer {
|
||||
|
||||
private static final int LBUF_SIZE = 1024;
|
||||
|
||||
private static JsonSerializer INSTANCE = new JsonSerializer();
|
||||
private static final String MEASURE_KEY = "measure";
|
||||
private static final JsonSerializer INSTANCE = new JsonSerializer();
|
||||
private Map<String, Object> apiMeasure = null;
|
||||
|
||||
private JsonSerializer() {
|
||||
}
|
||||
|
||||
private JsonSerializer(Map<String, Object> apiMeasure) {
|
||||
this.apiMeasure = apiMeasure;
|
||||
}
|
||||
|
||||
public static JsonSerializer instance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static JsonSerializer instance(Map<String, Object> apiMeasure) {
|
||||
return new JsonSerializer(apiMeasure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeMap(Map<?, ?> map) {
|
||||
return JsonUtil.toJson(map);
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder();
|
||||
builder.putAll(map);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -73,6 +87,10 @@ public class JsonSerializer implements Serializer {
|
|||
try (ByteArrayOutputStream out = new ByteArrayOutputStream(LBUF_SIZE)) {
|
||||
out.write(String.format("{\"%s\": ", label).getBytes(API.CHARSET));
|
||||
out.write(JsonUtil.toJson(list).getBytes(API.CHARSET));
|
||||
if (this.apiMeasure != null) {
|
||||
out.write(String.format(",\"%s\": ", MEASURE_KEY).getBytes(API.CHARSET));
|
||||
out.write(JsonUtil.toJson(this.apiMeasure).getBytes(API.CHARSET));
|
||||
}
|
||||
out.write("}".getBytes(API.CHARSET));
|
||||
return out.toString(API.CHARSET);
|
||||
} catch (Exception e) {
|
||||
|
|
@ -122,6 +140,11 @@ public class JsonSerializer implements Serializer {
|
|||
out.write(page.getBytes(API.CHARSET));
|
||||
}
|
||||
|
||||
if (this.apiMeasure != null) {
|
||||
out.write(String.format(",\"%s\":[", MEASURE_KEY).getBytes(API.CHARSET));
|
||||
out.write(JsonUtil.toJson(this.apiMeasure).getBytes(API.CHARSET));
|
||||
}
|
||||
|
||||
out.write("}".getBytes(API.CHARSET));
|
||||
return out.toString(API.CHARSET);
|
||||
} catch (HugeException e) {
|
||||
|
|
@ -144,7 +167,7 @@ public class JsonSerializer implements Serializer {
|
|||
|
||||
@Override
|
||||
public String writeTaskWithSchema(
|
||||
SchemaElement.TaskWithSchema taskWithSchema) {
|
||||
SchemaElement.TaskWithSchema taskWithSchema) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
long id = taskWithSchema.task() == null ?
|
||||
0L : taskWithSchema.task().asLong();
|
||||
|
|
@ -162,10 +185,14 @@ public class JsonSerializer implements Serializer {
|
|||
"TaskWithSchema, only support " +
|
||||
"[PropertyKey, IndexLabel]", schemaElement);
|
||||
}
|
||||
return builder.append("{\"").append(type).append("\": ")
|
||||
.append(schema)
|
||||
.append(", \"task_id\": ").append(id).append("}")
|
||||
.toString();
|
||||
builder.append("{\"").append(type).append("\": ")
|
||||
.append(schema).append(", \"task_id\": ")
|
||||
.append(id);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.append(String.format(",\"%s\":[", MEASURE_KEY));
|
||||
builder.append(JsonUtil.toJson(this.apiMeasure));
|
||||
}
|
||||
return builder.append("}").toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -245,27 +272,36 @@ public class JsonSerializer implements Serializer {
|
|||
|
||||
@Override
|
||||
public String writePaths(String name, Collection<HugeTraverser.Path> paths,
|
||||
boolean withCrossPoint,
|
||||
Iterator<Vertex> vertices) {
|
||||
boolean withCrossPoint, Iterator<?> vertices,
|
||||
Iterator<?> edges) {
|
||||
List<Map<String, Object>> pathList = new ArrayList<>(paths.size());
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
pathList.add(path.toMap(withCrossPoint));
|
||||
}
|
||||
|
||||
Map<String, Object> results;
|
||||
if (vertices == null) {
|
||||
results = ImmutableMap.of(name, pathList);
|
||||
} else {
|
||||
results = ImmutableMap.of(name, pathList, "vertices", vertices);
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder();
|
||||
builder.put(name, pathList);
|
||||
|
||||
if (vertices != null) {
|
||||
builder.put("vertices", vertices);
|
||||
}
|
||||
return JsonUtil.toJson(results);
|
||||
|
||||
if (edges != null) {
|
||||
builder.put("edges", edges);
|
||||
}
|
||||
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeCrosspoints(CrosspointsPaths paths,
|
||||
Iterator<Vertex> iterator,
|
||||
Iterator<?> vertices,
|
||||
Iterator<?> edges,
|
||||
boolean withPath) {
|
||||
Map<String, Object> results;
|
||||
List<Map<String, Object>> pathList;
|
||||
if (withPath) {
|
||||
pathList = new ArrayList<>();
|
||||
|
|
@ -275,50 +311,81 @@ public class JsonSerializer implements Serializer {
|
|||
} else {
|
||||
pathList = ImmutableList.of();
|
||||
}
|
||||
results = ImmutableMap.of("crosspoints", paths.crosspoints(),
|
||||
"paths", pathList,
|
||||
"vertices", iterator);
|
||||
return JsonUtil.toJson(results);
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
|
||||
.put("crosspoints",
|
||||
paths.crosspoints())
|
||||
.put("paths", pathList)
|
||||
.put("vertices", vertices)
|
||||
.put("edges", edges);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeSimilars(SimilarsMap similars,
|
||||
Iterator<Vertex> vertices) {
|
||||
return JsonUtil.toJson(ImmutableMap.of("similars", similars.toMap(),
|
||||
"vertices", vertices));
|
||||
Iterator<?> vertices) {
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
|
||||
.put("similars",
|
||||
similars.toMap())
|
||||
.put("vertices", vertices);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeWeightedPath(NodeWithWeight path,
|
||||
Iterator<Vertex> vertices) {
|
||||
public String writeWeightedPath(NodeWithWeight path, Iterator<?> vertices,
|
||||
Iterator<?> edges) {
|
||||
Map<String, Object> pathMap = path == null ?
|
||||
ImmutableMap.of() : path.toMap();
|
||||
return JsonUtil.toJson(ImmutableMap.of("path", pathMap,
|
||||
"vertices", vertices));
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
|
||||
.put("path", pathMap)
|
||||
.put("vertices", vertices)
|
||||
.put("edges", edges);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeWeightedPaths(WeightedPaths paths,
|
||||
Iterator<Vertex> vertices) {
|
||||
public String writeWeightedPaths(WeightedPaths paths, Iterator<?> vertices,
|
||||
Iterator<?> edges) {
|
||||
Map<Id, Map<String, Object>> pathMap = paths == null ?
|
||||
ImmutableMap.of() :
|
||||
paths.toMap();
|
||||
return JsonUtil.toJson(ImmutableMap.of("paths", pathMap,
|
||||
"vertices", vertices));
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
|
||||
.put("paths", pathMap)
|
||||
.put("vertices", vertices)
|
||||
.put("edges", edges);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeNodesWithPath(String name, List<Id> nodes, long size,
|
||||
Collection<HugeTraverser.Path> paths,
|
||||
Iterator<Vertex> vertices) {
|
||||
Iterator<?> vertices, Iterator<?> edges) {
|
||||
List<Map<String, Object>> pathList = new ArrayList<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
pathList.add(path.toMap(false));
|
||||
}
|
||||
|
||||
Map<String, Object> results;
|
||||
results = ImmutableMap.of(name, nodes, "size", size,
|
||||
"paths", pathList, "vertices", vertices);
|
||||
return JsonUtil.toJson(results);
|
||||
ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder()
|
||||
.put(name, nodes)
|
||||
.put("size", size)
|
||||
.put("paths", pathList)
|
||||
.put("vertices", vertices)
|
||||
.put("edges", edges);
|
||||
if (this.apiMeasure != null) {
|
||||
builder.put(MEASURE_KEY, this.apiMeasure);
|
||||
}
|
||||
|
||||
return JsonUtil.toJson(builder.build());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
import org.apache.hugegraph.auth.SchemaDefine.AuthElement;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.schema.EdgeLabel;
|
||||
|
|
@ -37,6 +34,8 @@ import org.apache.hugegraph.traversal.algorithm.FusiformSimilarityTraverser.Simi
|
|||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.NodeWithWeight;
|
||||
import org.apache.hugegraph.traversal.algorithm.SingleSourceShortestPathTraverser.WeightedPaths;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
public interface Serializer {
|
||||
|
||||
|
|
@ -77,23 +76,26 @@ public interface Serializer {
|
|||
<V extends AuthElement> String writeAuthElements(String label, List<V> users);
|
||||
|
||||
String writePaths(String name, Collection<HugeTraverser.Path> paths,
|
||||
boolean withCrossPoint, Iterator<Vertex> vertices);
|
||||
boolean withCrossPoint, Iterator<?> vertices,
|
||||
Iterator<?> edges);
|
||||
|
||||
default String writePaths(String name, Collection<HugeTraverser.Path> paths,
|
||||
boolean withCrossPoint) {
|
||||
return this.writePaths(name, paths, withCrossPoint, null);
|
||||
return this.writePaths(name, paths, withCrossPoint, null, null);
|
||||
}
|
||||
|
||||
String writeCrosspoints(CrosspointsPaths paths, Iterator<Vertex> iterator,
|
||||
boolean withPath);
|
||||
String writeCrosspoints(CrosspointsPaths paths, Iterator<?> vertices,
|
||||
Iterator<?> edges, boolean withPath);
|
||||
|
||||
String writeSimilars(SimilarsMap similars, Iterator<Vertex> vertices);
|
||||
String writeSimilars(SimilarsMap similars, Iterator<?> vertices);
|
||||
|
||||
String writeWeightedPath(NodeWithWeight path, Iterator<Vertex> vertices);
|
||||
String writeWeightedPath(NodeWithWeight path, Iterator<?> vertices,
|
||||
Iterator<?> edges);
|
||||
|
||||
String writeWeightedPaths(WeightedPaths paths, Iterator<Vertex> vertices);
|
||||
String writeWeightedPaths(WeightedPaths paths, Iterator<?> vertices,
|
||||
Iterator<?> edges);
|
||||
|
||||
String writeNodesWithPath(String name, List<Id> nodes, long size,
|
||||
Collection<HugeTraverser.Path> paths,
|
||||
Iterator<Vertex> vertices);
|
||||
Iterator<?> vertices, Iterator<?> edges);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,19 +26,29 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
import org.apache.commons.lang.mutable.MutableLong;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.id.IdGenerator;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import org.apache.hugegraph.job.UserJob;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class TriangleCountAlgorithm extends AbstractCommAlgorithm {
|
||||
|
||||
public static final String ALGO_NAME = "triangle_count";
|
||||
|
||||
protected static int workersWhenBoth(Map<String, Object> parameters) {
|
||||
Directions direction = direction4Out(parameters);
|
||||
int workers = workers(parameters);
|
||||
E.checkArgument(direction == Directions.BOTH || workers <= 0,
|
||||
"The workers must be not set when direction!=BOTH, " +
|
||||
"but got workers=%s and direction=%s",
|
||||
workers, direction);
|
||||
return workers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return ALGO_NAME;
|
||||
|
|
@ -60,16 +70,6 @@ public class TriangleCountAlgorithm extends AbstractCommAlgorithm {
|
|||
}
|
||||
}
|
||||
|
||||
protected static int workersWhenBoth(Map<String, Object> parameters) {
|
||||
Directions direction = direction4Out(parameters);
|
||||
int workers = workers(parameters);
|
||||
E.checkArgument(direction == Directions.BOTH || workers <= 0,
|
||||
"The workers must be not set when direction!=BOTH, " +
|
||||
"but got workers=%s and direction=%s",
|
||||
workers, direction);
|
||||
return workers;
|
||||
}
|
||||
|
||||
protected static class Traverser extends AlgoTraverser {
|
||||
|
||||
protected static final String KEY_TRIANGLES = "triangles";
|
||||
|
|
@ -83,8 +83,12 @@ public class TriangleCountAlgorithm extends AbstractCommAlgorithm {
|
|||
super(job, name, workers);
|
||||
}
|
||||
|
||||
protected static <V> Set<V> newOrderedSet() {
|
||||
return new TreeSet<>();
|
||||
}
|
||||
|
||||
public Object triangleCount(Directions direction, long degree) {
|
||||
Map<String, Long> results = triangles( direction, degree);
|
||||
Map<String, Long> results = triangles(direction, degree);
|
||||
results = InsertionOrderUtil.newMap(results);
|
||||
results.remove(KEY_TRIADS);
|
||||
return results;
|
||||
|
|
@ -191,7 +195,7 @@ public class TriangleCountAlgorithm extends AbstractCommAlgorithm {
|
|||
MutableLong edgesCount) {
|
||||
Iterator<Id> adjVertices = this.adjacentVertices(source,
|
||||
Directions.BOTH,
|
||||
null, degree);
|
||||
(Id) null, degree);
|
||||
Set<Id> set = newOrderedSet();
|
||||
while (adjVertices.hasNext()) {
|
||||
edgesCount.increment();
|
||||
|
|
@ -206,7 +210,7 @@ public class TriangleCountAlgorithm extends AbstractCommAlgorithm {
|
|||
Id empty = IdGenerator.ZERO;
|
||||
Iterator<Id> vertices;
|
||||
for (Id v : adjVertices) {
|
||||
vertices = this.adjacentVertices(v, dir, null, degree);
|
||||
vertices = this.adjacentVertices(v, dir, (Id) null, degree);
|
||||
Id lastVertex = empty;
|
||||
while (vertices.hasNext()) {
|
||||
Id vertex = vertices.next();
|
||||
|
|
@ -231,9 +235,5 @@ public class TriangleCountAlgorithm extends AbstractCommAlgorithm {
|
|||
protected long localTriads(int size) {
|
||||
return size * (size - 1L) / 2L;
|
||||
}
|
||||
|
||||
protected static <V> Set<V> newOrderedSet() {
|
||||
return new TreeSet<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,15 +21,17 @@ import java.util.Collection;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.strategy.TraverseStrategy;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class CollectionPathsTraverser extends HugeTraverser {
|
||||
|
|
@ -38,10 +40,10 @@ public class CollectionPathsTraverser extends HugeTraverser {
|
|||
super(graph);
|
||||
}
|
||||
|
||||
public Collection<Path> paths(Iterator<Vertex> sources,
|
||||
Iterator<Vertex> targets,
|
||||
EdgeStep step, int depth, boolean nearest,
|
||||
long capacity, long limit) {
|
||||
public WrappedPathCollection paths(Iterator<Vertex> sources,
|
||||
Iterator<Vertex> targets,
|
||||
EdgeStep step, int depth, boolean nearest,
|
||||
long capacity, long limit) {
|
||||
checkCapacity(capacity);
|
||||
checkLimit(limit);
|
||||
|
||||
|
|
@ -63,31 +65,33 @@ public class CollectionPathsTraverser extends HugeTraverser {
|
|||
"but got: %s", MAX_VERTICES, sourceList.size());
|
||||
checkPositive(depth, "max depth");
|
||||
|
||||
boolean concurrent = depth >= this.concurrentDepth();
|
||||
TraverseStrategy strategy = TraverseStrategy.create(
|
||||
depth >= this.concurrentDepth(),
|
||||
this.graph());
|
||||
concurrent, this.graph());
|
||||
Traverser traverser;
|
||||
if (nearest) {
|
||||
traverser = new NearestTraverser(this, strategy,
|
||||
sourceList, targetList, step,
|
||||
depth, capacity, limit);
|
||||
depth, capacity, limit, concurrent);
|
||||
} else {
|
||||
traverser = new Traverser(this, strategy,
|
||||
sourceList, targetList, step,
|
||||
depth, capacity, limit);
|
||||
depth, capacity, limit, concurrent);
|
||||
}
|
||||
|
||||
do {
|
||||
// Forward
|
||||
traverser.forward();
|
||||
if (traverser.finished()) {
|
||||
return traverser.paths();
|
||||
Collection<Path> paths = traverser.paths();
|
||||
return new WrappedPathCollection(paths, traverser.edgeResults.getEdges(paths));
|
||||
}
|
||||
|
||||
// Backward
|
||||
traverser.backward();
|
||||
if (traverser.finished()) {
|
||||
return traverser.paths();
|
||||
Collection<Path> paths = traverser.paths();
|
||||
return new WrappedPathCollection(paths, traverser.edgeResults.getEdges(paths));
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
|
@ -98,8 +102,9 @@ public class CollectionPathsTraverser extends HugeTraverser {
|
|||
|
||||
public Traverser(HugeTraverser traverser, TraverseStrategy strategy,
|
||||
Collection<Id> sources, Collection<Id> targets,
|
||||
EdgeStep step, int depth, long capacity, long limit) {
|
||||
super(traverser, strategy, sources, targets, capacity, limit);
|
||||
EdgeStep step, int depth, long capacity, long limit,
|
||||
boolean concurrent) {
|
||||
super(traverser, strategy, sources, targets, capacity, limit, concurrent);
|
||||
this.step = step;
|
||||
this.totalSteps = depth;
|
||||
}
|
||||
|
|
@ -180,15 +185,15 @@ public class CollectionPathsTraverser extends HugeTraverser {
|
|||
}
|
||||
}
|
||||
|
||||
private class NearestTraverser extends Traverser {
|
||||
private static class NearestTraverser extends Traverser {
|
||||
|
||||
public NearestTraverser(HugeTraverser traverser,
|
||||
TraverseStrategy strategy,
|
||||
Collection<Id> sources, Collection<Id> targets,
|
||||
EdgeStep step, int depth, long capacity,
|
||||
long limit) {
|
||||
long limit, boolean concurrent) {
|
||||
super(traverser, strategy, sources, targets, step,
|
||||
depth, capacity, limit);
|
||||
depth, capacity, limit, concurrent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -274,4 +279,23 @@ public class CollectionPathsTraverser extends HugeTraverser {
|
|||
return this.sourcesAll.size() + this.targetsAll.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static class WrappedPathCollection {
|
||||
|
||||
private final Collection<Path> paths;
|
||||
private final Set<Edge> edges;
|
||||
|
||||
public WrappedPathCollection(Collection<Path> paths, Set<Edge> edges) {
|
||||
this.paths = paths;
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Collection<Path> paths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public Set<Edge> edges() {
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,25 +22,55 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.WeightedEdgeStep;
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public class CustomizePathsTraverser extends HugeTraverser {
|
||||
|
||||
private final EdgeRecord edgeResults;
|
||||
|
||||
public CustomizePathsTraverser(HugeGraph graph) {
|
||||
super(graph);
|
||||
this.edgeResults = new EdgeRecord(false);
|
||||
}
|
||||
|
||||
public static List<Path> topNPath(List<Path> paths,
|
||||
boolean incr, long limit) {
|
||||
paths.sort((p1, p2) -> {
|
||||
WeightPath wp1 = (WeightPath) p1;
|
||||
WeightPath wp2 = (WeightPath) p2;
|
||||
int result = Double.compare(wp1.totalWeight(), wp2.totalWeight());
|
||||
return incr ? result : -result;
|
||||
});
|
||||
|
||||
if (limit == NO_LIMIT || paths.size() <= limit) {
|
||||
return paths;
|
||||
}
|
||||
return paths.subList(0, (int) limit);
|
||||
}
|
||||
|
||||
private static List<Node> sample(List<Node> nodes, long sample) {
|
||||
if (nodes.size() <= sample) {
|
||||
return nodes;
|
||||
}
|
||||
List<Node> result = newList((int) sample);
|
||||
int size = nodes.size();
|
||||
for (int random : CollectionUtil.randomSet(0, size, (int) sample)) {
|
||||
result.add(nodes.get(random));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Path> customizedPaths(Iterator<Vertex> vertices,
|
||||
|
|
@ -64,7 +94,8 @@ public class CustomizePathsTraverser extends HugeTraverser {
|
|||
int pathCount = 0;
|
||||
long access = 0;
|
||||
MultivaluedMap<Id, Node> newVertices = null;
|
||||
root : for (WeightedEdgeStep step : steps) {
|
||||
root:
|
||||
for (WeightedEdgeStep step : steps) {
|
||||
stepNum--;
|
||||
newVertices = newMultivalueMap();
|
||||
Iterator<Edge> edges;
|
||||
|
|
@ -75,7 +106,11 @@ public class CustomizePathsTraverser extends HugeTraverser {
|
|||
edges = this.edgesOfVertex(entry.getKey(), step.step());
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
this.edgeIterCounter.addAndGet(1L);
|
||||
Id target = edge.id().otherVertexId();
|
||||
|
||||
this.edgeResults.addEdge(entry.getKey(), target, edge);
|
||||
|
||||
for (Node n : entry.getValue()) {
|
||||
// If have loop, skip target
|
||||
if (n.contains(target)) {
|
||||
|
|
@ -113,6 +148,7 @@ public class CustomizePathsTraverser extends HugeTraverser {
|
|||
}
|
||||
}
|
||||
}
|
||||
this.vertexIterCounter.addAndGet(sources.size());
|
||||
// Re-init sources
|
||||
sources = newVertices;
|
||||
}
|
||||
|
|
@ -120,6 +156,9 @@ public class CustomizePathsTraverser extends HugeTraverser {
|
|||
return ImmutableList.of();
|
||||
}
|
||||
List<Path> paths = newList();
|
||||
if (newVertices == null) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
for (List<Node> nodes : newVertices.values()) {
|
||||
for (Node n : nodes) {
|
||||
if (sorted) {
|
||||
|
|
@ -133,36 +172,13 @@ public class CustomizePathsTraverser extends HugeTraverser {
|
|||
return paths;
|
||||
}
|
||||
|
||||
public static List<Path> topNPath(List<Path> paths,
|
||||
boolean incr, long limit) {
|
||||
paths.sort((p1, p2) -> {
|
||||
WeightPath wp1 = (WeightPath) p1;
|
||||
WeightPath wp2 = (WeightPath) p2;
|
||||
int result = Double.compare(wp1.totalWeight(), wp2.totalWeight());
|
||||
return incr ? result : -result;
|
||||
});
|
||||
|
||||
if (limit == NO_LIMIT || paths.size() <= limit) {
|
||||
return paths;
|
||||
}
|
||||
return paths.subList(0, (int) limit);
|
||||
}
|
||||
|
||||
private static List<Node> sample(List<Node> nodes, long sample) {
|
||||
if (nodes.size() <= sample) {
|
||||
return nodes;
|
||||
}
|
||||
List<Node> result = newList((int) sample);
|
||||
int size = nodes.size();
|
||||
for (int random : CollectionUtil.randomSet(0, size, (int) sample)) {
|
||||
result.add(nodes.get(random));
|
||||
}
|
||||
return result;
|
||||
public EdgeRecord edgeResults() {
|
||||
return edgeResults;
|
||||
}
|
||||
|
||||
public static class WeightNode extends Node {
|
||||
|
||||
private double weight;
|
||||
private final double weight;
|
||||
|
||||
public WeightNode(Id id, Node parent, double weight) {
|
||||
super(id, parent);
|
||||
|
|
@ -183,7 +199,7 @@ public class CustomizePathsTraverser extends HugeTraverser {
|
|||
|
||||
public static class WeightPath extends Path {
|
||||
|
||||
private List<Double> weights;
|
||||
private final List<Double> weights;
|
||||
private double totalWeight;
|
||||
|
||||
public WeightPath(List<Id> vertices,
|
||||
|
|
|
|||
|
|
@ -24,93 +24,29 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public class CustomizedCrosspointsTraverser extends HugeTraverser {
|
||||
|
||||
private final EdgeRecord edgeResults;
|
||||
|
||||
public CustomizedCrosspointsTraverser(HugeGraph graph) {
|
||||
super(graph);
|
||||
}
|
||||
|
||||
public CrosspointsPaths crosspointsPaths(Iterator<Vertex> vertices,
|
||||
List<PathPattern> pathPatterns,
|
||||
long capacity, long limit) {
|
||||
E.checkArgument(vertices.hasNext(),
|
||||
"The source vertices can't be empty");
|
||||
E.checkArgument(!pathPatterns.isEmpty(),
|
||||
"The steps pattern can't be empty");
|
||||
checkCapacity(capacity);
|
||||
checkLimit(limit);
|
||||
MultivaluedMap<Id, Node> initialSources = newMultivalueMap();
|
||||
List<HugeVertex> verticesList = newList();
|
||||
while (vertices.hasNext()) {
|
||||
HugeVertex vertex = (HugeVertex) vertices.next();
|
||||
verticesList.add(vertex);
|
||||
Node node = new Node(vertex.id(), null);
|
||||
initialSources.add(vertex.id(), node);
|
||||
}
|
||||
List<Path> paths = newList();
|
||||
|
||||
for (PathPattern pathPattern : pathPatterns) {
|
||||
MultivaluedMap<Id, Node> sources = initialSources;
|
||||
int stepNum = pathPattern.size();
|
||||
long access = 0;
|
||||
MultivaluedMap<Id, Node> newVertices = null;
|
||||
for (Step step : pathPattern.steps()) {
|
||||
stepNum--;
|
||||
newVertices = newMultivalueMap();
|
||||
Iterator<Edge> edges;
|
||||
|
||||
// Traversal vertices of previous level
|
||||
for (Map.Entry<Id, List<Node>> entry : sources.entrySet()) {
|
||||
List<Node> adjacency = newList();
|
||||
edges = this.edgesOfVertex(entry.getKey(), step.edgeStep);
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
for (Node n : entry.getValue()) {
|
||||
// If have loop, skip target
|
||||
if (n.contains(target)) {
|
||||
continue;
|
||||
}
|
||||
Node newNode = new Node(target, n);
|
||||
adjacency.add(newNode);
|
||||
|
||||
checkCapacity(capacity, ++access,
|
||||
"customized crosspoints");
|
||||
}
|
||||
}
|
||||
|
||||
// Add current node's adjacent nodes
|
||||
for (Node node : adjacency) {
|
||||
newVertices.add(node.id(), node);
|
||||
}
|
||||
}
|
||||
// Re-init sources
|
||||
sources = newVertices;
|
||||
}
|
||||
assert stepNum == 0;
|
||||
for (List<Node> nodes : newVertices.values()) {
|
||||
for (Node n : nodes) {
|
||||
paths.add(new Path(n.path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return intersectionPaths(verticesList, paths, limit);
|
||||
this.edgeResults = new EdgeRecord(false);
|
||||
}
|
||||
|
||||
private static CrosspointsPaths intersectionPaths(List<HugeVertex> sources,
|
||||
|
|
@ -162,9 +98,90 @@ public class CustomizedCrosspointsTraverser extends HugeTraverser {
|
|||
return new CrosspointsPaths(newSet(intersection), results);
|
||||
}
|
||||
|
||||
public EdgeRecord edgeResults() {
|
||||
return edgeResults;
|
||||
}
|
||||
|
||||
public CrosspointsPaths crosspointsPaths(Iterator<Vertex> vertices,
|
||||
List<PathPattern> pathPatterns,
|
||||
long capacity, long limit) {
|
||||
E.checkArgument(vertices.hasNext(),
|
||||
"The source vertices can't be empty");
|
||||
E.checkArgument(!pathPatterns.isEmpty(),
|
||||
"The steps pattern can't be empty");
|
||||
checkCapacity(capacity);
|
||||
checkLimit(limit);
|
||||
MultivaluedMap<Id, Node> initialSources = newMultivalueMap();
|
||||
List<HugeVertex> verticesList = newList();
|
||||
while (vertices.hasNext()) {
|
||||
HugeVertex vertex = (HugeVertex) vertices.next();
|
||||
verticesList.add(vertex);
|
||||
Node node = new Node(vertex.id(), null);
|
||||
initialSources.add(vertex.id(), node);
|
||||
}
|
||||
List<Path> paths = newList();
|
||||
long edgeCount = 0L;
|
||||
long vertexCount = 0L;
|
||||
|
||||
for (PathPattern pathPattern : pathPatterns) {
|
||||
MultivaluedMap<Id, Node> sources = initialSources;
|
||||
int stepNum = pathPattern.size();
|
||||
long access = 0;
|
||||
MultivaluedMap<Id, Node> newVertices = null;
|
||||
for (Step step : pathPattern.steps()) {
|
||||
stepNum--;
|
||||
newVertices = newMultivalueMap();
|
||||
Iterator<Edge> edges;
|
||||
|
||||
// Traversal vertices of previous level
|
||||
for (Map.Entry<Id, List<Node>> entry : sources.entrySet()) {
|
||||
List<Node> adjacency = newList();
|
||||
edges = this.edgesOfVertex(entry.getKey(), step.edgeStep);
|
||||
vertexCount += 1;
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
edgeCount += 1;
|
||||
Id target = edge.id().otherVertexId();
|
||||
|
||||
this.edgeResults.addEdge(entry.getKey(), target, edge);
|
||||
|
||||
for (Node n : entry.getValue()) {
|
||||
// If have loop, skip target
|
||||
if (n.contains(target)) {
|
||||
continue;
|
||||
}
|
||||
Node newNode = new Node(target, n);
|
||||
adjacency.add(newNode);
|
||||
|
||||
checkCapacity(capacity, ++access,
|
||||
"customized crosspoints");
|
||||
}
|
||||
}
|
||||
|
||||
// Add current node's adjacent nodes
|
||||
for (Node node : adjacency) {
|
||||
newVertices.add(node.id(), node);
|
||||
}
|
||||
}
|
||||
// Re-init sources
|
||||
sources = newVertices;
|
||||
}
|
||||
assert stepNum == 0;
|
||||
assert newVertices != null;
|
||||
for (List<Node> nodes : newVertices.values()) {
|
||||
for (Node n : nodes) {
|
||||
paths.add(new Path(n.path()));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.vertexIterCounter.addAndGet(vertexCount);
|
||||
this.edgeIterCounter.addAndGet(edgeCount);
|
||||
return intersectionPaths(verticesList, paths, limit);
|
||||
}
|
||||
|
||||
public static class PathPattern {
|
||||
|
||||
private List<Step> steps;
|
||||
private final List<Step> steps;
|
||||
|
||||
public PathPattern() {
|
||||
this.steps = newList();
|
||||
|
|
@ -201,8 +218,8 @@ public class CustomizedCrosspointsTraverser extends HugeTraverser {
|
|||
ImmutableSet.of(), ImmutableList.of()
|
||||
);
|
||||
|
||||
private Set<Id> crosspoints;
|
||||
private List<Path> paths;
|
||||
private final Set<Id> crosspoints;
|
||||
private final List<Path> paths;
|
||||
|
||||
public CrosspointsPaths(Set<Id> crosspoints, List<Path> paths) {
|
||||
this.crosspoints = crosspoints;
|
||||
|
|
|
|||
|
|
@ -22,27 +22,27 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedHashMap;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.schema.EdgeLabel;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.type.define.Frequency;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedHashMap;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public class FusiformSimilarityTraverser extends HugeTraverser {
|
||||
|
||||
private long accessed = 0L;
|
||||
|
|
@ -51,6 +51,20 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
super(graph);
|
||||
}
|
||||
|
||||
private static void checkGroupArgs(String groupProperty, int minGroups) {
|
||||
if (groupProperty == null) {
|
||||
E.checkArgument(minGroups == 0,
|
||||
"Can't set min group count when " +
|
||||
"group property not set");
|
||||
} else {
|
||||
E.checkArgument(!groupProperty.isEmpty(),
|
||||
"The group property can't be empty");
|
||||
E.checkArgument(minGroups > 0,
|
||||
"Must set min group count when " +
|
||||
"group property set");
|
||||
}
|
||||
}
|
||||
|
||||
public SimilarsMap fusiformSimilarity(Iterator<Vertex> vertices,
|
||||
Directions direction, String label,
|
||||
int minNeighbors, double alpha,
|
||||
|
|
@ -69,10 +83,10 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
HugeVertex vertex = (HugeVertex) vertices.next();
|
||||
// Find fusiform similarity for current vertex
|
||||
Set<Similar> result = this.fusiformSimilarityForVertex(
|
||||
vertex, direction, label,
|
||||
minNeighbors, alpha, minSimilars, top,
|
||||
groupProperty, minGroups, degree, capacity,
|
||||
withIntermediary);
|
||||
vertex, direction, label,
|
||||
minNeighbors, alpha, minSimilars, top,
|
||||
groupProperty, minGroups, degree, capacity,
|
||||
withIntermediary);
|
||||
if (result.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -87,11 +101,11 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
}
|
||||
|
||||
private Set<Similar> fusiformSimilarityForVertex(
|
||||
HugeVertex vertex, Directions direction,
|
||||
String label, int minNeighbors, double alpha,
|
||||
int minSimilars, int top, String groupProperty,
|
||||
int minGroups, long degree, long capacity,
|
||||
boolean withIntermediary) {
|
||||
HugeVertex vertex, Directions direction,
|
||||
String label, int minNeighbors, double alpha,
|
||||
int minSimilars, int top, String groupProperty,
|
||||
int minGroups, long degree, long capacity,
|
||||
boolean withIntermediary) {
|
||||
boolean matched = this.matchMinNeighborCount(vertex, direction, label,
|
||||
minNeighbors, degree);
|
||||
if (!matched) {
|
||||
|
|
@ -105,6 +119,7 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
Map<Id, MutableInt> similars = newMap();
|
||||
MultivaluedMap<Id, Id> intermediaries = new MultivaluedHashMap<>();
|
||||
Set<Id> neighbors = newIdSet();
|
||||
long vertexCount = 1L;
|
||||
while (edges.hasNext()) {
|
||||
Id target = ((HugeEdge) edges.next()).id().otherVertexId();
|
||||
if (neighbors.contains(target)) {
|
||||
|
|
@ -116,6 +131,7 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
Directions backDir = direction.opposite();
|
||||
Iterator<Edge> backEdges = this.edgesOfVertex(target, backDir,
|
||||
labelId, degree);
|
||||
vertexCount += 1L;
|
||||
Set<Id> currentSimilars = newIdSet();
|
||||
while (backEdges.hasNext()) {
|
||||
Id node = ((HugeEdge) backEdges.next()).id().otherVertexId();
|
||||
|
|
@ -137,6 +153,9 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
count.increment();
|
||||
}
|
||||
}
|
||||
this.edgeIterCounter.addAndGet(this.accessed);
|
||||
this.vertexIterCounter.addAndGet(vertexCount);
|
||||
|
||||
// Delete source vertex
|
||||
assert similars.containsKey(vertex.id());
|
||||
similars.remove(vertex.id());
|
||||
|
|
@ -189,20 +208,6 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
return result;
|
||||
}
|
||||
|
||||
private static void checkGroupArgs(String groupProperty, int minGroups) {
|
||||
if (groupProperty == null) {
|
||||
E.checkArgument(minGroups == 0,
|
||||
"Can't set min group count when " +
|
||||
"group property not set");
|
||||
} else {
|
||||
E.checkArgument(!groupProperty.isEmpty(),
|
||||
"The group property can't be empty");
|
||||
E.checkArgument(minGroups > 0,
|
||||
"Must set min group count when " +
|
||||
"group property set");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean matchMinNeighborCount(HugeVertex vertex,
|
||||
Directions direction,
|
||||
String label,
|
||||
|
|
@ -249,7 +254,7 @@ public class FusiformSimilarityTraverser extends HugeTraverser {
|
|||
this.id = id;
|
||||
this.score = score;
|
||||
assert newSet(intermediaries).size() == intermediaries.size() :
|
||||
"Invalid intermediaries";
|
||||
"Invalid intermediaries";
|
||||
this.intermediaries = intermediaries;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@ package org.apache.hugegraph.traversal.algorithm;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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 java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedHashMap;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.hugegraph.HugeException;
|
||||
|
|
@ -39,40 +40,39 @@ import org.apache.hugegraph.backend.query.Query;
|
|||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.backend.tx.GraphTransaction;
|
||||
import org.apache.hugegraph.config.CoreOptions;
|
||||
import org.apache.hugegraph.schema.SchemaLabel;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.type.define.CollectionType;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.type.define.HugeKeys;
|
||||
import org.apache.hugegraph.util.collection.CollectionFactory;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import org.apache.hugegraph.exception.NotFoundException;
|
||||
import org.apache.hugegraph.iterator.ExtendableIterator;
|
||||
import org.apache.hugegraph.iterator.FilterIterator;
|
||||
import org.apache.hugegraph.iterator.LimitIterator;
|
||||
import org.apache.hugegraph.iterator.MapperIterator;
|
||||
import org.apache.hugegraph.perf.PerfUtil.Watched;
|
||||
import org.apache.hugegraph.schema.SchemaLabel;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.optimize.TraversalUtil;
|
||||
import org.apache.hugegraph.type.HugeType;
|
||||
import org.apache.hugegraph.type.define.CollectionType;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.type.define.HugeKeys;
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import org.apache.hugegraph.util.Log;
|
||||
import org.apache.hugegraph.util.collection.CollectionFactory;
|
||||
import org.apache.hugegraph.util.collection.ObjectIntMapping;
|
||||
import org.apache.hugegraph.util.collection.ObjectIntMappingFactory;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedHashMap;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public class HugeTraverser {
|
||||
|
||||
protected static final Logger LOG = Log.logger(HugeTraverser.class);
|
||||
|
||||
private HugeGraph graph;
|
||||
|
||||
private static CollectionFactory collectionFactory;
|
||||
|
||||
public static final String DEFAULT_CAPACITY = "10000000";
|
||||
public static final String DEFAULT_ELEMENTS_LIMIT = "10000000";
|
||||
public static final String DEFAULT_PATHS_LIMIT = "10";
|
||||
|
|
@ -82,13 +82,16 @@ public class HugeTraverser {
|
|||
public static final String DEFAULT_SAMPLE = "100";
|
||||
public static final String DEFAULT_WEIGHT = "0";
|
||||
public static final int DEFAULT_MAX_DEPTH = 5000;
|
||||
|
||||
protected static final int MAX_VERTICES = 10;
|
||||
|
||||
// Empirical value of scan limit, with which results can be returned in 3s
|
||||
public static final String DEFAULT_PAGE_LIMIT = "100000";
|
||||
|
||||
public static final long NO_LIMIT = -1L;
|
||||
protected static final Logger LOG = Log.logger(HugeTraverser.class);
|
||||
protected static final int MAX_VERTICES = 10;
|
||||
private static CollectionFactory collectionFactory;
|
||||
private final HugeGraph graph;
|
||||
// for apimeasure
|
||||
public AtomicLong edgeIterCounter = new AtomicLong(0);
|
||||
public AtomicLong vertexIterCounter = new AtomicLong(0);
|
||||
|
||||
public HugeTraverser(HugeGraph graph) {
|
||||
this.graph = graph;
|
||||
|
|
@ -97,221 +100,6 @@ public class HugeTraverser {
|
|||
}
|
||||
}
|
||||
|
||||
public HugeGraph graph() {
|
||||
return this.graph;
|
||||
}
|
||||
|
||||
protected int concurrentDepth() {
|
||||
return this.graph.option(CoreOptions.OLTP_CONCURRENT_DEPTH);
|
||||
}
|
||||
|
||||
private CollectionType collectionType() {
|
||||
return this.graph.option(CoreOptions.OLTP_COLLECTION_TYPE);
|
||||
}
|
||||
|
||||
protected Set<Id> adjacentVertices(Id sourceV, Set<Id> vertices,
|
||||
Directions dir, Id label,
|
||||
Set<Id> excluded, long degree,
|
||||
long limit) {
|
||||
if (limit == 0) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
Set<Id> neighbors = newIdSet();
|
||||
for (Id source : vertices) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, dir,
|
||||
label, degree);
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge e = (HugeEdge) edges.next();
|
||||
Id target = e.id().otherVertexId();
|
||||
boolean matchExcluded = (excluded != null &&
|
||||
excluded.contains(target));
|
||||
if (matchExcluded || neighbors.contains(target) ||
|
||||
sourceV.equals(target)) {
|
||||
continue;
|
||||
}
|
||||
neighbors.add(target);
|
||||
if (limit != NO_LIMIT && neighbors.size() >= limit) {
|
||||
return neighbors;
|
||||
}
|
||||
}
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
protected Iterator<Id> adjacentVertices(Id source, Directions dir,
|
||||
Id label, long limit) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, dir, label, limit);
|
||||
return new MapperIterator<>(edges, e -> {
|
||||
HugeEdge edge = (HugeEdge) e;
|
||||
return edge.id().otherVertexId();
|
||||
});
|
||||
}
|
||||
|
||||
protected Set<Id> adjacentVertices(Id source, EdgeStep step) {
|
||||
Set<Id> neighbors = newSet();
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, step);
|
||||
while (edges.hasNext()) {
|
||||
neighbors.add(((HugeEdge) edges.next()).id().otherVertexId());
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
@Watched
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, Directions dir,
|
||||
Id label, long limit) {
|
||||
Id[] labels = {};
|
||||
if (label != null) {
|
||||
labels = new Id[]{label};
|
||||
}
|
||||
|
||||
Query query = GraphTransaction.constructEdgesQuery(source, dir, labels);
|
||||
if (limit != NO_LIMIT) {
|
||||
query.limit(limit);
|
||||
}
|
||||
return this.graph.edges(query);
|
||||
}
|
||||
|
||||
@Watched
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, Directions dir,
|
||||
Map<Id, String> labels, long limit) {
|
||||
if (labels == null || labels.isEmpty()) {
|
||||
return this.edgesOfVertex(source, dir, (Id) null, limit);
|
||||
}
|
||||
ExtendableIterator<Edge> results = new ExtendableIterator<>();
|
||||
for (Id label : labels.keySet()) {
|
||||
E.checkNotNull(label, "edge label");
|
||||
results.extend(this.edgesOfVertex(source, dir, label, limit));
|
||||
}
|
||||
|
||||
if (limit == NO_LIMIT) {
|
||||
return results;
|
||||
}
|
||||
|
||||
long[] count = new long[1];
|
||||
return new LimitIterator<>(results, e -> {
|
||||
return count[0]++ >= limit;
|
||||
});
|
||||
}
|
||||
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, EdgeStep edgeStep) {
|
||||
if (edgeStep.properties() == null || edgeStep.properties().isEmpty()) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source,
|
||||
edgeStep.direction(),
|
||||
edgeStep.labels(),
|
||||
edgeStep.limit());
|
||||
return edgeStep.skipSuperNodeIfNeeded(edges);
|
||||
}
|
||||
return this.edgesOfVertex(source, edgeStep, false);
|
||||
}
|
||||
|
||||
protected Iterator<Edge> edgesOfVertexWithSK(Id source, EdgeStep edgeStep) {
|
||||
assert edgeStep.properties() != null && !edgeStep.properties().isEmpty();
|
||||
return this.edgesOfVertex(source, edgeStep, true);
|
||||
}
|
||||
|
||||
private Iterator<Edge> edgesOfVertex(Id source, EdgeStep edgeStep,
|
||||
boolean mustAllSK) {
|
||||
Id[] edgeLabels = edgeStep.edgeLabels();
|
||||
Query query = GraphTransaction.constructEdgesQuery(source,
|
||||
edgeStep.direction(),
|
||||
edgeLabels);
|
||||
ConditionQuery filter = null;
|
||||
if (mustAllSK) {
|
||||
this.fillFilterBySortKeys(query, edgeLabels, edgeStep.properties());
|
||||
} else {
|
||||
filter = (ConditionQuery) query.copy();
|
||||
this.fillFilterByProperties(filter, edgeStep.properties());
|
||||
}
|
||||
query.capacity(Query.NO_CAPACITY);
|
||||
if (edgeStep.limit() != NO_LIMIT) {
|
||||
query.limit(edgeStep.limit());
|
||||
}
|
||||
Iterator<Edge> edges = this.graph().edges(query);
|
||||
if (filter != null) {
|
||||
ConditionQuery finalFilter = filter;
|
||||
edges = new FilterIterator<>(edges, (e) -> {
|
||||
return finalFilter.test((HugeEdge) e);
|
||||
});
|
||||
}
|
||||
return edgeStep.skipSuperNodeIfNeeded(edges);
|
||||
}
|
||||
|
||||
private void fillFilterBySortKeys(Query query, Id[] edgeLabels,
|
||||
Map<Id, Object> properties) {
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
E.checkArgument(edgeLabels.length == 1,
|
||||
"The properties filter condition can be set " +
|
||||
"only if just set one edge label");
|
||||
|
||||
this.fillFilterByProperties(query, properties);
|
||||
|
||||
ConditionQuery condQuery = (ConditionQuery) query;
|
||||
if (!GraphTransaction.matchFullEdgeSortKeys(condQuery, this.graph())) {
|
||||
Id label = condQuery.condition(HugeKeys.LABEL);
|
||||
E.checkArgument(false, "The properties %s does not match " +
|
||||
"sort keys of edge label '%s'",
|
||||
this.graph().mapPkId2Name(properties.keySet()),
|
||||
this.graph().edgeLabel(label).name());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillFilterByProperties(Query query,
|
||||
Map<Id, Object> properties) {
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConditionQuery condQuery = (ConditionQuery) query;
|
||||
TraversalUtil.fillConditionQuery(condQuery, properties, this.graph);
|
||||
}
|
||||
|
||||
protected long edgesCount(Id source, EdgeStep edgeStep) {
|
||||
Id[] edgeLabels = edgeStep.edgeLabels();
|
||||
Query query = GraphTransaction.constructEdgesQuery(source,
|
||||
edgeStep.direction(),
|
||||
edgeLabels);
|
||||
this.fillFilterBySortKeys(query, edgeLabels, edgeStep.properties());
|
||||
query.aggregate(Aggregate.AggregateFunc.COUNT, null);
|
||||
query.capacity(Query.NO_CAPACITY);
|
||||
query.limit(Query.NO_LIMIT);
|
||||
long count = graph().queryNumber(query).longValue();
|
||||
if (edgeStep.degree() == NO_LIMIT || count < edgeStep.degree()) {
|
||||
return count;
|
||||
} else if (edgeStep.skipDegree() != 0L &&
|
||||
count >= edgeStep.skipDegree()) {
|
||||
return 0L;
|
||||
} else {
|
||||
return edgeStep.degree();
|
||||
}
|
||||
}
|
||||
|
||||
protected Object getVertexLabelId(Object label) {
|
||||
if (label == null) {
|
||||
return null;
|
||||
}
|
||||
return SchemaLabel.getLabelId(this.graph, HugeType.VERTEX, label);
|
||||
}
|
||||
|
||||
protected Id getEdgeLabelId(Object label) {
|
||||
if (label == null) {
|
||||
return null;
|
||||
}
|
||||
return SchemaLabel.getLabelId(this.graph, HugeType.EDGE, label);
|
||||
}
|
||||
|
||||
protected void checkVertexExist(Id vertexId, String name) {
|
||||
try {
|
||||
this.graph.vertex(vertexId);
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The %s with id '%s' does not exist", name, vertexId), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkDegree(long degree) {
|
||||
checkPositiveOrNoLimit(degree, "max degree");
|
||||
}
|
||||
|
|
@ -377,9 +165,9 @@ public class HugeTraverser {
|
|||
}
|
||||
|
||||
public static <K, V extends Comparable<? super V>> Map<K, V> topN(
|
||||
Map<K, V> map,
|
||||
boolean sorted,
|
||||
long limit) {
|
||||
Map<K, V> map,
|
||||
boolean sorted,
|
||||
long limit) {
|
||||
if (sorted) {
|
||||
map = CollectionUtil.sortByValue(map, false);
|
||||
}
|
||||
|
|
@ -484,6 +272,247 @@ public class HugeTraverser {
|
|||
return path;
|
||||
}
|
||||
|
||||
public HugeGraph graph() {
|
||||
return this.graph;
|
||||
}
|
||||
|
||||
protected int concurrentDepth() {
|
||||
return this.graph.option(CoreOptions.OLTP_CONCURRENT_DEPTH);
|
||||
}
|
||||
|
||||
private CollectionType collectionType() {
|
||||
return this.graph.option(CoreOptions.OLTP_COLLECTION_TYPE);
|
||||
}
|
||||
|
||||
protected Set<Id> adjacentVertices(Id sourceV, Set<Id> vertices,
|
||||
Directions dir, Id label,
|
||||
Set<Id> excluded, long degree,
|
||||
long limit) {
|
||||
if (limit == 0) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
Set<Id> neighbors = newIdSet();
|
||||
for (Id source : vertices) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, dir,
|
||||
label, degree);
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge e = (HugeEdge) edges.next();
|
||||
Id target = e.id().otherVertexId();
|
||||
boolean matchExcluded = (excluded != null &&
|
||||
excluded.contains(target));
|
||||
if (matchExcluded || neighbors.contains(target) ||
|
||||
sourceV.equals(target)) {
|
||||
continue;
|
||||
}
|
||||
neighbors.add(target);
|
||||
if (limit != NO_LIMIT && neighbors.size() >= limit) {
|
||||
return neighbors;
|
||||
}
|
||||
}
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
protected Iterator<Id> adjacentVertices(Id source, Directions dir,
|
||||
Id label, long limit) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, dir, label, limit);
|
||||
return new MapperIterator<>(edges, e -> {
|
||||
HugeEdge edge = (HugeEdge) e;
|
||||
return edge.id().otherVertexId();
|
||||
});
|
||||
}
|
||||
|
||||
protected Set<Id> adjacentVertices(Id source, EdgeStep step) {
|
||||
Set<Id> neighbors = newSet();
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, step);
|
||||
while (edges.hasNext()) {
|
||||
neighbors.add(((HugeEdge) edges.next()).id().otherVertexId());
|
||||
}
|
||||
return neighbors;
|
||||
}
|
||||
|
||||
protected Iterator<Id> adjacentVertices(Id source, Directions dir,
|
||||
List<Id> labels, long limit) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source, dir, labels, limit);
|
||||
return new MapperIterator<>(edges, e -> {
|
||||
HugeEdge edge = (HugeEdge) e;
|
||||
return edge.id().otherVertexId();
|
||||
});
|
||||
}
|
||||
|
||||
@Watched
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, Directions dir,
|
||||
Id label, long limit) {
|
||||
Id[] labels = {};
|
||||
if (label != null) {
|
||||
labels = new Id[]{label};
|
||||
}
|
||||
|
||||
Query query = GraphTransaction.constructEdgesQuery(source, dir, labels);
|
||||
if (limit != NO_LIMIT) {
|
||||
query.limit(limit);
|
||||
}
|
||||
return this.graph.edges(query);
|
||||
}
|
||||
|
||||
@Watched
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, Directions dir,
|
||||
Map<Id, String> labels, long limit) {
|
||||
if (labels == null || labels.isEmpty()) {
|
||||
return this.edgesOfVertex(source, dir, (Id) null, limit);
|
||||
}
|
||||
ExtendableIterator<Edge> results = new ExtendableIterator<>();
|
||||
for (Id label : labels.keySet()) {
|
||||
E.checkNotNull(label, "edge label");
|
||||
results.extend(this.edgesOfVertex(source, dir, label, limit));
|
||||
}
|
||||
|
||||
if (limit == NO_LIMIT) {
|
||||
return results;
|
||||
}
|
||||
|
||||
long[] count = new long[1];
|
||||
return new LimitIterator<>(results, e -> count[0]++ >= limit);
|
||||
}
|
||||
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, Directions dir,
|
||||
List<Id> labels, long limit) {
|
||||
if (labels == null || labels.isEmpty()) {
|
||||
return this.edgesOfVertex(source, dir, (Id) null, limit);
|
||||
}
|
||||
ExtendableIterator<Edge> results = new ExtendableIterator<>();
|
||||
for (Id label : labels) {
|
||||
E.checkNotNull(label, "edge label");
|
||||
results.extend(this.edgesOfVertex(source, dir, label, limit));
|
||||
}
|
||||
|
||||
if (limit == NO_LIMIT) {
|
||||
return results;
|
||||
}
|
||||
|
||||
long[] count = new long[1];
|
||||
return new LimitIterator<>(results, e -> count[0]++ >= limit);
|
||||
}
|
||||
|
||||
protected Iterator<Edge> edgesOfVertex(Id source, EdgeStep edgeStep) {
|
||||
if (edgeStep.properties() == null || edgeStep.properties().isEmpty()) {
|
||||
Iterator<Edge> edges = this.edgesOfVertex(source,
|
||||
edgeStep.direction(),
|
||||
edgeStep.labels(),
|
||||
edgeStep.limit());
|
||||
return edgeStep.skipSuperNodeIfNeeded(edges);
|
||||
}
|
||||
return this.edgesOfVertex(source, edgeStep, false);
|
||||
}
|
||||
|
||||
protected Iterator<Edge> edgesOfVertexWithSK(Id source, EdgeStep edgeStep) {
|
||||
assert edgeStep.properties() != null && !edgeStep.properties().isEmpty();
|
||||
return this.edgesOfVertex(source, edgeStep, true);
|
||||
}
|
||||
|
||||
private Iterator<Edge> edgesOfVertex(Id source, EdgeStep edgeStep,
|
||||
boolean mustAllSK) {
|
||||
Id[] edgeLabels = edgeStep.edgeLabels();
|
||||
Query query = GraphTransaction.constructEdgesQuery(source,
|
||||
edgeStep.direction(),
|
||||
edgeLabels);
|
||||
ConditionQuery filter = null;
|
||||
if (mustAllSK) {
|
||||
this.fillFilterBySortKeys(query, edgeLabels, edgeStep.properties());
|
||||
} else {
|
||||
filter = (ConditionQuery) query.copy();
|
||||
this.fillFilterByProperties(filter, edgeStep.properties());
|
||||
}
|
||||
query.capacity(Query.NO_CAPACITY);
|
||||
if (edgeStep.limit() != NO_LIMIT) {
|
||||
query.limit(edgeStep.limit());
|
||||
}
|
||||
Iterator<Edge> edges = this.graph().edges(query);
|
||||
if (filter != null) {
|
||||
ConditionQuery finalFilter = filter;
|
||||
edges = new FilterIterator<>(edges, (e) -> {
|
||||
return finalFilter.test((HugeEdge) e);
|
||||
});
|
||||
}
|
||||
return edgeStep.skipSuperNodeIfNeeded(edges);
|
||||
}
|
||||
|
||||
private void fillFilterBySortKeys(Query query, Id[] edgeLabels,
|
||||
Map<Id, Object> properties) {
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
E.checkArgument(edgeLabels.length == 1,
|
||||
"The properties filter condition can be set " +
|
||||
"only if just set one edge label");
|
||||
|
||||
this.fillFilterByProperties(query, properties);
|
||||
|
||||
ConditionQuery condQuery = (ConditionQuery) query;
|
||||
if (!GraphTransaction.matchFullEdgeSortKeys(condQuery, this.graph())) {
|
||||
Id label = condQuery.condition(HugeKeys.LABEL);
|
||||
E.checkArgument(false, "The properties %s does not match " +
|
||||
"sort keys of edge label '%s'",
|
||||
this.graph().mapPkId2Name(properties.keySet()),
|
||||
this.graph().edgeLabel(label).name());
|
||||
}
|
||||
}
|
||||
|
||||
private void fillFilterByProperties(Query query,
|
||||
Map<Id, Object> properties) {
|
||||
if (properties == null || properties.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConditionQuery condQuery = (ConditionQuery) query;
|
||||
TraversalUtil.fillConditionQuery(condQuery, properties, this.graph);
|
||||
}
|
||||
|
||||
protected long edgesCount(Id source, EdgeStep edgeStep) {
|
||||
Id[] edgeLabels = edgeStep.edgeLabels();
|
||||
Query query = GraphTransaction.constructEdgesQuery(source,
|
||||
edgeStep.direction(),
|
||||
edgeLabels);
|
||||
this.fillFilterBySortKeys(query, edgeLabels, edgeStep.properties());
|
||||
query.aggregate(Aggregate.AggregateFunc.COUNT, null);
|
||||
query.capacity(Query.NO_CAPACITY);
|
||||
query.limit(Query.NO_LIMIT);
|
||||
long count = graph().queryNumber(query).longValue();
|
||||
if (edgeStep.degree() == NO_LIMIT || count < edgeStep.degree()) {
|
||||
return count;
|
||||
} else if (edgeStep.skipDegree() != 0L &&
|
||||
count >= edgeStep.skipDegree()) {
|
||||
return 0L;
|
||||
} else {
|
||||
return edgeStep.degree();
|
||||
}
|
||||
}
|
||||
|
||||
protected Object getVertexLabelId(Object label) {
|
||||
if (label == null) {
|
||||
return null;
|
||||
}
|
||||
return SchemaLabel.getLabelId(this.graph, HugeType.VERTEX, label);
|
||||
}
|
||||
|
||||
protected Id getEdgeLabelId(Object label) {
|
||||
if (label == null) {
|
||||
return null;
|
||||
}
|
||||
return SchemaLabel.getLabelId(this.graph, HugeType.EDGE, label);
|
||||
}
|
||||
|
||||
protected void checkVertexExist(Id vertexId, String name) {
|
||||
try {
|
||||
this.graph.vertex(vertexId);
|
||||
} catch (NotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"The %s with id '%s' does not exist", name, vertexId), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Node {
|
||||
|
||||
private final Id id;
|
||||
|
|
@ -560,6 +589,7 @@ public class HugeTraverser {
|
|||
|
||||
private final Id crosspoint;
|
||||
private final List<Id> vertices;
|
||||
private Set<Edge> edges = Collections.emptySet();
|
||||
|
||||
public Path(List<Id> vertices) {
|
||||
this(null, vertices);
|
||||
|
|
@ -570,6 +600,19 @@ public class HugeTraverser {
|
|||
this.vertices = vertices;
|
||||
}
|
||||
|
||||
public Path(List<Id> vertices, Set<Edge> edges) {
|
||||
this(null, vertices);
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Set<Edge> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public void setEdges(Set<Edge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Id crosspoint() {
|
||||
return this.crosspoint;
|
||||
}
|
||||
|
|
@ -615,6 +658,7 @@ public class HugeTraverser {
|
|||
* Compares the specified object with this path for equality.
|
||||
* Returns <tt>true</tt> if and only if both have same vertices list
|
||||
* without regard of crosspoint.
|
||||
*
|
||||
* @param other the object to be compared for equality with this path
|
||||
* @return <tt>true</tt> if the specified object is equal to this path
|
||||
*/
|
||||
|
|
@ -638,6 +682,13 @@ public class HugeTraverser {
|
|||
|
||||
private final Set<Path> paths;
|
||||
|
||||
private Set<Edge> edges = Collections.emptySet();
|
||||
|
||||
public PathSet(Set<Path> paths, Set<Edge> edges) {
|
||||
this(paths);
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public PathSet() {
|
||||
this(newSet());
|
||||
}
|
||||
|
|
@ -646,6 +697,18 @@ public class HugeTraverser {
|
|||
this.paths = paths;
|
||||
}
|
||||
|
||||
public Set<Path> getPaths() {
|
||||
return this.paths;
|
||||
}
|
||||
|
||||
public Set<Edge> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public void setEdges(Set<Edge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Path path) {
|
||||
return this.paths.add(path);
|
||||
|
|
@ -729,7 +792,7 @@ public class HugeTraverser {
|
|||
}
|
||||
|
||||
public void append(Id current) {
|
||||
for (Iterator<Path> iter = paths.iterator(); iter.hasNext();) {
|
||||
for (Iterator<Path> iter = paths.iterator(); iter.hasNext(); ) {
|
||||
Path path = iter.next();
|
||||
if (path.vertices().contains(current)) {
|
||||
iter.remove();
|
||||
|
|
@ -739,4 +802,80 @@ public class HugeTraverser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class EdgeRecord {
|
||||
private final Map<Long, Edge> edgeMap;
|
||||
private final ObjectIntMapping<Id> idMapping;
|
||||
|
||||
public EdgeRecord(boolean concurrent) {
|
||||
this.edgeMap = new HashMap<>();
|
||||
this.idMapping = ObjectIntMappingFactory.newObjectIntMapping(concurrent);
|
||||
}
|
||||
|
||||
private static Long makeVertexPairIndex(int source, int target) {
|
||||
return ((long) source & 0xFFFFFFFFL) |
|
||||
(((long) target << 32) & 0xFFFFFFFF00000000L);
|
||||
}
|
||||
|
||||
public static Set<Id> getEdgeIds(Set<Edge> edges) {
|
||||
return edges.stream().map(edge -> ((HugeEdge) edge).id()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private int code(Id id) {
|
||||
if (id.number()) {
|
||||
long l = id.asLong();
|
||||
if (0 <= l && l <= Integer.MAX_VALUE) {
|
||||
return (int) l;
|
||||
}
|
||||
}
|
||||
int code = this.idMapping.object2Code(id);
|
||||
assert code > 0;
|
||||
return -code;
|
||||
}
|
||||
|
||||
public void addEdge(Id source, Id target, Edge edge) {
|
||||
Long index = makeVertexPairIndex(this.code(source), this.code(target));
|
||||
this.edgeMap.put(index, edge);
|
||||
}
|
||||
|
||||
private Edge getEdge(Id source, Id target) {
|
||||
Long index = makeVertexPairIndex(this.code(source), this.code(target));
|
||||
return this.edgeMap.get(index);
|
||||
}
|
||||
|
||||
public Set<Edge> getEdges(HugeTraverser.Path path) {
|
||||
if (path == null || path.vertices().isEmpty()) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Iterator<Id> vertexIter = path.vertices().iterator();
|
||||
return getEdges(vertexIter);
|
||||
}
|
||||
|
||||
public Set<Edge> getEdges(Collection<HugeTraverser.Path> paths) {
|
||||
Set<Edge> edgeIds = new HashSet<>();
|
||||
for (HugeTraverser.Path path : paths) {
|
||||
edgeIds.addAll(getEdges(path));
|
||||
}
|
||||
return edgeIds;
|
||||
}
|
||||
|
||||
public Set<Edge> getEdges(Iterator<Id> vertexIter) {
|
||||
Set<Edge> edges = new HashSet<>();
|
||||
Id first = vertexIter.next();
|
||||
Id second;
|
||||
while (vertexIter.hasNext()) {
|
||||
second = vertexIter.next();
|
||||
Edge edge = getEdge(first, second);
|
||||
if (edge == null) {
|
||||
edge = getEdge(second, first);
|
||||
}
|
||||
if (edge != null) {
|
||||
edges.add(edge);
|
||||
}
|
||||
first = second;
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ import org.apache.hugegraph.HugeGraph;
|
|||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
public class JaccardSimilarTraverser extends OltpTraverser {
|
||||
|
|
@ -39,6 +39,12 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
super(graph);
|
||||
}
|
||||
|
||||
private static void reachCapacity(long count, long capacity) {
|
||||
if (capacity != NO_LIMIT && count > capacity) {
|
||||
throw new HugeException("Reach capacity '%s'", capacity);
|
||||
}
|
||||
}
|
||||
|
||||
public double jaccardSimilarity(Id vertex, Id other, Directions dir,
|
||||
String label, long degree) {
|
||||
E.checkNotNull(vertex, "vertex id");
|
||||
|
|
@ -51,9 +57,14 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
Id labelId = this.getEdgeLabelId(label);
|
||||
|
||||
Set<Id> sourceNeighbors = IteratorUtils.set(this.adjacentVertices(
|
||||
vertex, dir, labelId, degree));
|
||||
vertex, dir, labelId, degree));
|
||||
Set<Id> targetNeighbors = IteratorUtils.set(this.adjacentVertices(
|
||||
other, dir, labelId, degree));
|
||||
other, dir, labelId, degree));
|
||||
|
||||
this.vertexIterCounter.addAndGet(2L);
|
||||
this.edgeIterCounter.addAndGet(sourceNeighbors.size());
|
||||
this.edgeIterCounter.addAndGet(targetNeighbors.size());
|
||||
|
||||
return jaccardSimilarity(sourceNeighbors, targetNeighbors);
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +107,10 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
|
||||
// Query neighbors
|
||||
Set<Id> layer1s = this.adjacentVertices(source, step);
|
||||
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(layer1s.size());
|
||||
|
||||
reachCapacity(count.get() + layer1s.size(), capacity);
|
||||
count.addAndGet(layer1s.size());
|
||||
if (layer1s.isEmpty()) {
|
||||
|
|
@ -111,6 +126,10 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
return;
|
||||
}
|
||||
Set<Id> layer2s = this.adjacentVertices(id, step);
|
||||
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(layer2s.size());
|
||||
|
||||
if (layer2s.isEmpty()) {
|
||||
results.put(id, 0.0D);
|
||||
}
|
||||
|
|
@ -130,6 +149,10 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
return;
|
||||
}
|
||||
Set<Id> layer3s = this.adjacentVertices(id, step);
|
||||
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(layer3s.size());
|
||||
|
||||
reachCapacity(count.get() + layer3s.size(), capacity);
|
||||
if (layer3s.isEmpty()) {
|
||||
results.put(id, 0.0D);
|
||||
|
|
@ -152,6 +175,10 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
|
||||
// Query neighbors
|
||||
Set<Id> layer1s = this.adjacentVertices(source, step);
|
||||
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(layer1s.size());
|
||||
|
||||
reachCapacity(count + layer1s.size(), capacity);
|
||||
count += layer1s.size();
|
||||
if (layer1s.isEmpty()) {
|
||||
|
|
@ -168,6 +195,10 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
continue;
|
||||
}
|
||||
layer2s = this.adjacentVertices(neighbor, step);
|
||||
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(layer2s.size());
|
||||
|
||||
if (layer2s.isEmpty()) {
|
||||
results.put(neighbor, 0.0D);
|
||||
continue;
|
||||
|
|
@ -188,6 +219,10 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
continue;
|
||||
}
|
||||
layer3s = this.adjacentVertices(neighbor, step);
|
||||
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(layer3s.size());
|
||||
|
||||
reachCapacity(count + layer3s.size(), capacity);
|
||||
if (layer3s.isEmpty()) {
|
||||
results.put(neighbor, 0.0D);
|
||||
|
|
@ -201,10 +236,4 @@ public class JaccardSimilarTraverser extends OltpTraverser {
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void reachCapacity(long count, long capacity) {
|
||||
if (capacity != NO_LIMIT && count > capacity) {
|
||||
throw new HugeException("Reach capacity '%s'", capacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,12 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.KneighborRecords;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
public class KneighborTraverser extends OltpTraverser {
|
||||
|
||||
|
|
@ -53,12 +52,15 @@ public class KneighborTraverser extends OltpTraverser {
|
|||
Set<Id> all = newSet();
|
||||
|
||||
latest.add(sourceV);
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
|
||||
while (depth-- > 0) {
|
||||
long remaining = limit == NO_LIMIT ? NO_LIMIT : limit - all.size();
|
||||
latest = this.adjacentVertices(sourceV, latest, dir, labelId,
|
||||
all, degree, remaining);
|
||||
all.addAll(latest);
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(latest.size());
|
||||
if (reachLimit(limit, all.size())) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -84,9 +86,15 @@ public class KneighborTraverser extends OltpTraverser {
|
|||
return;
|
||||
}
|
||||
Iterator<Edge> edges = edgesOfVertex(v, step);
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
while (!this.reachLimit(limit, records.size()) && edges.hasNext()) {
|
||||
Id target = ((HugeEdge) edges.next()).id().otherVertexId();
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
records.addPath(v, target);
|
||||
|
||||
records.edgeResults().addEdge(v, target, edge);
|
||||
|
||||
this.edgeIterCounter.addAndGet(1L);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,12 @@ import java.util.function.Consumer;
|
|||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.KoutRecords;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
public class KoutTraverser extends OltpTraverser {
|
||||
|
||||
|
|
@ -66,6 +65,7 @@ public class KoutTraverser extends OltpTraverser {
|
|||
|
||||
long remaining = capacity == NO_LIMIT ?
|
||||
NO_LIMIT : capacity - latest.size();
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
while (depth-- > 0) {
|
||||
// Just get limit nodes in last layer if limit < remaining capacity
|
||||
if (depth == 0 && limit != NO_LIMIT &&
|
||||
|
|
@ -80,14 +80,16 @@ public class KoutTraverser extends OltpTraverser {
|
|||
latest = this.adjacentVertices(sourceV, latest, dir, labelId,
|
||||
null, degree, remaining);
|
||||
}
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(latest.size());
|
||||
if (capacity != NO_LIMIT) {
|
||||
// Update 'remaining' value to record remaining capacity
|
||||
remaining -= latest.size();
|
||||
|
||||
if (remaining <= 0 && depth > 0) {
|
||||
throw new HugeException(
|
||||
"Reach capacity '%s' while remaining depth '%s'",
|
||||
capacity, depth);
|
||||
"Reach capacity '%s' while remaining depth '%s'",
|
||||
capacity, depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,11 +116,17 @@ public class KoutTraverser extends OltpTraverser {
|
|||
return;
|
||||
}
|
||||
Iterator<Edge> edges = edgesOfVertex(v, step);
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
while (!this.reachLimit(limit, depth[0], records.size()) &&
|
||||
edges.hasNext()) {
|
||||
Id target = ((HugeEdge) edges.next()).id().otherVertexId();
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
records.addPath(v, target);
|
||||
this.checkCapacity(capacity, records.accessed(), depth[0]);
|
||||
|
||||
records.edgeResults().addEdge(v, target, edge);
|
||||
|
||||
this.edgeIterCounter.addAndGet(1L);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -136,8 +144,8 @@ public class KoutTraverser extends OltpTraverser {
|
|||
}
|
||||
if (accessed >= capacity && depth > 0) {
|
||||
throw new HugeException(
|
||||
"Reach capacity '%s' while remaining depth '%s'",
|
||||
capacity, depth);
|
||||
"Reach capacity '%s' while remaining depth '%s'",
|
||||
capacity, depth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,85 +19,27 @@ package org.apache.hugegraph.traversal.algorithm;
|
|||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.util.E;
|
||||
|
||||
public class MultiNodeShortestPathTraverser extends OltpTraverser {
|
||||
|
||||
public MultiNodeShortestPathTraverser(HugeGraph graph) {
|
||||
super(graph);
|
||||
}
|
||||
|
||||
public List<Path> multiNodeShortestPath(Iterator<Vertex> vertices,
|
||||
EdgeStep step, int maxDepth,
|
||||
long capacity) {
|
||||
List<Vertex> vertexList = IteratorUtils.list(vertices);
|
||||
int vertexCount = vertexList.size();
|
||||
E.checkState(vertexCount >= 2 && vertexCount <= MAX_VERTICES,
|
||||
"The number of vertices of multiple node shortest path " +
|
||||
"must in [2, %s], but got: %s",
|
||||
MAX_VERTICES, vertexList.size());
|
||||
List<Pair<Id, Id>> pairs = newList();
|
||||
cmn(vertexList, vertexCount, 2, 0, null, r -> {
|
||||
Id source = ((HugeVertex) r.get(0)).id();
|
||||
Id target = ((HugeVertex) r.get(1)).id();
|
||||
Pair<Id, Id> pair = Pair.of(source, target);
|
||||
pairs.add(pair);
|
||||
});
|
||||
|
||||
if (maxDepth >= this.concurrentDepth() && vertexCount > 10) {
|
||||
return this.multiNodeShortestPathConcurrent(pairs, step,
|
||||
maxDepth, capacity);
|
||||
} else {
|
||||
return this.multiNodeShortestPathSingle(pairs, step,
|
||||
maxDepth, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Path> multiNodeShortestPathConcurrent(List<Pair<Id, Id>> pairs,
|
||||
EdgeStep step,
|
||||
int maxDepth,
|
||||
long capacity) {
|
||||
List<Path> results = new CopyOnWriteArrayList<>();
|
||||
ShortestPathTraverser traverser =
|
||||
new ShortestPathTraverser(this.graph());
|
||||
this.traversePairs(pairs.iterator(), pair -> {
|
||||
Path path = traverser.shortestPath(pair.getLeft(), pair.getRight(),
|
||||
step, maxDepth, capacity);
|
||||
if (!Path.EMPTY.equals(path)) {
|
||||
results.add(path);
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<Path> multiNodeShortestPathSingle(List<Pair<Id, Id>> pairs,
|
||||
EdgeStep step, int maxDepth,
|
||||
long capacity) {
|
||||
List<Path> results = newList();
|
||||
ShortestPathTraverser traverser =
|
||||
new ShortestPathTraverser(this.graph());
|
||||
for (Pair<Id, Id> pair : pairs) {
|
||||
Path path = traverser.shortestPath(pair.getLeft(), pair.getRight(),
|
||||
step, maxDepth, capacity);
|
||||
if (!Path.EMPTY.equals(path)) {
|
||||
results.add(path);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static <T> void cmn(List<T> all, int m, int n, int current,
|
||||
List<T> result, Consumer<List<T>> consumer) {
|
||||
assert m <= all.size();
|
||||
|
|
@ -122,4 +64,89 @@ public class MultiNodeShortestPathTraverser extends OltpTraverser {
|
|||
result.remove(index);
|
||||
cmn(all, m - 1, n, current, result, consumer);
|
||||
}
|
||||
|
||||
public WrappedListPath multiNodeShortestPath(Iterator<Vertex> vertices,
|
||||
EdgeStep step, int maxDepth,
|
||||
long capacity) {
|
||||
List<Vertex> vertexList = IteratorUtils.list(vertices);
|
||||
int vertexCount = vertexList.size();
|
||||
E.checkState(vertexCount >= 2 && vertexCount <= MAX_VERTICES,
|
||||
"The number of vertices of multiple node shortest path " +
|
||||
"must in [2, %s], but got: %s",
|
||||
MAX_VERTICES, vertexList.size());
|
||||
List<Pair<Id, Id>> pairs = newList();
|
||||
cmn(vertexList, vertexCount, 2, 0, null, r -> {
|
||||
Id source = ((HugeVertex) r.get(0)).id();
|
||||
Id target = ((HugeVertex) r.get(1)).id();
|
||||
Pair<Id, Id> pair = Pair.of(source, target);
|
||||
pairs.add(pair);
|
||||
});
|
||||
|
||||
if (maxDepth >= this.concurrentDepth() && vertexCount > 10) {
|
||||
return this.multiNodeShortestPathConcurrent(pairs, step, maxDepth, capacity);
|
||||
} else {
|
||||
return this.multiNodeShortestPathSingle(pairs, step, maxDepth, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
public WrappedListPath multiNodeShortestPathConcurrent(List<Pair<Id, Id>> pairs,
|
||||
EdgeStep step, int maxDepth,
|
||||
long capacity) {
|
||||
List<Path> paths = new CopyOnWriteArrayList<>();
|
||||
Set<Edge> edges = new CopyOnWriteArraySet<>();
|
||||
ShortestPathTraverser traverser =
|
||||
new ShortestPathTraverser(this.graph());
|
||||
this.traversePairs(pairs.iterator(), pair -> {
|
||||
Path path = traverser.shortestPath(pair.getLeft(), pair.getRight(),
|
||||
step, maxDepth, capacity);
|
||||
if (!Path.EMPTY.equals(path)) {
|
||||
paths.add(path);
|
||||
}
|
||||
edges.addAll(path.getEdges());
|
||||
});
|
||||
this.vertexIterCounter.addAndGet(traverser.vertexIterCounter.get());
|
||||
this.edgeIterCounter.addAndGet(traverser.edgeIterCounter.get());
|
||||
|
||||
return new WrappedListPath(paths, edges);
|
||||
}
|
||||
|
||||
public WrappedListPath multiNodeShortestPathSingle(List<Pair<Id, Id>> pairs,
|
||||
EdgeStep step, int maxDepth,
|
||||
long capacity) {
|
||||
List<Path> paths = newList();
|
||||
Set<Edge> edges = newSet();
|
||||
ShortestPathTraverser traverser =
|
||||
new ShortestPathTraverser(this.graph());
|
||||
for (Pair<Id, Id> pair : pairs) {
|
||||
Path path = traverser.shortestPath(pair.getLeft(), pair.getRight(),
|
||||
step, maxDepth, capacity);
|
||||
if (!Path.EMPTY.equals(path)) {
|
||||
paths.add(path);
|
||||
}
|
||||
edges.addAll(path.getEdges());
|
||||
}
|
||||
this.vertexIterCounter.addAndGet(traverser.vertexIterCounter.get());
|
||||
this.edgeIterCounter.addAndGet(traverser.edgeIterCounter.get());
|
||||
|
||||
return new WrappedListPath(paths, edges);
|
||||
}
|
||||
|
||||
public static class WrappedListPath {
|
||||
|
||||
private final List<Path> paths;
|
||||
private final Set<Edge> edges;
|
||||
|
||||
public WrappedListPath(List<Path> paths, Set<Edge> edges) {
|
||||
this.paths = paths;
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public List<Path> paths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public Set<Edge> edges() {
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package org.apache.hugegraph.traversal.algorithm;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.NO_LIMIT;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
|
@ -25,21 +27,18 @@ import java.util.Set;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.EdgeRecord;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.strategy.TraverseStrategy;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
|
||||
import static org.apache.hugegraph.traversal.algorithm.HugeTraverser.NO_LIMIT;
|
||||
|
||||
public abstract class PathTraverser {
|
||||
|
||||
protected final HugeTraverser traverser;
|
||||
|
||||
protected int stepCount;
|
||||
protected final long capacity;
|
||||
protected final long limit;
|
||||
protected int stepCount;
|
||||
protected int totalSteps; // TODO: delete or implement abstract method
|
||||
|
||||
protected Map<Id, List<HugeTraverser.Node>> sources;
|
||||
|
|
@ -52,10 +51,11 @@ public abstract class PathTraverser {
|
|||
protected Set<HugeTraverser.Path> paths;
|
||||
|
||||
protected TraverseStrategy traverseStrategy;
|
||||
protected EdgeRecord edgeResults;
|
||||
|
||||
public PathTraverser(HugeTraverser traverser, TraverseStrategy strategy,
|
||||
Collection<Id> sources, Collection<Id> targets,
|
||||
long capacity, long limit) {
|
||||
long capacity, long limit, boolean concurrent) {
|
||||
this.traverser = traverser;
|
||||
this.traverseStrategy = strategy;
|
||||
|
||||
|
|
@ -79,6 +79,8 @@ public abstract class PathTraverser {
|
|||
this.targetsAll.putAll(this.targets);
|
||||
|
||||
this.paths = this.newPathSet();
|
||||
|
||||
this.edgeResults = new EdgeRecord(concurrent);
|
||||
}
|
||||
|
||||
public void forward() {
|
||||
|
|
@ -145,9 +147,13 @@ public abstract class PathTraverser {
|
|||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
this.traverser.edgeIterCounter.addAndGet(1L);
|
||||
|
||||
this.edgeResults.addEdge(v, target, edge);
|
||||
|
||||
this.processOne(v, target, forward);
|
||||
}
|
||||
this.traverser.vertexIterCounter.addAndGet(1L);
|
||||
}
|
||||
|
||||
private void processOne(Id source, Id target, boolean forward) {
|
||||
|
|
@ -205,10 +211,7 @@ public abstract class PathTraverser {
|
|||
protected boolean reachLimit() {
|
||||
HugeTraverser.checkCapacity(this.capacity, this.accessedNodes(),
|
||||
"template paths");
|
||||
if (this.limit == NO_LIMIT || this.pathCount() < this.limit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return this.limit != NO_LIMIT && this.pathCount() >= this.limit;
|
||||
}
|
||||
|
||||
protected int accessedNodes() {
|
||||
|
|
|
|||
|
|
@ -21,13 +21,12 @@ import java.util.Iterator;
|
|||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.PathsRecords;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import org.apache.hugegraph.perf.PerfUtil.Watched;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.PathsRecords;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
public class PathsTraverser extends HugeTraverser {
|
||||
|
||||
|
|
@ -75,6 +74,8 @@ public class PathsTraverser extends HugeTraverser {
|
|||
}
|
||||
traverser.backward(sourceV, targetDir);
|
||||
}
|
||||
vertexIterCounter.addAndGet(traverser.vertexCounter);
|
||||
edgeIterCounter.addAndGet(traverser.edgeCounter);
|
||||
return traverser.paths();
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +89,8 @@ public class PathsTraverser extends HugeTraverser {
|
|||
private final long limit;
|
||||
|
||||
private final PathSet paths;
|
||||
private long vertexCounter;
|
||||
private long edgeCounter;
|
||||
|
||||
public Traverser(Id sourceV, Id targetV, Id label,
|
||||
long degree, long capacity, long limit) {
|
||||
|
|
@ -96,6 +99,8 @@ public class PathsTraverser extends HugeTraverser {
|
|||
this.degree = degree;
|
||||
this.capacity = capacity;
|
||||
this.limit = limit;
|
||||
this.vertexCounter = 0L;
|
||||
this.edgeCounter = 0L;
|
||||
|
||||
this.paths = new PathSet();
|
||||
}
|
||||
|
|
@ -115,10 +120,11 @@ public class PathsTraverser extends HugeTraverser {
|
|||
}
|
||||
|
||||
edges = edgesOfVertex(vid, direction, this.label, this.degree);
|
||||
|
||||
this.vertexCounter += 1L;
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
this.edgeCounter += 1L;
|
||||
|
||||
PathSet results = this.record.findPath(target, null,
|
||||
true, false);
|
||||
|
|
@ -148,10 +154,11 @@ public class PathsTraverser extends HugeTraverser {
|
|||
}
|
||||
|
||||
edges = edgesOfVertex(vid, direction, this.label, this.degree);
|
||||
|
||||
this.vertexCounter += 1L;
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
this.edgeCounter += 1L;
|
||||
|
||||
PathSet results = this.record.findPath(target, null,
|
||||
true, false);
|
||||
|
|
@ -175,5 +182,9 @@ public class PathsTraverser extends HugeTraverser {
|
|||
checkCapacity(this.capacity, this.record.accessed(), "paths");
|
||||
return this.limit != NO_LIMIT && this.paths.size() >= this.limit;
|
||||
}
|
||||
|
||||
public long accessed() {
|
||||
return this.record.accessed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,15 +17,17 @@
|
|||
|
||||
package org.apache.hugegraph.traversal.algorithm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
public class SameNeighborTraverser extends HugeTraverser {
|
||||
|
||||
|
|
@ -46,11 +48,56 @@ public class SameNeighborTraverser extends HugeTraverser {
|
|||
Id labelId = this.getEdgeLabelId(label);
|
||||
|
||||
Set<Id> sourceNeighbors = IteratorUtils.set(this.adjacentVertices(
|
||||
vertex, direction, labelId, degree));
|
||||
vertex, direction, labelId, degree));
|
||||
Set<Id> targetNeighbors = IteratorUtils.set(this.adjacentVertices(
|
||||
other, direction, labelId, degree));
|
||||
other, direction, labelId, degree));
|
||||
Set<Id> sameNeighbors = (Set<Id>) CollectionUtil.intersect(
|
||||
sourceNeighbors, targetNeighbors);
|
||||
sourceNeighbors, targetNeighbors);
|
||||
|
||||
this.vertexIterCounter.addAndGet(2L);
|
||||
this.edgeIterCounter.addAndGet(sourceNeighbors.size());
|
||||
this.edgeIterCounter.addAndGet(targetNeighbors.size());
|
||||
|
||||
if (limit != NO_LIMIT) {
|
||||
int end = Math.min(sameNeighbors.size(), limit);
|
||||
sameNeighbors = CollectionUtil.subSet(sameNeighbors, 0, end);
|
||||
}
|
||||
return sameNeighbors;
|
||||
}
|
||||
|
||||
public Set<Id> sameNeighbors(List<Id> vertexIds, Directions direction,
|
||||
List<String> labels, long degree, int limit) {
|
||||
E.checkNotNull(vertexIds, "vertex ids");
|
||||
E.checkArgument(vertexIds.size() >= 2, "vertex_list size can't " +
|
||||
"be less than 2");
|
||||
for (Id id : vertexIds) {
|
||||
this.checkVertexExist(id, "vertex");
|
||||
}
|
||||
E.checkNotNull(direction, "direction");
|
||||
checkDegree(degree);
|
||||
checkLimit(limit);
|
||||
|
||||
List<Id> labelsId = new ArrayList<>();
|
||||
if (labels != null) {
|
||||
for (String label : labels) {
|
||||
labelsId.add(this.getEdgeLabelId(label));
|
||||
}
|
||||
}
|
||||
|
||||
Set<Id> sameNeighbors = new HashSet<>();
|
||||
for (int i = 0; i < vertexIds.size(); i++) {
|
||||
Set<Id> vertexNeighbors = IteratorUtils.set(this.adjacentVertices(
|
||||
vertexIds.get(i), direction, labelsId, degree));
|
||||
if (i == 0) {
|
||||
sameNeighbors = vertexNeighbors;
|
||||
} else {
|
||||
sameNeighbors = (Set<Id>) CollectionUtil.intersect(
|
||||
sameNeighbors, vertexNeighbors);
|
||||
}
|
||||
this.vertexIterCounter.addAndGet(1L);
|
||||
this.edgeIterCounter.addAndGet(vertexNeighbors.size());
|
||||
}
|
||||
|
||||
if (limit != NO_LIMIT) {
|
||||
int end = Math.min(sameNeighbors.size(), limit);
|
||||
sameNeighbors = CollectionUtil.subSet(sameNeighbors, 0, end);
|
||||
|
|
|
|||
|
|
@ -20,18 +20,19 @@ package org.apache.hugegraph.traversal.algorithm;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.perf.PerfUtil.Watched;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.ShortestPathRecords;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import org.apache.hugegraph.perf.PerfUtil.Watched;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public class ShortestPathTraverser extends HugeTraverser {
|
||||
|
|
@ -81,7 +82,15 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
checkCapacity(traverser.capacity, traverser.accessed(),
|
||||
"shortest path");
|
||||
}
|
||||
return paths.isEmpty() ? Path.EMPTY : paths.iterator().next();
|
||||
|
||||
this.vertexIterCounter.addAndGet(traverser.vertexCount);
|
||||
this.edgeIterCounter.addAndGet(traverser.pathResults.accessed());
|
||||
|
||||
Path path = paths.isEmpty() ? Path.EMPTY : paths.iterator().next();
|
||||
|
||||
Set<Edge> edges = traverser.edgeResults.getEdges(path);
|
||||
path.setEdges(edges);
|
||||
return path;
|
||||
}
|
||||
|
||||
public Path shortestPath(Id sourceV, Id targetV, EdgeStep step,
|
||||
|
|
@ -126,31 +135,40 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
checkCapacity(traverser.capacity, traverser.accessed(),
|
||||
"shortest path");
|
||||
}
|
||||
|
||||
this.vertexIterCounter.addAndGet(traverser.vertexCount);
|
||||
this.edgeIterCounter.addAndGet(traverser.pathResults.accessed());
|
||||
|
||||
paths.setEdges(traverser.edgeResults.getEdges(paths));
|
||||
return paths;
|
||||
}
|
||||
|
||||
private class Traverser {
|
||||
|
||||
private final ShortestPathRecords record;
|
||||
private final ShortestPathRecords pathResults;
|
||||
private final EdgeRecord edgeResults;
|
||||
private final Directions direction;
|
||||
private final Map<Id, String> labels;
|
||||
private final long degree;
|
||||
private final long skipDegree;
|
||||
private final long capacity;
|
||||
private long vertexCount;
|
||||
|
||||
public Traverser(Id sourceV, Id targetV, Directions dir,
|
||||
Map<Id, String> labels, long degree,
|
||||
long skipDegree, long capacity) {
|
||||
this.record = new ShortestPathRecords(sourceV, targetV);
|
||||
this.pathResults = new ShortestPathRecords(sourceV, targetV);
|
||||
this.edgeResults = new EdgeRecord(false);
|
||||
this.direction = dir;
|
||||
this.labels = labels;
|
||||
this.degree = degree;
|
||||
this.skipDegree = skipDegree;
|
||||
this.capacity = capacity;
|
||||
this.vertexCount = 0L;
|
||||
}
|
||||
|
||||
public PathSet traverse(boolean all) {
|
||||
return this.record.sourcesLessThanTargets() ?
|
||||
return this.pathResults.sourcesLessThanTargets() ?
|
||||
this.forward(all) : this.backward(all);
|
||||
}
|
||||
|
||||
|
|
@ -162,21 +180,26 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
PathSet results = new PathSet();
|
||||
long degree = this.skipDegree > 0L ? this.skipDegree : this.degree;
|
||||
|
||||
this.record.startOneLayer(true);
|
||||
while (this.record.hasNextKey()) {
|
||||
Id source = this.record.nextKey();
|
||||
this.pathResults.startOneLayer(true);
|
||||
while (this.pathResults.hasNextKey()) {
|
||||
Id source = this.pathResults.nextKey();
|
||||
|
||||
Iterator<Edge> edges = edgesOfVertex(source, this.direction,
|
||||
this.labels, degree);
|
||||
edges = skipSuperNodeIfNeeded(edges, this.degree,
|
||||
this.skipDegree);
|
||||
|
||||
this.vertexCount += 1L;
|
||||
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
|
||||
PathSet paths = this.record.findPath(target,
|
||||
t -> !this.superNode(t, this.direction),
|
||||
all, false);
|
||||
this.edgeResults.addEdge(source, target, edge);
|
||||
|
||||
PathSet paths = this.pathResults.findPath(target,
|
||||
t -> !this.superNode(t, this.direction),
|
||||
all, false);
|
||||
|
||||
if (paths.isEmpty()) {
|
||||
continue;
|
||||
|
|
@ -186,9 +209,10 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
return paths;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.record.finishOneLayer();
|
||||
this.pathResults.finishOneLayer();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
@ -202,21 +226,26 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
long degree = this.skipDegree > 0L ? this.skipDegree : this.degree;
|
||||
Directions opposite = this.direction.opposite();
|
||||
|
||||
this.record.startOneLayer(false);
|
||||
while (this.record.hasNextKey()) {
|
||||
Id source = this.record.nextKey();
|
||||
this.pathResults.startOneLayer(false);
|
||||
while (this.pathResults.hasNextKey()) {
|
||||
Id source = this.pathResults.nextKey();
|
||||
|
||||
Iterator<Edge> edges = edgesOfVertex(source, opposite,
|
||||
this.labels, degree);
|
||||
edges = skipSuperNodeIfNeeded(edges, this.degree,
|
||||
this.skipDegree);
|
||||
|
||||
this.vertexCount += 1L;
|
||||
|
||||
while (edges.hasNext()) {
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
|
||||
PathSet paths = this.record.findPath(target,
|
||||
t -> !this.superNode(t, opposite),
|
||||
all, false);
|
||||
this.edgeResults.addEdge(source, target, edge);
|
||||
|
||||
PathSet paths = this.pathResults.findPath(target,
|
||||
t -> !this.superNode(t, opposite),
|
||||
all, false);
|
||||
|
||||
if (paths.isEmpty()) {
|
||||
continue;
|
||||
|
|
@ -229,7 +258,7 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
}
|
||||
|
||||
// Re-init targets
|
||||
this.record.finishOneLayer();
|
||||
this.pathResults.finishOneLayer();
|
||||
|
||||
return results;
|
||||
}
|
||||
|
|
@ -244,7 +273,7 @@ public class ShortestPathTraverser extends HugeTraverser {
|
|||
}
|
||||
|
||||
private long accessed() {
|
||||
return this.record.accessed();
|
||||
return this.pathResults.accessed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
package org.apache.hugegraph.traversal.algorithm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -26,14 +29,14 @@ import java.util.Set;
|
|||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.backend.query.QueryResults;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.CollectionUtil;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.hugegraph.util.InsertionOrderUtil;
|
||||
import org.apache.hugegraph.util.NumericUtil;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
|
|
@ -57,13 +60,21 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
|
||||
Id labelId = this.getEdgeLabelId(label);
|
||||
Traverser traverser = new Traverser(sourceV, dir, labelId, weight,
|
||||
degree, skipDegree, capacity,
|
||||
limit);
|
||||
degree, skipDegree, capacity, limit);
|
||||
while (true) {
|
||||
// Found, reach max depth or reach capacity, stop searching
|
||||
traverser.forward();
|
||||
if (traverser.done()) {
|
||||
return traverser.shortestPaths();
|
||||
this.vertexIterCounter.addAndGet(traverser.vertexCount);
|
||||
this.edgeIterCounter.addAndGet(traverser.edgeCount);
|
||||
WeightedPaths paths = traverser.shortestPaths();
|
||||
List<List<Id>> pathList = paths.pathList();
|
||||
Set<Edge> edges = new HashSet<>();
|
||||
for (List<Id> path : pathList) {
|
||||
edges.addAll(traverser.edgeRecord.getEdges(path.iterator()));
|
||||
}
|
||||
paths.setEdges(edges);
|
||||
return paths;
|
||||
}
|
||||
checkCapacity(traverser.capacity, traverser.size, "shortest path");
|
||||
}
|
||||
|
|
@ -91,18 +102,107 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
traverser.forward();
|
||||
Map<Id, NodeWithWeight> results = traverser.shortestPaths();
|
||||
if (results.containsKey(targetV) || traverser.done()) {
|
||||
return results.get(targetV);
|
||||
this.vertexIterCounter.addAndGet(traverser.vertexCount);
|
||||
this.edgeIterCounter.addAndGet(traverser.edgeCount);
|
||||
NodeWithWeight nodeWithWeight = results.get(targetV);
|
||||
if (nodeWithWeight != null) {
|
||||
Iterator<Id> vertexIter = nodeWithWeight.node.path().iterator();
|
||||
Set<Edge> edges = traverser.edgeRecord.getEdges(vertexIter);
|
||||
nodeWithWeight.setEdges(edges);
|
||||
}
|
||||
return nodeWithWeight;
|
||||
}
|
||||
checkCapacity(traverser.capacity, traverser.size, "shortest path");
|
||||
}
|
||||
}
|
||||
|
||||
public static class NodeWithWeight implements Comparable<NodeWithWeight> {
|
||||
|
||||
private final double weight;
|
||||
private final Node node;
|
||||
|
||||
private Set<Edge> edges = Collections.emptySet();
|
||||
|
||||
public NodeWithWeight(double weight, Node node) {
|
||||
this.weight = weight;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public NodeWithWeight(double weight, Id id, NodeWithWeight prio) {
|
||||
this(weight, new Node(id, prio.node()));
|
||||
}
|
||||
|
||||
public Set<Edge> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public void setEdges(Set<Edge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public double weight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public Node node() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
public Map<String, Object> toMap() {
|
||||
return ImmutableMap.of("weight", this.weight,
|
||||
"vertices", this.node().path());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(NodeWithWeight other) {
|
||||
return Double.compare(this.weight, other.weight);
|
||||
}
|
||||
}
|
||||
|
||||
public static class WeightedPaths extends LinkedHashMap<Id, NodeWithWeight> {
|
||||
|
||||
private static final long serialVersionUID = -313873642177730993L;
|
||||
private Set<Edge> edges = Collections.emptySet();
|
||||
|
||||
public Set<Edge> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public void setEdges(Set<Edge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Set<Id> vertices() {
|
||||
Set<Id> vertices = newIdSet();
|
||||
vertices.addAll(this.keySet());
|
||||
for (NodeWithWeight nw : this.values()) {
|
||||
vertices.addAll(nw.node().path());
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public List<List<Id>> pathList() {
|
||||
List<List<Id>> pathList = new ArrayList<>();
|
||||
for (NodeWithWeight nw : this.values()) {
|
||||
pathList.add(nw.node.path());
|
||||
}
|
||||
return pathList;
|
||||
}
|
||||
|
||||
public Map<Id, Map<String, Object>> toMap() {
|
||||
Map<Id, Map<String, Object>> results = newMap();
|
||||
for (Map.Entry<Id, NodeWithWeight> entry : this.entrySet()) {
|
||||
Id source = entry.getKey();
|
||||
NodeWithWeight nw = entry.getValue();
|
||||
Map<String, Object> result = nw.toMap();
|
||||
results.put(source, result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
private class Traverser {
|
||||
|
||||
private WeightedPaths findingNodes = new WeightedPaths();
|
||||
private WeightedPaths foundNodes = new WeightedPaths();
|
||||
private Set<NodeWithWeight> sources;
|
||||
private Id source;
|
||||
private final Directions direction;
|
||||
private final Id label;
|
||||
private final String weight;
|
||||
|
|
@ -110,15 +210,21 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
private final long skipDegree;
|
||||
private final long capacity;
|
||||
private final long limit;
|
||||
private long size;
|
||||
private final WeightedPaths findingNodes = new WeightedPaths();
|
||||
private final WeightedPaths foundNodes = new WeightedPaths();
|
||||
private final EdgeRecord edgeRecord;
|
||||
private final Id source;
|
||||
private final long size;
|
||||
private Set<NodeWithWeight> sources;
|
||||
private long vertexCount;
|
||||
private long edgeCount;
|
||||
private boolean done = false;
|
||||
|
||||
public Traverser(Id sourceV, Directions dir, Id label, String weight,
|
||||
long degree, long skipDegree, long capacity,
|
||||
long limit) {
|
||||
long degree, long skipDegree, long capacity, long limit) {
|
||||
this.source = sourceV;
|
||||
this.sources = ImmutableSet.of(new NodeWithWeight(
|
||||
0D, new Node(sourceV, null)));
|
||||
0D, new Node(sourceV, null)));
|
||||
this.direction = dir;
|
||||
this.label = label;
|
||||
this.weight = weight;
|
||||
|
|
@ -127,6 +233,9 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
this.capacity = capacity;
|
||||
this.limit = limit;
|
||||
this.size = 0L;
|
||||
this.vertexCount = 0L;
|
||||
this.edgeCount = 0L;
|
||||
this.edgeRecord = new EdgeRecord(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,12 +252,16 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
Id target = edge.id().otherVertexId();
|
||||
|
||||
this.edgeCount += 1L;
|
||||
|
||||
if (this.foundNodes.containsKey(target) ||
|
||||
this.source.equals(target)) {
|
||||
// Already find shortest path for target, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
this.edgeRecord.addEdge(node.node().id(), target, edge);
|
||||
|
||||
double currentWeight = this.edgeWeight(edge);
|
||||
double weight = currentWeight + node.weight();
|
||||
NodeWithWeight nw = new NodeWithWeight(weight, target, node);
|
||||
|
|
@ -164,9 +277,10 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
}
|
||||
}
|
||||
}
|
||||
this.vertexCount += sources.size();
|
||||
|
||||
Map<Id, NodeWithWeight> sorted = CollectionUtil.sortByValue(
|
||||
this.findingNodes, true);
|
||||
this.findingNodes, true);
|
||||
double minWeight = 0;
|
||||
Set<NodeWithWeight> newSources = InsertionOrderUtil.newSet();
|
||||
for (Map.Entry<Id, NodeWithWeight> entry : sorted.entrySet()) {
|
||||
|
|
@ -209,7 +323,7 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
edgeWeight = 1.0;
|
||||
} else {
|
||||
edgeWeight = NumericUtil.convertToNumber(
|
||||
edge.value(this.weight)).doubleValue();
|
||||
edge.value(this.weight)).doubleValue();
|
||||
}
|
||||
return edgeWeight;
|
||||
}
|
||||
|
|
@ -232,62 +346,4 @@ public class SingleSourceShortestPathTraverser extends HugeTraverser {
|
|||
return edgeList.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NodeWithWeight implements Comparable<NodeWithWeight> {
|
||||
|
||||
private final double weight;
|
||||
private final Node node;
|
||||
|
||||
public NodeWithWeight(double weight, Node node) {
|
||||
this.weight = weight;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public NodeWithWeight(double weight, Id id, NodeWithWeight prio) {
|
||||
this(weight, new Node(id, prio.node()));
|
||||
}
|
||||
|
||||
public double weight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public Node node() {
|
||||
return this.node;
|
||||
}
|
||||
|
||||
public Map<String, Object> toMap() {
|
||||
return ImmutableMap.of("weight", this.weight,
|
||||
"vertices", this.node().path());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(NodeWithWeight other) {
|
||||
return Double.compare(this.weight, other.weight);
|
||||
}
|
||||
}
|
||||
|
||||
public static class WeightedPaths extends LinkedHashMap<Id, NodeWithWeight> {
|
||||
|
||||
private static final long serialVersionUID = -313873642177730993L;
|
||||
|
||||
public Set<Id> vertices() {
|
||||
Set<Id> vertices = newIdSet();
|
||||
vertices.addAll(this.keySet());
|
||||
for (NodeWithWeight nw : this.values()) {
|
||||
vertices.addAll(nw.node().path());
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
|
||||
public Map<Id, Map<String, Object>> toMap() {
|
||||
Map<Id, Map<String, Object>> results = newMap();
|
||||
for (Map.Entry<Id, NodeWithWeight> entry : this.entrySet()) {
|
||||
Id source = entry.getKey();
|
||||
NodeWithWeight nw = entry.getValue();
|
||||
Map<String, Object> result = nw.toMap();
|
||||
results.put(source, result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,16 +22,15 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.type.define.Directions;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeEdge;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public class SubGraphTraverser extends HugeTraverser {
|
||||
|
||||
|
|
@ -39,46 +38,6 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
super(graph);
|
||||
}
|
||||
|
||||
public PathSet 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, false);
|
||||
}
|
||||
|
||||
public PathSet 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, sourceInRing);
|
||||
}
|
||||
|
||||
private PathSet subGraphPaths(Id sourceV, Directions dir, String label,
|
||||
int depth, long degree, long capacity,
|
||||
long limit, boolean rings,
|
||||
boolean sourceInRing) {
|
||||
E.checkNotNull(sourceV, "source vertex id");
|
||||
this.checkVertexExist(sourceV, "source vertex");
|
||||
E.checkNotNull(dir, "direction");
|
||||
checkPositive(depth, "max depth");
|
||||
checkDegree(degree);
|
||||
checkCapacity(capacity);
|
||||
checkLimit(limit);
|
||||
|
||||
Id labelId = this.getEdgeLabelId(label);
|
||||
Traverser traverser = new Traverser(sourceV, labelId, depth, degree,
|
||||
capacity, limit, rings,
|
||||
sourceInRing);
|
||||
PathSet paths = new PathSet();
|
||||
while (true) {
|
||||
paths.addAll(traverser.forward(dir));
|
||||
if (--depth <= 0 || traverser.reachLimit() ||
|
||||
traverser.finished()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
private static boolean hasMultiEdges(List<Edge> edges, Id target) {
|
||||
boolean hasOutEdge = false;
|
||||
boolean hasInEdge = false;
|
||||
|
|
@ -97,20 +56,109 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
return false;
|
||||
}
|
||||
|
||||
public PathSet 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, false);
|
||||
}
|
||||
|
||||
public PathSet 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, sourceInRing);
|
||||
}
|
||||
|
||||
private PathSet subGraphPaths(Id sourceV, Directions dir, String label,
|
||||
int depth, long degree, long capacity,
|
||||
long limit, boolean rings,
|
||||
boolean sourceInRing) {
|
||||
E.checkNotNull(sourceV, "source vertex id");
|
||||
this.checkVertexExist(sourceV, "source vertex");
|
||||
E.checkNotNull(dir, "direction");
|
||||
checkPositive(depth, "max depth");
|
||||
checkDegree(degree);
|
||||
checkCapacity(capacity);
|
||||
checkLimit(limit);
|
||||
|
||||
Id labelId = this.getEdgeLabelId(label);
|
||||
Traverser traverser = new Traverser(sourceV, labelId, depth, degree,
|
||||
capacity, limit, rings,
|
||||
sourceInRing);
|
||||
PathSet paths = new PathSet();
|
||||
do {
|
||||
paths.addAll(traverser.forward(dir));
|
||||
} while (--depth > 0 && !traverser.reachLimit() &&
|
||||
!traverser.finished());
|
||||
this.vertexIterCounter.addAndGet(traverser.accessedVertices.size());
|
||||
this.edgeIterCounter.addAndGet(traverser.edgeCount);
|
||||
paths.setEdges(traverser.edgeRecord.getEdges(paths));
|
||||
return paths;
|
||||
}
|
||||
|
||||
private static class RingPath extends Path {
|
||||
|
||||
public RingPath(Id crosspoint, List<Id> vertices) {
|
||||
super(crosspoint, vertices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 0;
|
||||
for (Id id : this.vertices()) {
|
||||
hashCode ^= id.hashCode();
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specified object with this path for equality.
|
||||
* Returns <tt>true</tt> if other path is equal to or
|
||||
* reversed of this path.
|
||||
*
|
||||
* @param other the object to be compared
|
||||
* @return <tt>true</tt> if the specified object is equal to or
|
||||
* reversed of this path
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof RingPath)) {
|
||||
return false;
|
||||
}
|
||||
List<Id> vertices = this.vertices();
|
||||
List<Id> otherVertices = ((Path) other).vertices();
|
||||
|
||||
if (vertices.equals(otherVertices)) {
|
||||
return true;
|
||||
}
|
||||
if (vertices.size() != otherVertices.size()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, size = vertices.size(); i < size; i++) {
|
||||
int j = size - i - 1;
|
||||
if (!vertices.get(i).equals(otherVertices.get(j))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class Traverser {
|
||||
|
||||
private final Id source;
|
||||
private MultivaluedMap<Id, Node> sources = newMultivalueMap();
|
||||
private Set<Id> accessedVertices = newIdSet();
|
||||
|
||||
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 final Set<Id> accessedVertices = newIdSet();
|
||||
private final EdgeRecord edgeRecord;
|
||||
private MultivaluedMap<Id, Node> sources = newMultivalueMap();
|
||||
private int depth;
|
||||
private long pathCount;
|
||||
private long edgeCount;
|
||||
|
||||
public Traverser(Id sourceV, Id label, int depth, long degree,
|
||||
long capacity, long limit, boolean rings,
|
||||
|
|
@ -126,6 +174,8 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
this.rings = rings;
|
||||
this.sourceInRing = sourceInRing;
|
||||
this.pathCount = 0L;
|
||||
this.edgeCount = 0L;
|
||||
this.edgeRecord = new EdgeRecord(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -140,7 +190,7 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
Id vid = entry.getKey();
|
||||
// Record edgeList to determine if multiple edges exist
|
||||
List<Edge> edgeList = IteratorUtils.list(edgesOfVertex(
|
||||
vid, direction, this.label, this.degree));
|
||||
vid, direction, this.label, this.degree));
|
||||
edges = edgeList.iterator();
|
||||
|
||||
if (!edges.hasNext()) {
|
||||
|
|
@ -163,7 +213,11 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
while (edges.hasNext()) {
|
||||
neighborCount++;
|
||||
HugeEdge edge = (HugeEdge) edges.next();
|
||||
this.edgeCount += 1L;
|
||||
Id target = edge.id().otherVertexId();
|
||||
|
||||
this.edgeRecord.addEdge(vid, target, edge);
|
||||
|
||||
// Avoid deduplicate path
|
||||
if (currentNeighbors.contains(target)) {
|
||||
continue;
|
||||
|
|
@ -241,62 +295,11 @@ public class SubGraphTraverser extends HugeTraverser {
|
|||
private boolean reachLimit() {
|
||||
checkCapacity(this.capacity, this.accessedVertices.size(),
|
||||
this.rings ? "rings" : "rays");
|
||||
if (this.limit == NO_LIMIT || this.pathCount < this.limit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return this.limit != NO_LIMIT && this.pathCount >= this.limit;
|
||||
}
|
||||
|
||||
private boolean finished() {
|
||||
return this.sources.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private static class RingPath extends Path {
|
||||
|
||||
public RingPath(Id crosspoint, List<Id> vertices) {
|
||||
super(crosspoint, vertices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 0;
|
||||
for (Id id : this.vertices()) {
|
||||
hashCode ^= id.hashCode();
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the specified object with this path for equality.
|
||||
* Returns <tt>true</tt> if other path is equal to or
|
||||
* reversed of this path.
|
||||
* @param other the object to be compared
|
||||
* @return <tt>true</tt> if the specified object is equal to or
|
||||
* reversed of this path
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof RingPath)) {
|
||||
return false;
|
||||
}
|
||||
List<Id> vertices = this.vertices();
|
||||
List<Id> otherVertices = ((Path) other).vertices();
|
||||
|
||||
if (vertices.equals(otherVertices)) {
|
||||
return true;
|
||||
}
|
||||
if (vertices.size() != otherVertices.size()) {
|
||||
return false;
|
||||
}
|
||||
assert vertices.size() == otherVertices.size();
|
||||
for (int i = 0, size = vertices.size(); i < size; i++) {
|
||||
int j = size - i - 1;
|
||||
if (!vertices.get(i).equals(otherVertices.get(j))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ import java.util.Set;
|
|||
|
||||
import org.apache.hugegraph.HugeGraph;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.EdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.steps.RepeatEdgeStep;
|
||||
import org.apache.hugegraph.traversal.algorithm.strategy.TraverseStrategy;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
import org.apache.hugegraph.structure.HugeVertex;
|
||||
import org.apache.hugegraph.util.E;
|
||||
import org.apache.tinkerpop.gremlin.structure.Edge;
|
||||
import org.apache.tinkerpop.gremlin.structure.Vertex;
|
||||
|
||||
public class TemplatePathsTraverser extends HugeTraverser {
|
||||
|
||||
|
|
@ -39,11 +39,11 @@ public class TemplatePathsTraverser extends HugeTraverser {
|
|||
super(graph);
|
||||
}
|
||||
|
||||
public Set<Path> templatePaths(Iterator<Vertex> sources,
|
||||
Iterator<Vertex> targets,
|
||||
List<RepeatEdgeStep> steps,
|
||||
boolean withRing,
|
||||
long capacity, long limit) {
|
||||
public WrappedPathSet templatePaths(Iterator<Vertex> sources,
|
||||
Iterator<Vertex> targets,
|
||||
List<RepeatEdgeStep> steps,
|
||||
boolean withRing, long capacity,
|
||||
long limit) {
|
||||
checkCapacity(capacity);
|
||||
checkLimit(limit);
|
||||
|
||||
|
|
@ -68,23 +68,26 @@ public class TemplatePathsTraverser extends HugeTraverser {
|
|||
for (RepeatEdgeStep step : steps) {
|
||||
totalSteps += step.maxTimes();
|
||||
}
|
||||
|
||||
boolean concurrent = totalSteps >= this.concurrentDepth();
|
||||
TraverseStrategy strategy = TraverseStrategy.create(
|
||||
totalSteps >= this.concurrentDepth(),
|
||||
this.graph());
|
||||
concurrent, this.graph());
|
||||
Traverser traverser = new Traverser(this, strategy,
|
||||
sourceList, targetList, steps,
|
||||
withRing, capacity, limit);
|
||||
withRing, capacity, limit, concurrent);
|
||||
do {
|
||||
// Forward
|
||||
traverser.forward();
|
||||
if (traverser.finished()) {
|
||||
return traverser.paths();
|
||||
Set<Path> paths = traverser.paths();
|
||||
return new WrappedPathSet(paths, traverser.edgeResults.getEdges(paths));
|
||||
}
|
||||
|
||||
// Backward
|
||||
traverser.backward();
|
||||
if (traverser.finished()) {
|
||||
return traverser.paths();
|
||||
Set<Path> paths = traverser.paths();
|
||||
return new WrappedPathSet(paths, traverser.edgeResults.getEdges(paths));
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
|
@ -98,14 +101,14 @@ public class TemplatePathsTraverser extends HugeTraverser {
|
|||
protected int sourceIndex;
|
||||
protected int targetIndex;
|
||||
|
||||
protected boolean sourceFinishOneStep = false;
|
||||
protected boolean targetFinishOneStep = false;
|
||||
protected boolean sourceFinishOneStep;
|
||||
protected boolean targetFinishOneStep;
|
||||
|
||||
public Traverser(HugeTraverser traverser, TraverseStrategy strategy,
|
||||
Collection<Id> sources, Collection<Id> targets,
|
||||
List<RepeatEdgeStep> steps, boolean withRing,
|
||||
long capacity, long limit) {
|
||||
super(traverser, strategy, sources, targets, capacity, limit);
|
||||
long capacity, long limit, boolean concurrent) {
|
||||
super(traverser, strategy, sources, targets, capacity, limit, concurrent);
|
||||
|
||||
this.steps = steps;
|
||||
this.withRing = withRing;
|
||||
|
|
@ -135,7 +138,7 @@ public class TemplatePathsTraverser extends HugeTraverser {
|
|||
public void afterTraverse(EdgeStep step, boolean forward) {
|
||||
|
||||
Map<Id, List<Node>> all = forward ? this.sourcesAll :
|
||||
this.targetsAll;
|
||||
this.targetsAll;
|
||||
this.addNewVerticesToAll(all);
|
||||
this.reInitCurrentStepIfNeeded(step, forward);
|
||||
this.stepCount++;
|
||||
|
|
@ -276,4 +279,23 @@ public class TemplatePathsTraverser extends HugeTraverser {
|
|||
this.targetIndex == this.sourceIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class WrappedPathSet {
|
||||
|
||||
private final Set<Path> paths;
|
||||
private final Set<Edge> edges;
|
||||
|
||||
public WrappedPathSet(Set<Path> paths, Set<Edge> edges) {
|
||||
this.paths = paths;
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
public Set<Path> paths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
public Set<Edge> edges() {
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ import java.util.Stack;
|
|||
import java.util.function.Function;
|
||||
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.Path;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.PathSet;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.record.Int2IntRecord;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.record.Record;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.record.RecordType;
|
||||
import org.apache.hugegraph.util.collection.CollectionFactory;
|
||||
import org.apache.hugegraph.util.collection.IntMap;
|
||||
import org.apache.hugegraph.util.collection.IntSet;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.Path;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.PathSet;
|
||||
|
||||
public class ShortestPathRecords extends DoubleWayMultiPathsRecords {
|
||||
|
||||
|
|
|
|||
|
|
@ -25,17 +25,18 @@ import java.util.function.Function;
|
|||
|
||||
import org.apache.hugegraph.HugeException;
|
||||
import org.apache.hugegraph.backend.id.Id;
|
||||
import org.apache.hugegraph.type.define.CollectionType;
|
||||
import org.apache.hugegraph.util.collection.CollectionFactory;
|
||||
import org.apache.hugegraph.util.collection.IntIterator;
|
||||
import org.apache.hugegraph.util.collection.IntMap;
|
||||
import org.apache.hugegraph.util.collection.IntSet;
|
||||
import org.apache.hugegraph.perf.PerfUtil.Watched;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.EdgeRecord;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.Path;
|
||||
import org.apache.hugegraph.traversal.algorithm.HugeTraverser.PathSet;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.record.Int2IntRecord;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.record.Record;
|
||||
import org.apache.hugegraph.traversal.algorithm.records.record.RecordType;
|
||||
import org.apache.hugegraph.type.define.CollectionType;
|
||||
import org.apache.hugegraph.util.collection.CollectionFactory;
|
||||
import org.apache.hugegraph.util.collection.IntIterator;
|
||||
import org.apache.hugegraph.util.collection.IntMap;
|
||||
import org.apache.hugegraph.util.collection.IntSet;
|
||||
|
||||
public abstract class SingleWayMultiPathsRecords extends AbstractRecords {
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ public abstract class SingleWayMultiPathsRecords extends AbstractRecords {
|
|||
private final int sourceCode;
|
||||
private final boolean nearest;
|
||||
private final IntSet accessedVertices;
|
||||
|
||||
private final EdgeRecord edgeResults;
|
||||
private IntIterator parentRecordKeys;
|
||||
|
||||
public SingleWayMultiPathsRecords(RecordType type, boolean concurrent,
|
||||
|
|
@ -58,6 +59,7 @@ public abstract class SingleWayMultiPathsRecords extends AbstractRecords {
|
|||
firstRecord.addPath(this.sourceCode, 0);
|
||||
this.records = new Stack<>();
|
||||
this.records.push(firstRecord);
|
||||
this.edgeResults = new EdgeRecord(concurrent);
|
||||
|
||||
this.accessedVertices = CollectionFactory.newIntSet();
|
||||
}
|
||||
|
|
@ -176,6 +178,10 @@ public abstract class SingleWayMultiPathsRecords extends AbstractRecords {
|
|||
return this.records;
|
||||
}
|
||||
|
||||
public EdgeRecord edgeResults() {
|
||||
return edgeResults;
|
||||
}
|
||||
|
||||
public abstract int size();
|
||||
|
||||
public abstract List<Id> ids(long limit);
|
||||
|
|
|
|||
|
|
@ -19,14 +19,15 @@ package org.apache.hugegraph.api.traversers;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.apache.hugegraph.api.BaseApiTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.hugegraph.api.BaseApiTest;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class JaccardSimilarityApiTest extends BaseApiTest {
|
||||
|
||||
static final String PATH = TRAVERSERS_API + "/jaccardsimilarity";
|
||||
|
|
@ -72,9 +73,10 @@ public class JaccardSimilarityApiTest extends BaseApiTest {
|
|||
"\"top\": 3}", markoId);
|
||||
Response r = client().post(PATH, reqBody);
|
||||
String content = assertResponseStatus(200, r);
|
||||
Double rippleJaccardSimilarity = assertJsonContains(content, rippleId);
|
||||
Double peterJaccardSimilarity = assertJsonContains(content, peterId);
|
||||
Double jsonJaccardSimilarity = assertJsonContains(content, jsonId);
|
||||
Map<?, ?> jaccardSimilarity = assertJsonContains(content, "jaccard_similarity");
|
||||
Double rippleJaccardSimilarity = assertMapContains(jaccardSimilarity, rippleId);
|
||||
Double peterJaccardSimilarity = assertMapContains(jaccardSimilarity, peterId);
|
||||
Double jsonJaccardSimilarity = assertMapContains(jaccardSimilarity, jsonId);
|
||||
Assert.assertEquals(0.3333, rippleJaccardSimilarity.doubleValue(),
|
||||
0.0001);
|
||||
Assert.assertEquals(0.25, peterJaccardSimilarity.doubleValue(), 0.0001);
|
||||
|
|
|
|||
Loading…
Reference in New Issue