Initial commit with all the necessary files
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / query / builder / GraphTraversalBuilder.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.aai
4  * ================================================================================
5  * Copyright (C) 2017 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.openecomp.aai.query.builder;
22
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Optional;
28 import java.util.Set;
29
30 import org.apache.tinkerpop.gremlin.process.traversal.P;
31 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
32 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
35 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
36 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
37 import org.apache.tinkerpop.gremlin.structure.Direction;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39
40 import org.openecomp.aai.db.props.AAIProperties;
41 import org.openecomp.aai.exceptions.AAIException;
42 import org.openecomp.aai.introspection.Introspector;
43 import org.openecomp.aai.introspection.Loader;
44 import org.openecomp.aai.schema.enums.ObjectMetadata;
45 import org.openecomp.aai.schema.enums.PropertyMetadata;
46 import org.openecomp.aai.serialization.db.EdgeRule;
47 import org.openecomp.aai.serialization.db.EdgeRules;
48 import org.openecomp.aai.serialization.db.EdgeType;
49 import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
50
51 /**
52  * The Class GraphTraversalBuilder.
53  */
54 public abstract class GraphTraversalBuilder extends QueryBuilder {
55
56         protected GraphTraversal<Vertex, Vertex> traversal = null;
57         protected Admin<Vertex, Vertex> completeTraversal = null;
58         private EdgeRules edgeRules = EdgeRules.getInstance();
59         
60         protected int parentStepIndex = 0;
61         protected int containerStepIndex = 0;
62         protected int stepIndex = 0;
63         
64         /**
65          * Instantiates a new graph traversal builder.
66          *
67          * @param loader the loader
68          */
69         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
70                 super(loader, source);
71                 
72                 traversal = __.start();
73                 
74         }
75         
76         /**
77          * Instantiates a new graph traversal builder.
78          *
79          * @param loader the loader
80          * @param start the start
81          */
82         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
83                 super(loader, source, start);
84                 
85                 traversal = __.start();
86                 
87         }
88
89         /**
90          * @{inheritDoc}
91          */
92         @Override
93         public QueryBuilder getVerticesByIndexedProperty(String key, Object value) {
94         
95                 return this.getVerticesByProperty(key, value);
96         }
97
98         /**
99          * @{inheritDoc}
100          */
101         @Override
102         public QueryBuilder getVerticesByIndexedProperty(String key, List<?> values) {
103                 return this.getVerticesByProperty(key, values);
104         }
105         
106         /**
107          * @{inheritDoc}
108          */
109         @Override
110         public QueryBuilder getVerticesByProperty(String key, Object value) {
111                 
112                 //this is because the index is registered as an Integer
113                 value = this.correctObjectType(value);
114                 
115                 traversal.has(key, value);
116                 
117                 stepIndex++;
118                 return this;
119         }
120         
121         /**
122          * @{inheritDoc}
123          */
124         @Override
125         public QueryBuilder getVerticesByProperty(final String key, final List<?> values) {
126                 
127                 //this is because the index is registered as an Integer
128                 List<Object> correctedValues = new ArrayList<>();
129                 for (Object item : values) {
130                         correctedValues.add(this.correctObjectType(item));
131                 }
132                 
133                 traversal.has(key, P.within(correctedValues));
134                 
135                 stepIndex++;
136                 return this;
137         }
138
139         /**
140          * @{inheritDoc}
141          */
142         @Override
143         public QueryBuilder getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
144                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
145                 stepIndex++;
146                 return this;
147         }
148
149         /**
150          * @{inheritDoc}
151          */
152         @Override
153         public QueryBuilder getTypedVerticesByMap(String type, LinkedHashMap<String, String> map) {
154                 
155                 for (String key : map.keySet()) {
156                         traversal.has(key, map.get(key));
157                         stepIndex++;
158                 }
159                 traversal.has(AAIProperties.NODE_TYPE, type);
160                 stepIndex++;
161                 return this;
162         }
163
164         /**
165          * @{inheritDoc}
166          */
167         @Override
168         public QueryBuilder createDBQuery(Introspector obj) {
169                 this.createKeyQuery(obj);
170                 this.createContainerQuery(obj);
171                 return this;
172         }
173
174         /**
175          * @{inheritDoc}
176          */
177         @Override
178         public QueryBuilder createKeyQuery(Introspector obj) {
179                 Set<String> keys = obj.getKeys();
180                 Object val;
181                 for (String key : keys) {
182                         val = obj.getValue(key);
183                         Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
184                         if (metadata.isPresent()) {
185                                 //use the db name for the field rather than the object model
186                                 key = metadata.get();
187                         }
188                         if (val != null) {
189                                 //this is because the index is registered as an Integer
190                                 if (val.getClass().equals(Long.class)) {
191                                         traversal.has(key,new Integer(val.toString()));
192                                 } else {
193                                         traversal.has(key, val);
194                                 }
195                                 stepIndex++;
196                         }
197                 }
198                 return this;
199         }
200
201         @Override
202         public QueryBuilder exactMatchQuery(Introspector obj) {
203                 this.createKeyQuery(obj);
204                 allPropertiesQuery(obj);
205                 this.createContainerQuery(obj);
206                 return this;
207         }
208         
209         private void allPropertiesQuery(Introspector obj) {
210                 Set<String> props = obj.getProperties();
211                 Set<String> keys = obj.getKeys();
212                 Object val;
213                 for (String prop : props) {
214                         if (obj.isSimpleType(prop) && !keys.contains(prop)) {
215                                 val = obj.getValue(prop);
216                                 if (val != null) {
217                                         Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
218                                         if (metadata.isPresent()) {
219                                                 //use the db name for the field rather than the object model
220                                                 prop = metadata.get();
221                                         }
222                                         //this is because the index is registered as an Integer
223                                         if (val != null && val.getClass().equals(Long.class)) {
224                                                 traversal.has(prop,new Integer(val.toString()));
225                                         } else {
226                                                 traversal.has(prop, val);
227                                         }
228                                         stepIndex++;
229                                 }
230                         }
231                 }
232         }
233         
234         /**
235          * @{inheritDoc}
236          */
237         @Override
238         
239         public QueryBuilder createContainerQuery(Introspector obj) {
240                 String type = obj.getChildDBName();
241                 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
242                 if (abstractType != null) {
243                         String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
244                         traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
245                 } else {
246                         traversal.has(AAIProperties.NODE_TYPE, type);
247                 }
248                 stepIndex++;
249                 markContainer();
250                 return this;
251         }
252
253         /**
254          * @throws NoEdgeRuleFoundException 
255          * @throws AAIException 
256          * @{inheritDoc}
257          */
258         @Override
259         public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
260                 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
261                 if ("true".equals(isAbstractType)) {
262                         markParentBoundary();
263                         traversal.union(handleAbstractEdge(type, parent, child));
264                         stepIndex += 1;
265                 } else {
266                         this.edgeQuery(type, parent, child);
267                 }
268                 return this;
269                         
270         }
271         
272         private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
273                 String childName = child.getDbName();
274                 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
275                 String[] inheritors = inheritorMetadata.split(",");
276                 Traversal<Vertex, Vertex>[] unionTraversals = new Traversal[inheritors.length];
277                 int traversalIndex = 0;
278                 for (int i = 0; i < inheritors.length; i++) {
279                         String inheritor = inheritors[i];
280                         if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) {
281                                 EdgeRule rule = edgeRules.getEdgeRule(type, inheritor, childName);
282                                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
283                                 if (rule.getDirection().equals(Direction.OUT)) {
284                                         innerTraversal.out(rule.getLabel());
285                                 } else {
286                                         innerTraversal.in(rule.getLabel());
287                                 }
288                                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
289                                 unionTraversals[traversalIndex] = innerTraversal;
290                                 traversalIndex++;
291                         }
292                 }
293                 if (traversalIndex < inheritors.length) {
294                         Traversal<Vertex, Vertex>[] temp = Arrays.copyOfRange(unionTraversals, 0, traversalIndex);
295                         unionTraversals = temp;
296                 }
297                 return unionTraversals;
298         }
299         /**
300          * @throws NoEdgeRuleFoundException 
301          * @throws AAIException 
302          * @{inheritDoc}
303          */
304         @Override
305         public QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
306                 
307                 String nodeType = parent.<String>property(AAIProperties.NODE_TYPE).orElse(null);
308                 Introspector parentObj = loader.introspectorFromName(nodeType);
309                 this.edgeQuery(type, parentObj, child);
310                 return this;
311                         
312         }
313         
314         /**
315          * @{inheritDoc}
316          */
317         @Override
318         public QueryBuilder union(QueryBuilder... builder) {
319                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
320                 for (int i = 0; i < builder.length; i++) {
321                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
322                 }
323                 this.traversal.union(traversals);
324                 stepIndex++;
325                 
326                 return this;
327         }
328         
329         /**
330          * @{inheritDoc}
331          */
332         @Override
333         public QueryBuilder where(QueryBuilder... builder) {
334                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
335                 for (int i = 0; i < builder.length; i++) {
336                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
337                         stepIndex++;
338                 }
339                 
340                 return this;
341         }
342         
343         /**
344          * Edge query.
345          *
346          * @param outType the out type
347          * @param inType the in type
348          * @throws NoEdgeRuleFoundException 
349          * @throws AAIException 
350          */
351         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException {
352                 String outType = outObj.getDbName();
353                 String inType = inObj.getDbName();
354                 
355                 if (outObj.isContainer()) {
356                         outType = outObj.getChildDBName();
357                 }
358                 if (inObj.isContainer()) {
359                         inType = inObj.getChildDBName();
360                 }
361                 markParentBoundary();
362                 EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType);
363                 if (rule.getDirection().equals(Direction.OUT)) {
364                         traversal.out(rule.getLabel());
365                 } else {
366                         traversal.in(rule.getLabel());
367                 }
368                 stepIndex++;
369                 this.createContainerQuery(inObj);
370         }
371         
372         @Override
373         public QueryBuilder limit(long amount) {
374                 traversal.limit(amount);
375                 return this;
376         }
377
378         /**
379          * @{inheritDoc}
380          */
381         @Override
382         public <T> T getQuery() {
383                 return (T)this.traversal;
384         }
385         
386         /**
387          * @{inheritDoc}
388          */
389         @Override
390         public QueryBuilder getParentQuery() {
391
392                 return cloneQueryAtStep(parentStepIndex);
393         }
394         
395         @Override
396         public QueryBuilder getContainerQuery() {
397                 
398                 if (this.parentStepIndex == 0) {
399                         return removeQueryStepsBetween(0, containerStepIndex);
400                 } else {
401                         return cloneQueryAtStep(containerStepIndex);
402                 }
403         }
404         
405         /**
406          * @{inheritDoc}
407          */
408         @Override
409         public void markParentBoundary() {
410                 parentStepIndex = stepIndex;
411         }
412         
413         @Override
414         public void markContainer() {
415                 containerStepIndex = stepIndex;
416         }
417         
418         
419         /**
420          * @{inheritDoc}
421          */
422         @Override
423         public Vertex getStart() {
424                 return this.start;
425         }
426
427         protected int getParentStepIndex() {
428                 return parentStepIndex;
429         }
430
431         protected int getContainerStepIndex() {
432                 return containerStepIndex;
433         }
434
435         protected int getStepIndex() {
436                 return stepIndex;
437         }
438
439         protected abstract QueryBuilder cloneQueryAtStep(int index);
440         /**
441          * end is exclusive
442          * 
443          * @param start
444          * @param end
445          * @return
446          */
447         protected abstract QueryBuilder removeQueryStepsBetween(int start, int end);
448         
449         private void executeQuery() {
450                 
451                 Admin<Vertex, Vertex> admin;
452                 if (start != null) {
453                         admin = source.V(start).asAdmin();
454                 } else {
455                         admin = source.V().asAdmin();
456
457                 }
458                 
459                 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
460                 
461                 this.completeTraversal = admin;
462         }
463         
464         @Override
465         public boolean hasNext() {
466                 if (this.completeTraversal == null) {
467                         executeQuery();
468                 }
469                 
470                 return this.completeTraversal.hasNext();
471         }
472         
473         @Override
474         public Vertex next() {
475                 if (this.completeTraversal == null) {
476                         executeQuery();
477                 }
478                 
479                 return this.completeTraversal.next();
480         }
481         
482         @Override
483         public List<Vertex> toList() {
484                 if (this.completeTraversal == null) {
485                         executeQuery();
486                 }
487                 
488                 return this.completeTraversal.toList();
489         }
490
491 }