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