Release 1.13.0 docker artifact
[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-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *    http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.aai.query.builder;
22
23 import com.google.common.collect.ArrayListMultimap;
24 import com.google.common.collect.Multimap;
25
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Optional;
31 import java.util.Set;
32
33 import org.apache.tinkerpop.gremlin.process.traversal.P;
34 import org.apache.tinkerpop.gremlin.process.traversal.Path;
35 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
36 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
37 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
38 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
39 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
40 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
41 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
42 import org.apache.tinkerpop.gremlin.structure.Direction;
43 import org.apache.tinkerpop.gremlin.structure.Edge;
44 import org.apache.tinkerpop.gremlin.structure.Vertex;
45 import org.onap.aai.db.props.AAIProperties;
46 import org.onap.aai.edges.EdgeRule;
47 import org.onap.aai.edges.EdgeRuleQuery;
48 import org.onap.aai.edges.enums.EdgeType;
49 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
50 import org.onap.aai.exceptions.AAIException;
51 import org.onap.aai.introspection.Introspector;
52 import org.onap.aai.introspection.Loader;
53 import org.onap.aai.schema.enums.ObjectMetadata;
54 import org.onap.aai.schema.enums.PropertyMetadata;
55 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
56
57 /**
58  * The Class GraphTraversalBuilder.
59  */
60 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
61
62     protected GraphTraversal<Vertex, E> traversal = null;
63     protected Admin<Vertex, E> completeTraversal = null;
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         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> getVerticesByProperty(String key, Object value) {
94
95         // correct value call because the index is registered as an Integer
96         this.vertexHas(key, this.correctObjectType(value));
97         stepIndex++;
98         return (QueryBuilder<Vertex>) this;
99     }
100
101     @Override
102     protected void vertexHas(String key, Object value) {
103         traversal.has(key, value);
104     }
105
106     @Override
107     protected void vertexHasNot(String key) {
108         traversal.hasNot(key);
109     }
110
111     @Override
112     protected void vertexHas(String key) {
113         traversal.has(key);
114     }
115
116     // TODO: Remove this once we test this - at this point i dont thib this is required
117     // because predicare is an object
118     /*
119      * @Override
120      * protected void vertexHas(final String key, final P<?> predicate) {
121      * traversal.has(key, predicate);
122      * }
123      */
124
125     /**
126      * @{inheritDoc}
127      */
128     @Override
129     public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
130
131         // this is because the index is registered as an Integer
132         List<Object> correctedValues = new ArrayList<>();
133         for (Object item : values) {
134             correctedValues.add(this.correctObjectType(item));
135         }
136
137         this.vertexHas(key, P.within(correctedValues));
138         stepIndex++;
139         return (QueryBuilder<Vertex>) this;
140     }
141
142     /**
143      * @{inheritDoc}
144      */
145     public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
146         ArrayList<String> values = new ArrayList<>(Arrays.asList(value.split(",")));
147         int size = values.size();
148         for (int i = 0; i < size; i++) {
149             values.set(i, values.get(i).trim());
150         }
151         this.vertexHas(key, P.within(values));
152
153         stepIndex++;
154         return (QueryBuilder<Vertex>) this;
155     }
156
157     /**
158      * @{inheritDoc}
159      */
160     @Override
161     public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
162
163         // correct value call because the index is registered as an Integer
164         // TODO Check if this needs to be in QB and add these as internal
165         this.vertexHas(key, org.janusgraph.core.attribute.Text.textPrefix(value));
166
167         stepIndex++;
168         return (QueryBuilder<Vertex>) this;
169     }
170
171     /**
172      * @{inheritDoc}
173      */
174     @Override
175     public QueryBuilder<Vertex> getVerticesByProperty(String key) {
176         this.vertexHas(key);
177         stepIndex++;
178         return (QueryBuilder<Vertex>) this;
179     }
180
181     /**
182      * @{inheritDoc}
183      */
184     @Override
185     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
186         this.vertexHasNot(key);
187         stepIndex++;
188         return (QueryBuilder<Vertex>) this;
189     }
190
191     /**
192      * @{inheritDoc}
193      */
194     @Override
195     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
196
197         // correct value call because the index is registered as an Integer
198         this.vertexHas(key, P.neq(this.correctObjectType(value)));
199         stepIndex++;
200         return (QueryBuilder<Vertex>) this;
201     }
202
203     /**
204      * @{inheritDoc}
205      */
206     @Override
207     public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
208
209         // this is because the index is registered as an Integer
210         List<Object> correctedValues = new ArrayList<>();
211         for (Object item : values) {
212             correctedValues.add(this.correctObjectType(item));
213         }
214
215         this.vertexHas(key, P.without(correctedValues));
216         stepIndex++;
217         return (QueryBuilder<Vertex>) this;
218     }
219
220     @Override
221     public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) {
222         this.vertexHas(key, P.gte(this.correctObjectType(value)));
223
224         stepIndex++;
225         return (QueryBuilder<Vertex>) this;
226     }
227
228     @Override
229     public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) {
230         this.vertexHas(key, P.lte(this.correctObjectType(value)));
231
232         stepIndex++;
233         return (QueryBuilder<Vertex>) this;
234     }
235
236     /**
237      * @{inheritDoc}
238      */
239     @Override
240     public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
241         traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
242         stepIndex++;
243         return (QueryBuilder<Vertex>) this;
244     }
245
246     /**
247      * @{inheritDoc}
248      */
249     @Override
250     public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
251
252         for (Map.Entry<String, String> es : map.entrySet()) {
253             this.vertexHas(es.getKey(), es.getValue());
254             stepIndex++;
255         }
256         traversal.has(AAIProperties.NODE_TYPE, type);
257         stepIndex++;
258         return (QueryBuilder<Vertex>) this;
259     }
260
261     @Override
262     public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
263
264         if (value != null && !"".equals(value)) {
265             boolean bValue = false;
266
267             if (value instanceof String) {// "true"
268                 bValue = Boolean.valueOf(value.toString());
269             } else if (value instanceof Boolean) {// true
270                 bValue = (Boolean) value;
271             }
272
273             this.vertexHas(key, bValue);
274             stepIndex++;
275         }
276         return (QueryBuilder<Vertex>) this;
277     }
278
279     /**
280      * @{inheritDoc}
281      */
282     @Override
283     public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
284         Set<String> keys = obj.getKeys();
285         Object val;
286         for (String key : keys) {
287             val = obj.getValue(key);
288             Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
289             if (metadata.isPresent()) {
290                 // use the db name for the field rather than the object model
291                 key = metadata.get();
292             }
293             if (val != null) {
294                 // this is because the index is registered as an Integer
295                 if (val.getClass().equals(Long.class)) {
296                     this.vertexHas(key, Integer.valueOf(val.toString()));
297                 } else {
298                     this.vertexHas(key, val);
299                 }
300                 stepIndex++;
301             }
302         }
303         return (QueryBuilder<Vertex>) this;
304     }
305
306     @Override
307     public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
308         this.createKeyQuery(obj);
309         allPropertiesQuery(obj);
310         this.createContainerQuery(obj);
311         return (QueryBuilder<Vertex>) this;
312     }
313
314     private void allPropertiesQuery(Introspector obj) {
315         Set<String> props = obj.getProperties();
316         Set<String> keys = obj.getKeys();
317         Object val;
318         for (String prop : props) {
319             if (obj.isSimpleType(prop) && !keys.contains(prop)) {
320                 val = obj.getValue(prop);
321                 if (val != null) {
322                     Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
323                     if (metadata.isPresent()) {
324                         // use the db name for the field rather than the object model
325                         prop = metadata.get();
326                     }
327                     // this is because the index is registered as an Integer
328                     if (val.getClass().equals(Long.class)) {
329                         this.vertexHas(prop, Integer.valueOf(val.toString()));
330                     } else {
331                         this.vertexHas(prop, val);
332                     }
333                     stepIndex++;
334                 }
335             }
336         }
337     }
338
339     /**
340      * @{inheritDoc}
341      */
342     @Override
343
344     public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
345         String type = obj.getChildDBName();
346         String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
347         if (abstractType != null) {
348             String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
349             traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
350         } else {
351             traversal.has(AAIProperties.NODE_TYPE, type);
352         }
353         stepIndex++;
354         markContainer();
355         return (QueryBuilder<Vertex>) this;
356     }
357
358     /**
359      * @throws NoEdgeRuleFoundException
360      * @throws AAIException
361      * @{inheritDoc}
362      */
363     @Override
364     public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
365             throws AAIException {
366         createTraversal(type, parent, child, false);
367         return (QueryBuilder<Vertex>) this;
368
369     }
370
371     @Override
372     public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
373             throws AAIException {
374         this.createTraversal(type, parent, child, true);
375         return (QueryBuilder<Vertex>) this;
376     }
377
378     private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge)
379             throws AAIException {
380         String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
381         if ("true".equals(isAbstractType)) {
382             markParentBoundary();
383             traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
384             stepIndex++;
385         } else {
386             this.edgeQueryToVertex(type, parent, child, null);
387         }
388     }
389
390     /**
391      * @{inheritDoc}
392      */
393     @Override
394     public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
395             List<String> labels) throws AAIException {
396         this.edgeQueryToVertex(type, out, in, labels);
397         return (QueryBuilder<Vertex>) this;
398     }
399
400     private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent,
401             Introspector child, boolean isPrivateEdge) throws AAIException {
402         String childName = child.getDbName();
403         String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
404         String[] inheritors = inheritorMetadata.split(",");
405         List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
406
407         for (int i = 0; i < inheritors.length; i++) {
408             String inheritor = inheritors[i];
409             EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
410             if (edgeRules.hasRule(qB.build())) {
411                 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
412                 try {
413                     rules = edgeRules.getRules(qB.edgeType(type).build());
414                 } catch (EdgeRuleNotFoundException e) {
415                     throw new NoEdgeRuleFoundException(e);
416                 }
417
418                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
419
420                 final List<String> inLabels = new ArrayList<>();
421                 final List<String> outLabels = new ArrayList<>();
422
423                 rules.values().forEach(rule -> {
424                     if (rule.getDirection().equals(Direction.IN)) {
425                         inLabels.add(rule.getLabel());
426                     } else {
427                         outLabels.add(rule.getLabel());
428                     }
429                 });
430
431                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
432                     innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
433                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
434                     innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
435                 } else {
436                     innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
437                             __.in(inLabels.toArray(new String[inLabels.size()])));
438                 }
439
440                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
441                 unionTraversals.add(innerTraversal);
442             }
443         }
444
445         return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
446     }
447
448     public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
449             List<String> labels) throws AAIException {
450         Introspector outObj = loader.introspectorFromName(outNodeType);
451         Introspector inObj = loader.introspectorFromName(inNodeType);
452         this.edgeQuery(type, outObj, inObj, labels);
453
454         return (QueryBuilder<Edge>) this;
455     }
456
457     /**
458      * @{inheritDoc}
459      */
460     @Override
461     public QueryBuilder<E> union(QueryBuilder... builder) {
462         GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
463         for (int i = 0; i < builder.length; i++) {
464             traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
465         }
466         this.traversal.union(traversals);
467         stepIndex++;
468
469         return this;
470     }
471
472     /**
473      * @{inheritDoc}
474      */
475     @Override
476     public QueryBuilder<E> where(QueryBuilder... builder) {
477         for (int i = 0; i < builder.length; i++) {
478             this.traversal.where((GraphTraversal<Vertex, Vertex>) builder[i].getQuery());
479             stepIndex++;
480         }
481
482         return this;
483     }
484
485     /**
486      * @{inheritDoc}
487      */
488     @Override
489     public QueryBuilder<E> or(QueryBuilder... builder) {
490         GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
491         for (int i = 0; i < builder.length; i++) {
492             traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
493         }
494         this.traversal.or(traversals);
495         stepIndex++;
496
497         return this;
498     }
499
500     @Override
501     public QueryBuilder<E> store(String name) {
502
503         this.traversal.store(name);
504         stepIndex++;
505
506         return this;
507     }
508
509     @Override
510     public QueryBuilder<E> cap(String name) {
511         this.traversal.cap(name);
512         stepIndex++;
513
514         return this;
515     }
516
517     @Override
518     public QueryBuilder<E> unfold() {
519         this.traversal.unfold();
520         stepIndex++;
521
522         return this;
523     }
524
525     @Override
526     public QueryBuilder<E> dedup() {
527
528         this.traversal.dedup();
529         stepIndex++;
530
531         return this;
532     }
533
534     @Override
535     public QueryBuilder<E> emit() {
536
537         this.traversal.emit();
538         stepIndex++;
539
540         return this;
541
542     }
543
544     @Override
545     public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
546
547         this.traversal.repeat((GraphTraversal<Vertex, E>) builder.getQuery());
548         stepIndex++;
549
550         return this;
551     }
552
553     @Override
554     public QueryBuilder<E> until(QueryBuilder<E> builder) {
555         this.traversal.until((GraphTraversal<Vertex, E>) builder.getQuery());
556         stepIndex++;
557
558         return this;
559     }
560
561     @Override
562     public QueryBuilder<E> groupCount() {
563         this.traversal.groupCount();
564         stepIndex++;
565
566         return this;
567     }
568
569     @Override
570     public QueryBuilder<E> both() {
571         this.traversal.both();
572         stepIndex++;
573
574         return this;
575     }
576
577     @Override
578     public QueryBuilder<Tree> tree() {
579
580         this.traversal.tree();
581         stepIndex++;
582
583         return (QueryBuilder<Tree>) this;
584     }
585
586     @Override
587     public QueryBuilder<E> by(String name) {
588         this.traversal.by(name);
589         stepIndex++;
590
591         return this;
592     }
593
594     @Override
595     public QueryBuilder<E> valueMap() {
596         this.traversal.valueMap();
597         stepIndex++;
598
599         return this;
600     }
601
602     @Override
603     public QueryBuilder<E> valueMap(String... names) {
604         this.traversal.valueMap(names);
605         stepIndex++;
606
607         return this;
608     }
609
610     /**
611      * {@inheritDoc}
612      */
613     @Override
614     public QueryBuilder<E> simplePath() {
615         this.traversal.simplePath();
616         stepIndex++;
617         return this;
618     }
619
620     /**
621      * {@inheritDoc}
622      */
623     @Override
624     public QueryBuilder<Path> path() {
625         this.traversal.path();
626         stepIndex++;
627         return (QueryBuilder<Path>) this;
628     }
629
630     @Override
631     public QueryBuilder<Edge> outE() {
632         this.traversal.outE();
633         stepIndex++;
634         return (QueryBuilder<Edge>) this;
635     }
636
637     @Override
638     public QueryBuilder<Edge> inE() {
639         this.traversal.inE();
640         stepIndex++;
641         return (QueryBuilder<Edge>) this;
642     }
643
644     @Override
645     public QueryBuilder<Vertex> outV() {
646         this.traversal.outV();
647         stepIndex++;
648         return (QueryBuilder<Vertex>) this;
649     }
650
651     @Override
652     public QueryBuilder<Vertex> inV() {
653         this.traversal.inV();
654         stepIndex++;
655         return (QueryBuilder<Vertex>) this;
656     }
657
658     @Override
659     public QueryBuilder<E> as(String name) {
660         this.traversal.as(name);
661
662         stepIndex++;
663         return this;
664     }
665
666     @Override
667     public QueryBuilder<E> not(QueryBuilder<E> builder) {
668         this.traversal.not(builder.getQuery());
669
670         stepIndex++;
671         return this;
672     }
673
674     @Override
675     public QueryBuilder<E> select(String name) {
676         this.traversal.select(name);
677
678         stepIndex++;
679
680         return this;
681     }
682
683     @Override
684     public QueryBuilder<E> select(String... names) {
685         if (names.length == 1) {
686             this.traversal.select(names[0]);
687         } else if (names.length == 2) {
688             this.traversal.select(names[0], names[1]);
689         } else if (names.length > 2) {
690             String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
691             this.traversal.select(names[0], names[1], otherNames);
692         }
693
694         stepIndex++;
695
696         return this;
697     }
698
699     /**
700      * Edge query.
701      *
702      * @param outObj the out type
703      * @param inObj the in type
704      * @throws NoEdgeRuleFoundException
705      * @throws AAIException
706      */
707     private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
708             throws AAIException {
709         String outType = outObj.getDbName();
710         String inType = inObj.getDbName();
711
712         if (outObj.isContainer()) {
713             outType = outObj.getChildDBName();
714         }
715         if (inObj.isContainer()) {
716             inType = inObj.getChildDBName();
717         }
718         markParentBoundary();
719         Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
720         EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
721
722         if (labels == null) {
723             try {
724                 rules.putAll(edgeRules.getRules(qB.build()));
725             } catch (EdgeRuleNotFoundException e) {
726                 // is ok per original functioning of this section
727                 // TODO add "best try" sort of flag to the EdgeRuleQuery
728                 // to indicate if the exception should be thrown or not
729             }
730         } else {
731             for (String label : labels) {
732                 try {
733                     rules.putAll(edgeRules.getRules(qB.label(label).build()));
734                 } catch (EdgeRuleNotFoundException e) {
735                     throw new NoEdgeRuleFoundException(e);
736                 }
737             }
738             if (rules.isEmpty()) {
739                 throw new NoEdgeRuleFoundException(
740                         "No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
741             }
742         }
743
744         final List<String> inLabels = new ArrayList<>();
745         final List<String> outLabels = new ArrayList<>();
746
747         for (EdgeRule rule : rules.values()) {
748             if (labels != null && !labels.contains(rule.getLabel())) {
749                 return;
750             } else {
751                 if (Direction.IN.equals(rule.getDirection())) {
752                     inLabels.add(rule.getLabel());
753                 } else {
754                     outLabels.add(rule.getLabel());
755                 }
756             }
757         }
758
759         if (inLabels.isEmpty() && !outLabels.isEmpty()) {
760             traversal.out(outLabels.toArray(new String[outLabels.size()]));
761         } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
762             traversal.in(inLabels.toArray(new String[inLabels.size()]));
763         } else {
764             traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
765                     __.in(inLabels.toArray(new String[inLabels.size()])));
766         }
767
768         stepIndex++;
769
770         this.createContainerQuery(inObj);
771
772     }
773
774     /**
775      * Edge query.
776      *
777      * @param outObj the out type
778      * @param inObj the in type
779      * @throws NoEdgeRuleFoundException
780      * @throws AAIException
781      */
782     private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
783             throws AAIException {
784         String outType = outObj.getDbName();
785         String inType = inObj.getDbName();
786
787         if (outObj.isContainer()) {
788             outType = outObj.getChildDBName();
789         }
790         if (inObj.isContainer()) {
791             inType = inObj.getChildDBName();
792         }
793
794         markParentBoundary();
795         Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
796         EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
797
798         try {
799             if (labels == null) {
800                 rules.putAll(edgeRules.getRules(qB.build()));
801             } else {
802                 for (String label : labels) {
803                     rules.putAll(edgeRules.getRules(qB.label(label).build()));
804                 }
805             }
806         } catch (EdgeRuleNotFoundException e) {
807             throw new NoEdgeRuleFoundException(e);
808         }
809
810         final List<String> inLabels = new ArrayList<>();
811         final List<String> outLabels = new ArrayList<>();
812
813         for (EdgeRule rule : rules.values()) {
814             if (labels != null && !labels.contains(rule.getLabel())) {
815                 return;
816             } else {
817                 if (Direction.IN.equals(rule.getDirection())) {
818                     inLabels.add(rule.getLabel());
819                 } else {
820                     outLabels.add(rule.getLabel());
821                 }
822             }
823         }
824
825         if (inLabels.isEmpty() && !outLabels.isEmpty()) {
826             traversal.outE(outLabels.toArray(new String[outLabels.size()]));
827         } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
828             traversal.inE(inLabels.toArray(new String[inLabels.size()]));
829         } else {
830             traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])),
831                     __.inE(inLabels.toArray(new String[inLabels.size()])));
832         }
833     }
834
835     @Override
836     public QueryBuilder<E> limit(long amount) {
837         traversal.limit(amount);
838         return this;
839     }
840
841     /**
842      * @{inheritDoc}
843      */
844     @Override
845     public <E2> E2 getQuery() {
846         return (E2) this.traversal;
847     }
848
849     /**
850      * @{inheritDoc}
851      */
852     @Override
853     public QueryBuilder<E> getParentQuery() {
854
855         return cloneQueryAtStep(parentStepIndex);
856     }
857
858     @Override
859     public QueryBuilder<E> getContainerQuery() {
860
861         if (this.parentStepIndex == 0) {
862             return removeQueryStepsBetween(0, containerStepIndex);
863         } else {
864             return cloneQueryAtStep(containerStepIndex);
865         }
866     }
867
868     /**
869      * @{inheritDoc}
870      */
871     @Override
872     public void markParentBoundary() {
873         parentStepIndex = stepIndex;
874     }
875
876     @Override
877     public void markContainer() {
878         containerStepIndex = stepIndex;
879     }
880
881     /**
882      * @{inheritDoc}
883      */
884     @Override
885     public Vertex getStart() {
886         return this.start;
887     }
888
889     protected int getParentStepIndex() {
890         return parentStepIndex;
891     }
892
893     protected int getContainerStepIndex() {
894         return containerStepIndex;
895     }
896
897     protected int getStepIndex() {
898         return stepIndex;
899     }
900
901     /**
902      * end is exclusive
903      *
904      * @param start
905      * @param end
906      * @return
907      */
908     protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
909
910     protected void executeQuery() {
911
912         Admin<Vertex, Vertex> admin;
913         if (start != null) {
914             this.completeTraversal = traversal.asAdmin();
915         } else {
916             admin = source.V().asAdmin();
917             TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
918
919             this.completeTraversal = (Admin<Vertex, E>) admin;
920
921         }
922
923     }
924
925     @Override
926     public boolean hasNext() {
927         if (this.completeTraversal == null) {
928             executeQuery();
929         }
930
931         return this.completeTraversal.hasNext();
932     }
933
934     @Override
935     public E next() {
936         if (this.completeTraversal == null) {
937             executeQuery();
938         }
939
940         return this.completeTraversal.next();
941     }
942
943     @Override
944     public List<E> toList() {
945         if (this.completeTraversal == null) {
946             executeQuery();
947         }
948         return this.completeTraversal.toList();
949     }
950
951     protected QueryBuilder<Edge> has(String key, String value) {
952         traversal.has(key, value);
953
954         return (QueryBuilder<Edge>) this;
955     }
956
957 }