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