Add to aai-common query builder get parent step
[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 java.util.ArrayList;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Optional;
26 import java.util.Set;
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.util.TraversalHelper;
36 import org.apache.tinkerpop.gremlin.structure.Direction;
37 import org.apache.tinkerpop.gremlin.structure.Edge;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39 import org.onap.aai.db.props.AAIProperties;
40 import org.onap.aai.exceptions.AAIException;
41 import org.onap.aai.introspection.Introspector;
42 import org.onap.aai.introspection.Loader;
43 import org.onap.aai.schema.enums.ObjectMetadata;
44 import org.onap.aai.schema.enums.PropertyMetadata;
45 import org.onap.aai.serialization.db.EdgeRule;
46 import org.onap.aai.serialization.db.EdgeRules;
47 import org.onap.aai.serialization.db.EdgeType;
48 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
49
50 /**
51  * The Class GraphTraversalBuilder.
52  */
53 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
54
55         protected GraphTraversal<Vertex, E> traversal = null;
56         protected Admin<Vertex, E> completeTraversal = null;
57         private EdgeRules edgeRules = EdgeRules.getInstance();
58         
59         protected int parentStepIndex = 0;
60         protected int containerStepIndex = 0;
61         protected int stepIndex = 0;
62         
63         /**
64          * Instantiates a new graph traversal builder.
65          *
66          * @param loader the loader
67          */
68         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
69                 super(loader, source);
70                 
71                 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
72                 
73         }
74         
75         /**
76          * Instantiates a new graph traversal builder.
77          *
78          * @param loader the loader
79          */
80         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, EdgeRules edgeRules) {
81                 super(loader, source);
82                 this.edgeRules = edgeRules;
83                 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
84                 
85         }
86         
87         /**
88          * Instantiates a new graph traversal builder.
89          *
90          * @param loader the loader
91          * @param start the start
92          */
93         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
94                 super(loader, source, start);
95                 
96                 traversal = (GraphTraversal<Vertex, E>) __.__(start);
97                 
98         }
99         
100         /**
101          * Instantiates a new graph traversal builder.
102          *
103          * @param loader the loader
104          * @param start the start
105          */
106         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start, EdgeRules edgeRules) {
107                 super(loader, source, start);
108                 this.edgeRules = edgeRules;
109                 traversal = (GraphTraversal<Vertex, E>) __.__(start);
110                 
111         }
112
113         /**
114          * @{inheritDoc}
115          */
116         @Override
117         public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
118                 
119                 // correct value call because the index is registered as an Integer
120                 traversal.has(key, this.correctObjectType(value));
121                 
122                 stepIndex++;
123                 return (QueryBuilder<Vertex>) this;
124         }
125         
126         /**
127          * @{inheritDoc}
128          */
129         @Override
130         public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
131                 
132                 //this is because the index is registered as an Integer
133                 List<Object> correctedValues = new ArrayList<>();
134                 for (Object item : values) {
135                         correctedValues.add(this.correctObjectType(item));
136                 }
137                 
138                 traversal.has(key, P.within(correctedValues));
139                 
140                 stepIndex++;
141                 return (QueryBuilder<Vertex>) this;
142         }
143         
144         /**
145          * @{inheritDoc}
146          */
147         @Override
148         public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
149                 
150                 // correct value call because the index is registered as an Integer
151                 traversal.has(key, P.neq(this.correctObjectType(value)));
152                 
153                 stepIndex++;
154                 return (QueryBuilder<Vertex>) this;
155         }
156         
157         /**
158          * @{inheritDoc}
159          */
160         @Override
161         public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
162                 
163                 //this is because the index is registered as an Integer
164                 List<Object> correctedValues = new ArrayList<>();
165                 for (Object item : values) {
166                         correctedValues.add(this.correctObjectType(item));
167                 }
168                 
169                 traversal.has(key, P.without(correctedValues));
170                 
171                 stepIndex++;
172                 return (QueryBuilder<Vertex>) this;
173         }
174
175         /**
176          * @{inheritDoc}
177          */
178         @Override
179         public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
180                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
181                 stepIndex++;
182                 return (QueryBuilder<Vertex>) this;
183         }
184
185         /**
186          * @{inheritDoc}
187          */
188         @Override
189         public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
190                 
191                 for (Map.Entry<String, String> es : map.entrySet()) {
192                         traversal.has(es.getKey(), es.getValue());
193                         stepIndex++;
194                 }
195                 traversal.has(AAIProperties.NODE_TYPE, type);
196                 stepIndex++;
197                 return (QueryBuilder<Vertex>) this;
198         }
199
200         /**
201          * @{inheritDoc}
202          */
203         @Override
204         public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
205                 Set<String> keys = obj.getKeys();
206                 Object val;
207                 for (String key : keys) {
208                         val = obj.getValue(key);
209                         Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
210                         if (metadata.isPresent()) {
211                                 //use the db name for the field rather than the object model
212                                 key = metadata.get();
213                         }
214                         if (val != null) {
215                                 //this is because the index is registered as an Integer
216                                 if (val.getClass().equals(Long.class)) {
217                                         traversal.has(key,new Integer(val.toString()));
218                                 } else {
219                                         traversal.has(key, val);
220                                 }
221                                 stepIndex++;
222                         }
223                 }
224                 return (QueryBuilder<Vertex>) this;
225         }
226
227         @Override
228         public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
229                 this.createKeyQuery(obj);
230                 allPropertiesQuery(obj);
231                 this.createContainerQuery(obj);
232                 return (QueryBuilder<Vertex>) this;
233         }
234         
235         private void allPropertiesQuery(Introspector obj) {
236                 Set<String> props = obj.getProperties();
237                 Set<String> keys = obj.getKeys();
238                 Object val;
239                 for (String prop : props) {
240                         if (obj.isSimpleType(prop) && !keys.contains(prop)) {
241                                 val = obj.getValue(prop);
242                                 if (val != null) {
243                                         Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
244                                         if (metadata.isPresent()) {
245                                                 //use the db name for the field rather than the object model
246                                                 prop = metadata.get();
247                                         }
248                                         //this is because the index is registered as an Integer
249                                         if (val.getClass().equals(Long.class)) {
250                                                 traversal.has(prop,new Integer(val.toString()));
251                                         } else {
252                                                 traversal.has(prop, val);
253                                         }
254                                         stepIndex++;
255                                 }
256                         }
257                 }
258         }
259         
260         /**
261          * @{inheritDoc}
262          */
263         @Override
264         
265         public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
266                 String type = obj.getChildDBName();
267                 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
268                 if (abstractType != null) {
269                         String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
270                         traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
271                 } else {
272                         traversal.has(AAIProperties.NODE_TYPE, type);
273                 }
274                 stepIndex++;
275                 markContainer();
276                 return (QueryBuilder<Vertex>) this;
277         }
278
279         /**
280          * @throws NoEdgeRuleFoundException 
281          * @throws AAIException 
282          * @{inheritDoc}
283          */
284         @Override
285         public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
286                 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
287                 if ("true".equals(isAbstractType)) {
288                         markParentBoundary();
289                         traversal.union(handleAbstractEdge(type, parent, child));
290                         stepIndex++;
291                 } else {
292                         this.edgeQueryToVertex(type, parent, child, null);
293                 }
294                 return (QueryBuilder<Vertex>) this;
295                         
296         }
297
298         /**
299          *
300          * @{inheritDoc}
301          */
302         @Override
303         public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException {
304                 this.edgeQueryToVertex(type, out, in, labels);
305                 return (QueryBuilder<Vertex>) this;
306         }
307
308
309         private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
310                 String childName = child.getDbName();
311                 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
312                 String[] inheritors = inheritorMetadata.split(",");
313                 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
314
315                 for (int i = 0; i < inheritors.length; i++) {
316                         String inheritor = inheritors[i];
317                         if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) {
318                                 Map<String, EdgeRule> rules = edgeRules.getEdgeRules(type, inheritor, childName);
319                                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
320                                 
321                                 final List<String> inLabels = new ArrayList<>();
322                                 final List<String> outLabels = new ArrayList<>();
323
324                                 rules.forEach((k,v) -> {
325                                         if (v.getDirection().equals(Direction.IN)) {
326                                                 inLabels.add(k);
327                                         } else {
328                                                 outLabels.add(k);
329                                         }
330                                 } );
331                 
332                                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
333                                         innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
334                                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
335                                         innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
336                                 } else {                                
337                                         innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
338                                 }
339                                 
340                                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
341                                 unionTraversals.add(innerTraversal);
342                         }
343                 }
344                 
345                 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
346         }
347
348         @Override
349         public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List<String> labels) throws AAIException {
350                 Introspector outObj = loader.introspectorFromName(outNodeType);
351                 Introspector inObj = loader.introspectorFromName(inNodeType);
352                 this.edgeQuery(type, outObj, inObj, labels);
353
354                 return (QueryBuilder<Edge>)this;
355         }
356
357
358         /**
359          * @{inheritDoc}
360          */
361         @Override
362         public QueryBuilder<E> union(QueryBuilder... builder) {
363                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
364                 for (int i = 0; i < builder.length; i++) {
365                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
366                 }
367                 this.traversal.union(traversals);
368                 stepIndex++;
369                 
370                 return this;
371         }
372         
373         /**
374          * @{inheritDoc}
375          */
376         @Override
377         public QueryBuilder<E> where(QueryBuilder... builder) {
378                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
379                 for (int i = 0; i < builder.length; i++) {
380                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
381                         stepIndex++;
382                 }
383                 
384                 return this;
385         }
386         
387         @Override
388         public QueryBuilder<E> store(String name) {
389                 
390                 this.traversal.store(name);
391                 stepIndex++;
392                 
393                 return this;
394         }
395         
396         @Override
397         public QueryBuilder<E> cap(String name) {
398                 this.traversal.cap(name);
399                 stepIndex++;
400                 
401                 return this;
402         }
403         
404         @Override
405         public QueryBuilder<E> unfold() {
406                 this.traversal.unfold();
407                 stepIndex++;
408                         
409                 return this;
410         }
411         
412         @Override
413         public QueryBuilder<E> dedup() {
414                 
415                 this.traversal.dedup();
416                 stepIndex++;
417                 
418                 return this;
419         }
420         
421         @Override
422         public QueryBuilder<E> emit() {
423                 
424                 this.traversal.emit();
425                 stepIndex++;
426                 
427                 return this;
428                 
429         }
430         
431         @Override
432         public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
433                 
434                 this.traversal.repeat((GraphTraversal<Vertex, E>)builder.getQuery());
435                 stepIndex++;
436
437                 return this;
438         }
439         
440         @Override
441         public QueryBuilder<E> until(QueryBuilder<E> builder) {
442                 this.traversal.until((GraphTraversal<Vertex,E>)builder.getQuery());
443                 stepIndex++;
444                 
445                 return this;
446         }
447         
448         @Override
449         public QueryBuilder<E> groupCount() {
450                 this.traversal.groupCount();
451                 stepIndex++;
452                 
453                 return this;
454         }
455         
456         @Override
457         public QueryBuilder<E> both() {
458                 this.traversal.both();
459                 stepIndex++;
460                 
461                 return this;
462         }
463         
464         @Override
465         public QueryBuilder<E> by(String name) {
466                 this.traversal.by(name);
467                 stepIndex++;
468                 
469                 return this;
470         }
471         
472         /**
473          * {@inheritDoc}
474          */
475         @Override
476         public QueryBuilder<E> simplePath(){
477                 this.traversal.simplePath();
478                 stepIndex++;
479                 return this;
480         }
481
482         /**
483          * {@inheritDoc}
484          */
485         @Override
486         public QueryBuilder<Path> path(){
487                 this.traversal.path();
488                 stepIndex++;
489                 return (QueryBuilder<Path>)this;
490         }
491         
492         @Override
493         public QueryBuilder<Edge> outE() {
494                 this.traversal.outE();
495                 stepIndex++;
496                 return (QueryBuilder<Edge>)this;
497         }
498         
499         @Override
500         public QueryBuilder<Edge> inE() {
501                 this.traversal.inE();
502                 stepIndex++;
503                 return (QueryBuilder<Edge>)this;
504         }
505         
506         @Override
507         public QueryBuilder<Vertex> outV() {
508                 this.traversal.outV();
509                 stepIndex++;
510                 return (QueryBuilder<Vertex>)this;
511         }
512         
513         @Override
514         public QueryBuilder<Vertex> inV() {
515                 this.traversal.inV();
516                 stepIndex++;
517                 return (QueryBuilder<Vertex>)this;
518         }
519         
520         @Override
521         public QueryBuilder<E> as(String name) {
522                 this.traversal.as(name);
523                 
524                 stepIndex++;
525                 return this;
526         }
527         
528         @Override
529         public QueryBuilder<E> not(QueryBuilder<E> builder) {
530                 this.traversal.not(builder.getQuery());
531                 
532                 stepIndex++;
533                 return this;
534         }
535         
536         @Override
537         public QueryBuilder<E> select(String name) {
538                 this.traversal.select(name);
539                 
540                 stepIndex++;
541                 
542                 return this;
543         }
544         
545         /**
546          * Edge query.
547          *
548          * @param outObj the out type
549          * @param inObj the in type
550          * @throws NoEdgeRuleFoundException 
551          * @throws AAIException 
552          */
553         private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
554                 String outType = outObj.getDbName();
555                 String inType = inObj.getDbName();
556                 
557                 if (outObj.isContainer()) {
558                         outType = outObj.getChildDBName();
559                 }
560                 if (inObj.isContainer()) {
561                         inType = inObj.getChildDBName();
562                 }
563                 markParentBoundary();
564                 Map<String, EdgeRule> rules;
565                 if (labels == null) {
566                         rules = edgeRules.getEdgeRules(type, outType, inType);
567                 } else {
568                         rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels);
569                 }
570                 
571                 final List<String> inLabels = new ArrayList<>();
572                 final List<String> outLabels = new ArrayList<>();
573
574                 rules.forEach((k, edgeRule) -> {
575                         if (labels != null && !labels.contains(k)) {
576                                 return;
577                         } else {
578                                 if (edgeRule.getDirection().equals(Direction.IN)) {
579                                         inLabels.add(edgeRule.getLabel());
580                                 } else {
581                                         outLabels.add(edgeRule.getLabel());
582                                 }
583                         }
584                 });
585
586                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
587                         traversal.out(outLabels.toArray(new String[outLabels.size()]));
588                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
589                         traversal.in(inLabels.toArray(new String[inLabels.size()]));
590                 } else {                                
591                         traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
592                 }
593                 
594                 stepIndex++;
595
596                 this.createContainerQuery(inObj);
597                 
598         }
599         
600         /**
601          * Edge query.
602          *
603          * @param outObj the out type
604          * @param inObj the in type
605          * @throws NoEdgeRuleFoundException 
606          * @throws AAIException 
607          */
608         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
609                 String outType = outObj.getDbName();
610                 String inType = inObj.getDbName();
611                 
612                 if (outObj.isContainer()) {
613                         outType = outObj.getChildDBName();
614                 }
615                 if (inObj.isContainer()) {
616                         inType = inObj.getChildDBName();
617                 }
618                 
619                 markParentBoundary();
620                 Map<String, EdgeRule> rules;
621                 if (labels == null) {
622                         rules = edgeRules.getEdgeRules(type, outType, inType);
623                 } else {
624                         rules = edgeRules.getEdgeRulesWithLabels(type, outType, inType, labels);
625                 }
626                 
627                 final List<String> inLabels = new ArrayList<>();
628                 final List<String> outLabels = new ArrayList<>();
629
630                 rules.forEach((k, edgeRule) -> {
631                         if (labels != null && !labels.contains(k)) {
632                                 return;
633                         } else {
634                                 if (edgeRule.getDirection().equals(Direction.IN)) {
635                                         inLabels.add(edgeRule.getLabel());
636                                 } else {
637                                         outLabels.add(edgeRule.getLabel());
638                                 }
639                         }
640                 });
641
642                 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
643                         traversal.outE(outLabels.toArray(new String[outLabels.size()]));
644                 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
645                         traversal.inE(inLabels.toArray(new String[inLabels.size()]));
646                 } else {                                
647                         traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), __.inE(inLabels.toArray(new String[inLabels.size()])));
648                 }
649         }
650         
651         @Override
652         public QueryBuilder<E> limit(long amount) {
653                 traversal.limit(amount);
654                 return this;
655         }
656
657         /**
658          * @{inheritDoc}
659          */
660         @Override
661         public <E2> E2 getQuery() {
662                 return (E2)this.traversal;
663         }
664         
665         /**
666          * @{inheritDoc}
667          */
668         @Override
669         public QueryBuilder<E> getParentQuery() {
670
671                 return cloneQueryAtStep(parentStepIndex);
672         }
673         
674         @Override
675         public QueryBuilder<E> getContainerQuery() {
676                 
677                 if (this.parentStepIndex == 0) {
678                         return removeQueryStepsBetween(0, containerStepIndex);
679                 } else {
680                         return cloneQueryAtStep(containerStepIndex);
681                 }
682         }
683         
684         /**
685          * @{inheritDoc}
686          */
687         @Override
688         public void markParentBoundary() {
689                 parentStepIndex = stepIndex;
690         }
691         
692         @Override
693         public void markContainer() {
694                 containerStepIndex = stepIndex;
695         }
696         
697         
698         /**
699          * @{inheritDoc}
700          */
701         @Override
702         public Vertex getStart() {
703                 return this.start;
704         }
705
706         protected int getParentStepIndex() {
707                 return parentStepIndex;
708         }
709
710         protected int getContainerStepIndex() {
711                 return containerStepIndex;
712         }
713
714         protected int getStepIndex() {
715                 return stepIndex;
716         }
717
718         protected abstract QueryBuilder<E> cloneQueryAtStep(int index);
719         /**
720          * end is exclusive
721          * 
722          * @param start
723          * @param end
724          * @return
725          */
726         protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
727         
728         private void executeQuery() {
729                 
730                 Admin admin;
731                 if (start != null) {
732                         this.completeTraversal = traversal.asAdmin();
733                 } else {
734                         admin = source.V().asAdmin();
735                         TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
736                         
737                         this.completeTraversal = (Admin<Vertex, E>) admin;
738
739                 }
740                 
741                 
742         }
743         
744         @Override
745         public boolean hasNext() {
746                 if (this.completeTraversal == null) {
747                         executeQuery();
748                 }
749                 
750                 return this.completeTraversal.hasNext();
751         }
752         
753         @Override
754         public E next() {
755                 if (this.completeTraversal == null) {
756                         executeQuery();
757                 }
758                 
759                 return this.completeTraversal.next();
760         }
761         
762         @Override
763         public List<E> toList() {
764                 if (this.completeTraversal == null) {
765                         executeQuery();
766                 }
767                 
768                 return this.completeTraversal.toList();
769         }
770
771         protected QueryBuilder<Edge> has(String key, String value) {
772                 traversal.has(key, value);
773
774                 return (QueryBuilder<Edge>)this;
775         }
776 }