14e218f3242b598fcb0faa9a788058fe6e5fe9e8
[aai/traversal.git] / aai-traversal / src / main / java / org / onap / aai / rest / search / GenericQueryProcessor.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright (C) 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.rest.search;
23
24 import java.io.FileNotFoundException;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Vector;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 import javax.ws.rs.core.MultivaluedHashMap;
38 import javax.ws.rs.core.MultivaluedMap;
39
40 import org.apache.tinkerpop.gremlin.process.traversal.P;
41 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
42 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
43 import org.apache.tinkerpop.gremlin.structure.Graph;
44 import org.apache.tinkerpop.gremlin.structure.Vertex;
45 import org.javatuples.Pair;
46 import org.onap.aai.query.builder.MissingOptionalParameter;
47 import org.onap.aai.restcore.util.URITools;
48 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
49 import org.onap.aai.serialization.queryformats.SubGraphStyle;
50
51 import jersey.repackaged.com.google.common.base.Joiner;
52
53 public abstract class GenericQueryProcessor {
54
55         protected final Optional<URI> uri;
56         protected final MultivaluedMap<String, String> queryParams;
57         protected final Optional<Collection<Vertex>> vertices;
58         protected static Pattern p = Pattern.compile("query/(.*+)");
59         protected Optional<String> gremlin;
60         protected final TransactionalGraphEngine dbEngine;
61         protected static GremlinServerSingleton gremlinServerSingleton = GremlinServerSingleton.getInstance();
62         protected static GroovyQueryBuilderSingleton queryBuilderSingleton = GroovyQueryBuilderSingleton.getInstance();
63         protected final boolean isGremlin;
64         
65         protected GenericQueryProcessor(Builder builder) {
66                 this.uri = builder.getUri();
67                 this.dbEngine = builder.getDbEngine();
68                 this.vertices = builder.getVertices();
69                 this.gremlin = builder.getGremlin();
70                 this.isGremlin = builder.isGremlin();
71                 if (uri.isPresent()) {
72                         queryParams = URITools.getQueryMap(uri.get());
73                 } else {
74                         queryParams = new MultivaluedHashMap<>();
75                 }
76         }
77         
78         protected abstract GraphTraversal<?,?> runQuery(String query, Map<String, Object> params);
79         
80         protected List<Object> processSubGraph(SubGraphStyle style, GraphTraversal<?,?> g) {
81                 final List<Object> resultVertices = new Vector<>();
82                 g.store("x");
83                 
84                 if (SubGraphStyle.prune.equals(style) || SubGraphStyle.star.equals(style)) {
85                         g.barrier().bothE();
86                         if (SubGraphStyle.prune.equals(style)) {
87                                 g.where(__.otherV().where(P.within("x")));
88                         }
89                         g.dedup().subgraph("subGraph").cap("subGraph").map(x -> (Graph)x.get()).next().traversal().V().forEachRemaining(x -> {
90                                 resultVertices.add(x);
91                         });
92                 } else {
93                         resultVertices.addAll(g.toList());
94                 }
95                 return resultVertices;
96         }
97         
98         public List<Object> execute(SubGraphStyle style) throws FileNotFoundException {
99                 final List<Object> resultVertices;
100
101                 Pair<String, Map<String, Object>> tuple = this.createQuery();
102                 String query = tuple.getValue0();
103                 Map<String, Object> params = tuple.getValue1();
104
105                 if (query.equals("") && (vertices.isPresent() && vertices.get().isEmpty())) {
106                         //nothing to do, just exit
107                         return new ArrayList<>();
108                 }
109                 GraphTraversal<?,?> g = this.runQuery(query, params);
110                 
111                 resultVertices = this.processSubGraph(style, g);
112                 
113                 return resultVertices;
114         }
115         
116         protected Pair<String, Map<String, Object>> createQuery() {
117                 Map<String, Object> params = new HashMap<>();
118                 String query = "";
119                 if (!this.isGremlin) {
120                         Matcher m = p.matcher(uri.get().getPath());
121                         String queryName = "";
122                         List<String> optionalParameters = Collections.emptyList();
123                         if (m.find()) {
124                                 queryName = m.group(1);
125                                 CustomQueryConfig queryConfig = gremlinServerSingleton.getCustomQueryConfig(queryName);
126                                 if ( queryConfig != null ) {
127                                         query = queryConfig.getQuery();
128                                         optionalParameters = queryConfig.getQueryOptionalProperties();
129                                 }
130                         }
131                         
132                         for (String key : queryParams.keySet()) {
133                                 params.put(key, queryParams.getFirst(key));
134                                 if ( optionalParameters.contains(key) ){
135                                         optionalParameters.remove(key);
136                                 }
137                         }
138                         
139                         if (!optionalParameters.isEmpty()){
140                                 MissingOptionalParameter missingParameter = MissingOptionalParameter.getInstance();
141                                 for ( String key : optionalParameters ) {
142                                         params.put(key, missingParameter);
143                                 }
144                         }
145                         
146                         List<Object> ids = new ArrayList<>();
147                         
148                         if (vertices.isPresent() && !vertices.get().isEmpty()) {
149                                 for (Vertex v : vertices.get()) {
150                                         ids.add(v.id());
151                                 }
152                                 if (query == null) {
153                                         query = "";
154                                 } else {
155                                         query = queryBuilderSingleton.executeTraversal(dbEngine, query, params);
156                                 }
157                                 StringBuilder sb = new StringBuilder();
158                                 sb.append("[");
159                                 sb.append(Joiner.on(",").join(ids));
160                                 sb.append("]");
161                                 String startPrefix = "aaiStartQuery = " + sb.toString() + " as Object[];g.V(aaiStartQuery)";
162                                 if (!"".equals(query)) {
163                                         query = startPrefix + query;
164                                 } else {
165                                         query = startPrefix;
166                                 }
167                         }
168                         
169                 } else {
170                         query = gremlin.get();
171                 }
172                 
173                 return new Pair<>(query, params);
174         }
175         
176         public static class Builder {
177
178                 private final TransactionalGraphEngine dbEngine;
179                 private Optional<URI> uri = Optional.empty();
180                 private Optional<String> gremlin = Optional.empty();
181                 private boolean isGremlin = false;
182                 private Optional<Collection<Vertex>> vertices = Optional.empty();
183                 private QueryProcessorType processorType = QueryProcessorType.GREMLIN_SERVER;
184                 
185                 public Builder(TransactionalGraphEngine dbEngine) {
186                         this.dbEngine = dbEngine;
187                 }
188                 
189                 public Builder queryFrom(URI uri) {
190                         this.uri = Optional.of(uri);
191                         this.isGremlin = false;
192                         return this;
193                 }
194                 
195                 public Builder startFrom(Collection<Vertex> vertices) {
196                         this.vertices = Optional.of(vertices);
197                         return this;
198                 }
199                 
200                 public Builder queryFrom(String gremlin) {
201                         this.gremlin = Optional.of(gremlin);
202                         this.isGremlin = true;
203                         return this;
204                 }
205                 
206                 public Builder processWith(QueryProcessorType type) {
207                         this.processorType = type;
208                         return this;
209                 }
210                 public TransactionalGraphEngine getDbEngine() {
211                         return dbEngine;
212                 }
213
214                 public Optional<URI> getUri() {
215                         return uri;
216                 }
217
218                 public Optional<String> getGremlin() {
219                         return gremlin;
220                 }
221
222                 public boolean isGremlin() {
223                         return isGremlin;
224                 }
225
226                 public Optional<Collection<Vertex>> getVertices() {
227                         return vertices;
228                 }
229                 
230                 public QueryProcessorType getProcessorType() {
231                         return processorType;
232                 }
233                 
234                 public GenericQueryProcessor create() {
235                         
236                         if (this.getProcessorType().equals(QueryProcessorType.GREMLIN_SERVER)) {
237                                 return new GremlinServerImpl(this);
238                         } else if (this.getProcessorType().equals(QueryProcessorType.LOCAL_GROOVY)) {
239                                 return new GroovyShellImpl(this);
240                         } else {
241                                 return new GremlinServerImpl(this);
242                         }
243                 }
244                 
245         }
246 }