ecaadba26ac9badfe9c6b1f9d28acd7ccd726eef
[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.Edge;
39 import org.apache.tinkerpop.gremlin.structure.Vertex;
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<E> extends QueryBuilder<E> {
55
56         protected GraphTraversal<Vertex, E> traversal = null;
57         protected Admin<Vertex, E> 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 = (GraphTraversal<Vertex, E>) __.<E>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 = (GraphTraversal<Vertex, E>) __.__(start);
86                 
87         }
88
89         /**
90          * @{inheritDoc}
91          */
92         @Override
93         public QueryBuilder<Vertex> getVerticesByIndexedProperty(String key, Object value) {
94         
95                 return this.getVerticesByProperty(key, value);
96         }
97
98         /**
99          * @{inheritDoc}
100          */
101         @Override
102         public QueryBuilder<Vertex> getVerticesByIndexedProperty(String key, List<?> values) {
103                 return this.getVerticesByProperty(key, values);
104         }
105         
106         /**
107          * @{inheritDoc}
108          */
109         @Override
110         public QueryBuilder<Vertex> 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 (QueryBuilder<Vertex>) this;
119         }
120         
121         /**
122          * @{inheritDoc}
123          */
124         @Override
125         public QueryBuilder<Vertex> 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 (QueryBuilder<Vertex>) this;
137         }
138
139         /**
140          * @{inheritDoc}
141          */
142         @Override
143         public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
144                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
145                 stepIndex++;
146                 return (QueryBuilder<Vertex>) this;
147         }
148
149         /**
150          * @{inheritDoc}
151          */
152         @Override
153         public QueryBuilder<Vertex> 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 (QueryBuilder<Vertex>) this;
162         }
163
164         /**
165          * @{inheritDoc}
166          */
167         @Override
168         public QueryBuilder<Vertex> createDBQuery(Introspector obj) {
169                 this.createKeyQuery(obj);
170                 this.createContainerQuery(obj);
171                 return (QueryBuilder<Vertex>) this;
172         }
173
174         /**
175          * @{inheritDoc}
176          */
177         @Override
178         public QueryBuilder<Vertex> 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 (QueryBuilder<Vertex>) this;
199         }
200
201         @Override
202         public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
203                 this.createKeyQuery(obj);
204                 allPropertiesQuery(obj);
205                 this.createContainerQuery(obj);
206                 return (QueryBuilder<Vertex>) 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.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<Vertex> 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 (QueryBuilder<Vertex>) this;
251         }
252
253         /**
254          * @throws NoEdgeRuleFoundException 
255          * @throws AAIException 
256          * @{inheritDoc}
257          */
258         @Override
259         public QueryBuilder<Vertex> 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.edgeQueryToVertex(type, parent, child);
267                 }
268                 return (QueryBuilder<Vertex>) 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<Vertex> 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.edgeQueryToVertex(type, parentObj, child);
310                 return (QueryBuilder<Vertex>) this;
311                         
312         }
313         
314         @Override
315         public QueryBuilder<Edge> getEdgesBetween(EdgeType type, String outNodeType, String inNodeType) throws AAIException {
316                 Introspector outObj = loader.introspectorFromName(outNodeType);
317                 Introspector inObj = loader.introspectorFromName(inNodeType);
318                 this.edgeQuery(type, outObj, inObj);
319                 
320                 return (QueryBuilder<Edge>)this;
321
322         }
323         /**
324          * @{inheritDoc}
325          */
326         @Override
327         public QueryBuilder<E> union(QueryBuilder... builder) {
328                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
329                 for (int i = 0; i < builder.length; i++) {
330                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
331                 }
332                 this.traversal.union(traversals);
333                 stepIndex++;
334                 
335                 return this;
336         }
337         
338         /**
339          * @{inheritDoc}
340          */
341         @Override
342         public QueryBuilder<E> where(QueryBuilder... builder) {
343                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
344                 for (int i = 0; i < builder.length; i++) {
345                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
346                         stepIndex++;
347                 }
348                 
349                 return this;
350         }
351         
352         @Override
353         public QueryBuilder<E> store(String name) {
354                 
355                 this.traversal.store(name);
356                 stepIndex++;
357                 
358                 return this;
359         }
360         
361         @Override
362         public QueryBuilder<E> cap(String name) {
363                 this.traversal.cap(name);
364                 stepIndex++;
365                 
366                 return this;
367         }
368         
369         @Override
370         public QueryBuilder<E> unfold() {
371                 this.traversal.unfold();
372                 stepIndex++;
373                         
374                 return this;
375         }
376         
377         @Override
378         public QueryBuilder<E> dedup() {
379                 
380                 this.traversal.dedup();
381                 stepIndex++;
382                 
383                 return this;
384         }
385         
386         @Override
387         public QueryBuilder<E> emit() {
388                 
389                 this.traversal.emit();
390                 stepIndex++;
391                 
392                 return this;
393                 
394         }
395         
396         @Override
397         public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
398                 
399                 this.traversal.repeat((GraphTraversal<Vertex, E>)builder.getQuery());
400                 stepIndex++;
401
402                 return this;
403         }
404         
405         @Override
406         public QueryBuilder<E> until(QueryBuilder<E> builder) {
407                 this.traversal.until((GraphTraversal<Vertex,E>)builder.getQuery());
408                 stepIndex++;
409                 
410                 return this;
411         }
412         
413         /**
414          * {@inheritDoc}
415          */
416         @Override
417         public QueryBuilder<E> simplePath(){
418                 this.traversal.simplePath();
419                 stepIndex++;
420                 return this;
421         }
422         
423         @Override
424         public QueryBuilder<Edge> outE() {
425                 this.traversal.outE();
426                 stepIndex++;
427                 return (QueryBuilder<Edge>)this;
428         }
429         
430         @Override
431         public QueryBuilder<Edge> inE() {
432                 this.traversal.inE();
433                 stepIndex++;
434                 return (QueryBuilder<Edge>)this;
435         }
436         
437         @Override
438         public QueryBuilder<Vertex> outV() {
439                 this.traversal.outV();
440                 stepIndex++;
441                 return (QueryBuilder<Vertex>)this;
442         }
443         
444         @Override
445         public QueryBuilder<Vertex> inV() {
446                 this.traversal.inV();
447                 stepIndex++;
448                 return (QueryBuilder<Vertex>)this;
449         }
450         
451         @Override
452         public QueryBuilder<E> as(String name) {
453                 this.traversal.as(name);
454                 
455                 stepIndex++;
456                 return this;
457         }
458         
459         @Override
460         public QueryBuilder<E> not(QueryBuilder<E> builder) {
461                 this.traversal.not(builder.getQuery());
462                 
463                 stepIndex++;
464                 return this;
465         }
466         
467         @Override
468         public QueryBuilder<E> select(String name) {
469                 this.traversal.select(name);
470                 
471                 stepIndex++;
472                 
473                 return this;
474         }
475         
476         /**
477          * Edge query.
478          *
479          * @param outType the out type
480          * @param inType the in type
481          * @throws NoEdgeRuleFoundException 
482          * @throws AAIException 
483          */
484         private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException {
485                 String outType = outObj.getDbName();
486                 String inType = inObj.getDbName();
487                 
488                 if (outObj.isContainer()) {
489                         outType = outObj.getChildDBName();
490                 }
491                 if (inObj.isContainer()) {
492                         inType = inObj.getChildDBName();
493                 }
494                 markParentBoundary();
495                 EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType);
496                 if (rule.getDirection().equals(Direction.OUT)) {
497                         traversal.out(rule.getLabel());
498                 } else {
499                         traversal.in(rule.getLabel());
500                 }
501                 stepIndex++;
502                 this.createContainerQuery(inObj);
503                 
504         }
505         
506         /**
507          * Edge query.
508          *
509          * @param outType the out type
510          * @param inType the in type
511          * @throws NoEdgeRuleFoundException 
512          * @throws AAIException 
513          */
514         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException {
515                 String outType = outObj.getDbName();
516                 String inType = inObj.getDbName();
517                 
518                 if (outObj.isContainer()) {
519                         outType = outObj.getChildDBName();
520                 }
521                 if (inObj.isContainer()) {
522                         inType = inObj.getChildDBName();
523                 }
524                 markParentBoundary();
525                 EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType);
526                 if (rule.getDirection().equals(Direction.OUT)) {
527                         traversal.outE(rule.getLabel());
528                 } else {
529                         traversal.inE(rule.getLabel());
530                 }
531                 stepIndex++;
532                 
533         }
534         
535         @Override
536         public QueryBuilder<E> limit(long amount) {
537                 traversal.limit(amount);
538                 return this;
539         }
540
541         /**
542          * @{inheritDoc}
543          */
544         @Override
545         public <E2> E2 getQuery() {
546                 return (E2)this.traversal;
547         }
548         
549         /**
550          * @{inheritDoc}
551          */
552         @Override
553         public QueryBuilder<E> getParentQuery() {
554
555                 return cloneQueryAtStep(parentStepIndex);
556         }
557         
558         @Override
559         public QueryBuilder<E> getContainerQuery() {
560                 
561                 if (this.parentStepIndex == 0) {
562                         return removeQueryStepsBetween(0, containerStepIndex);
563                 } else {
564                         return cloneQueryAtStep(containerStepIndex);
565                 }
566         }
567         
568         /**
569          * @{inheritDoc}
570          */
571         @Override
572         public void markParentBoundary() {
573                 parentStepIndex = stepIndex;
574         }
575         
576         @Override
577         public void markContainer() {
578                 containerStepIndex = stepIndex;
579         }
580         
581         
582         /**
583          * @{inheritDoc}
584          */
585         @Override
586         public Vertex getStart() {
587                 return this.start;
588         }
589
590         protected int getParentStepIndex() {
591                 return parentStepIndex;
592         }
593
594         protected int getContainerStepIndex() {
595                 return containerStepIndex;
596         }
597
598         protected int getStepIndex() {
599                 return stepIndex;
600         }
601
602         protected abstract QueryBuilder<E> cloneQueryAtStep(int index);
603         /**
604          * end is exclusive
605          * 
606          * @param start
607          * @param end
608          * @return
609          */
610         protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
611         
612         private void executeQuery() {
613                 
614                 Admin admin;
615                 if (start != null) {
616                         this.completeTraversal = traversal.asAdmin();
617                 } else {
618                         admin = source.V().asAdmin();
619                         TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
620                         
621                         this.completeTraversal = (Admin<Vertex, E>) admin;
622
623                 }
624                 
625                 
626         }
627         
628         @Override
629         public boolean hasNext() {
630                 if (this.completeTraversal == null) {
631                         executeQuery();
632                 }
633                 
634                 return this.completeTraversal.hasNext();
635         }
636         
637         @Override
638         public E next() {
639                 if (this.completeTraversal == null) {
640                         executeQuery();
641                 }
642                 
643                 return this.completeTraversal.next();
644         }
645         
646         @Override
647         public List<E> toList() {
648                 if (this.completeTraversal == null) {
649                         executeQuery();
650                 }
651                 
652                 return this.completeTraversal.toList();
653         }
654
655 }