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