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