Added support for Multiple Edges
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / query / builder / GraphTraversalBuilder.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 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  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22 package org.onap.aai.query.builder;
23
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Map;
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.onap.aai.db.props.AAIProperties;
41 import org.onap.aai.exceptions.AAIException;
42 import org.onap.aai.introspection.Introspector;
43 import org.onap.aai.introspection.Loader;
44 import org.onap.aai.schema.enums.ObjectMetadata;
45 import org.onap.aai.schema.enums.PropertyMetadata;
46 import org.onap.aai.serialization.db.EdgeRule;
47 import org.onap.aai.serialization.db.EdgeRules;
48 import org.onap.aai.serialization.db.EdgeType;
49 import org.onap.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          */
81         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, EdgeRules edgeRules) {
82                 super(loader, source);
83                 this.edgeRules = edgeRules;
84                 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
85                 
86         }
87         
88         /**
89          * Instantiates a new graph traversal builder.
90          *
91          * @param loader the loader
92          * @param start the start
93          */
94         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
95                 super(loader, source, start);
96                 
97                 traversal = (GraphTraversal<Vertex, E>) __.__(start);
98                 
99         }
100         
101         /**
102          * Instantiates a new graph traversal builder.
103          *
104          * @param loader the loader
105          * @param start the start
106          */
107         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules edgeRules) {
108                 super(loader, source, start);
109                 this.edgeRules = edgeRules;
110                 traversal = (GraphTraversal<Vertex, E>) __.__(start);
111                 
112         }
113
114         /**
115          * @{inheritDoc}
116          */
117         @Override
118         public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
119                 
120                 // correct value call because the index is registered as an Integer
121                 traversal.has(key, this.correctObjectType(value));
122                 
123                 stepIndex++;
124                 return (QueryBuilder<Vertex>) this;
125         }
126         
127         /**
128          * @{inheritDoc}
129          */
130         @Override
131         public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
132                 
133                 //this is because the index is registered as an Integer
134                 List<Object> correctedValues = new ArrayList<>();
135                 for (Object item : values) {
136                         correctedValues.add(this.correctObjectType(item));
137                 }
138                 
139                 traversal.has(key, P.within(correctedValues));
140                 
141                 stepIndex++;
142                 return (QueryBuilder<Vertex>) this;
143         }
144
145         /**
146          * @{inheritDoc}
147          */
148         @Override
149         public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
150                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
151                 stepIndex++;
152                 return (QueryBuilder<Vertex>) this;
153         }
154
155         /**
156          * @{inheritDoc}
157          */
158         @Override
159         public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
160                 
161                 for (Map.Entry<String, String> es : map.entrySet()) {
162                         traversal.has(es.getKey(), es.getValue());
163                         stepIndex++;
164                 }
165                 traversal.has(AAIProperties.NODE_TYPE, type);
166                 stepIndex++;
167                 return (QueryBuilder<Vertex>) this;
168         }
169
170         /**
171          * @{inheritDoc}
172          */
173         @Override
174         public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
175                 Set<String> keys = obj.getKeys();
176                 Object val;
177                 for (String key : keys) {
178                         val = obj.getValue(key);
179                         Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
180                         if (metadata.isPresent()) {
181                                 //use the db name for the field rather than the object model
182                                 key = metadata.get();
183                         }
184                         if (val != null) {
185                                 //this is because the index is registered as an Integer
186                                 if (val.getClass().equals(Long.class)) {
187                                         traversal.has(key,new Integer(val.toString()));
188                                 } else {
189                                         traversal.has(key, val);
190                                 }
191                                 stepIndex++;
192                         }
193                 }
194                 return (QueryBuilder<Vertex>) this;
195         }
196
197         @Override
198         public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
199                 this.createKeyQuery(obj);
200                 allPropertiesQuery(obj);
201                 this.createContainerQuery(obj);
202                 return (QueryBuilder<Vertex>) this;
203         }
204         
205         private void allPropertiesQuery(Introspector obj) {
206                 Set<String> props = obj.getProperties();
207                 Set<String> keys = obj.getKeys();
208                 Object val;
209                 for (String prop : props) {
210                         if (obj.isSimpleType(prop) && !keys.contains(prop)) {
211                                 val = obj.getValue(prop);
212                                 if (val != null) {
213                                         Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
214                                         if (metadata.isPresent()) {
215                                                 //use the db name for the field rather than the object model
216                                                 prop = metadata.get();
217                                         }
218                                         //this is because the index is registered as an Integer
219                                         if (val.getClass().equals(Long.class)) {
220                                                 traversal.has(prop,new Integer(val.toString()));
221                                         } else {
222                                                 traversal.has(prop, val);
223                                         }
224                                         stepIndex++;
225                                 }
226                         }
227                 }
228         }
229         
230         /**
231          * @{inheritDoc}
232          */
233         @Override
234         
235         public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
236                 String type = obj.getChildDBName();
237                 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
238                 if (abstractType != null) {
239                         String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
240                         traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
241                 } else {
242                         traversal.has(AAIProperties.NODE_TYPE, type);
243                 }
244                 stepIndex++;
245                 markContainer();
246                 return (QueryBuilder<Vertex>) this;
247         }
248
249         /**
250          * @throws NoEdgeRuleFoundException 
251          * @throws AAIException 
252          * @{inheritDoc}
253          */
254         @Override
255         public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
256                 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
257                 if ("true".equals(isAbstractType)) {
258                         markParentBoundary();
259                         traversal.union(handleAbstractEdge(type, parent, child));
260                         stepIndex++;
261                 } else {
262                         this.edgeQueryToVertex(type, parent, child, null);
263                 }
264                 return (QueryBuilder<Vertex>) this;
265                         
266         }
267
268         /**
269          *
270          * @{inheritDoc}
271          */
272         @Override
273         public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException {
274                 this.edgeQueryToVertex(type, out, in, labels);
275                 return (QueryBuilder<Vertex>) this;
276         }
277
278
279         private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
280                 String childName = child.getDbName();
281                 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
282                 String[] inheritors = inheritorMetadata.split(",");
283                 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
284
285                 for (int i = 0; i < inheritors.length; i++) {
286                         String inheritor = inheritors[i];
287                         if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) {
288                                 Map<String, EdgeRule> rules = edgeRules.getEdgeRules(type, inheritor, childName);
289                                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
290                                 
291                                 final List<String> inLabels = new ArrayList<>();
292                                 final List<String> outLabels = new ArrayList<>();
293
294                                 rules.forEach((k,v) -> {
295                                         if (v.getDirection().equals(Direction.IN)) {
296                                                 inLabels.add(k);
297                                         } else {
298                                                 outLabels.add(k);
299                                         }
300                                 } );
301                 
302                                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
303                                         innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
304                                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
305                                         innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
306                                 } else {                                
307                                         innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
308                                 }
309                                 
310                                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
311                                 unionTraversals.add(innerTraversal);
312                         }
313                 }
314                 
315                 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
316         }
317
318         public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List<String> labels) throws AAIException {
319                 Introspector outObj = loader.introspectorFromName(outNodeType);
320                 Introspector inObj = loader.introspectorFromName(inNodeType);
321                 this.edgeQuery(type, outObj, inObj, labels);
322
323                 return (QueryBuilder<Edge>)this;
324         }
325
326
327         /**
328          * @{inheritDoc}
329          */
330         @Override
331         public QueryBuilder<E> union(QueryBuilder... builder) {
332                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
333                 for (int i = 0; i < builder.length; i++) {
334                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
335                 }
336                 this.traversal.union(traversals);
337                 stepIndex++;
338                 
339                 return this;
340         }
341         
342         /**
343          * @{inheritDoc}
344          */
345         @Override
346         public QueryBuilder<E> where(QueryBuilder... builder) {
347                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
348                 for (int i = 0; i < builder.length; i++) {
349                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
350                         stepIndex++;
351                 }
352                 
353                 return this;
354         }
355         
356         @Override
357         public QueryBuilder<E> store(String name) {
358                 
359                 this.traversal.store(name);
360                 stepIndex++;
361                 
362                 return this;
363         }
364         
365         @Override
366         public QueryBuilder<E> cap(String name) {
367                 this.traversal.cap(name);
368                 stepIndex++;
369                 
370                 return this;
371         }
372         
373         @Override
374         public QueryBuilder<E> unfold() {
375                 this.traversal.unfold();
376                 stepIndex++;
377                         
378                 return this;
379         }
380         
381         @Override
382         public QueryBuilder<E> dedup() {
383                 
384                 this.traversal.dedup();
385                 stepIndex++;
386                 
387                 return this;
388         }
389         
390         @Override
391         public QueryBuilder<E> emit() {
392                 
393                 this.traversal.emit();
394                 stepIndex++;
395                 
396                 return this;
397                 
398         }
399         
400         @Override
401         public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
402                 
403                 this.traversal.repeat((GraphTraversal<Vertex, E>)builder.getQuery());
404                 stepIndex++;
405
406                 return this;
407         }
408         
409         @Override
410         public QueryBuilder<E> until(QueryBuilder<E> builder) {
411                 this.traversal.until((GraphTraversal<Vertex,E>)builder.getQuery());
412                 stepIndex++;
413                 
414                 return this;
415         }
416         
417         /**
418          * {@inheritDoc}
419          */
420         @Override
421         public QueryBuilder<E> simplePath(){
422                 this.traversal.simplePath();
423                 stepIndex++;
424                 return this;
425         }
426         
427         @Override
428         public QueryBuilder<Edge> outE() {
429                 this.traversal.outE();
430                 stepIndex++;
431                 return (QueryBuilder<Edge>)this;
432         }
433         
434         @Override
435         public QueryBuilder<Edge> inE() {
436                 this.traversal.inE();
437                 stepIndex++;
438                 return (QueryBuilder<Edge>)this;
439         }
440         
441         @Override
442         public QueryBuilder<Vertex> outV() {
443                 this.traversal.outV();
444                 stepIndex++;
445                 return (QueryBuilder<Vertex>)this;
446         }
447         
448         @Override
449         public QueryBuilder<Vertex> inV() {
450                 this.traversal.inV();
451                 stepIndex++;
452                 return (QueryBuilder<Vertex>)this;
453         }
454         
455         @Override
456         public QueryBuilder<E> as(String name) {
457                 this.traversal.as(name);
458                 
459                 stepIndex++;
460                 return this;
461         }
462         
463         @Override
464         public QueryBuilder<E> not(QueryBuilder<E> builder) {
465                 this.traversal.not(builder.getQuery());
466                 
467                 stepIndex++;
468                 return this;
469         }
470         
471         @Override
472         public QueryBuilder<E> select(String name) {
473                 this.traversal.select(name);
474                 
475                 stepIndex++;
476                 
477                 return this;
478         }
479         
480         /**
481          * Edge query.
482          *
483          * @param outObj the out type
484          * @param inObj the in type
485          * @throws NoEdgeRuleFoundException 
486          * @throws AAIException 
487          */
488         private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
489                 String outType = outObj.getDbName();
490                 String inType = inObj.getDbName();
491                 
492                 if (outObj.isContainer()) {
493                         outType = outObj.getChildDBName();
494                 }
495                 if (inObj.isContainer()) {
496                         inType = inObj.getChildDBName();
497                 }
498                 markParentBoundary();
499                 Map<String, EdgeRule> rules;
500                 if (labels == null) {
501                         rules = edgeRules.getEdgeRules(type, outType, inType);
502                 } else {
503                         rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels);
504                 }
505                 
506                 final List<String> inLabels = new ArrayList<>();
507                 final List<String> outLabels = new ArrayList<>();
508
509                 rules.forEach((k, edgeRule) -> {
510                         if (labels != null && !labels.contains(k)) {
511                                 return;
512                         } else {
513                                 if (edgeRule.getDirection().equals(Direction.IN)) {
514                                         inLabels.add(edgeRule.getLabel());
515                                 } else {
516                                         outLabels.add(edgeRule.getLabel());
517                                 }
518                         }
519                 });
520
521                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
522                         traversal.out(outLabels.toArray(new String[outLabels.size()]));
523                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
524                         traversal.in(inLabels.toArray(new String[inLabels.size()]));
525                 } else {                                
526                         traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
527                 }
528                 
529                 stepIndex++;
530
531                 this.createContainerQuery(inObj);
532                 
533         }
534         
535         /**
536          * Edge query.
537          *
538          * @param outObj the out type
539          * @param inObj the in type
540          * @throws NoEdgeRuleFoundException 
541          * @throws AAIException 
542          */
543         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
544                 String outType = outObj.getDbName();
545                 String inType = inObj.getDbName();
546                 
547                 if (outObj.isContainer()) {
548                         outType = outObj.getChildDBName();
549                 }
550                 if (inObj.isContainer()) {
551                         inType = inObj.getChildDBName();
552                 }
553                 
554                 markParentBoundary();
555                 Map<String, EdgeRule> rules;
556                 if (labels == null) {
557                         rules = edgeRules.getEdgeRules(type, outType, inType);
558                 } else {
559                         rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels);
560                 }
561                 
562                 final List<String> inLabels = new ArrayList<>();
563                 final List<String> outLabels = new ArrayList<>();
564
565                 rules.forEach((k, edgeRule) -> {
566                         if (labels != null && !labels.contains(k)) {
567                                 return;
568                         } else {
569                                 if (edgeRule.getDirection().equals(Direction.IN)) {
570                                         inLabels.add(edgeRule.getLabel());
571                                 } else {
572                                         outLabels.add(edgeRule.getLabel());
573                                 }
574                         }
575                 });
576
577                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
578                         traversal.outE(outLabels.toArray(new String[outLabels.size()]));
579                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
580                         traversal.inE(inLabels.toArray(new String[inLabels.size()]));
581                 } else {                                
582                         traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), __.inE(inLabels.toArray(new String[inLabels.size()])));
583                 }
584         }
585         
586         @Override
587         public QueryBuilder<E> limit(long amount) {
588                 traversal.limit(amount);
589                 return this;
590         }
591
592         /**
593          * @{inheritDoc}
594          */
595         @Override
596         public <E2> E2 getQuery() {
597                 return (E2)this.traversal;
598         }
599         
600         /**
601          * @{inheritDoc}
602          */
603         @Override
604         public QueryBuilder<E> getParentQuery() {
605
606                 return cloneQueryAtStep(parentStepIndex);
607         }
608         
609         @Override
610         public QueryBuilder<E> getContainerQuery() {
611                 
612                 if (this.parentStepIndex == 0) {
613                         return removeQueryStepsBetween(0, containerStepIndex);
614                 } else {
615                         return cloneQueryAtStep(containerStepIndex);
616                 }
617         }
618         
619         /**
620          * @{inheritDoc}
621          */
622         @Override
623         public void markParentBoundary() {
624                 parentStepIndex = stepIndex;
625         }
626         
627         @Override
628         public void markContainer() {
629                 containerStepIndex = stepIndex;
630         }
631         
632         
633         /**
634          * @{inheritDoc}
635          */
636         @Override
637         public Vertex getStart() {
638                 return this.start;
639         }
640
641         protected int getParentStepIndex() {
642                 return parentStepIndex;
643         }
644
645         protected int getContainerStepIndex() {
646                 return containerStepIndex;
647         }
648
649         protected int getStepIndex() {
650                 return stepIndex;
651         }
652
653         protected abstract QueryBuilder<E> cloneQueryAtStep(int index);
654         /**
655          * end is exclusive
656          * 
657          * @param start
658          * @param end
659          * @return
660          */
661         protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
662         
663         private void executeQuery() {
664                 
665                 Admin admin;
666                 if (start != null) {
667                         this.completeTraversal = traversal.asAdmin();
668                 } else {
669                         admin = source.V().asAdmin();
670                         TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
671                         
672                         this.completeTraversal = (Admin<Vertex, E>) admin;
673
674                 }
675                 
676                 
677         }
678         
679         @Override
680         public boolean hasNext() {
681                 if (this.completeTraversal == null) {
682                         executeQuery();
683                 }
684                 
685                 return this.completeTraversal.hasNext();
686         }
687         
688         @Override
689         public E next() {
690                 if (this.completeTraversal == null) {
691                         executeQuery();
692                 }
693                 
694                 return this.completeTraversal.next();
695         }
696         
697         @Override
698         public List<E> toList() {
699                 if (this.completeTraversal == null) {
700                         executeQuery();
701                 }
702                 
703                 return this.completeTraversal.toList();
704         }
705
706 }