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