package org.gbif.checklistbank.neo.traverse; import org.gbif.api.vocabulary.Rank; import org.gbif.checklistbank.neo.NeoProperties; import org.gbif.checklistbank.neo.RelType; import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.graphdb.traversal.Evaluators; import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.graphdb.traversal.Uniqueness; import org.neo4j.kernel.impl.traversal.MonoDirectionalTraversalDescription; /** * Various reusable traversal descriptions for taxonomic neo dbs. */ public class Traversals { public static final TraversalDescription PARENT = new MonoDirectionalTraversalDescription() .relationships(RelType.PARENT_OF, Direction.INCOMING) .depthFirst() .evaluator(Evaluators.toDepth(1)) .evaluator(Evaluators.excludeStartPosition()); public static final TraversalDescription PARENTS = new MonoDirectionalTraversalDescription() .relationships(RelType.PARENT_OF, Direction.INCOMING) .depthFirst() .evaluator(Evaluators.excludeStartPosition()); public static final TraversalDescription LINNEAN_PARENTS = new MonoDirectionalTraversalDescription() .relationships(RelType.PARENT_OF, Direction.INCOMING) .depthFirst() .evaluator(new LinneanRankEvaluator()) .evaluator(Evaluators.excludeStartPosition()); public static final TraversalDescription CHILDREN = new MonoDirectionalTraversalDescription() .relationships(RelType.PARENT_OF, Direction.OUTGOING) .breadthFirst() .evaluator(Evaluators.toDepth(1)) .evaluator(Evaluators.excludeStartPosition()) .uniqueness(Uniqueness.NODE_PATH); public static final TraversalDescription SYNONYMS = new MonoDirectionalTraversalDescription() .relationships(RelType.SYNONYM_OF, Direction.INCOMING) .relationships(RelType.PROPARTE_SYNONYM_OF, Direction.INCOMING) .breadthFirst() .evaluator(Evaluators.toDepth(1)) .evaluator(Evaluators.excludeStartPosition()) .uniqueness(Uniqueness.NODE_PATH); public static final TraversalDescription ACCEPTED = new MonoDirectionalTraversalDescription() .relationships(RelType.SYNONYM_OF, Direction.OUTGOING) .relationships(RelType.PROPARTE_SYNONYM_OF, Direction.OUTGOING) .breadthFirst() .evaluator(Evaluators.toDepth(1)) .evaluator(Evaluators.excludeStartPosition()) .uniqueness(Uniqueness.NODE_PATH); /** * Finds all nodes connected via a basionym_of relation regardless of the direction. */ public static final TraversalDescription BASIONYM_GROUP = new MonoDirectionalTraversalDescription() .relationships(RelType.BASIONYM_OF) .breadthFirst() .uniqueness(Uniqueness.NODE_PATH); /** * Traversal that iterates depth first over all accepted descendants including the starting node. * There is no particular order for the direct children. * Use the SORTED_TREE traversals if a taxonomic order is required! */ public static final TraversalDescription ACCEPTED_TREE = new MonoDirectionalTraversalDescription() .relationships(RelType.PARENT_OF, Direction.OUTGOING) .depthFirst() .uniqueness(Uniqueness.NODE_PATH); /** * Traversal that iterates depth first over all descendants including synonyms and the starting node. * The node of pro parte synonyms will be visited only once as pro_parte relationships are ignored. * There is no particular order for the direct children. * See SORTED_TREE traversals if a taxonomic order is required! */ public static final TraversalDescription TREE_WITHOUT_PRO_PARTE = ACCEPTED_TREE .relationships(RelType.SYNONYM_OF, Direction.INCOMING); /** * Traversal that iterates depth first over all descendants including synonyms and the starting node. * The node of pro parte synonyms will be visited multiple times. * There is no particular order for the direct children. * See SORTED_TREE traversals if a taxonomic order is required! */ public static final TraversalDescription TREE = TREE_WITHOUT_PRO_PARTE .relationships(RelType.PROPARTE_SYNONYM_OF, Direction.INCOMING); /** * Traversal that iterates depth first over all descendants including synonyms. * The node of pro parte synonyms will be visited multiple times, once for each synonym/pro_parte relationship! * There is no particular order for the direct children. * See SORTED_TREE if a taxonomic order is required! */ public static final TraversalDescription DESCENDANTS = TREE .evaluator(Evaluators.excludeStartPosition()); /** * Traversal that iterates over all child taxa and their synonyms in a taxonomic order, i.e. by rank and secondary ordered by the name. * The traversal includes the initial starting node. * The node of pro parte synonyms will be visited multiple times, once for each synonym/pro_parte relationship! * * This traversal differes from DESCENDANTS that it includes the starting node and yields the nodes in a taxonomic order. * The order is a bit expensive to calculate and requires more memory. So use DESCENDANTS whenever possible. */ public static final TraversalDescription SORTED_TREE = new MonoDirectionalTraversalDescription() .depthFirst() .expand(TaxonomicOrderExpander.TREE_WITH_PPSYNONYMS_EXPANDER) .uniqueness(Uniqueness.NODE_PATH); /** * Traversal that iterates over all child taxa and their synonyms in a taxonomic order, but excludes pro parte relations, the node of pro parte synonyms will * therefore be visited only once via the synonym_of relationship. * The traversal includes the initial starting node. * * This traversal differes from DESCENDANTS that it includes the starting node and yields the nodes in a taxonomic order. * The order is a bit expensive to calculate and requires more memory. So use DESCENDANTS whenever possible. */ public static final TraversalDescription SORTED_TREE_WITHOUT_PRO_PARTE = new MonoDirectionalTraversalDescription() .depthFirst() .expand(TaxonomicOrderExpander.TREE_WITH_SYNONYMS_EXPANDER) .uniqueness(Uniqueness.NODE_PATH); /** * Traversal that iterates over all accepted child taxa in taxonomic order, i.e. by rank and secondary ordered by the name. * The traversal includes the initial starting node! */ public static final TraversalDescription SORTED_ACCEPTED_TREE = new MonoDirectionalTraversalDescription() .depthFirst() .expand(TaxonomicOrderExpander.TREE_EXPANDER) .evaluator(new AcceptedOnlyEvaluator()) .uniqueness(Uniqueness.NODE_PATH); /** * Tries to find a parent node with the given rank * @param start node to start looking for parents, excluded from search * @return the parent node with requested rank or null */ public static Node findParentWithRank(Node start, Rank rank) { try(ResourceIterator<Node> parents = Traversals.PARENTS.traverse(start).nodes().iterator()) { while (parents.hasNext()) { Node p = parents.next(); if ((int)p.getProperty(NeoProperties.RANK, -1) == rank.ordinal()) { return p; } } } return null; } }