11164d96e097637311df4387d266f5990cd7bdec
[aai/aai-common.git] / aai-core / src / main / java / org / openecomp / aai / query / builder / GraphTraversalBuilder.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * org.openecomp.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
21 package org.openecomp.aai.query.builder;
22
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.LinkedHashMap;
26 import java.util.List;
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.DefaultGraphTraversal;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
35 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
36 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
37 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
38 import org.apache.tinkerpop.gremlin.structure.Direction;
39 import org.apache.tinkerpop.gremlin.structure.Edge;
40 import org.apache.tinkerpop.gremlin.structure.Vertex;
41
42 import org.openecomp.aai.db.props.AAIProperties;
43 import org.openecomp.aai.exceptions.AAIException;
44 import org.openecomp.aai.introspection.Introspector;
45 import org.openecomp.aai.introspection.Loader;
46 import org.openecomp.aai.schema.enums.ObjectMetadata;
47 import org.openecomp.aai.schema.enums.PropertyMetadata;
48 import org.openecomp.aai.serialization.db.EdgeRule;
49 import org.openecomp.aai.serialization.db.EdgeRules;
50 import org.openecomp.aai.serialization.db.EdgeType;
51 import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
52
53 /**
54  * The Class GraphTraversalBuilder.
55  */
56 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
57
58         protected GraphTraversal<Vertex, E> traversal = null;
59         protected Admin<Vertex, E> completeTraversal = null;
60         private EdgeRules edgeRules = EdgeRules.getInstance();
61         
62         protected int parentStepIndex = 0;
63         protected int containerStepIndex = 0;
64         protected int stepIndex = 0;
65         
66         /**
67          * Instantiates a new graph traversal builder.
68          *
69          * @param loader the loader
70          */
71         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
72                 super(loader, source);
73                 
74                 traversal = new DefaultGraphTraversal<>();
75                 
76         }
77         
78         /**
79          * Instantiates a new graph traversal builder.
80          *
81          * @param loader the loader
82          * @param start the start
83          */
84         public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
85                 super(loader, source, start);
86                 
87                 traversal = new DefaultGraphTraversal<>();
88                 
89         }
90
91         /**
92          * @{inheritDoc}
93          */
94         @Override
95         public QueryBuilder<Vertex> getVerticesByIndexedProperty(String key, Object value) {
96         
97                 return this.getVerticesByProperty(key, value);
98         }
99
100         /**
101          * @{inheritDoc}
102          */
103         @Override
104         public QueryBuilder<Vertex> getVerticesByIndexedProperty(String key, List<?> values) {
105                 return this.getVerticesByProperty(key, values);
106         }
107         
108         /**
109          * @{inheritDoc}
110          */
111         @Override
112         public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
113                 
114                 //this is because the index is registered as an Integer
115                 value = this.correctObjectType(value);
116                 
117                 traversal.has(key, value);
118                 
119                 stepIndex++;
120                 return (QueryBuilder<Vertex>) this;
121         }
122         
123         /**
124          * @{inheritDoc}
125          */
126         @Override
127         public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
128                 
129                 //this is because the index is registered as an Integer
130                 List<Object> correctedValues = new ArrayList<>();
131                 for (Object item : values) {
132                         correctedValues.add(this.correctObjectType(item));
133                 }
134                 
135                 traversal.has(key, P.within(correctedValues));
136                 
137                 stepIndex++;
138                 return (QueryBuilder<Vertex>) this;
139         }
140
141         /**
142          * @{inheritDoc}
143          */
144         @Override
145         public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
146                 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
147                 stepIndex++;
148                 return (QueryBuilder<Vertex>) this;
149         }
150
151         /**
152          * @{inheritDoc}
153          */
154         @Override
155         public QueryBuilder<Vertex> getTypedVerticesByMap(String type, LinkedHashMap<String, String> map) {
156                 
157                 for (String key : map.keySet()) {
158                         traversal.has(key, map.get(key));
159                         stepIndex++;
160                 }
161                 traversal.has(AAIProperties.NODE_TYPE, type);
162                 stepIndex++;
163                 return (QueryBuilder<Vertex>) this;
164         }
165
166         /**
167          * @{inheritDoc}
168          */
169         @Override
170         public QueryBuilder<Vertex> createDBQuery(Introspector obj) {
171                 this.createKeyQuery(obj);
172                 this.createContainerQuery(obj);
173                 return (QueryBuilder<Vertex>) this;
174         }
175
176         /**
177          * @{inheritDoc}
178          */
179         @Override
180         public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
181                 Set<String> keys = obj.getKeys();
182                 Object val;
183                 for (String key : keys) {
184                         val = obj.getValue(key);
185                         Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
186                         if (metadata.isPresent()) {
187                                 //use the db name for the field rather than the object model
188                                 key = metadata.get();
189                         }
190                         if (val != null) {
191                                 //this is because the index is registered as an Integer
192                                 if (val.getClass().equals(Long.class)) {
193                                         traversal.has(key,new Integer(val.toString()));
194                                 } else {
195                                         traversal.has(key, val);
196                                 }
197                                 stepIndex++;
198                         }
199                 }
200                 return (QueryBuilder<Vertex>) this;
201         }
202
203         @Override
204         public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
205                 this.createKeyQuery(obj);
206                 allPropertiesQuery(obj);
207                 this.createContainerQuery(obj);
208                 return (QueryBuilder<Vertex>) this;
209         }
210         
211         private void allPropertiesQuery(Introspector obj) {
212                 Set<String> props = obj.getProperties();
213                 Set<String> keys = obj.getKeys();
214                 Object val;
215                 for (String prop : props) {
216                         if (obj.isSimpleType(prop) && !keys.contains(prop)) {
217                                 val = obj.getValue(prop);
218                                 if (val != null) {
219                                         Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
220                                         if (metadata.isPresent()) {
221                                                 //use the db name for the field rather than the object model
222                                                 prop = metadata.get();
223                                         }
224                                         //this is because the index is registered as an Integer
225                                         if (val != null && val.getClass().equals(Long.class)) {
226                                                 traversal.has(prop,new Integer(val.toString()));
227                                         } else {
228                                                 traversal.has(prop, val);
229                                         }
230                                         stepIndex++;
231                                 }
232                         }
233                 }
234         }
235         
236         /**
237          * @{inheritDoc}
238          */
239         @Override
240         
241         public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
242                 String type = obj.getChildDBName();
243                 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
244                 if (abstractType != null) {
245                         String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
246                         traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
247                 } else {
248                         traversal.has(AAIProperties.NODE_TYPE, type);
249                 }
250                 stepIndex++;
251                 markContainer();
252                 return (QueryBuilder<Vertex>) this;
253         }
254
255         /**
256          * @throws NoEdgeRuleFoundException 
257          * @throws AAIException 
258          * @{inheritDoc}
259          */
260         @Override
261         public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
262                 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
263                 if ("true".equals(isAbstractType)) {
264                         markParentBoundary();
265                         traversal.union(handleAbstractEdge(type, parent, child));
266                         stepIndex += 1;
267                 } else {
268                         this.edgeQueryToVertex(type, parent, child);
269                 }
270                 return (QueryBuilder<Vertex>) this;
271                         
272         }
273         
274         private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
275                 String childName = child.getDbName();
276                 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
277                 String[] inheritors = inheritorMetadata.split(",");
278                 Traversal<Vertex, Vertex>[] unionTraversals = new Traversal[inheritors.length];
279                 int traversalIndex = 0;
280                 for (int i = 0; i < inheritors.length; i++) {
281                         String inheritor = inheritors[i];
282                         if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) {
283                                 EdgeRule rule = edgeRules.getEdgeRule(type, inheritor, childName);
284                                 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
285                                 if (rule.getDirection().equals(Direction.OUT)) {
286                                         innerTraversal.out(rule.getLabel());
287                                 } else {
288                                         innerTraversal.in(rule.getLabel());
289                                 }
290                                 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
291                                 unionTraversals[traversalIndex] = innerTraversal;
292                                 traversalIndex++;
293                         }
294                 }
295                 if (traversalIndex < inheritors.length) {
296                         Traversal<Vertex, Vertex>[] temp = Arrays.copyOfRange(unionTraversals, 0, traversalIndex);
297                         unionTraversals = temp;
298                 }
299                 return unionTraversals;
300         }
301         /**
302          * @throws NoEdgeRuleFoundException 
303          * @throws AAIException 
304          * @{inheritDoc}
305          */
306         @Override
307         public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException, NoEdgeRuleFoundException {
308                 
309                 String nodeType = parent.<String>property(AAIProperties.NODE_TYPE).orElse(null);
310                 Introspector parentObj = loader.introspectorFromName(nodeType);
311                 this.edgeQueryToVertex(type, parentObj, child);
312                 return (QueryBuilder<Vertex>) this;
313                         
314         }
315         
316         @Override
317         public QueryBuilder<Edge> getEdgesBetween(EdgeType type, String outNodeType, String inNodeType) throws AAIException {
318                 Introspector outObj = loader.introspectorFromName(outNodeType);
319                 Introspector inObj = loader.introspectorFromName(inNodeType);
320                 this.edgeQuery(type, outObj, inObj);
321                 
322                 return (QueryBuilder<Edge>)this;
323
324         }
325         /**
326          * @{inheritDoc}
327          */
328         @Override
329         public QueryBuilder<E> union(QueryBuilder... builder) {
330                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
331                 for (int i = 0; i < builder.length; i++) {
332                         traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
333                 }
334                 this.traversal.union(traversals);
335                 stepIndex++;
336                 
337                 return this;
338         }
339         
340         /**
341          * @{inheritDoc}
342          */
343         @Override
344         public QueryBuilder<E> where(QueryBuilder... builder) {
345                 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
346                 for (int i = 0; i < builder.length; i++) {
347                         this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
348                         stepIndex++;
349                 }
350                 
351                 return this;
352         }
353         
354         /**
355          * Edge query.
356          *
357          * @param outType the out type
358          * @param inType the in type
359          * @throws NoEdgeRuleFoundException 
360          * @throws AAIException 
361          */
362         private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException {
363                 String outType = outObj.getDbName();
364                 String inType = inObj.getDbName();
365                 
366                 if (outObj.isContainer()) {
367                         outType = outObj.getChildDBName();
368                 }
369                 if (inObj.isContainer()) {
370                         inType = inObj.getChildDBName();
371                 }
372                 markParentBoundary();
373                 EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType);
374                 if (rule.getDirection().equals(Direction.OUT)) {
375                         traversal.out(rule.getLabel());
376                 } else {
377                         traversal.in(rule.getLabel());
378                 }
379                 stepIndex++;
380                 this.createContainerQuery(inObj);
381                 
382         }
383         
384         /**
385          * Edge query.
386          *
387          * @param outType the out type
388          * @param inType the in type
389          * @throws NoEdgeRuleFoundException 
390          * @throws AAIException 
391          */
392         private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException {
393                 String outType = outObj.getDbName();
394                 String inType = inObj.getDbName();
395                 
396                 if (outObj.isContainer()) {
397                         outType = outObj.getChildDBName();
398                 }
399                 if (inObj.isContainer()) {
400                         inType = inObj.getChildDBName();
401                 }
402                 markParentBoundary();
403                 EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType);
404                 if (rule.getDirection().equals(Direction.OUT)) {
405                         traversal.outE(rule.getLabel());
406                 } else {
407                         traversal.inE(rule.getLabel());
408                 }
409                 stepIndex++;
410                 
411         }
412         
413         @Override
414         public QueryBuilder<E> limit(long amount) {
415                 traversal.limit(amount);
416                 return this;
417         }
418
419         /**
420          * @{inheritDoc}
421          */
422         @Override
423         public <E2> E2 getQuery() {
424                 return (E2)this.traversal;
425         }
426         
427         /**
428          * @{inheritDoc}
429          */
430         @Override
431         public QueryBuilder<E> getParentQuery() {
432
433                 return cloneQueryAtStep(parentStepIndex);
434         }
435         
436         @Override
437         public QueryBuilder<E> getContainerQuery() {
438                 
439                 if (this.parentStepIndex == 0) {
440                         return removeQueryStepsBetween(0, containerStepIndex);
441                 } else {
442                         return cloneQueryAtStep(containerStepIndex);
443                 }
444         }
445         
446         /**
447          * @{inheritDoc}
448          */
449         @Override
450         public void markParentBoundary() {
451                 parentStepIndex = stepIndex;
452         }
453         
454         @Override
455         public void markContainer() {
456                 containerStepIndex = stepIndex;
457         }
458         
459         
460         /**
461          * @{inheritDoc}
462          */
463         @Override
464         public Vertex getStart() {
465                 return this.start;
466         }
467
468         protected int getParentStepIndex() {
469                 return parentStepIndex;
470         }
471
472         protected int getContainerStepIndex() {
473                 return containerStepIndex;
474         }
475
476         protected int getStepIndex() {
477                 return stepIndex;
478         }
479
480         protected abstract QueryBuilder<E> cloneQueryAtStep(int index);
481         /**
482          * end is exclusive
483          * 
484          * @param start
485          * @param end
486          * @return
487          */
488         protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
489         
490         private void executeQuery() {
491                 
492                 Admin<Vertex, Vertex> admin;
493                 if (start != null) {
494                         admin = source.V(start).asAdmin();
495                 } else {
496                         admin = source.V().asAdmin();
497
498                 }
499                 
500                 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
501                 
502                 this.completeTraversal = (Admin<Vertex, E>) admin;
503         }
504         
505         @Override
506         public boolean hasNext() {
507                 if (this.completeTraversal == null) {
508                         executeQuery();
509                 }
510                 
511                 return this.completeTraversal.hasNext();
512         }
513         
514         @Override
515         public E next() {
516                 if (this.completeTraversal == null) {
517                         executeQuery();
518                 }
519                 
520                 return this.completeTraversal.next();
521         }
522         
523         @Override
524         public List<E> toList() {
525                 if (this.completeTraversal == null) {
526                         executeQuery();
527                 }
528                 
529                 return this.completeTraversal.toList();
530         }
531
532 }