b96847c28a263615195300de94b22cc7774df35d
[aai/aai-common.git] / aai-core / src / main / java / org / onap / aai / query / builder / TraversalURIOptimizedQuery.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Modifications Copyright © 2024 Deutsche Telekom.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *    http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.aai.query.builder;
24
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import java.util.stream.Collectors;
31
32 import org.apache.commons.lang3.tuple.ImmutablePair;
33 import org.apache.tinkerpop.gremlin.process.traversal.Step;
34 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
35 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
36 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
37 import org.apache.tinkerpop.gremlin.process.traversal.step.filter.HasStep;
38 import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
39 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
40 import org.apache.tinkerpop.gremlin.structure.Vertex;
41 import org.onap.aai.db.props.AAIProperties;
42 import org.onap.aai.introspection.Introspector;
43 import org.onap.aai.introspection.Loader;
44 import org.onap.aai.schema.enums.ObjectMetadata;
45
46 public class TraversalURIOptimizedQuery<E> extends TraversalQuery<E> {
47
48     protected Map<Integer, String> stepToAaiUri = new HashMap<>();
49
50     public TraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source) {
51         super(loader, source);
52         optimize = true;
53     }
54
55     public TraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source,
56             GraphTraversal<Vertex, Vertex> traversal) {
57         super(loader, source);
58         optimize = true;
59     }
60
61     public TraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source, Vertex start) {
62         super(loader, source, start);
63         optimize = true;
64     }
65
66     protected TraversalURIOptimizedQuery(GraphTraversal traversal, Loader loader, GraphTraversalSource source,
67             GraphTraversalBuilder graphTraversalBuilder) {
68         super(traversal, loader, source, graphTraversalBuilder);
69         optimize = true;
70     }
71
72     protected TraversalURIOptimizedQuery(GraphTraversal traversal, Loader loader, GraphTraversalSource source,
73             GraphTraversalBuilder graphTraversalBuilder, Map<Integer, String> stepToAaiUri) {
74         super(traversal, loader, source, graphTraversalBuilder);
75         optimize = graphTraversalBuilder.optimize;
76         this.stepToAaiUri = stepToAaiUri;
77     }
78
79     @Override
80     protected void executeQuery() {
81
82         this.completeTraversal = this.traversal.asAdmin().clone();
83
84         if (this.optimize) {
85             this.completeTraversal = this.pivotTraversal(this.completeTraversal);
86         }
87
88         if (start == null) {
89             Traversal.Admin<Vertex, Vertex> admin = source.V().asAdmin();
90             TraversalHelper.insertTraversal(admin.getEndStep(), completeTraversal, admin);
91
92             this.completeTraversal = (Traversal.Admin<Vertex, E>) admin;
93
94         }
95
96     }
97
98     private Traversal.Admin<Vertex, E> pivotTraversal(Traversal.Admin<Vertex, E> traversalAdmin) {
99         // if we do not have an index or other conditions do no optimization
100         if (stepToAaiUri.isEmpty()) {
101             return traversalAdmin;
102         }
103
104         Traversal.Admin<Vertex, E> traversalAdminStart = traversalAdmin.clone();
105
106         List<Step> steps = traversalAdmin.getSteps();
107         // clean up traversal steps
108         for (int i = 0; i < steps.size(); i++) {
109             traversalAdminStart.removeStep(0);
110         }
111
112         int lastURIStepIndex = getLastURIStepIndex();
113         ((GraphTraversal<Vertex, E>) traversalAdminStart).has(AAIProperties.AAI_URI,
114                 stepToAaiUri.get(lastURIStepIndex));
115
116         ImmutablePair<Integer, Integer> indexAndStepCountTuple = getHasContainerAdjustedIndexAndSplitPosition(steps,
117                 lastURIStepIndex);
118         int adjustedIndex = indexAndStepCountTuple.getKey();
119         for (int i = adjustedIndex; i < steps.size(); i++) {
120             Step step = steps.get(i);
121             boolean isFirstStep = i == adjustedIndex;
122             if (isFirstStep && step instanceof HasStep) {
123                 int splitPosition = indexAndStepCountTuple.getValue();
124                 List<HasContainer> newContainers = ((HasStep<?>) step).getHasContainers().stream()
125                         .skip(splitPosition)
126                         .collect(Collectors.toList());
127                 traversalAdminStart
128                         .addStep(new HasStep<Vertex>(traversalAdminStart, newContainers.toArray(new HasContainer[0])));
129                 i++;
130             }
131             traversalAdminStart.addStep(steps.get(i));
132         }
133
134         return traversalAdminStart;
135     }
136
137     /**
138      * Adjust lastURIStepIndex by the number of steps that are in hasContainers.
139      * A HasContainer can contain multiple steps, which skews the original index.
140      * Returns the step index and split position inside the hasContainer
141      * 
142      * @param steps            the list of steps to go through
143      * @param lastURIStepIndex the list index to adjust
144      * @return a Tuple<Integer, Integer> of the form (index, splitPosition)
145      */
146     private ImmutablePair<Integer, Integer> getHasContainerAdjustedIndexAndSplitPosition(List<Step> steps,
147             int lastURIStepIndex) {
148         int stepCount = 0;
149         for (int j = 0; j <= lastURIStepIndex; j++) {
150             Step step = steps.get(j);
151             if (step instanceof HasStep) {
152                 stepCount += ((HasStep<?>) step).getHasContainers().size();
153             } else {
154                 stepCount++;
155             }
156             if (stepCount == lastURIStepIndex) {
157                 int splitPosition = stepCount + 1 - lastURIStepIndex;
158                 return new ImmutablePair<>(j + 1, splitPosition);
159             } else if (stepCount > lastURIStepIndex) {
160                 int splitPosition = stepCount + 1 - lastURIStepIndex;
161                 return new ImmutablePair<>(j, splitPosition);
162             }
163         }
164         return new ImmutablePair<>(lastURIStepIndex, lastURIStepIndex);
165     }
166
167     @Override
168     public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
169         super.createKeyQuery(obj);
170
171         if (shouldAddStepUri(obj)) {
172             Optional<String> uri = getStepUriFromIntrospector(obj);
173             if (uri.isPresent()) {
174                 if (stepToAaiUri.isEmpty()) {
175                     stepToAaiUri.put(stepIndex + 1, uri.get());
176                 } else {
177                     stepToAaiUri.put(stepIndex, uri.get());
178                 }
179             }
180         } else {
181             optimize = false;
182             stepToAaiUri = new HashMap<>();
183         }
184         return (QueryBuilder<Vertex>) this;
185     }
186
187     private boolean shouldAddStepUri(Introspector obj) {
188         boolean shouldOptimize = optimize;
189
190         if (shouldOptimize && start != null) {
191             shouldOptimize = false;
192         }
193
194         if (shouldOptimize && stepToAaiUri.isEmpty() && !obj.isTopLevel()) {
195             shouldOptimize = false;
196         }
197
198         if (shouldOptimize && obj.getMetadata(ObjectMetadata.ABSTRACT) != null) {
199             shouldOptimize = false;
200         }
201
202         return shouldOptimize;
203     }
204
205     private Optional<String> getStepUriFromIntrospector(Introspector obj) {
206         String uri = "";
207         try {
208             uri = obj.getURI();
209         } catch (Exception e) {
210         }
211
212         if ("".equals(uri)) {
213             return Optional.empty();
214         }
215
216         if (!stepToAaiUri.isEmpty()) {
217             uri = stepToAaiUri.get(getLastURIStepIndex()) + uri;
218         }
219
220         return Optional.of(uri);
221     }
222
223     protected int getLastURIStepIndex() {
224         return Collections.max(stepToAaiUri.keySet());
225     }
226
227     private Map<Integer, String> getStepToAaiUriWithoutStepGreaterThan(final int index) {
228         return stepToAaiUri.entrySet().stream().filter(kv -> kv.getKey() <= index)
229                 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
230     }
231
232     @Override
233     protected QueryBuilder<E> cloneQueryAtStep(int index) {
234         GraphTraversal.Admin<Vertex, E> cloneAdmin = getCloneAdmin(index);
235         return new TraversalURIOptimizedQuery<>(cloneAdmin, loader, source, this,
236                 getStepToAaiUriWithoutStepGreaterThan(index));
237     }
238
239 }