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:
DanGuge 2023-08-19 13:41:08 +08:00 committed by GitHub
parent b02c2bdaa7
commit d12f5734e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 2391 additions and 1345 deletions

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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")

View File

@ -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 {

View File

@ -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 {

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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")

View File

@ -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);
}
}

View File

@ -264,4 +264,4 @@ public class ServerOptions extends OptionHolder {
disallowEmpty(),
true
);
}
}

View File

@ -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() &&

View File

@ -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());
}
}

View File

@ -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);
}

View File

@ -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<>();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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,

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
};

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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() {

View File

@ -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();
}
}
}

View File

@ -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);

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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);