2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.aai.rest.search;
22 import java.io.FileNotFoundException;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
28 import javax.ws.rs.core.MultivaluedHashMap;
29 import javax.ws.rs.core.MultivaluedMap;
31 import org.apache.tinkerpop.gremlin.process.traversal.P;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
34 import org.apache.tinkerpop.gremlin.structure.Graph;
35 import org.apache.tinkerpop.gremlin.structure.Vertex;
36 import org.javatuples.Pair;
37 import org.onap.aai.query.builder.MissingOptionalParameter;
38 import org.onap.aai.rest.dsl.DslQueryProcessor;
39 import org.onap.aai.restcore.util.URITools;
40 import org.onap.aai.serialization.engines.TransactionalGraphEngine;
41 import org.onap.aai.serialization.queryformats.SubGraphStyle;
43 import jersey.repackaged.com.google.common.base.Joiner;
45 public abstract class GenericQueryProcessor {
47 protected final Optional<URI> uri;
48 protected final MultivaluedMap<String, String> queryParams;
49 protected final Optional<Collection<Vertex>> vertices;
50 protected static Pattern p = Pattern.compile("query/(.*+)");
51 protected Optional<String> gremlin;
52 protected final TransactionalGraphEngine dbEngine;
53 protected static GremlinServerSingleton gremlinServerSingleton = GremlinServerSingleton.getInstance();
54 protected static GroovyQueryBuilderSingleton queryBuilderSingleton = GroovyQueryBuilderSingleton.getInstance();
55 protected final boolean isGremlin;
56 /* dsl parameters to store dsl query and to check
57 * if this is a DSL request
59 protected Optional<String> dsl;
60 protected final boolean isDsl ;
62 protected GenericQueryProcessor(Builder builder) {
63 this.uri = builder.getUri();
64 this.dbEngine = builder.getDbEngine();
65 this.vertices = builder.getVertices();
66 this.gremlin = builder.getGremlin();
67 this.isGremlin = builder.isGremlin();
68 this.dsl = builder.getDsl();
69 this.isDsl = builder.isDsl();
71 if (uri.isPresent()) {
72 queryParams = URITools.getQueryMap(uri.get());
74 queryParams = new MultivaluedHashMap<>();
78 protected abstract GraphTraversal<?,?> runQuery(String query, Map<String, Object> params);
80 protected List<Object> processSubGraph(SubGraphStyle style, GraphTraversal<?,?> g) {
81 final List<Object> resultVertices = new Vector<>();
84 if (SubGraphStyle.prune.equals(style) || SubGraphStyle.star.equals(style)) {
86 if (SubGraphStyle.prune.equals(style)) {
87 g.where(__.otherV().where(P.within("x")));
89 g.dedup().subgraph("subGraph").cap("subGraph").map(x -> (Graph)x.get()).next().traversal().V().forEachRemaining(x -> {
90 resultVertices.add(x);
93 resultVertices.addAll(g.toList());
95 return resultVertices;
98 public List<Object> execute(SubGraphStyle style) throws FileNotFoundException {
99 final List<Object> resultVertices;
101 Pair<String, Map<String, Object>> tuple = this.createQuery();
102 String query = tuple.getValue0();
103 Map<String, Object> params = tuple.getValue1();
105 if (query.equals("") && (vertices.isPresent() && vertices.get().isEmpty())) {
106 //nothing to do, just exit
107 return new ArrayList<>();
109 GraphTraversal<?,?> g = this.runQuery(query, params);
111 resultVertices = this.processSubGraph(style, g);
113 return resultVertices;
116 protected Pair<String, Map<String, Object>> createQuery() {
117 Map<String, Object> params = new HashMap<>();
119 if (this.isGremlin) {
120 query = gremlin.get();
122 }else if (this.isDsl) {
123 String dslUserQuery = dsl.get();
124 String dslQuery = new DslQueryProcessor.Builder().build(dslUserQuery);
126 query = queryBuilderSingleton.executeTraversal(dbEngine, dslQuery, params);
127 String startPrefix = "g.V()";
128 query = startPrefix + query;
131 Matcher m = p.matcher(uri.get().getPath());
132 String queryName = "";
133 List<String> optionalParameters = Collections.emptyList();
135 queryName = m.group(1);
136 CustomQueryConfig queryConfig = gremlinServerSingleton.getCustomQueryConfig(queryName);
137 if ( queryConfig != null ) {
138 query = queryConfig.getQuery();
139 optionalParameters = queryConfig.getQueryOptionalProperties();
143 for (String key : queryParams.keySet()) {
144 params.put(key, queryParams.getFirst(key));
145 if ( optionalParameters.contains(key) ){
146 optionalParameters.remove(key);
150 if (!optionalParameters.isEmpty()){
151 MissingOptionalParameter missingParameter = MissingOptionalParameter.getInstance();
152 for ( String key : optionalParameters ) {
153 params.put(key, missingParameter);
157 if (vertices.isPresent() && !vertices.get().isEmpty()) {
159 // Get the vertices and convert them into object array
160 // The reason for this was .V() takes in an array of objects
161 // not a list of objects so that needs to be converted
162 // Also instead of statically creating the list which is a bad practice
163 // We are binding the array dynamically to the groovy processor correctly
164 // This will fix the memory issue of the method size too big
165 // as statically creating a list string and passing is not appropriate
166 params.put("startVertexes", vertices.get().toArray());
171 query = queryBuilderSingleton.executeTraversal(dbEngine, query, params);
174 String startPrefix = "g.V(startVertexes)";
176 if (!"".equals(query)) {
177 query = startPrefix + query;
185 return new Pair<>(query, params);
188 public static class Builder {
190 private final TransactionalGraphEngine dbEngine;
191 private Optional<URI> uri = Optional.empty();
192 private Optional<String> gremlin = Optional.empty();
193 private boolean isGremlin = false;
194 private Optional<Collection<Vertex>> vertices = Optional.empty();
195 private QueryProcessorType processorType = QueryProcessorType.GREMLIN_SERVER;
197 private Optional<String> dsl = Optional.empty();
198 private boolean isDsl = false;
200 public Builder(TransactionalGraphEngine dbEngine) {
201 this.dbEngine = dbEngine;
204 public Builder queryFrom(URI uri) {
205 this.uri = Optional.of(uri);
206 this.isGremlin = false;
210 public Builder startFrom(Collection<Vertex> vertices) {
211 this.vertices = Optional.of(vertices);
215 public Builder queryFrom( String query, String queryType) {
217 if(queryType.equals("gremlin")){
218 this.gremlin = Optional.of(query);
219 this.isGremlin = true;
221 if(queryType.equals("dsl")){
222 this.dsl = Optional.of(query);
228 public Builder processWith(QueryProcessorType type) {
229 this.processorType = type;
232 public TransactionalGraphEngine getDbEngine() {
236 public Optional<URI> getUri() {
240 public Optional<String> getGremlin() {
244 public boolean isGremlin() {
248 public Optional<String> getDsl() {
252 public boolean isDsl() {
256 public Optional<Collection<Vertex>> getVertices() {
260 public QueryProcessorType getProcessorType() {
261 return processorType;
264 public GenericQueryProcessor create() {
266 if (this.getProcessorType().equals(QueryProcessorType.GREMLIN_SERVER)) {
267 return new GremlinServerImpl(this);
268 } else if (this.getProcessorType().equals(QueryProcessorType.LOCAL_GROOVY)) {
269 return new GroovyShellImpl(this);
271 return new GremlinServerImpl(this);