/* * Copyright 2008 Network Engine for Objects in Lund AB [neotechnology.com] * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.neo4j.graphalgo.benchmark.graphgeneration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.neo4j.api.core.Direction; import org.neo4j.api.core.Node; import org.neo4j.api.core.Relationship; import org.neo4j.api.core.RelationshipType; import org.neo4j.api.core.ReturnableEvaluator; import org.neo4j.api.core.StopEvaluator; import org.neo4j.api.core.TraversalPosition; import org.neo4j.api.core.Traverser; import org.neo4j.api.core.Traverser.Order; /** * This class can be used to generate a subgraph from a graph, represented as a * set of nodes and a set of edges. The subgraph always starts out empty, and * can be emptied again with the clear() method. It can then be filled with * nodes and edges through the various methods supplied. This can of course be * used to retrieve the set of nodes and the set of edges from a NeoService. * @author Patrik Larsson */ public class SubGraph { Set<Node> nodes = new HashSet<Node>(); Set<Relationship> edges = new HashSet<Relationship>(); /** * Empties this subgraph. */ public void clear() { nodes = new HashSet<Node>(); edges = new HashSet<Relationship>(); } /** * Adds a tree to this subgraph by doing a breadth first search of a given * depth, adding all nodes found and the first edge leading to each node. * @param node * The starting node * @param searchDepth * The search depth. * @param relationshipType * Relation type to traverse. * @param direction * Direction in which to traverse edges. */ public void addTreeFromCentralNode( Node node, final int searchDepth, RelationshipType relationshipType, Direction direction ) { Traverser traverser = node.traverse( Order.BREADTH_FIRST, new StopEvaluator() { public boolean isStopNode( TraversalPosition currentPos ) { return currentPos.depth() >= searchDepth; } }, ReturnableEvaluator.ALL, relationshipType, direction ); for ( Node node2 : traverser ) { nodes.add( node2 ); edges.add( traverser.currentPosition().lastRelationshipTraversed() ); } } /** * Makes a search of a given depth from a given node and adds all found * nodes and edges to this subgraph. * @param node * The starting node * @param searchDepth * The search depth. * @param relationshipType * Relation type to traverse. * @param direction * Direction in which to traverse edges. * @param includeBoundaryEdges * If false, edges between nodes where the maximum depth has been * reached will not be included since the search depth is * considered to have been exhausted at them. */ public void addSubGraphFromCentralNode( Node node, final int searchDepth, RelationshipType relationshipType, Direction direction, boolean includeBoundaryEdges ) { internalAddSubGraphFromCentralNode( node, searchDepth, relationshipType, direction, includeBoundaryEdges, new HashMap<Node,Integer>() ); } /** * Same as addSubGraphFromCentralNode, but the internal version with some * extra data sent along. * @param nodeScanDepths * This stores at what depth a certain node was added so we can * ignore it when we reach it with a lower depth. */ protected void internalAddSubGraphFromCentralNode( Node node, final int searchDepth, RelationshipType relationshipType, Direction direction, boolean includeBoundaryEdges, Map<Node,Integer> nodeScanDepths ) { // We stop here if this node has already been scanned and we this time // have a "shorter" way to go beyond it. Integer previousDepth = nodeScanDepths.get( node ); if ( previousDepth != null && previousDepth >= searchDepth ) { return; } nodes.add( node ); nodeScanDepths.put( node, searchDepth ); if ( searchDepth == 0 && includeBoundaryEdges ) { for ( Relationship relationship : node.getRelationships( relationshipType, direction ) ) { if ( nodes.contains( relationship.getOtherNode( node ) ) ) { edges.add( relationship ); } } } if ( searchDepth <= 0 ) { return; } for ( Relationship relationship : node.getRelationships( relationshipType, direction ) ) { edges.add( relationship ); internalAddSubGraphFromCentralNode( relationship .getOtherNode( node ), searchDepth - 1, relationshipType, direction, includeBoundaryEdges, nodeScanDepths ); } } /** * @return the edges */ public Set<Relationship> getEdges() { return edges; } /** * @return the nodes */ public Set<Node> getNodes() { return nodes; } }