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