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