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