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