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