package org.neo4j.kernel; import java.util.Iterator; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Expander; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Path; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipExpander; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.traversal.BranchOrderingPolicy; import org.neo4j.graphdb.traversal.BranchSelector; import org.neo4j.graphdb.traversal.PruneEvaluator; import org.neo4j.graphdb.traversal.TraversalBranch; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.helpers.Predicate; import org.neo4j.kernel.impl.traversal.FinalExpansionSource; import org.neo4j.kernel.impl.traversal.TraversalDescriptionImpl; /** * A factory for objects regarding traversal of the graph. F.ex. it has a * method {@link #description()} for creating a new * {@link TraversalDescription}, methods for creating new * {@link TraversalBranch} instances and more. */ public class Traversal { private static final BranchOrderingPolicy PREORDER_DEPTH_FIRST_SELECTOR = new BranchOrderingPolicy() { public BranchSelector create( TraversalBranch startSource ) { return new PreorderDepthFirstSelector( startSource ); } }; private static final BranchOrderingPolicy POSTORDER_DEPTH_FIRST_SELECTOR = new BranchOrderingPolicy() { public BranchSelector create( TraversalBranch startSource ) { return new PostorderDepthFirstSelector( startSource ); } }; private static final BranchOrderingPolicy PREORDER_BREADTH_FIRST_SELECTOR = new BranchOrderingPolicy() { public BranchSelector create( TraversalBranch startSource ) { return new PreorderBreadthFirstSelector( startSource ); } }; private static final BranchOrderingPolicy POSTORDER_BREADTH_FIRST_SELECTOR = new BranchOrderingPolicy() { public BranchSelector create( TraversalBranch startSource ) { return new PostorderBreadthFirstSelector( startSource ); } }; private static final Predicate<Path> RETURN_ALL = new Predicate<Path>() { public boolean accept( Path item ) { return true; } }; private static final Predicate<Path> RETURN_ALL_BUT_START_NODE = new Predicate<Path>() { public boolean accept( Path item ) { return item.length() > 0; } }; /** * Creates a new {@link TraversalDescription} with default value for * everything so that it's OK to call * {@link TraversalDescription#traverse(org.neo4j.graphdb.Node)} without * modification. But it isn't a very useful traversal, instead you should * add rules and behaviours to it before traversing. * * @return a new {@link TraversalDescription} with default values. */ public static TraversalDescription description() { return new TraversalDescriptionImpl(); } /** * Creates a new {@link RelationshipExpander} which is set to expand * relationships with {@code type} and {@code direction}. * * @param type the {@link RelationshipType} to expand. * @param dir the {@link Direction} to expand. * @return a new {@link RelationshipExpander}. */ public static Expander expanderForTypes( RelationshipType type, Direction dir ) { return StandardExpander.create( type, dir ); } /** * Returns an empty {@link Expander} which, if not modified, will expand * all relationships when asked to expand a {@link Node}. Criterias * can be added to narrow the {@link Expansion}. * @return an empty {@link Expander} which, if not modified, will expand * all relationship for {@link Node}s. */ public static Expander emptyExpander() { return StandardExpander.DEFAULT; // TODO: should this be a PROPER empty? } /** * Creates a new {@link RelationshipExpander} which is set to expand * relationships with two different types and directions. * * @param type1 a {@link RelationshipType} to expand. * @param dir1 a {@link Direction} to expand. * @param type2 another {@link RelationshipType} to expand. * @param dir2 another {@link Direction} to expand. * @return a new {@link RelationshipExpander}. */ public static Expander expanderForTypes( RelationshipType type1, Direction dir1, RelationshipType type2, Direction dir2 ) { return StandardExpander.create( type1, dir1, type2, dir2 ); } /** * Creates a new {@link RelationshipExpander} which is set to expand * relationships with multiple types and directions. * * @param type1 a {@link RelationshipType} to expand. * @param dir1 a {@link Direction} to expand. * @param type2 another {@link RelationshipType} to expand. * @param dir2 another {@link Direction} to expand. * @param more additional pairs or type/direction to expand. * @return a new {@link RelationshipExpander}. */ public static Expander expanderForTypes( RelationshipType type1, Direction dir1, RelationshipType type2, Direction dir2, Object... more ) { return StandardExpander.create( type1, dir1, type2, dir2, more ); } /** * Returns a {@link RelationshipExpander} which expands relationships * of all types and directions. * @return a relationship expander which expands all relationships. */ public static Expander expanderForAllTypes() { return expanderForAllTypes( Direction.BOTH ); } /** * Returns a {@link RelationshipExpander} which expands relationships * of all types in the given {@code direction}. * @return a relationship expander which expands all relationships in * the given {@code direction}. */ public static Expander expanderForAllTypes( Direction direction ) { return StandardExpander.create( direction ); } /** * Returns a {@link RelationshipExpander} wrapped as an {@link Expander}. * @param expander {@link RelationshipExpander} to wrap. * @return a {@link RelationshipExpander} wrapped as an {@link Expander}. */ public static Expander expander( RelationshipExpander expander ) { if ( expander instanceof Expander ) { return (Expander) expander; } return StandardExpander.wrap( expander ); } /** * Combines two {@link TraversalBranch}s with a common * {@link TraversalBranch#node() head node} in order to obtain an * {@link TraversalBranch} representing a path from the start node of the * <code>source</code> {@link TraversalBranch} to the start node of the * <code>target</code> {@link TraversalBranch}. The resulting * {@link TraversalBranch} will not {@link TraversalBranch#next() expand * further}, and does not provide a {@link TraversalBranch#parent() parent} * {@link TraversalBranch}. * * @param source the {@link TraversalBranch} where the resulting path starts * @param target the {@link TraversalBranch} where the resulting path ends * @throws IllegalArgumentException if the {@link TraversalBranch#node() * head nodes} of the supplied {@link TraversalBranch}s does not * match * @return an {@link TraversalBranch} that represents the path from the * start node of the <code>source</code> {@link TraversalBranch} to * the start node of the <code>target</code> {@link TraversalBranch} */ public static TraversalBranch combineSourcePaths( TraversalBranch source, TraversalBranch target ) { if ( !source.node().equals( target.node() ) ) { throw new IllegalArgumentException( "The nodes of the head and tail must match" ); } Path headPath = source.position(), tailPath = target.position(); Relationship[] relationships = new Relationship[headPath.length() + tailPath.length()]; Iterator<Relationship> iter = headPath.relationships().iterator(); for ( int i = 0; iter.hasNext(); i++ ) { relationships[i] = iter.next(); } iter = tailPath.relationships().iterator(); for ( int i = relationships.length - 1; iter.hasNext(); i-- ) { relationships[i] = iter.next(); } return new FinalExpansionSource( tailPath.startNode(), relationships ); } /** * A {@link PruneEvaluator} which prunes everything beyond {@code depth}. * @param depth the depth to prune beyond (after). * @return a {@link PruneEvaluator} which prunes everything after * {@code depth}. */ public static PruneEvaluator pruneAfterDepth( final int depth ) { return new PruneEvaluator() { public boolean pruneAfter( Path position ) { return position.length() >= depth; } }; } /** * A traversal return filter which returns all {@link Path}s it encounters. * * @return a return filter which returns everything. */ public static Predicate<Path> returnAll() { return RETURN_ALL; } /** * A traversal return filter which returns all {@link Path}s except the * position of the start node. * * @return a return filter which returns everything except the start node. */ public static Predicate<Path> returnAllButStartNode() { return RETURN_ALL_BUT_START_NODE; } /** * Returns a "preorder depth first" ordering policy. A depth first selector * always tries to select positions (from the current position) which are * deeper than the current position. * * @return a {@link BranchOrderingPolicy} for a preorder depth first * selector. */ public static BranchOrderingPolicy preorderDepthFirst() { return PREORDER_DEPTH_FIRST_SELECTOR; } /** * Returns a "postorder depth first" ordering policy. A depth first selector * always tries to select positions (from the current position) which are * deeper than the current position. A postorder depth first selector * selects deeper position before the shallower ones. * * @return a {@link BranchOrderingPolicy} for a postorder depth first * selector. */ public static BranchOrderingPolicy postorderDepthFirst() { return POSTORDER_DEPTH_FIRST_SELECTOR; } /** * Returns a "preorder breadth first" ordering policy. A breadth first * selector always selects all positions on the current depth before * advancing to the next depth. * * @return a {@link BranchOrderingPolicy} for a preorder breadth first * selector. */ public static BranchOrderingPolicy preorderBreadthFirst() { return PREORDER_BREADTH_FIRST_SELECTOR; } /** * Returns a "postorder breadth first" ordering policy. A breadth first * selector always selects all positions on the current depth before * advancing to the next depth. A postorder breadth first selector selects * the levels in the reversed order, starting with the deepest. * * @return a {@link BranchOrderingPolicy} for a postorder breadth first * selector. */ public static BranchOrderingPolicy postorderBreadthFirst() { return POSTORDER_BREADTH_FIRST_SELECTOR; } /** * Provides hooks to help build a string representation of a {@link Path}. * @param <T> the type of {@link Path}. */ public static interface PathDescriptor<T extends Path> { /** * Returns a string representation of a {@link Node}. * @param path the {@link Path} we're building a string representation * from. * @param node the {@link Node} to return a string representation of. * @return a string representation of a {@link Node}. */ String nodeRepresentation( T path, Node node ); /** * Returns a string representation of a {@link Relationship}. * @param path the {@link Path} we're building a string representation * from. * @param from the previous {@link Node} in the path. * @param relationship the {@link Relationship} to return a string * representation of. * @return a string representation of a {@link Relationship}. */ String relationshipRepresentation( T path, Node from, Relationship relationship ); } /** * The default {@link PathDescriptor} used in common toString() * representations in classes implementing {@link Path}. * @param <T> the type of {@link Path}. */ public static class DefaultPathDescriptor<T extends Path> implements PathDescriptor<T> { public String nodeRepresentation( Path path, Node node ) { return "(" + node.getId() + ")"; } public String relationshipRepresentation( Path path, Node from, Relationship relationship ) { String prefix = "--", suffix = "--"; if ( from.equals( relationship.getEndNode() ) ) { prefix = "<--"; } else { suffix = "-->"; } return prefix + "<" + relationship.getType().name() + "," + relationship.getId() + "]" + suffix; } } /** * Method for building a string representation of a {@link Path}, using * the given {@code builder}. * @param <T> the type of {@link Path}. * @param path the {@link Path} to build a string representation of. * @param builder the {@link PathDescriptor} to get * {@link Node} and {@link Relationship} representations from. * @return a string representation of a {@link Path}. */ public static <T extends Path> String pathToString( T path, PathDescriptor<T> builder ) { Node current = path.startNode(); StringBuilder result = new StringBuilder(); for ( Relationship rel : path.relationships() ) { result.append( builder.nodeRepresentation( path, current ) ); result.append( builder.relationshipRepresentation( path, current, rel ) ); current = rel.getOtherNode( current ); } result.append( builder.nodeRepresentation( path, current ) ); return result.toString(); } /** * Returns the default string representation of a {@link Path}. It uses * the {@link DefaultPathDescriptor} to get representations. * @param path the {@link Path} to build a string representation of. * @return the default string representation of a {@link Path}. */ public static String defaultPathToString( Path path ) { return pathToString( path, new DefaultPathDescriptor<Path>() ); } /** * Returns a quite simple string representation of a {@link Path}. It * doesn't print relationship types or ids, just directions. * @param path the {@link Path} to build a string representation of. * @return a quite simple representation of a {@link Path}. */ public static String simplePathToString( Path path ) { return pathToString( path, new DefaultPathDescriptor<Path>() { @Override public String relationshipRepresentation( Path path, Node from, Relationship relationship ) { return relationship.getStartNode().equals( from ) ? "-->" : "<--"; } } ); } /** * Returns a quite simple string representation of a {@link Path}. It * doesn't print relationship types or ids, just directions. it uses the * {@code nodePropertyKey} to try to display that property value as in the * node representation instead of the node id. If that property doesn't * exist, the id is used. * @param path the {@link Path} to build a string representation of. * @return a quite simple representation of a {@link Path}. */ public static String simplePathToString( Path path, final String nodePropertyKey ) { return pathToString( path, new DefaultPathDescriptor<Path>() { @Override public String nodeRepresentation( Path path, Node node ) { return "(" + node.getProperty( nodePropertyKey, node.getId() ) + ")"; } @Override public String relationshipRepresentation( Path path, Node from, Relationship relationship ) { return relationship.getStartNode().equals( from ) ? "-->" : "<--"; } } ); } }