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