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 }