Add gremlin-based pagination to aai-common
[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
23 package org.onap.aai.query.builder;
24
25 import com.google.common.base.Joiner;
26 import com.google.common.collect.ArrayListMultimap;
27 import com.google.common.collect.Multimap;
28
29 import java.util.*;
30
31 import org.apache.tinkerpop.gremlin.process.traversal.Path;
32 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
35 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
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.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.exceptions.AAIException;
45 import org.onap.aai.introspection.Introspector;
46 import org.onap.aai.introspection.Loader;
47 import org.onap.aai.query.entities.PaginationResult;
48 import org.onap.aai.restcore.search.GremlinGroovyShell;
49 import org.onap.aai.schema.enums.ObjectMetadata;
50 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * The Class GremlinQueryBuilder.
56  */
57 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
58
59     private static final String ARGUMENT2 = "#!#argument#!#";
60     private static final String HAS = ".has('";
61     private static final String SINGLE_QUOTE = "'";
62     private static final String ESCAPE_SINGLE_QUOTE = "\\\'";
63     private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell();
64     private GraphTraversal<?, ?> completeTraversal = null;
65     protected List<String> list = null;
66
67     private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class);
68
69     /**
70      * Instantiates a new gremlin query builder.
71      *
72      * @param loader the loader
73      */
74     public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
75         super(loader, source);
76         list = new ArrayList<>();
77     }
78
79     /**
80      * Instantiates a new gremlin query builder.
81      *
82      * @param loader the loader
83      * @param start the start
84      */
85     public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
86         super(loader, source, start);
87         list = new ArrayList<>();
88     }
89
90     @Override
91     public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
92         // TODO not implemented because this is implementation is no longer used
93         this.createKeyQuery(obj);
94         this.createContainerQuery(obj);
95         return (QueryBuilder<Vertex>) this;
96     }
97
98     @Override
99     protected void vertexHas(String key, Object value) {
100         list.add(HAS + key + "', " + value + ")");
101     }
102
103     @Override
104     protected void vertexHasNot(String key) {
105         list.add(".hasNot('" + key + "')");
106
107     }
108
109     @Override
110     protected void vertexHas(String key) {
111         list.add(HAS + key + "')");
112     }
113
114     /**
115      * @{inheritDoc}
116      */
117     @Override
118     public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
119
120         String term = "";
121         if (value != null && !(value instanceof String)) {
122             String valueString = value.toString();
123
124             if (valueString.indexOf('\'') != -1) {
125                 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
126             }
127             LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
128             term = value.toString();
129         } else if (value != null && value instanceof String) {
130             String valueString = value.toString();
131
132             if (valueString.indexOf('\'') != -1) {
133                 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
134             }
135             LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
136             term = "'" + value + "'";
137         } else {
138             term = "'" + value + "'";
139         }
140         this.vertexHas(key, term);
141         stepIndex++;
142         return (QueryBuilder<Vertex>) this;
143     }
144
145     /**
146      * @{inheritDoc}
147      */
148     @Override
149     public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
150         this.vertexHas(key, value);
151         stepIndex++;
152         return (QueryBuilder<Vertex>) this;
153     }
154
155     @Override
156     public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
157
158         if (value != null && !"".equals(value)) {
159             boolean bValue = false;
160             if (value instanceof String) {
161                 bValue = Boolean.valueOf(value.toString());
162             } else if (value instanceof Boolean) {
163                 bValue = (Boolean) value;
164             }
165
166             this.vertexHas(key, bValue);
167             stepIndex++;
168         }
169         return (QueryBuilder<Vertex>) this;
170     }
171
172     /**
173      * @{inheritDoc}
174      */
175     @Override
176     public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
177
178         String predicate = "P.within(#!#argument#!#)";
179         List<String> arguments = new ArrayList<>();
180         for (Object item : values) {
181             if (item != null && !(item instanceof String)) {
182                 arguments.add(item.toString());
183             } else {
184                 arguments.add("'" + item + "'");
185             }
186         }
187         String argument = Joiner.on(",").join(arguments);
188         predicate = predicate.replace(ARGUMENT2, argument);
189         this.vertexHas(key, predicate);
190         stepIndex++;
191         return (QueryBuilder<Vertex>) this;
192     }
193
194     /**
195      * @{inheritDoc}
196      */
197     @Override
198     public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
199         ArrayList<String> arguments = new ArrayList<>(Arrays.asList(value.split(",")));
200         // add the single quotes
201         for (int i = 0; i < arguments.size(); i++) {
202             if (arguments.get(i) != null && !arguments.get(i).startsWith("'") && !arguments.get(i).endsWith("'")) {
203                 arguments.set(i, "'" + arguments.get(i).trim() + "'");
204             } else {
205                 arguments.set(i, arguments.get(i).trim());
206             }
207         }
208         String predicate = "P.within(#!#argument#!#)";
209         String argument = Joiner.on(",").join(arguments);
210         predicate = predicate.replace(ARGUMENT2, argument);
211         this.vertexHas(key, predicate);
212         stepIndex++;
213         return (QueryBuilder<Vertex>) this;
214     }
215
216     /**
217      * @{inheritDoc}
218      */
219     @Override
220     public QueryBuilder<Vertex> getVerticesByProperty(String key) {
221         this.vertexHas(key);
222         stepIndex++;
223         return (QueryBuilder<Vertex>) this;
224     }
225
226     /**
227      * @{inheritDoc}
228      */
229     @Override
230     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
231         this.vertexHasNot(key);
232         stepIndex++;
233         return (QueryBuilder<Vertex>) this;
234     }
235
236     /**
237      * @{inheritDoc}
238      */
239     @Override
240     public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
241         String term = "";
242         String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
243         if (value != null && !(value instanceof String)) {
244             term = value.toString();
245         } else {
246             term = "'" + value + "'";
247         }
248         predicate = predicate.replace(ARGUMENT2, term);
249         this.vertexHas(key, predicate);
250         stepIndex++;
251         return (QueryBuilder<Vertex>) this;
252     }
253
254     /**
255      * @{inheritDoc}
256      */
257     @Override
258     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
259
260         String term = "";
261         String predicate = "P.neq(#!#argument#!#)";
262         if (value != null && !(value instanceof String)) {
263             term = value.toString();
264         } else {
265             term = "'" + value + "'";
266         }
267         predicate = predicate.replace(ARGUMENT2, term);
268         this.vertexHas(key, predicate);
269         stepIndex++;
270         return (QueryBuilder<Vertex>) this;
271     }
272
273     /**
274      * @{inheritDoc}
275      */
276     @Override
277     public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
278
279         String predicate = "P.without(#!#argument#!#)";
280         List<String> arguments = new ArrayList<>();
281         for (Object item : values) {
282             if (item != null && !(item instanceof String)) {
283                 arguments.add(item.toString());
284             } else {
285                 arguments.add("'" + item + "'");
286             }
287         }
288         String argument = Joiner.on(",").join(arguments);
289         predicate = predicate.replace(ARGUMENT2, argument);
290         this.vertexHas(key, predicate);
291         stepIndex++;
292         return (QueryBuilder<Vertex>) this;
293     }
294
295     @Override
296     public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
297         String predicate = "P.gte(#!#argument1#!#)";
298         String term;
299         if (value != null && !(value instanceof String)) {
300             term = value.toString();
301         } else {
302             term = "'" + value + "'";
303         }
304         predicate = predicate.replace("#!#argument1#!#", term);
305         this.vertexHas(key, predicate);
306         stepIndex++;
307         return (QueryBuilder<Vertex>) this;
308     }
309
310     @Override
311     public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
312         String predicate = "P.lte(#!#argument1#!#)";
313         String term;
314         if (value != null && !(value instanceof String)) {
315             term = value.toString();
316         } else {
317             term = "'" + value + "'";
318         }
319         predicate = predicate.replace("#!#argument1#!#", term);
320         this.vertexHas(key, predicate);
321         stepIndex++;
322         return (QueryBuilder<Vertex>) this;
323     }
324
325     /**
326      * @{inheritDoc}
327      */
328     @Override
329     public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
330         // TODO
331         return (QueryBuilder<Vertex>) this;
332     }
333
334     /**
335      * @{inheritDoc}
336      */
337     @Override
338     public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
339
340         for (Map.Entry<String, String> es : map.entrySet()) {
341             // TODO what is this and where is it used - need to check
342             list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
343             stepIndex++;
344         }
345         list.add(".has('aai-node-type', '" + type + "')");
346         stepIndex++;
347         return (QueryBuilder<Vertex>) this;
348     }
349
350     /**
351      * @{inheritDoc}
352      */
353     @Override
354     public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
355         Set<String> keys = obj.getKeys();
356
357         for (String key : keys) {
358
359             this.getVerticesByProperty(key, obj.<Object>getValue(key));
360
361         }
362         return (QueryBuilder<Vertex>) this;
363     }
364
365     /**
366      * @throws NoEdgeRuleFoundException
367      * @throws AAIException
368      * @{inheritDoc}
369      */
370     @Override
371     public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
372             throws AAIException {
373         String parentName = parent.getDbName();
374         String childName = child.getDbName();
375         if (parent.isContainer()) {
376             parentName = parent.getChildDBName();
377         }
378         if (child.isContainer()) {
379             childName = child.getChildDBName();
380         }
381         this.edgeQueryToVertex(type, parentName, childName, null);
382         return this;
383
384     }
385
386     @Override
387     public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
388             throws AAIException {
389         String parentName = parent.getDbName();
390         String childName = child.getDbName();
391         if (parent.isContainer()) {
392             parentName = parent.getChildDBName();
393         }
394         if (child.isContainer()) {
395             childName = child.getChildDBName();
396         }
397         this.edgeQueryToVertex(type, parentName, childName, null, true);
398         return this;
399     }
400
401     /**
402      *
403      * @{inheritDoc}
404      */
405     @Override
406     public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
407             List<String> labels) throws AAIException {
408         String parentName = out.getDbName();
409         String childName = in.getDbName();
410         if (out.isContainer()) {
411             parentName = out.getChildDBName();
412         }
413         if (in.isContainer()) {
414             childName = in.getChildDBName();
415         }
416         this.edgeQueryToVertex(type, parentName, childName, labels);
417         return (QueryBuilder<Vertex>) this;
418     }
419
420     public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
421             List<String> labels) throws AAIException {
422         this.edgeQuery(type, outNodeType, inNodeType, labels);
423         return (QueryBuilder<Edge>) this;
424     }
425
426     private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels)
427             throws AAIException {
428         this.edgeQueryToVertex(type, outType, inType, labels, false);
429     }
430
431     /**
432      * Edge query.
433      *
434      * @param outType the out type
435      * @param inType the in type
436      * @throws NoEdgeRuleFoundException
437      * @throws AAIException
438      */
439     private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels,
440             boolean isPrivateEdge) throws AAIException {
441         markParentBoundary();
442         Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
443         EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
444
445         try {
446             if (labels == null) {
447                 rules.putAll(edgeRules.getRules(qB.build()));
448             } else {
449                 for (String label : labels) {
450                     rules.putAll(edgeRules.getRules(qB.label(label).build()));
451                 }
452             }
453         } catch (EdgeRuleNotFoundException e) {
454             throw new NoEdgeRuleFoundException(e);
455         }
456
457         final List<String> inLabels = new ArrayList<>();
458         final List<String> outLabels = new ArrayList<>();
459
460         for (EdgeRule rule : rules.values()) {
461             if (labels != null && !labels.contains(rule.getLabel())) {
462                 return;
463             } else {
464                 if (Direction.IN.equals(rule.getDirection())) {
465                     inLabels.add(rule.getLabel());
466                     if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
467                         outLabels.add(rule.getLabel());
468                     }
469                 } else {
470                     outLabels.add(rule.getLabel());
471                     if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
472                         inLabels.add(rule.getLabel());
473                     }
474                 }
475             }
476         }
477
478         if (inLabels.isEmpty() && outLabels.isEmpty()) {
479             throw new NoEdgeRuleFoundException(
480                     "no " + type.toString() + " edge rule between " + outType + " and " + inType);
481         } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
482             list.add(".out('" + String.join("','", outLabels) + "')");
483         } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
484             list.add(".in('" + String.join("','", inLabels) + "')");
485         } else {
486             list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('"
487                     + String.join("','", outLabels) + "'))");
488         }
489         stepIndex++;
490         list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
491         stepIndex++;
492
493     }
494
495     /**
496      * Edge query.
497      *
498      * @param outType the out type
499      * @param inType the in type
500      * @throws NoEdgeRuleFoundException
501      * @throws AAIException
502      */
503     private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
504         markParentBoundary();
505         Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
506         EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
507         try {
508             if (labels == null) {
509                 rules.putAll(edgeRules.getRules(qB.build()));
510             } else {
511                 for (String label : labels) {
512                     rules.putAll(edgeRules.getRules(qB.label(label).build()));
513                 }
514             }
515         } catch (EdgeRuleNotFoundException e) {
516             throw new NoEdgeRuleFoundException(e);
517         }
518
519         final List<String> inLabels = new ArrayList<>();
520         final List<String> outLabels = new ArrayList<>();
521
522         for (EdgeRule rule : rules.values()) {
523             if (labels != null && !labels.contains(rule.getLabel())) {
524                 return;
525             } else {
526                 if (Direction.IN.equals(rule.getDirection())) {
527                     inLabels.add(rule.getLabel());
528                 } else {
529                     outLabels.add(rule.getLabel());
530                 }
531             }
532         }
533
534         if (inLabels.isEmpty() && outLabels.isEmpty()) {
535             throw new NoEdgeRuleFoundException(
536                     "no " + type.toString() + " edge rule between " + outType + " and " + inType);
537         } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
538             list.add(".outE('" + String.join("','", outLabels) + "')");
539         } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
540             list.add(".inE('" + String.join("','", inLabels) + "')");
541         } else {
542             list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('"
543                     + String.join("','", outLabels) + "'))");
544         }
545
546         stepIndex++;
547
548     }
549
550     @Override
551     public QueryBuilder<E> limit(long amount) {
552         list.add(".limit(" + amount + ")");
553         return this;
554     }
555
556     /**
557      * @{inheritDoc}
558      */
559     @Override
560     public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
561         String type = obj.getChildDBName();
562         String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
563         if (abstractType != null) {
564             String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
565             String[] wrapped = new String[inheritors.length];
566             StringBuilder command = new StringBuilder();
567             command.append("P.within(");
568             for (int i = 0; i < inheritors.length; i++) {
569                 wrapped[i] = "'" + inheritors[i] + "'";
570             }
571             command.append(Joiner.on(",").join(wrapped));
572             command.append(")");
573             list.add(".has('aai-node-type', " + command + ")");
574
575         } else {
576             list.add(".has('aai-node-type', '" + type + "')");
577         }
578         stepIndex++;
579         this.markContainer();
580         return (QueryBuilder<Vertex>) this;
581     }
582
583     @Override
584     public QueryBuilder<E> union(QueryBuilder<E>... builder) {
585         markParentBoundary();
586         String[] traversals = new String[builder.length];
587         StringBuilder command = new StringBuilder();
588         for (int i = 0; i < builder.length; i++) {
589             traversals[i] = "__" + builder[i].getQuery();
590         }
591         command.append(".union(");
592         command.append(Joiner.on(",").join(traversals));
593         command.append(")");
594         list.add(command.toString());
595         stepIndex++;
596
597         return this;
598     }
599
600     @Override
601     public QueryBuilder<E> where(QueryBuilder<E>... builder) {
602         markParentBoundary();
603         List<String> traversals = new ArrayList<>();
604         for (int i = 0; i < builder.length; i++) {
605             traversals.add(".where(__" + builder[i].getQuery() + ")");
606             stepIndex++;
607         }
608         list.addAll(traversals);
609
610         return this;
611     }
612
613     @Override
614     public QueryBuilder<E> or(QueryBuilder<E>... builder) {
615         markParentBoundary();
616         String[] traversals = new String[builder.length];
617         StringBuilder command = new StringBuilder();
618         for (int i = 0; i < builder.length; i++) {
619             traversals[i] = "__" + builder[i].getQuery();
620         }
621         command.append(".or(");
622         command.append(Joiner.on(",").join(traversals));
623         command.append(")");
624         list.add(command.toString());
625         stepIndex++;
626
627         return this;
628     }
629
630     @Override
631     public QueryBuilder<E> store(String name) {
632         this.list.add(".store('" + name + "')");
633         stepIndex++;
634
635         return this;
636     }
637
638     @Override
639     public QueryBuilder<E> cap(String name) {
640         this.list.add(".cap('" + name + "')");
641         stepIndex++;
642
643         return this;
644     }
645
646     @Override
647     public QueryBuilder<E> unfold() {
648         this.list.add(".unfold()");
649         stepIndex++;
650
651         return this;
652     }
653
654     @Override
655     public QueryBuilder<E> fold() {
656         this.list.add(".fold()");
657         stepIndex++;
658         return this;
659     }
660
661     @Override
662     public QueryBuilder<E> id() {
663         this.list.add(".id()");
664         stepIndex++;
665         return this;
666     }
667
668     @Override
669     public QueryBuilder<E> dedup() {
670         this.list.add(".dedup()");
671         stepIndex++;
672
673         return this;
674     }
675
676     @Override
677     public QueryBuilder<E> emit() {
678         this.list.add(".emit()");
679         stepIndex++;
680
681         return this;
682     }
683
684     @Override
685     public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
686         this.list.add(".repeat(__" + builder.getQuery() + ")");
687         stepIndex++;
688
689         return this;
690     }
691
692     @Override
693     public QueryBuilder<E> until(QueryBuilder<E> builder) {
694         this.list.add(".until(__" + builder.getQuery() + ")");
695         stepIndex++;
696
697         return this;
698     }
699
700     @Override
701     public QueryBuilder<E> groupCount() {
702         this.list.add(".groupCount()");
703         stepIndex++;
704
705         return this;
706     }
707
708     @Override
709     public QueryBuilder<E> both() {
710         this.list.add(".both()");
711         stepIndex++;
712
713         return this;
714     }
715
716     @Override
717     public QueryBuilder<Tree> tree() {
718         this.list.add(".tree()");
719         stepIndex++;
720
721         return (QueryBuilder<Tree>) this;
722     }
723
724     @Override
725     public QueryBuilder<E> by(String name) {
726         this.list.add(".by('" + name + "')");
727         stepIndex++;
728
729         return this;
730     }
731
732     @Override
733     public QueryBuilder<E> valueMap() {
734         this.list.add(".valueMap()");
735         stepIndex++;
736
737         return this;
738     }
739
740     @Override
741     public QueryBuilder<E> valueMap(String... names) {
742         String stepString = ".valueMap('";
743         for (int i = 0; i < names.length; i++) {
744             stepString = stepString + names[i] + "'";
745             if (i != (names.length - 1)) {
746                 stepString = stepString + ",'";
747             }
748         }
749         stepString = stepString + ")";
750         this.list.add(stepString);
751         stepIndex++;
752
753         return this;
754     }
755
756     /**
757      * {@inheritDoc}
758      */
759     @Override
760     public QueryBuilder<E> simplePath() {
761         this.list.add(".simplePath()");
762         stepIndex++;
763         return this;
764     }
765
766     /**
767      * {@inheritDoc}
768      */
769     @Override
770     public QueryBuilder<Path> path() {
771         this.list.add(".path()");
772         stepIndex++;
773         return (QueryBuilder<Path>) this;
774     }
775
776     @Override
777     public QueryBuilder<Edge> outE() {
778         this.list.add(".outE()");
779         stepIndex++;
780
781         return (QueryBuilder<Edge>) this;
782     }
783
784     @Override
785     public QueryBuilder<Edge> inE() {
786         this.list.add(".inE()");
787         stepIndex++;
788
789         return (QueryBuilder<Edge>) this;
790     }
791
792     @Override
793     public QueryBuilder<Vertex> outV() {
794         this.list.add(".outV()");
795         stepIndex++;
796
797         return (QueryBuilder<Vertex>) this;
798     }
799
800     @Override
801     public QueryBuilder<Vertex> inV() {
802         this.list.add(".inV()");
803         stepIndex++;
804
805         return (QueryBuilder<Vertex>) this;
806     }
807
808     @Override
809     public QueryBuilder<E> not(QueryBuilder<E> builder) {
810         this.list.add(".not(" + "__" + builder.getQuery() + ")");
811         stepIndex++;
812
813         return this;
814     }
815
816     @Override
817     public QueryBuilder<E> as(String name) {
818         this.list.add(".as('" + name + "')");
819         stepIndex++;
820
821         return this;
822     }
823
824     @Override
825     public QueryBuilder<E> select(String name) {
826         this.list.add(".select('" + name + "')");
827         stepIndex++;
828
829         return this;
830     }
831
832     @Override
833     public QueryBuilder<E> select(Pop pop, String name) {
834         this.list.add(".select(Pop." + pop.toString() + ",'" + name + "')");
835         stepIndex++;
836
837         return this;
838     }
839
840     @Override
841     public QueryBuilder<E> select(String... names) {
842         String stepString = ".select('";
843         for (int i = 0; i < names.length; i++) {
844             stepString = stepString + names[i] + "'";
845             if (i != (names.length - 1)) {
846                 stepString = stepString + ",'";
847             }
848         }
849         stepString = stepString + ")";
850         this.list.add(stepString);
851         stepIndex++;
852
853         return this;
854     }
855
856     /**
857      * @{inheritDoc}
858      */
859     @Override
860     public QueryBuilder<E> getParentQuery() {
861         return cloneQueryAtStep(parentStepIndex);
862     }
863
864     @Override
865     public QueryBuilder<E> getContainerQuery() {
866         return cloneQueryAtStep(containerStepIndex);
867     }
868
869     /**
870      * @{inheritDoc}
871      */
872     @Override
873     public <T2> T2 getQuery() {
874         StringBuilder sb = new StringBuilder();
875
876         for (String piece : this.list) {
877             sb.append(piece);
878         }
879
880         return (T2) sb.toString();
881     }
882
883     /**
884      * @{inheritDoc}
885      */
886     @Override
887     public void markParentBoundary() {
888         parentStepIndex = stepIndex;
889     }
890
891     @Override
892     public void markContainer() {
893         this.containerStepIndex = stepIndex;
894     }
895
896     /**
897      * @{inheritDoc}
898      */
899     @Override
900     public Vertex getStart() {
901         return this.start;
902     }
903
904     protected int getParentStepIndex() {
905         return parentStepIndex;
906     }
907
908     protected int getContainerStepIndex() {
909         return containerStepIndex;
910     }
911
912     protected int getStepIndex() {
913         return stepIndex;
914     }
915
916     private void executeQuery() {
917         String queryString = "g" + Joiner.on("").join(list);
918         Map<String, Object> params = new HashMap<>();
919         if (this.start == null) {
920             params.put("g", source.V());
921         } else {
922             params.put("g", source.V(this.start));
923         }
924         this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
925     }
926
927     @Override
928     public boolean hasNext() {
929         if (this.completeTraversal == null) {
930             executeQuery();
931         }
932
933         return this.completeTraversal.hasNext();
934     }
935
936     @Override
937     public E next() {
938         if (this.completeTraversal == null) {
939             executeQuery();
940         }
941
942         return (E) this.completeTraversal.next();
943     }
944
945     @Override
946     public List<E> toList() {
947         if (this.completeTraversal == null) {
948             executeQuery();
949         }
950
951         return (List<E>) this.completeTraversal.toList();
952     }
953
954     protected QueryBuilder<Edge> has(String key, String value) {
955         this.list.add(HAS + key + "','" + value + "')");
956
957         return (QueryBuilder<Edge>) this;
958     }
959
960     @Override
961     public PaginationResult<E> toPaginationResult(Pageable pageable) {
962         // TODO Auto-generated method stub
963         throw new UnsupportedOperationException("Unimplemented method 'toPaginationResult'");
964     }
965
966     @Override
967     public QueryBuilder<E> sort(Sort sort) {
968         // TODO Auto-generated method stub
969         throw new UnsupportedOperationException("Unimplemented method 'sort'");
970     }
971
972 }