Release 1.13.6 Docker Artifact
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / serialization / engines / query / GraphTraversalQueryEngine.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aai.serialization.engines.query;
22
23 import static org.onap.aai.edges.enums.AAIDirection.*;
24 import static org.onap.aai.edges.enums.EdgeField.PRIVATE;
25 import static org.onap.aai.edges.enums.EdgeProperty.CONTAINS;
26 import static org.onap.aai.edges.enums.EdgeProperty.DELETE_OTHER_V;
27
28 import java.util.List;
29 import java.util.Set;
30
31 import org.apache.tinkerpop.gremlin.process.traversal.Order;
32 import org.apache.tinkerpop.gremlin.process.traversal.P;
33 import org.apache.tinkerpop.gremlin.process.traversal.Path;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
35 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
36 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
37 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
38 import org.apache.tinkerpop.gremlin.structure.Direction;
39 import org.apache.tinkerpop.gremlin.structure.Edge;
40 import org.apache.tinkerpop.gremlin.structure.Element;
41 import org.apache.tinkerpop.gremlin.structure.Vertex;
42 import org.onap.aai.db.props.AAIProperties;
43 import org.onap.aai.edges.enums.EdgeField;
44 import org.onap.aai.edges.enums.EdgeProperty;
45 import org.onap.aai.introspection.Loader;
46 import org.onap.aai.logging.StopWatch;
47
48 /*
49  * This class needs some big explanation despite its compact size.
50  * This controls all the queries performed by the CRUD API in A&AI.
51  * findParents, findChildren, and findDeletable require special attention
52  *   These methods use 'repeat'. You cannot use 'emit' with repeat currently
53  *   as it is extremely buggy as of tinkerpop-3.0.1-incubating. The way around
54  *   it (for now) is to sideEffect all the vertices we traverse into an ArrayList.
55  *
56  */
57 public class GraphTraversalQueryEngine extends QueryEngine {
58
59     /**
60      * Instantiates a new graph traversal query engine.
61      *
62      * @param g graph traversal source to traverse the graph
63      */
64     public GraphTraversalQueryEngine(GraphTraversalSource g) {
65         super(g);
66     }
67
68     /**
69      * {@inheritDoc}
70      */
71     @Override
72     public List<Vertex> findParents(Vertex start) {
73         try {
74             StopWatch.conditionalStart();
75
76             @SuppressWarnings("unchecked")
77             final GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).emit(v -> true)
78                     .repeat(__.union(__.inE().has(CONTAINS.toString(), OUT.toString()).outV(),
79                             __.outE().has(CONTAINS.toString(), IN.toString()).inV()));
80             return pipe.toList();
81         } finally {
82             dbTimeMsecs += StopWatch.stopIfStarted();
83         }
84     }
85
86     /**
87      * {@inheritDoc}
88      */
89     @Override
90     public List<Vertex> findParents(String[] uris) {
91         try {
92             StopWatch.conditionalStart();
93             final GraphTraversal<Vertex, Vertex> pipe =
94                     this.g.V().has(AAIProperties.AAI_URI, P.within(uris)).order().by(AAIProperties.AAI_URI, Order.decr);
95             return pipe.toList();
96         } finally {
97             dbTimeMsecs += StopWatch.stopIfStarted();
98         }
99     }
100
101     /**
102      * {@inheritDoc}
103      */
104     @Override
105     public List<Vertex> findAllChildren(Vertex start) {
106
107         @SuppressWarnings("unchecked")
108         GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).emit(v -> true)
109                 .repeat(__.union(__.outE().has(CONTAINS.toString(), OUT.toString()).inV(),
110                         __.inE().has(CONTAINS.toString(), IN.toString()).outV()));
111
112         return pipe.toList();
113
114     }
115
116     /**
117      * {@inheritDoc}
118      */
119     @Override
120     public List<Vertex> findChildrenOfType(Vertex start, String type) {
121         @SuppressWarnings("unchecked")
122         GraphTraversal<Vertex, Vertex> pipe = this.g.V(start)
123                 .union(__.outE().has(CONTAINS.toString(), OUT.toString()).inV(),
124                         __.inE().has(CONTAINS.toString(), IN.toString()).outV())
125                 .has(AAIProperties.NODE_TYPE, type).dedup();
126
127         return pipe.toList();
128     }
129
130     /**
131      * {@inheritDoc}
132      */
133     @Override
134     public List<Vertex> findChildren(Vertex start) {
135         @SuppressWarnings("unchecked")
136         GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).union(__.outE().has(CONTAINS.toString(), OUT.toString()),
137                 __.inE().has(CONTAINS.toString(), IN.toString())).otherV().dedup();
138
139         return pipe.toList();
140     }
141
142     /**
143      * {@inheritDoc}
144      */
145     @Override
146     public List<Vertex> findDeletable(Vertex start) {
147         try {
148             StopWatch.conditionalStart();
149             @SuppressWarnings("unchecked")
150             GraphTraversal<Vertex, Vertex> pipe = this.g.V(start).emit(v -> true)
151                     .repeat(__.union(__.outE().has(DELETE_OTHER_V.toString(), OUT.toString()).inV(),
152                             __.inE().has(DELETE_OTHER_V.toString(), IN.toString()).outV()))
153                     .dedup();
154
155             return pipe.toList();
156         } finally {
157             dbTimeMsecs += StopWatch.stopIfStarted();
158         }
159     }
160
161     /**
162      * {@inheritDoc}
163      */
164     @Override
165     public List<Vertex> findDeletable(List<Vertex> startVertexes) {
166         try {
167             StopWatch.conditionalStart();
168             Vertex[] vertices = new Vertex[startVertexes.size()];
169             vertices = startVertexes.toArray(vertices);
170             GraphTraversal<Vertex, Vertex> pipe = this.g.V(vertices).emit(v -> true)
171                     .repeat(__.union(__.outE().has(DELETE_OTHER_V.toString(), OUT.toString()).inV(),
172                             __.inE().has(DELETE_OTHER_V.toString(), IN.toString()).outV()))
173                     .dedup();
174
175             return pipe.toList();
176         } finally {
177             dbTimeMsecs += StopWatch.stopIfStarted();
178         }
179     }
180
181     /**
182      * {@inheritDoc}
183      */
184     @Override
185     public List<Vertex> findRelatedVertices(Vertex start, Direction direction, String label, String nodeType) {
186         GraphTraversal<Vertex, Vertex> pipe = this.g.V(start);
187         switch (direction) {
188             case OUT:
189                 pipe.out(label);
190                 break;
191             case IN:
192                 pipe.in(label);
193                 break;
194             case BOTH:
195                 pipe.both(label);
196                 break;
197             default:
198                 break;
199         }
200
201         pipe.has(AAIProperties.NODE_TYPE, nodeType).dedup();
202         return pipe.toList();
203     }
204
205     @Override
206     public Tree<Element> findSubGraph(Vertex start, int iterations, boolean nodeOnly) {
207         final GraphTraversal<Vertex, ?> t = this.g.V(start).emit(v -> true).times(iterations)
208                 .repeat(__.union(__.outE().has(CONTAINS.toString(), OUT.toString()).inV(),
209                         __.inE().has(CONTAINS.toString(), IN.toString()).outV()));
210
211         if (!nodeOnly) {
212             t.union(__.identity(), __.bothE().has(CONTAINS.toString(), NONE.toString()).dedup().otherV());
213         }
214         t.tree();
215         if (t.hasNext()) {
216             return (Tree) t.next();
217         } else {
218             return new Tree();
219         }
220     }
221
222     @Override
223     public List<Edge> findEdgesForVersion(Vertex start, Loader loader) {
224         // From the given start vertex find both the
225         // out edges that has property CONTAINS set to NONE
226         // whose in vertexes has an object that is declared in the oxm
227         // And do the same thing vice versa to get a list of edges
228         // Then check that the edge should not have the property private set to true
229         // and remove the duplicates and return the list of edges
230         final Set<String> objects = loader.getAllObjects().keySet();
231         GraphTraversal<Vertex, Edge> pipeline = this.g.V(start)
232                 .union(__.inE().has(CONTAINS.toString(), NONE.toString())
233                         .where(__.outV().has(AAIProperties.NODE_TYPE, P.within(objects))),
234                         __.outE().has(CONTAINS.toString(), NONE.toString())
235                                 .where(__.inV().has(AAIProperties.NODE_TYPE, P.within(objects))))
236                 .not(__.has("private", true)).dedup();
237
238         return pipeline.toList();
239     }
240
241     @Override
242     public List<Vertex> findCousinVertices(Vertex start, String... labels) {
243         // Start at the given vertex
244         // Do a union to copy the start vertex to be run against all
245         // so for the start vertex it gets all of in edges that contains other v set to none
246         // and also all the other out edges with contains other v set to none
247         // And filter the edges based on the property private not set
248         // so that means it will be a regular edge
249         // and find the other end of the vertex so if setup like this:
250         // v2 -> e1 -> v3
251         // It will return v3
252         GraphTraversal<Vertex, Vertex> pipeline = this.g.V(start)
253                 .union(__.inE(labels).has(CONTAINS.toString(), NONE.toString()),
254                         __.outE(labels).has(CONTAINS.toString(), NONE.toString()))
255                 .not(__.has(PRIVATE.toString(), true)).otherV().dedup();
256
257         return pipeline.toList();
258     }
259
260     public List<Path> findCousinsAsPath(Vertex start) {
261         return this.g.V(start).bothE().where(__.and(__.has(EdgeProperty.CONTAINS.toString(), NONE.toString()),
262                 __.not(__.has(EdgeField.PRIVATE.toString(), true)))).otherV().path().toList();
263     }
264
265     public double getDBTimeMsecs() {
266         return (dbTimeMsecs);
267     }
268 }