View Javadoc

1   /*
2    * Copyright 2015 Data Archiving and Networked Services (an institute of
3    * Koninklijke Nederlandse Akademie van Wetenschappen), King's College London,
4    * Georg-August-Universitaet Goettingen Stiftung Oeffentlichen Rechts
5    *
6    * Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
7    * the European Commission - subsequent versions of the EUPL (the "Licence");
8    * You may not use this work except in compliance with the Licence.
9    * You may obtain a copy of the Licence at:
10   *
11   * https://joinup.ec.europa.eu/software/page/eupl
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the Licence is distributed on an "AS IS" basis,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the Licence for the specific language governing
17   * permissions and limitations under the Licence.
18   */
19  
20  package eu.ehri.project.models.utils;
21  
22  import com.tinkerpop.blueprints.Direction;
23  import com.tinkerpop.blueprints.Edge;
24  import com.tinkerpop.blueprints.Element;
25  import com.tinkerpop.blueprints.Vertex;
26  import com.tinkerpop.gremlin.java.GremlinPipeline;
27  import com.tinkerpop.pipes.PipeFunction;
28  import com.tinkerpop.pipes.branch.LoopPipe;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * Utilities for dealing with Gremlin pipelines and common
34   * graph-manipulation functions.
35   */
36  public final class JavaHandlerUtils {
37  
38      public static final Logger logger = LoggerFactory.getLogger(JavaHandlerUtils.class);
39  
40      private static final int LOOP_MAX = 20;
41  
42      /**
43       * Pipe function that quits after a certain number of loops.
44       *
45       * @param maxLoops The number of loops to run
46       * @return A pipe function
47       */
48      private static <S> PipeFunction<LoopPipe.LoopBundle<S>, Boolean> maxLoopFuncFactory(final int maxLoops) {
49          return vertexLoopBundle -> vertexLoopBundle.getLoops() < maxLoops;
50      }
51  
52      /**
53       * Pipe function with a max loop clause with the default of 20.
54       */
55      public static final PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean> defaultMaxLoops
56              = maxLoopFuncFactory(LOOP_MAX);
57  
58      /**
59       * Pipe function that always allows looping to continue.
60       */
61      public static final PipeFunction<LoopPipe.LoopBundle<Vertex>, Boolean> noopLoopFunc
62              = vertexLoopBundle -> true;
63  
64      /**
65       * Given a pipeline
66       *
67       * @param element  The element
68       * @param propName The cache property name
69       * @param pipe     A pipeline to count
70       */
71      public static <S, E> void cacheCount(Element element, GremlinPipeline<S, E> pipe, String propName) {
72          element.setProperty(propName, pipe.count());
73      }
74  
75      /**
76       * Add a relationship in such a way that it is the only one of its type
77       * from the source to the target.
78       *
79       * @param from  The source vertex
80       * @param to    The target vertex
81       * @param label The relationship label name
82       * @return Whether or not changes were made to the graph.
83       */
84      public static boolean addSingleRelationship(Vertex from, Vertex to, String label) {
85          if (!from.equals(to)) {
86              for (Edge edge : from.getEdges(Direction.OUT, label)) {
87                  if (edge.getVertex(Direction.IN).equals(to)) {
88                      logger.warn("Attempting to add relationship '{}' that already exists: {}", label, to);
89                      return false;
90                  } else {
91                      edge.remove();
92                      logger.warn("Removed prior '{}' relationship added in single mode: {}",
93                              label, from);
94                  }
95              }
96              from.addEdge(label, to);
97              return true;
98          } else {
99              logger.warn("Attempt to add self-referential '{}' relationship " +
100                     "where single relationship specified: {}",
101                     label, to);
102             return false;
103         }
104     }
105 
106     /**
107      * Add a relationship that can only exist once between source and target.
108      *
109      * @param from  The source vertex
110      * @param to    The target vertex
111      * @param label The relationship label name
112      * @return Whether or not changes were made to the graph.
113      */
114     public static boolean addUniqueRelationship(Vertex from, Vertex to, String label) {
115         for (Edge edge : from.getEdges(Direction.OUT, label)) {
116             if (edge.getVertex(Direction.IN).equals(to)) {
117                 return false;
118             }
119         }
120         from.addEdge(label, to);
121         return true;
122     }
123 
124     /**
125      * Determine if a relationship with the given label(s) exists
126      * between source and target vertex.
127      *
128      * @param from   The source vertex
129      * @param to     The target vertex
130      * @param labels The set of labels
131      * @return If a matching relationship is found
132      */
133     public static boolean hasRelationship(Vertex from, Vertex to, String... labels) {
134         for (Edge edge : from.getEdges(Direction.OUT, labels)) {
135             if (edge.getVertex(Direction.IN).equals(to)) {
136                 return true;
137             }
138         }
139         return false;
140     }
141 
142     /**
143      * Remove all relationships with the given labels between
144      * source and target vertex.
145      *
146      * @param from   The source vertex
147      * @param to     The target vertex
148      * @param labels The set of labels
149      */
150     public static boolean removeAllRelationships(Vertex from, Vertex to, String... labels) {
151         int removed = 0;
152         for (Edge edge : from.getEdges(Direction.OUT, labels)) {
153             if (edge.getVertex(Direction.IN).equals(to)) {
154                 edge.remove();
155                 removed++;
156             }
157         }
158         return removed > 0;
159     }
160 
161     /**
162      * Check if a vertex has edges with the given direct/label.
163      *
164      * @param vertex    The source vertex
165      * @param direction The edge direction
166      * @param labels    The set of labels
167      * @return Whether any edges exist
168      */
169     public static boolean hasEdge(Vertex vertex, Direction direction, String... labels) {
170         return vertex.getEdges(direction, labels).iterator().hasNext();
171     }
172 }