Merge "minor fixes in GremlinQueryBuilder.java"
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / query / builder / GremlinQueryBuilder.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright © 2018 IBM.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *    http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22 package org.onap.aai.query.builder;
23
24 import com.google.common.base.Joiner;
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.Multimap;
27
28 import org.apache.tinkerpop.gremlin.process.traversal.Path;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
30 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
31 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
32 import org.apache.tinkerpop.gremlin.structure.Direction;
33 import org.apache.tinkerpop.gremlin.structure.Edge;
34 import org.apache.tinkerpop.gremlin.structure.Vertex;
35 import org.onap.aai.db.props.AAIProperties;
36 import org.onap.aai.exceptions.AAIException;
37 import org.onap.aai.introspection.Introspector;
38 import org.onap.aai.introspection.Loader;
39 import org.onap.aai.restcore.search.GremlinGroovyShellSingleton;
40 import org.onap.aai.schema.enums.ObjectMetadata;
41 import org.onap.aai.edges.EdgeRule;
42 import org.onap.aai.edges.EdgeRuleQuery;
43 import org.onap.aai.edges.enums.EdgeType;
44 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
45 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
46
47 import java.util.*;
48
49 /**
50  * The Class GremlinQueryBuilder.
51  */
52 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
53
54     private static final String ARGUMENT2 = "#!#argument#!#";
55     private static final String HAS = ".has('";
56     private GremlinGroovyShellSingleton gremlinGroovy = GremlinGroovyShellSingleton.getInstance();
57     private GraphTraversal<?, ?> completeTraversal = null;
58     protected List<String> list = null;
59
60     /**
61      * Instantiates a new gremlin query builder.
62      *
63      * @param loader the loader
64      */
65     public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
66         super(loader, source);
67         list = new ArrayList<>();
68     }
69
70     /**
71      * Instantiates a new gremlin query builder.
72      *
73      * @param loader the loader
74      * @param start the start
75      */
76     public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
77         super(loader, source, start);
78         list = new ArrayList<>();
79     }
80
81     @Override
82     public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
83         // TODO not implemented because this is implementation is no longer used
84         this.createKeyQuery(obj);
85         this.createContainerQuery(obj);
86         return (QueryBuilder<Vertex>) this;
87     }
88
89     /**
90      * @{inheritDoc}
91      */
92     @Override
93     public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
94
95         String term = "";
96         if (value != null && !(value instanceof String) ) {
97             term = value.toString();
98         } else {
99             term = "'" + value + "'";
100         }
101         list.add(HAS + key + "', " + term + ")");
102         stepIndex++;
103         return (QueryBuilder<Vertex>) this;
104     }
105
106     @Override
107     public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
108         boolean bValue = false;
109
110         if(value instanceof String){
111             bValue = Boolean.valueOf(value.toString());
112         } else if(value instanceof Boolean){
113             bValue = (Boolean) value;
114         }
115
116         list.add(HAS + key + "', " + bValue + ")");
117         stepIndex++;
118         return (QueryBuilder<Vertex>) this;
119     }
120
121     /**
122      * @{inheritDoc}
123      */
124     @Override
125     public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
126
127         String predicate = "P.within(#!#argument#!#)";
128         List<String> arguments = new ArrayList<>();
129         for (Object item : values) {
130             if (item != null && !(item instanceof String)) {
131                 arguments.add(item.toString());
132             } else {
133                 arguments.add("'" + item + "'");
134             }
135         }
136         String argument = Joiner.on(",").join(arguments);
137         predicate = predicate.replace(ARGUMENT2, argument);
138         list.add(HAS + key + "', " + predicate + ")");
139         stepIndex++;
140         return (QueryBuilder<Vertex>) this;
141     }
142     
143     /**
144      * @{inheritDoc}
145      */
146     @Override
147     public QueryBuilder<Vertex> getVerticesByProperty(String key) {
148
149         list.add(HAS + key + "')");
150         stepIndex++;
151         return (QueryBuilder<Vertex>) this;
152     }
153     
154     /**
155      * @{inheritDoc}
156      */
157     @Override
158     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
159
160         list.add(".hasNot('" + key + "')");
161         stepIndex++;
162         return (QueryBuilder<Vertex>) this;
163     }
164
165     /**
166      * @{inheritDoc}
167      */
168     @Override
169     public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
170
171         String term = "";
172         String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
173         if (value != null && !(value instanceof String) ) {
174             term = value.toString();
175         } else {
176             term = "'" + value + "'";
177         }
178         predicate = predicate.replace(ARGUMENT2, term);
179         list.add(HAS + key + "', " + predicate + ")");
180         stepIndex++;
181         return (QueryBuilder<Vertex>) this;
182     }
183
184     /**
185      * @{inheritDoc}
186      */
187     @Override
188     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
189
190         String term = "";
191         String predicate = "P.neq(#!#argument#!#)";
192         if (value != null && !(value instanceof String) ) {
193             term = value.toString();
194         } else {
195             term = "'" + value + "'";
196         }
197         predicate = predicate.replace(ARGUMENT2, term);
198         list.add(HAS + key + "', " + predicate + ")");
199         stepIndex++;
200         return (QueryBuilder<Vertex>) this;
201     }
202     
203     /**
204      * @{inheritDoc}
205      */
206     @Override
207     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
208
209         String predicate = "P.without(#!#argument#!#)";
210         List<String> arguments = new ArrayList<>();
211         for (Object item : values) {
212             if (item != null && !(item instanceof String)) {
213                 arguments.add(item.toString());
214             } else {
215                 arguments.add("'" + item + "'");
216             }
217         }
218         String argument = Joiner.on(",").join(arguments);
219         predicate = predicate.replace(ARGUMENT2, argument);
220         list.add(HAS + key + "', " + predicate + ")");
221         stepIndex++;
222         return (QueryBuilder<Vertex>) this;
223     }
224     
225     @Override
226     public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
227         String predicate = "P.gte(#!#argument1#!#)";
228         String term;
229         if (value != null && !(value instanceof String) ) {
230             term = value.toString();
231         } else {
232             term = "'" + value + "'";
233         }
234         predicate = predicate.replace("#!#argument1#!#", term);
235         list.add(HAS + key + "', " + predicate + ")");
236         stepIndex++;
237         return (QueryBuilder<Vertex>) this;
238     }
239     
240     @Override
241     public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
242         String predicate = "P.lte(#!#argument1#!#)";
243         String term;
244         if (value != null && !(value instanceof String) ) {
245             term = value.toString();
246         } else {
247             term = "'" + value + "'";
248         }
249         predicate = predicate.replace("#!#argument1#!#", term);
250         list.add(HAS + key + "', " + predicate + ")");
251         stepIndex++;
252         return (QueryBuilder<Vertex>) this;
253     }
254     
255
256
257     
258     /**
259      * @{inheritDoc}
260      */
261     @Override
262     public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
263         //TODO
264         return (QueryBuilder<Vertex>) this;
265     }
266     
267     /**
268      * @{inheritDoc}
269      */
270     @Override
271     public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
272         
273         for (Map.Entry<String, String> es : map.entrySet()) {
274             list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
275             stepIndex++;
276         }
277         list.add(".has('aai-node-type', '" + type + "')");
278         stepIndex++;
279         return (QueryBuilder<Vertex>) this;
280     }
281     
282     /**
283      * @{inheritDoc}
284      */
285     @Override
286     public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
287         Set<String> keys = obj.getKeys();
288
289         for (String key : keys) {
290             
291             this.getVerticesByProperty(key, obj.<Object>getValue(key));
292             
293         }        
294         return (QueryBuilder<Vertex>) this;
295     }
296     
297     /**
298      * @throws NoEdgeRuleFoundException 
299      * @throws AAIException 
300      * @{inheritDoc}
301      */
302     @Override
303     public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
304         String parentName = parent.getDbName();
305         String childName = child.getDbName();
306         if (parent.isContainer()) {
307             parentName = parent.getChildDBName();
308         }
309         if (child.isContainer()) {
310             childName = child.getChildDBName();
311         }
312         this.edgeQueryToVertex(type, parentName, childName, null);
313         return this;
314             
315     }
316
317     @Override
318     public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException{
319         String parentName = parent.getDbName();
320         String childName = child.getDbName();
321         if (parent.isContainer()) {
322             parentName = parent.getChildDBName();
323         }
324         if (child.isContainer()) {
325             childName = child.getChildDBName();
326         }
327         this.edgeQueryToVertex(type, parentName, childName, null, true);
328         return this;
329     }
330
331     /**
332      *
333      * @{inheritDoc}
334      */
335     @Override
336     public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException {
337         String parentName = out.getDbName();
338         String childName = in.getDbName();
339         if (out.isContainer()) {
340             parentName = out.getChildDBName();
341         }
342         if (in.isContainer()) {
343             childName = in.getChildDBName();
344         }
345         this.edgeQueryToVertex(type, parentName, childName, labels);
346         return (QueryBuilder<Vertex>) this;
347     }
348
349
350     public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List<String> labels) throws AAIException {
351         this.edgeQuery(type, outNodeType, inNodeType, labels);
352         return (QueryBuilder<Edge>)this;
353     }
354
355     private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
356         this.edgeQueryToVertex(type, outType, inType, labels, false);
357     }
358
359     /**
360      * Edge query.
361      *
362      * @param outType the out type
363      * @param inType the in type
364      * @throws NoEdgeRuleFoundException
365      * @throws AAIException
366      */
367     private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels, boolean isPrivateEdge) throws AAIException {
368         markParentBoundary();
369         Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
370         EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
371
372         try {
373             if (labels == null) {
374                 rules.putAll(edgeRules.getRules(qB.build()));
375             } else {
376                 for (String label : labels) {
377                     rules.putAll(edgeRules.getRules(qB.label(label).build()));
378                 }
379             }
380         } catch (EdgeRuleNotFoundException e) {
381             throw new NoEdgeRuleFoundException(e);
382         }
383
384         final List<String> inLabels = new ArrayList<>();
385         final List<String> outLabels = new ArrayList<>();
386
387         for (EdgeRule rule : rules.values()) {
388             if (labels != null && !labels.contains(rule.getLabel())) {
389                 return;
390             } else {
391                 if (Direction.IN.equals(rule.getDirection())) {
392                     inLabels.add(rule.getLabel());
393                 } else {
394                     outLabels.add(rule.getLabel());
395                 }
396             }
397         }
398
399         if(inLabels.isEmpty() && outLabels.isEmpty()) {
400             throw new NoEdgeRuleFoundException("no " + type.toString() + " edge rule between " + outType + " and " + inType );
401         } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
402             list.add(".out('" + String.join("','", outLabels) + "')");
403         } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
404             list.add(".in('" + String.join("','", inLabels) + "')");
405         } else {
406             list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('" + String.join("','", outLabels) + "'))");
407         }
408         stepIndex++;
409         list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
410         stepIndex++;
411         
412     }
413     
414     /**
415      * Edge query.
416      *
417      * @param outType the out type
418      * @param inType the in type
419      * @throws NoEdgeRuleFoundException 
420      * @throws AAIException 
421      */
422     private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
423         markParentBoundary();
424         Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
425         EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
426         try {
427             if (labels == null) {
428                 rules.putAll(edgeRules.getRules(qB.build()));
429             } else {
430                 for (String label : labels) {
431                     rules.putAll(edgeRules.getRules(qB.label(label).build()));
432                 }
433             }
434         } catch (EdgeRuleNotFoundException e) {
435             throw new NoEdgeRuleFoundException(e);
436         }
437         
438         final List<String> inLabels = new ArrayList<>();
439         final List<String> outLabels = new ArrayList<>();
440
441         for (EdgeRule rule : rules.values()) {
442             if (labels != null && !labels.contains(rule.getLabel())) {
443                 return;
444             } else {
445                 if (Direction.IN.equals(rule.getDirection())) {
446                     inLabels.add(rule.getLabel());
447                 } else {
448                     outLabels.add(rule.getLabel());
449                 }
450             }
451         }
452
453         if(inLabels.isEmpty() && outLabels.isEmpty()) {
454             throw new NoEdgeRuleFoundException("no " + type.toString() + " edge rule between " + outType + " and " + inType );
455         } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
456             list.add(".outE('" + String.join("','", outLabels) + "')");
457         } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
458             list.add(".inE('" + String.join("','", inLabels) + "')");
459         } else {
460             list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('" + String.join("','", outLabels) + "'))");
461         }
462         
463         stepIndex++;
464         
465     }
466     @Override
467     public QueryBuilder<E> limit(long amount) {
468         list.add(".limit(" + amount + ")");
469         return this;
470     }
471     /**
472      * @{inheritDoc}
473      */
474     @Override
475     public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
476         String type = obj.getChildDBName();
477         String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
478         if (abstractType != null) {
479             String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
480             String[] wrapped = new String[inheritors.length];
481             StringBuilder command = new StringBuilder();
482             command.append("P.within(");
483             for (int i = 0; i < inheritors.length; i++) {
484                 wrapped[i] = "'" + inheritors[i] + "'";
485             }
486             command.append(Joiner.on(",").join(wrapped));
487             command.append(")");
488             list.add(".has('aai-node-type', " + command + ")");
489             
490         } else {
491             list.add(".has('aai-node-type', '" + type + "')");
492         }
493         stepIndex++;
494         this.markContainer();
495         return (QueryBuilder<Vertex>) this;
496     }
497     
498     @Override
499     public QueryBuilder<E> union(QueryBuilder<E>... builder) {
500         markParentBoundary();
501         String[] traversals = new String[builder.length];
502         StringBuilder command = new StringBuilder();
503         for (int i = 0; i < builder.length; i++) {
504             traversals[i] = "__" + builder[i].getQuery();
505         }
506         command.append(".union(");
507         command.append(Joiner.on(",").join(traversals));
508         command.append(")");
509         list.add(command.toString());
510         stepIndex++;
511         
512         return this;
513     }
514     
515     @Override
516     public QueryBuilder<E> where(QueryBuilder<E>... builder) {
517         markParentBoundary();
518         List<String> traversals = new ArrayList<>();
519         for (int i = 0; i < builder.length; i++) {
520             traversals.add(".where(__" + builder[i].getQuery() + ")");
521             stepIndex++;
522         }
523         list.addAll(traversals);
524         
525         
526         return this;
527     }
528
529     @Override
530     public QueryBuilder<E> or(QueryBuilder<E>... builder) {
531         markParentBoundary();
532         String[] traversals = new String[builder.length];
533         StringBuilder command = new StringBuilder();
534         for (int i = 0; i < builder.length; i++) {
535             traversals[i] = "__" + builder[i].getQuery();
536         }
537         command.append(".or(");
538         command.append(Joiner.on(",").join(traversals));
539         command.append(")");
540         list.add(command.toString());
541         stepIndex++;
542
543         return this;
544     }
545     
546     @Override
547     public QueryBuilder<E> store(String name) {
548         this.list.add(".store('"+ name + "')");
549         stepIndex++;
550         
551         return this;
552     }
553     
554     @Override
555     public QueryBuilder<E> cap(String name) {
556         this.list.add(".cap('"+ name + "')");
557         stepIndex++;
558         
559         return this;
560     }
561     
562     @Override
563     public QueryBuilder<E> unfold() {
564         this.list.add(".unfold()");
565         stepIndex++;
566         
567         return this;
568     }
569     
570     @Override
571     public QueryBuilder<E> dedup() {
572         this.list.add(".dedup()");
573         stepIndex++;
574         
575         return this;
576     }
577     
578     @Override
579     public QueryBuilder<E> emit() {
580         this.list.add(".emit()");
581         stepIndex++;
582         
583         return this;
584     }
585     
586     @Override
587     public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
588         this.list.add(".repeat(__" + builder.getQuery()  + ")");
589         stepIndex++;
590         
591         return this;
592     }
593     
594     @Override
595     public QueryBuilder<E> until(QueryBuilder<E> builder) {
596         this.list.add(".until(__" + builder.getQuery() + ")");
597         stepIndex++;
598         
599         return this;
600     }
601     
602     @Override
603     public QueryBuilder<E> groupCount() {
604         this.list.add(".groupCount()");
605         stepIndex++;
606         
607         return this;
608     }
609     
610     @Override
611     public QueryBuilder<E> both() {
612         this.list.add(".both()");
613         stepIndex++;
614         
615         return this;
616     }
617     
618     @Override
619     public QueryBuilder<Tree> tree() {
620         this.list.add(".tree()");
621         stepIndex++;
622         
623         return (QueryBuilder<Tree>)this;
624     }
625     
626     @Override
627     public QueryBuilder<E> by(String name) {
628         this.list.add(".by('"+ name + "')");
629         stepIndex++;
630         
631         return this;
632     }
633     
634     /**
635      * {@inheritDoc}
636      */
637     @Override
638     public QueryBuilder<E> simplePath(){
639         this.list.add(".simplePath()");
640         stepIndex++;
641         return this;
642     }
643
644     /**
645      * {@inheritDoc}
646      */
647     @Override
648     public QueryBuilder<Path> path(){
649         this.list.add(".path()");
650         stepIndex++;
651         return (QueryBuilder<Path>)this;
652     }
653     
654     @Override
655     public QueryBuilder<Edge> outE() {
656         this.list.add(".outE()");
657         stepIndex++;
658         
659         return (QueryBuilder<Edge>)this;
660     }
661     
662     @Override
663     public QueryBuilder<Edge> inE() {
664         this.list.add(".inE()");
665         stepIndex++;
666         
667         return (QueryBuilder<Edge>)this;
668     }
669     
670     @Override
671     public QueryBuilder<Vertex> outV() {
672         this.list.add(".outV()");
673         stepIndex++;
674         
675         return (QueryBuilder<Vertex>)this;
676     }
677     
678     @Override
679     public QueryBuilder<Vertex> inV() {
680         this.list.add(".inV()");
681         stepIndex++;
682         
683         return (QueryBuilder<Vertex>)this;
684     }
685     
686     @Override
687     public QueryBuilder<E> not(QueryBuilder<E> builder) {
688         this.list.add(".not(" + "__" + builder.getQuery() + ")");
689         stepIndex++;
690         
691         return this;
692     }
693     
694     @Override
695     public QueryBuilder<E> as(String name) {
696         this.list.add(".as('" + name + "')");
697         stepIndex++;
698         
699         return this;
700     }
701     
702     @Override
703     public QueryBuilder<E> select(String name) {
704         this.list.add(".select('" + name + "')");
705         stepIndex++;
706         
707         return this;
708     }
709     /**
710      * @{inheritDoc}
711      */
712     @Override
713     public QueryBuilder<E> getParentQuery() {
714         return cloneQueryAtStep(parentStepIndex);
715     }
716     
717     @Override
718     public QueryBuilder<E> getContainerQuery() {
719         return cloneQueryAtStep(containerStepIndex);
720     }
721     
722     /**
723      * @{inheritDoc}
724      */
725     @Override
726     public <T2> T2 getQuery() {
727         StringBuilder sb = new StringBuilder();
728         
729         for (String piece : this.list) {
730             sb.append(piece);
731         }
732         
733         return (T2)sb.toString();
734     }
735     
736     /**
737      * @{inheritDoc}
738      */
739     @Override
740     public void markParentBoundary() {
741         parentStepIndex = stepIndex;
742     }
743     
744     @Override
745     public void markContainer() {
746         this.containerStepIndex = stepIndex;
747     }
748     
749     /**
750      * @{inheritDoc}
751      */
752     @Override
753     public Vertex getStart() {
754         return this.start;
755     }
756
757     protected int getParentStepIndex() {
758         return parentStepIndex;
759     }
760
761     protected int getContainerStepIndex() {
762         return containerStepIndex;
763     }
764
765     protected int getStepIndex() {
766         return stepIndex;
767     }
768     
769     private void executeQuery() {
770         String queryString = "g" + Joiner.on("").join(list);
771         Map<String, Object> params = new HashMap<>();
772         if (this.start == null) {
773             params.put("g", source.V());
774         } else {
775             params.put("g", source.V(this.start));
776         }
777         this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
778     }
779     @Override
780     public boolean hasNext() {
781         if (this.completeTraversal == null) {
782             executeQuery();
783         }
784         
785         return this.completeTraversal.hasNext();
786     }
787     
788     @Override
789     public E next() {
790         if (this.completeTraversal == null) {
791             executeQuery();
792         }
793         
794         return (E)this.completeTraversal.next();
795     }
796     
797     @Override
798     public List<E> toList() {
799         if (this.completeTraversal == null) {
800             executeQuery();
801         }
802         
803         return (List<E>)this.completeTraversal.toList();
804     }
805
806     protected QueryBuilder<Edge> has(String key, String value) {
807         this.list.add(HAS + key + "','" + value + "')");
808
809         return (QueryBuilder<Edge>)this;
810     }
811     
812 }