diff --git a/java/src/main/java/org/softwareheritage/graph/Entry.java b/java/src/main/java/org/softwareheritage/graph/Entry.java --- a/java/src/main/java/org/softwareheritage/graph/Entry.java +++ b/java/src/main/java/org/softwareheritage/graph/Entry.java @@ -14,7 +14,7 @@ public void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); System.err.println("Graph loaded."); } diff --git a/java/src/main/java/org/softwareheritage/graph/Graph.java b/java/src/main/java/org/softwareheritage/graph/Graph.java --- a/java/src/main/java/org/softwareheritage/graph/Graph.java +++ b/java/src/main/java/org/softwareheritage/graph/Graph.java @@ -46,14 +46,27 @@ /** Mapping long id → node types */ NodeTypesMap nodeTypesMap; + /** + * Defines which directions of the graph can be loaded in memory. + */ + public enum LoadDirection { + FORWARD, + BACKWARD, + BOTH + } + + /** The direction of the graph loaded into memory */ + private LoadDirection loadedDirection; + /** * Constructor. * * @param path path and basename of the compressed graph to load + * @param direction the direction of the graph to load into memory */ - private Graph(String path) throws IOException { - loadInternal(path, null, LoadMethod.MAPPED); + private Graph(String path, LoadDirection direction) throws IOException { + loadInternal(path, null, LoadMethod.MAPPED, direction); } /** @@ -64,60 +77,74 @@ MEMORY, MAPPED, OFFLINE, } - protected Graph loadInternal(String path, ProgressLogger pl, LoadMethod method) throws IOException { + protected Graph loadInternal(String path, ProgressLogger pl, LoadMethod method, LoadDirection direction) throws IOException { this.path = path; + this.loadedDirection = direction; if (method == LoadMethod.MEMORY) { - this.graph = ImmutableGraph.load(path, pl); - this.graphTransposed = ImmutableGraph.load(path + "-transposed", pl); + loadInternal(ImmutableGraph::load, path, pl, direction); } else if (method == LoadMethod.MAPPED) { - this.graph = ImmutableGraph.loadMapped(path, pl); - this.graphTransposed = ImmutableGraph.loadMapped(path + "-transposed", pl); + loadInternal(ImmutableGraph::loadMapped, path, pl, direction); } else if (method == LoadMethod.OFFLINE) { - this.graph = ImmutableGraph.loadOffline(path, pl); - this.graphTransposed = ImmutableGraph.loadOffline(path + "-transposed", pl); + loadInternal(ImmutableGraph::loadOffline, path, pl, direction); } this.nodeTypesMap = new NodeTypesMap(path); this.nodeIdMap = new NodeIdMap(path, numNodes()); return this; } + private interface GraphLoader { + ImmutableGraph load(String path, ProgressLogger pl) throws IOException; + } + + private void loadInternal(GraphLoader loader, String path, ProgressLogger pl, LoadDirection direction) throws IOException { + if (direction == LoadDirection.FORWARD) { + this.graph = loader.load(path, pl); + } else if (direction == LoadDirection.BACKWARD) { + this.graph = loader.load(path + "-transposed", pl); + } else if (direction == LoadDirection.BOTH) { + this.graph = loader.load(path, pl); + this.graphTransposed = loader.load(path + "-transposed", pl); + } + } + protected Graph() { } - public static Graph load(String path, ProgressLogger pl) throws IOException { - return new Graph().loadInternal(path, pl, LoadMethod.MEMORY); + public static Graph load(String path, ProgressLogger pl, LoadDirection direction) throws IOException { + return new Graph().loadInternal(path, pl, LoadMethod.MEMORY, direction); } - public static Graph loadMapped(String path, ProgressLogger pl) throws IOException { - return new Graph().loadInternal(path, pl, LoadMethod.MAPPED); + public static Graph loadMapped(String path, ProgressLogger pl, LoadDirection direction) throws IOException { + return new Graph().loadInternal(path, pl, LoadMethod.MAPPED, direction); } - public static Graph loadOffline(String path, ProgressLogger pl) throws IOException { - return new Graph().loadInternal(path, null, LoadMethod.OFFLINE); + public static Graph loadOffline(String path, ProgressLogger pl, LoadDirection direction) throws IOException { + return new Graph().loadInternal(path, pl, LoadMethod.OFFLINE, direction); } - public static Graph load(String path) throws IOException { - return new Graph().loadInternal(path, null, LoadMethod.MEMORY); + public static Graph load(String path, LoadDirection direction) throws IOException { + return load(path, null, direction); } - public static Graph loadMapped(String path) throws IOException { - return new Graph().loadInternal(path, null, LoadMethod.MAPPED); + public static Graph loadMapped(String path, LoadDirection direction) throws IOException { + return loadMapped(path, null, direction); } - public static Graph loadOffline(String path) throws IOException { - return new Graph().loadInternal(path, null, LoadMethod.OFFLINE); + public static Graph loadOffline(String path, LoadDirection direction) throws IOException { + return loadOffline(path, null, direction); } /** * Constructor used for copy() */ protected Graph(ImmutableGraph graph, ImmutableGraph graphTransposed, String path, NodeIdMap nodeIdMap, - NodeTypesMap nodeTypesMap) { + NodeTypesMap nodeTypesMap, LoadDirection direction) { this.graph = graph; this.graphTransposed = graphTransposed; this.path = path; this.nodeIdMap = nodeIdMap; this.nodeTypesMap = nodeTypesMap; + this.loadedDirection = direction; } /** @@ -125,27 +152,38 @@ */ @Override public Graph copy() { - return new Graph(this.graph.copy(), this.graphTransposed.copy(), this.path, this.nodeIdMap, this.nodeTypesMap); + ImmutableGraph initial = this.graph.copy(); + ImmutableGraph transposed = null; + if (this.loadedDirection == LoadDirection.BOTH) { + transposed = this.graphTransposed.copy(); + } + return new Graph(initial, transposed, this.path, this.nodeIdMap, this.nodeTypesMap, this.loadedDirection); } @Override public boolean randomAccess() { - return graph.randomAccess() && graphTransposed.randomAccess(); + return graph.randomAccess() && (this.loadedDirection != LoadDirection.BOTH || graphTransposed.randomAccess()); } /** * Return a transposed version of the graph. */ public Graph transpose() { - return new Graph(this.graphTransposed, this.graph, this.path, this.nodeIdMap, this.nodeTypesMap); + if (this.loadedDirection != LoadDirection.BOTH) { + throw new UnsupportedOperationException("Unable to transpose graph: both directions must be loaded into memory."); + } + return new Graph(this.graphTransposed, this.graph, this.path, this.nodeIdMap, this.nodeTypesMap, this.loadedDirection); } /** * Return a symmetric version of the graph. */ public Graph symmetrize() { + if (this.loadedDirection != LoadDirection.BOTH) { + throw new UnsupportedOperationException("Unable to symmetrize graph: both directions must be loaded into memory."); + } ImmutableGraph symmetric = Transform.union(graph, graphTransposed); - return new Graph(symmetric, symmetric, this.path, this.nodeIdMap, this.nodeTypesMap); + return new Graph(symmetric, symmetric, this.path, this.nodeIdMap, this.nodeTypesMap, this.loadedDirection); } /** @@ -307,4 +345,11 @@ public Node.Type getNodeType(long nodeId) { return nodeTypesMap.getType(nodeId); } + + /** + * Returns the direction of the graph loaded into memory. + */ + public LoadDirection getLoadedDirection() { + return loadedDirection; + } } diff --git a/java/src/main/java/org/softwareheritage/graph/Traversal.java b/java/src/main/java/org/softwareheritage/graph/Traversal.java --- a/java/src/main/java/org/softwareheritage/graph/Traversal.java +++ b/java/src/main/java/org/softwareheritage/graph/Traversal.java @@ -70,8 +70,11 @@ if (!direction.matches("forward|backward")) { throw new IllegalArgumentException("Unknown traversal direction: " + direction); } - - if (direction.equals("backward")) { + Graph.LoadDirection loadedDirection = graph.getLoadedDirection(); + if (loadedDirection != Graph.LoadDirection.BOTH && !loadedDirection.name().toLowerCase().equals(direction)) { + throw new IllegalArgumentException("Invalid traversal direction: " + direction + ". Different direction was loaded."); + } + if (loadedDirection == Graph.LoadDirection.BOTH && direction.equals("backward")) { this.graph = graph.transpose(); } else { this.graph = graph; diff --git a/java/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java b/java/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java --- a/java/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java +++ b/java/src/main/java/org/softwareheritage/graph/benchmark/AccessEdge.java @@ -25,7 +25,7 @@ Benchmark bench = new Benchmark(); bench.parseCommandLineArgs(args); - Graph graph = Graph.loadMapped(bench.args.graphPath); + Graph graph = Graph.loadMapped(bench.args.graphPath, Graph.LoadDirection.BOTH); long[] nodeIds = bench.args.random.generateNodeIds(graph, bench.args.nbNodes); diff --git a/java/src/main/java/org/softwareheritage/graph/benchmark/BFS.java b/java/src/main/java/org/softwareheritage/graph/benchmark/BFS.java --- a/java/src/main/java/org/softwareheritage/graph/benchmark/BFS.java +++ b/java/src/main/java/org/softwareheritage/graph/benchmark/BFS.java @@ -50,7 +50,7 @@ boolean useTransposed = config.getBoolean("useTransposed"); System.err.println("Loading graph " + graphPath + " ..."); - Graph graph = Graph.loadMapped(graphPath); + Graph graph = Graph.loadMapped(graphPath, Graph.LoadDirection.BOTH); System.err.println("Graph loaded."); if (useTransposed) diff --git a/java/src/main/java/org/softwareheritage/graph/benchmark/Browsing.java b/java/src/main/java/org/softwareheritage/graph/benchmark/Browsing.java --- a/java/src/main/java/org/softwareheritage/graph/benchmark/Browsing.java +++ b/java/src/main/java/org/softwareheritage/graph/benchmark/Browsing.java @@ -25,7 +25,7 @@ Benchmark bench = new Benchmark(); bench.parseCommandLineArgs(args); - Graph graph = Graph.loadMapped(bench.args.graphPath); + Graph graph = Graph.loadMapped(bench.args.graphPath, Graph.LoadDirection.BOTH); long[] dirNodeIds = bench.args.random.generateNodeIdsOfType(graph, bench.args.nbNodes, Node.Type.DIR); long[] revNodeIds = bench.args.random.generateNodeIdsOfType(graph, bench.args.nbNodes, Node.Type.REV); diff --git a/java/src/main/java/org/softwareheritage/graph/benchmark/Provenance.java b/java/src/main/java/org/softwareheritage/graph/benchmark/Provenance.java --- a/java/src/main/java/org/softwareheritage/graph/benchmark/Provenance.java +++ b/java/src/main/java/org/softwareheritage/graph/benchmark/Provenance.java @@ -24,7 +24,7 @@ Benchmark bench = new Benchmark(); bench.parseCommandLineArgs(args); - Graph graph = Graph.loadMapped(bench.args.graphPath); + Graph graph = Graph.loadMapped(bench.args.graphPath, Graph.LoadDirection.BOTH); long[] nodeIds = bench.args.random.generateNodeIds(graph, bench.args.nbNodes); diff --git a/java/src/main/java/org/softwareheritage/graph/benchmark/Vault.java b/java/src/main/java/org/softwareheritage/graph/benchmark/Vault.java --- a/java/src/main/java/org/softwareheritage/graph/benchmark/Vault.java +++ b/java/src/main/java/org/softwareheritage/graph/benchmark/Vault.java @@ -24,7 +24,7 @@ Benchmark bench = new Benchmark(); bench.parseCommandLineArgs(args); - Graph graph = Graph.loadMapped(bench.args.graphPath); + Graph graph = Graph.loadMapped(bench.args.graphPath, Graph.LoadDirection.BOTH); long[] nodeIds = bench.args.random.generateNodeIds(graph, bench.args.nbNodes); diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindCommonAncestor.java b/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindCommonAncestor.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindCommonAncestor.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindCommonAncestor.java @@ -12,7 +12,7 @@ private void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); System.err.println("Graph loaded."); } diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindPath.java b/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindPath.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindPath.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/forks/FindPath.java @@ -14,7 +14,7 @@ private void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename).symmetrize(); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH).symmetrize(); System.err.println("Graph loaded."); this.emptySnapshot = null; } diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCC.java b/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCC.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCC.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCC.java @@ -72,7 +72,7 @@ private void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename).symmetrize(); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH).symmetrize(); System.err.println("Graph loaded."); this.emptySnapshot = null; this.whitelist = null; diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCliques.java b/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCliques.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCliques.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/forks/ForkCliques.java @@ -24,7 +24,7 @@ private void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); System.err.println("Graph loaded."); this.whitelist = null; } diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/forks/ListEmptyOrigins.java b/java/src/main/java/org/softwareheritage/graph/experiments/forks/ListEmptyOrigins.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/forks/ListEmptyOrigins.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/forks/ListEmptyOrigins.java @@ -49,7 +49,7 @@ private void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); System.err.println("Graph loaded."); this.emptySnapshot = null; } diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/multiplicationfactor/GenDistribution.java b/java/src/main/java/org/softwareheritage/graph/experiments/multiplicationfactor/GenDistribution.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/multiplicationfactor/GenDistribution.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/multiplicationfactor/GenDistribution.java @@ -124,7 +124,7 @@ private void load_graph(String graphBasename) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); System.err.println("Graph loaded."); } } diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/topology/AveragePaths.java b/java/src/main/java/org/softwareheritage/graph/experiments/topology/AveragePaths.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/topology/AveragePaths.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/topology/AveragePaths.java @@ -24,7 +24,7 @@ public AveragePaths(String graphBasename, String allowedNodes, String outdir) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - this.graph = Graph.loadMapped(graphBasename); + this.graph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); this.subgraph = new Subgraph(this.graph, new AllowedNodes(allowedNodes)); this.outdir = outdir; System.err.println("Graph loaded."); diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/topology/ClusteringCoefficient.java b/java/src/main/java/org/softwareheritage/graph/experiments/topology/ClusteringCoefficient.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/topology/ClusteringCoefficient.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/topology/ClusteringCoefficient.java @@ -27,7 +27,7 @@ public ClusteringCoefficient(String graphBasename, String outdirPath) throws IOException { this.outdirPath = outdirPath; System.err.println("Loading graph " + graphBasename + " ..."); - Graph directedGraph = Graph.loadMapped(graphBasename); + Graph directedGraph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); this.graph = directedGraph.symmetrize(); System.err.println("Graph loaded."); diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/topology/ConnectedComponents.java b/java/src/main/java/org/softwareheritage/graph/experiments/topology/ConnectedComponents.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/topology/ConnectedComponents.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/topology/ConnectedComponents.java @@ -23,7 +23,7 @@ private void load_graph(String graphBasename, String nodeTypes) throws IOException { System.err.println("Loading graph " + graphBasename + " ..."); - var underlyingGraph = Graph.loadMapped(graphBasename); + var underlyingGraph = Graph.loadMapped(graphBasename, Graph.LoadDirection.BOTH); var underlyingGraphSym = underlyingGraph.symmetrize(); graph = new Subgraph(underlyingGraphSym, new AllowedNodes(nodeTypes)); System.err.println("Graph loaded."); diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/topology/InOutDegree.java b/java/src/main/java/org/softwareheritage/graph/experiments/topology/InOutDegree.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/topology/InOutDegree.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/topology/InOutDegree.java @@ -233,7 +233,7 @@ final ProgressLogger pl = new ProgressLogger(); - Graph graph = Graph.loadMapped(basename); + Graph graph = Graph.loadMapped(basename, Graph.LoadDirection.BOTH); run(graph, resultsDir); } } diff --git a/java/src/main/java/org/softwareheritage/graph/experiments/topology/SubdatasetSizeFunction.java b/java/src/main/java/org/softwareheritage/graph/experiments/topology/SubdatasetSizeFunction.java --- a/java/src/main/java/org/softwareheritage/graph/experiments/topology/SubdatasetSizeFunction.java +++ b/java/src/main/java/org/softwareheritage/graph/experiments/topology/SubdatasetSizeFunction.java @@ -84,7 +84,7 @@ final String basename = jsapResult.getString("basename"); - Graph graph = Graph.loadMapped(basename); + Graph graph = Graph.loadMapped(basename, Graph.LoadDirection.BOTH); run(graph); } } diff --git a/java/src/main/java/org/softwareheritage/graph/server/App.java b/java/src/main/java/org/softwareheritage/graph/server/App.java --- a/java/src/main/java/org/softwareheritage/graph/server/App.java +++ b/java/src/main/java/org/softwareheritage/graph/server/App.java @@ -6,13 +6,16 @@ import io.javalin.Javalin; import io.javalin.http.Context; import io.javalin.plugin.json.JavalinJackson; +import it.unimi.dsi.lang.EnumStringParser; import org.softwareheritage.graph.Graph; import org.softwareheritage.graph.Stats; import org.softwareheritage.graph.SWHID; import java.io.IOException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; /** * Web framework of the swh-graph server RPC API. @@ -34,6 +37,13 @@ "Binding port of the server."), new UnflaggedOption("graphPath", JSAP.STRING_PARSER, JSAP.NO_DEFAULT, JSAP.REQUIRED, JSAP.NOT_GREEDY, "The basename of the compressed graph."), + new FlaggedOption("direction", EnumStringParser.getParser(Graph.LoadDirection.class, true), + Graph.LoadDirection.BOTH.name(), JSAP.NOT_REQUIRED, 'd', "direction", + "Direction of the graph loaded in memory - " + + Arrays.stream(Graph.LoadDirection.values()) + .map(Graph.LoadDirection::name) + .map(String::toLowerCase) + .collect(Collectors.joining("|")) + "."), new Switch("timings", 't', "timings", "Show timings in API result metadata."),}); JSAPResult config = jsap.parse(args); @@ -44,8 +54,9 @@ String graphPath = config.getString("graphPath"); int port = config.getInt("port"); boolean showTimings = config.getBoolean("timings"); + var direction = (Graph.LoadDirection) config.getObject("direction"); - startServer(graphPath, port, showTimings); + startServer(graphPath, port, showTimings, direction); } /** @@ -54,9 +65,10 @@ * @param graphPath basename of the compressed graph * @param port binding port of the server * @param showTimings true if timings should be in results metadata, false otherwise + * @param loadDirection the direction of the graph to load int memory */ - private static void startServer(String graphPath, int port, boolean showTimings) throws IOException { - Graph graph = Graph.loadMapped(graphPath); + private static void startServer(String graphPath, int port, boolean showTimings, Graph.LoadDirection loadDirection) throws IOException { + Graph graph = Graph.loadMapped(graphPath, loadDirection); Stats stats = new Stats(graphPath); // Clean up on exit @@ -97,12 +109,14 @@ ctx.json(stats); }); + String loadDirectionStr = loadDirection == Graph.LoadDirection.BACKWARD ? "backward" : "forward"; + // Graph traversal endpoints // By default the traversal is a forward DFS using all edges app.get("/leaves/:src", ctx -> { SWHID src = new SWHID(ctx.pathParam("src")); - String direction = ctx.queryParam("direction", "forward"); + String direction = ctx.queryParam("direction", loadDirectionStr); String edgesFmt = ctx.queryParam("edges", "*"); Endpoint endpoint = new Endpoint(graph, direction, edgesFmt); @@ -112,7 +126,7 @@ app.get("/neighbors/:src", ctx -> { SWHID src = new SWHID(ctx.pathParam("src")); - String direction = ctx.queryParam("direction", "forward"); + String direction = ctx.queryParam("direction", loadDirectionStr); String edgesFmt = ctx.queryParam("edges", "*"); Endpoint endpoint = new Endpoint(graph, direction, edgesFmt); @@ -122,7 +136,7 @@ app.get("/visit/nodes/:src", ctx -> { SWHID src = new SWHID(ctx.pathParam("src")); - String direction = ctx.queryParam("direction", "forward"); + String direction = ctx.queryParam("direction", loadDirectionStr); String edgesFmt = ctx.queryParam("edges", "*"); Endpoint endpoint = new Endpoint(graph, direction, edgesFmt); @@ -132,7 +146,7 @@ app.get("/visit/paths/:src", ctx -> { SWHID src = new SWHID(ctx.pathParam("src")); - String direction = ctx.queryParam("direction", "forward"); + String direction = ctx.queryParam("direction", loadDirectionStr); String edgesFmt = ctx.queryParam("edges", "*"); Endpoint endpoint = new Endpoint(graph, direction, edgesFmt); @@ -143,7 +157,7 @@ app.get("/walk/:src/:dst", ctx -> { SWHID src = new SWHID(ctx.pathParam("src")); String dstFmt = ctx.pathParam("dst"); - String direction = ctx.queryParam("direction", "forward"); + String direction = ctx.queryParam("direction", loadDirectionStr); String edgesFmt = ctx.queryParam("edges", "*"); String algorithm = ctx.queryParam("traversal", "dfs"); diff --git a/java/src/main/java/org/softwareheritage/graph/utils/ExportSubdataset.java b/java/src/main/java/org/softwareheritage/graph/utils/ExportSubdataset.java --- a/java/src/main/java/org/softwareheritage/graph/utils/ExportSubdataset.java +++ b/java/src/main/java/org/softwareheritage/graph/utils/ExportSubdataset.java @@ -22,7 +22,7 @@ public static void main(String[] args) throws IOException, ClassNotFoundException { System.err.print("Loading everything..."); String graphPath = args[0]; - Graph graph = Graph.loadMapped(graphPath); + Graph graph = Graph.loadMapped(graphPath, Graph.LoadDirection.BOTH); Object2LongFunction mphMap = NodeIdMap.loadMph(graphPath + ".mph"); System.err.println(" done."); diff --git a/java/src/main/java/org/softwareheritage/graph/utils/FindEarliestRevision.java b/java/src/main/java/org/softwareheritage/graph/utils/FindEarliestRevision.java --- a/java/src/main/java/org/softwareheritage/graph/utils/FindEarliestRevision.java +++ b/java/src/main/java/org/softwareheritage/graph/utils/FindEarliestRevision.java @@ -36,7 +36,7 @@ System.err.println("loading transposed graph..."); ts = System.nanoTime(); - Graph graph = Graph.loadMapped(graphPath).transpose(); + Graph graph = Graph.loadMapped(graphPath, Graph.LoadDirection.BOTH).transpose(); elapsed = Duration.ofNanos(System.nanoTime() - ts); System.err.println(String.format("transposed graph loaded (duration: %s).", elapsed)); diff --git a/java/src/test/java/org/softwareheritage/graph/GraphTest.java b/java/src/test/java/org/softwareheritage/graph/GraphTest.java --- a/java/src/test/java/org/softwareheritage/graph/GraphTest.java +++ b/java/src/test/java/org/softwareheritage/graph/GraphTest.java @@ -16,17 +16,29 @@ public class GraphTest { static Graph graph; + static Graph forwardGraph; + static Graph backwardGraph; @BeforeAll public static void setUp() throws IOException { Path graphPath = Paths.get("..", "swh", "graph", "tests", "dataset", "output", "example"); - graph = Graph.loadMapped(graphPath.toString()); + graph = Graph.loadMapped(graphPath.toString(), Graph.LoadDirection.BOTH); + forwardGraph = Graph.loadMapped(graphPath.toString(), Graph.LoadDirection.FORWARD); + backwardGraph = Graph.loadMapped(graphPath.toString(), Graph.LoadDirection.BACKWARD); } public Graph getGraph() { return graph; } + public Graph getForwardGraph() { + return forwardGraph; + } + + public Graph getBackwardGraph() { + return backwardGraph; + } + public static SWHID fakeSWHID(String type, int num) { return new SWHID(String.format("swh:1:%s:%040d", type, num)); } diff --git a/java/src/test/java/org/softwareheritage/graph/LoadDirectionTest.java b/java/src/test/java/org/softwareheritage/graph/LoadDirectionTest.java new file mode 100644 --- /dev/null +++ b/java/src/test/java/org/softwareheritage/graph/LoadDirectionTest.java @@ -0,0 +1,45 @@ +package org.softwareheritage.graph; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.softwareheritage.graph.server.Endpoint; + +import java.util.ArrayList; + +// Avoid warnings concerning Endpoint.Output.result manual cast +@SuppressWarnings("unchecked") +public class LoadDirectionTest extends GraphTest { + + @Test + public void leavesForwardSameAsBoth() { + Graph graph = getGraph(); + Graph forwardGraph = getForwardGraph(); + SWHID src = new SWHID("swh:1:dir:0000000000000000000000000000000000000008"); + Endpoint endpoint = new Endpoint(graph, "forward", "dir:*"); + Endpoint forwardEndpoint = new Endpoint(forwardGraph, "forward", "dir:*"); + + ArrayList expectedLeaves = (ArrayList) endpoint.leaves(new Endpoint.Input(src)).result; + ArrayList actualLeaves = (ArrayList) forwardEndpoint.leaves(new Endpoint.Input(src)).result; + GraphTest.assertEqualsAnyOrder(expectedLeaves, actualLeaves); + } + + @Test + public void leavesBackwardSameAsBoth() { + Graph graph = getGraph(); + Graph backwardGraph = getBackwardGraph(); + SWHID src = new SWHID("swh:1:cnt:0000000000000000000000000000000000000005"); + Endpoint endpoint = new Endpoint(graph, "backward", "cnt:dir,dir:dir"); + Endpoint backwardEndpoint = new Endpoint(backwardGraph, "backward", "cnt:dir,dir:dir"); + + ArrayList expectedLeaves = (ArrayList) endpoint.leaves(new Endpoint.Input(src)).result; + ArrayList actualLeaves = (ArrayList) backwardEndpoint.leaves(new Endpoint.Input(src)).result; + GraphTest.assertEqualsAnyOrder(expectedLeaves, actualLeaves); + } + + @Test + public void invalidQueryDirection() { + Graph backwardGraph = getBackwardGraph(); + Assertions.assertThrows(IllegalArgumentException.class, + () -> new Endpoint(backwardGraph, "forward", "cnt:dir,dir:dir")); + } +}