2 * ============LICENSE_START=======================================================
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
23 package org.onap.aai.query.builder;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
29 import java.util.Optional;
30 import java.util.stream.Collectors;
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;
46 public class TraversalURIOptimizedQuery<E> extends TraversalQuery<E> {
48 protected Map<Integer, String> stepToAaiUri = new HashMap<>();
50 public TraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source) {
51 super(loader, source);
55 public TraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source,
56 GraphTraversal<Vertex, Vertex> traversal) {
57 super(loader, source);
61 public TraversalURIOptimizedQuery(Loader loader, GraphTraversalSource source, Vertex start) {
62 super(loader, source, start);
66 protected TraversalURIOptimizedQuery(GraphTraversal traversal, Loader loader, GraphTraversalSource source,
67 GraphTraversalBuilder graphTraversalBuilder) {
68 super(traversal, loader, source, graphTraversalBuilder);
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;
80 protected void executeQuery() {
82 this.completeTraversal = this.traversal.asAdmin().clone();
85 this.completeTraversal = this.pivotTraversal(this.completeTraversal);
89 Traversal.Admin<Vertex, Vertex> admin = source.V().asAdmin();
90 TraversalHelper.insertTraversal(admin.getEndStep(), completeTraversal, admin);
92 this.completeTraversal = (Traversal.Admin<Vertex, E>) admin;
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;
104 Traversal.Admin<Vertex, E> traversalAdminStart = traversalAdmin.clone();
106 List<Step> steps = traversalAdmin.getSteps();
107 // clean up traversal steps
108 for (int i = 0; i < steps.size(); i++) {
109 traversalAdminStart.removeStep(0);
112 int lastURIStepIndex = getLastURIStepIndex();
113 ((GraphTraversal<Vertex, E>) traversalAdminStart).has(AAIProperties.AAI_URI,
114 stepToAaiUri.get(lastURIStepIndex));
116 ImmutablePair<Integer, Integer> indexAndStepCountTuple = getHasContainerAdjustedIndexAndSplitPosition(steps,
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()
126 .collect(Collectors.toList());
128 .addStep(new HasStep<Vertex>(traversalAdminStart, newContainers.toArray(new HasContainer[0])));
131 traversalAdminStart.addStep(steps.get(i));
134 return traversalAdminStart;
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
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)
146 private ImmutablePair<Integer, Integer> getHasContainerAdjustedIndexAndSplitPosition(List<Step> steps,
147 int lastURIStepIndex) {
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();
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);
164 return new ImmutablePair<>(lastURIStepIndex, lastURIStepIndex);
168 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
169 super.createKeyQuery(obj);
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());
177 stepToAaiUri.put(stepIndex, uri.get());
182 stepToAaiUri = new HashMap<>();
184 return (QueryBuilder<Vertex>) this;
187 private boolean shouldAddStepUri(Introspector obj) {
188 boolean shouldOptimize = optimize;
190 if (shouldOptimize && start != null) {
191 shouldOptimize = false;
194 if (shouldOptimize && stepToAaiUri.isEmpty() && !obj.isTopLevel()) {
195 shouldOptimize = false;
198 if (shouldOptimize && obj.getMetadata(ObjectMetadata.ABSTRACT) != null) {
199 shouldOptimize = false;
202 return shouldOptimize;
205 private Optional<String> getStepUriFromIntrospector(Introspector obj) {
209 } catch (Exception e) {
212 if ("".equals(uri)) {
213 return Optional.empty();
216 if (!stepToAaiUri.isEmpty()) {
217 uri = stepToAaiUri.get(getLastURIStepIndex()) + uri;
220 return Optional.of(uri);
223 protected int getLastURIStepIndex() {
224 return Collections.max(stepToAaiUri.keySet());
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));
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));