2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * * Modifications Copyright © 2024 DEUTSCHE TELEKOM AG.
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 com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.Multimap;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
32 import java.util.Optional;
35 import org.apache.tinkerpop.gremlin.process.traversal.P;
36 import org.apache.tinkerpop.gremlin.process.traversal.Path;
37 import org.apache.tinkerpop.gremlin.process.traversal.Step;
38 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
39 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
40 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
41 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
42 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
43 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
44 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
45 import org.apache.tinkerpop.gremlin.structure.Direction;
46 import org.apache.tinkerpop.gremlin.structure.Edge;
47 import org.apache.tinkerpop.gremlin.structure.Vertex;
48 import org.onap.aai.db.props.AAIProperties;
49 import org.onap.aai.edges.EdgeRule;
50 import org.onap.aai.edges.EdgeRuleQuery;
51 import org.onap.aai.edges.enums.EdgeType;
52 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
53 import org.onap.aai.exceptions.AAIException;
54 import org.onap.aai.introspection.Introspector;
55 import org.onap.aai.introspection.Loader;
56 import org.onap.aai.schema.enums.ObjectMetadata;
57 import org.onap.aai.schema.enums.PropertyMetadata;
58 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
61 * The Class GraphTraversalBuilder.
63 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
65 protected GraphTraversal<Vertex, E> traversal = null;
66 protected Admin<Vertex, E> completeTraversal = null;
68 protected QueryBuilder<E> containerQuery;
69 protected QueryBuilder<E> parentQuery;
72 * Instantiates a new graph traversal builder.
74 * @param loader the loader
76 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
77 super(loader, source);
78 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
82 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, GraphTraversal<Vertex, E> traversal) {
83 super(loader, source);
84 this.traversal = traversal;
89 * Instantiates a new graph traversal builder.
91 * @param loader the loader
92 * @param start the start
94 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
95 super(loader, source, start);
97 traversal = (GraphTraversal<Vertex, E>) __.__(start);
105 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
107 // correct value call because the index is registered as an Integer
108 this.vertexHas(key, this.correctObjectType(value));
110 return (QueryBuilder<Vertex>) this;
114 protected void vertexHas(String key, Object value) {
115 traversal.has(key, value);
119 protected void vertexHasNot(String key) {
120 traversal.hasNot(key);
124 protected void vertexHas(String key) {
128 // TODO: Remove this once we test this - at this point i dont thib this is required
129 // because predicare is an object
132 * protected void vertexHas(final String key, final P<?> predicate) {
133 * traversal.has(key, predicate);
141 public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
143 // this is because the index is registered as an Integer
144 List<Object> correctedValues = new ArrayList<>();
145 for (Object item : values) {
146 correctedValues.add(this.correctObjectType(item));
149 this.vertexHas(key, P.within(correctedValues));
151 return (QueryBuilder<Vertex>) this;
157 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
158 ArrayList<String> values = new ArrayList<>(Arrays.asList(value.split(",")));
159 int size = values.size();
160 for (int i = 0; i < size; i++) {
161 values.set(i, values.get(i).trim());
163 this.vertexHas(key, P.within(values));
166 return (QueryBuilder<Vertex>) this;
173 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
175 // correct value call because the index is registered as an Integer
176 // TODO Check if this needs to be in QB and add these as internal
177 this.vertexHas(key, org.janusgraph.core.attribute.Text.textPrefix(value));
180 return (QueryBuilder<Vertex>) this;
187 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
190 return (QueryBuilder<Vertex>) this;
197 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
198 this.vertexHasNot(key);
200 return (QueryBuilder<Vertex>) this;
207 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
209 // correct value call because the index is registered as an Integer
210 this.vertexHas(key, P.neq(this.correctObjectType(value)));
212 return (QueryBuilder<Vertex>) this;
219 public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
221 // this is because the index is registered as an Integer
222 List<Object> correctedValues = new ArrayList<>();
223 for (Object item : values) {
224 correctedValues.add(this.correctObjectType(item));
227 this.vertexHas(key, P.without(correctedValues));
229 return (QueryBuilder<Vertex>) this;
233 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) {
234 this.vertexHas(key, P.gte(this.correctObjectType(value)));
237 return (QueryBuilder<Vertex>) this;
241 public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) {
242 this.vertexHas(key, P.lte(this.correctObjectType(value)));
245 return (QueryBuilder<Vertex>) this;
252 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
253 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
255 return (QueryBuilder<Vertex>) this;
262 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
264 for (Map.Entry<String, String> es : map.entrySet()) {
265 this.vertexHas(es.getKey(), es.getValue());
268 traversal.has(AAIProperties.NODE_TYPE, type);
270 return (QueryBuilder<Vertex>) this;
274 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
276 if (value != null && !"".equals(value)) {
277 boolean bValue = false;
279 if (value instanceof String) {// "true"
280 bValue = Boolean.valueOf(value.toString());
281 } else if (value instanceof Boolean) {// true
282 bValue = (Boolean) value;
285 this.vertexHas(key, bValue);
288 return (QueryBuilder<Vertex>) this;
295 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
296 Set<String> keys = obj.getKeys();
298 for (String key : keys) {
299 val = obj.getValue(key);
300 Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
301 if (metadata.isPresent()) {
302 // use the db name for the field rather than the object model
303 key = metadata.get();
306 // this is because the index is registered as an Integer
307 if (val.getClass().equals(Long.class)) {
308 this.vertexHas(key, Integer.valueOf(val.toString()));
310 this.vertexHas(key, val);
315 return (QueryBuilder<Vertex>) this;
319 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
320 this.createKeyQuery(obj);
321 allPropertiesQuery(obj);
322 this.createContainerQuery(obj);
323 return (QueryBuilder<Vertex>) this;
326 private void allPropertiesQuery(Introspector obj) {
327 Set<String> props = obj.getProperties();
328 Set<String> keys = obj.getKeys();
330 for (String prop : props) {
331 if (obj.isSimpleType(prop) && !keys.contains(prop)) {
332 val = obj.getValue(prop);
334 Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
335 if (metadata.isPresent()) {
336 // use the db name for the field rather than the object model
337 prop = metadata.get();
339 // this is because the index is registered as an Integer
340 if (val.getClass().equals(Long.class)) {
341 this.vertexHas(prop, Integer.valueOf(val.toString()));
343 this.vertexHas(prop, val);
352 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
353 String type = obj.getChildDBName();
354 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
355 if (abstractType != null) {
356 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
357 traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
359 traversal.has(AAIProperties.NODE_TYPE, type);
363 return (QueryBuilder<Vertex>) this;
367 * @throws NoEdgeRuleFoundException
368 * @throws AAIException
372 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
373 throws AAIException {
374 createTraversal(type, parent, child, false);
375 return (QueryBuilder<Vertex>) this;
380 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
381 throws AAIException {
382 this.createTraversal(type, parent, child, true);
383 return (QueryBuilder<Vertex>) this;
386 private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge)
387 throws AAIException {
388 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
389 if ("true".equals(isAbstractType)) {
390 markParentBoundary();
391 traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
394 this.edgeQueryToVertex(type, parent, child, null);
402 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
403 List<String> labels) throws AAIException {
404 this.edgeQueryToVertex(type, out, in, labels);
405 return (QueryBuilder<Vertex>) this;
408 private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent,
409 Introspector child, boolean isPrivateEdge) throws AAIException {
410 String childName = child.getDbName();
411 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
412 String[] inheritors = inheritorMetadata.split(",");
413 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
415 for (int i = 0; i < inheritors.length; i++) {
416 String inheritor = inheritors[i];
417 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
418 if (edgeRules.hasRule(qB.build())) {
419 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
421 rules = edgeRules.getRules(qB.edgeType(type).build());
422 } catch (EdgeRuleNotFoundException e) {
423 throw new NoEdgeRuleFoundException(e);
426 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
428 final List<String> inLabels = new ArrayList<>();
429 final List<String> outLabels = new ArrayList<>();
431 rules.values().forEach(rule -> {
432 if (rule.getDirection().equals(Direction.IN)) {
433 inLabels.add(rule.getLabel());
435 outLabels.add(rule.getLabel());
439 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
440 innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
441 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
442 innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
444 innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
445 __.in(inLabels.toArray(new String[inLabels.size()])));
448 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
449 unionTraversals.add(innerTraversal);
453 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
456 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
457 List<String> labels) throws AAIException {
458 Introspector outObj = loader.introspectorFromName(outNodeType);
459 Introspector inObj = loader.introspectorFromName(inNodeType);
460 this.edgeQuery(type, outObj, inObj, labels);
462 return (QueryBuilder<Edge>) this;
469 public QueryBuilder<E> union(QueryBuilder... builder) {
470 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
471 for (int i = 0; i < builder.length; i++) {
472 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
474 this.traversal.union(traversals);
484 public QueryBuilder<E> where(QueryBuilder... builder) {
485 for (int i = 0; i < builder.length; i++) {
486 this.traversal.where((GraphTraversal<Vertex, Vertex>) builder[i].getQuery());
497 public QueryBuilder<E> or(QueryBuilder... builder) {
498 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
499 for (int i = 0; i < builder.length; i++) {
500 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
502 this.traversal.or(traversals);
509 public QueryBuilder<E> store(String name) {
511 this.traversal.store(name);
518 public QueryBuilder<E> cap(String name) {
519 this.traversal.cap(name);
526 public QueryBuilder<E> unfold() {
527 this.traversal.unfold();
534 public QueryBuilder<E> dedup() {
536 this.traversal.dedup();
543 public QueryBuilder<E> emit() {
545 this.traversal.emit();
553 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
555 this.traversal.repeat((GraphTraversal<Vertex, E>) builder.getQuery());
562 public QueryBuilder<E> until(QueryBuilder<E> builder) {
563 this.traversal.until((GraphTraversal<Vertex, E>) builder.getQuery());
570 public QueryBuilder<E> groupCount() {
571 this.traversal.groupCount();
578 public QueryBuilder<E> both() {
579 this.traversal.both();
586 public QueryBuilder<Tree> tree() {
588 this.traversal.tree();
591 return (QueryBuilder<Tree>) this;
595 public QueryBuilder<E> by(String name) {
596 this.traversal.by(name);
603 public QueryBuilder<E> valueMap() {
604 this.traversal.valueMap();
611 public QueryBuilder<E> valueMap(String... names) {
612 this.traversal.valueMap(names);
622 public QueryBuilder<E> simplePath() {
623 this.traversal.simplePath();
632 public QueryBuilder<Path> path() {
633 this.traversal.path();
635 return (QueryBuilder<Path>) this;
639 public QueryBuilder<Edge> outE() {
640 this.traversal.outE();
642 return (QueryBuilder<Edge>) this;
646 public QueryBuilder<Edge> inE() {
647 this.traversal.inE();
649 return (QueryBuilder<Edge>) this;
653 public QueryBuilder<Vertex> outV() {
654 this.traversal.outV();
656 return (QueryBuilder<Vertex>) this;
660 public QueryBuilder<Vertex> inV() {
661 this.traversal.inV();
663 return (QueryBuilder<Vertex>) this;
667 public QueryBuilder<E> as(String name) {
668 this.traversal.as(name);
675 public QueryBuilder<E> not(QueryBuilder<E> builder) {
676 this.traversal.not(builder.getQuery());
683 public QueryBuilder<E> select(String name) {
684 this.traversal.select(name);
692 public QueryBuilder<E> select(String... names) {
693 if (names.length == 1) {
694 this.traversal.select(names[0]);
695 } else if (names.length == 2) {
696 this.traversal.select(names[0], names[1]);
697 } else if (names.length > 2) {
698 String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
699 this.traversal.select(names[0], names[1], otherNames);
710 * @param outObj the out type
711 * @param inObj the in type
712 * @throws NoEdgeRuleFoundException
713 * @throws AAIException
715 private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
716 throws AAIException {
717 String outType = outObj.getDbName();
718 String inType = inObj.getDbName();
720 if (outObj.isContainer()) {
721 outType = outObj.getChildDBName();
723 if (inObj.isContainer()) {
724 inType = inObj.getChildDBName();
726 markParentBoundary();
727 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
728 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
730 if (labels == null) {
732 rules.putAll(edgeRules.getRules(qB.build()));
733 } catch (EdgeRuleNotFoundException e) {
734 // is ok per original functioning of this section
735 // TODO add "best try" sort of flag to the EdgeRuleQuery
736 // to indicate if the exception should be thrown or not
739 for (String label : labels) {
741 rules.putAll(edgeRules.getRules(qB.label(label).build()));
742 } catch (EdgeRuleNotFoundException e) {
743 throw new NoEdgeRuleFoundException(e);
746 if (rules.isEmpty()) {
747 throw new NoEdgeRuleFoundException(
748 "No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
752 final List<String> inLabels = new ArrayList<>();
753 final List<String> outLabels = new ArrayList<>();
755 for (EdgeRule rule : rules.values()) {
756 if (labels != null && !labels.contains(rule.getLabel())) {
759 if (Direction.IN.equals(rule.getDirection())) {
760 inLabels.add(rule.getLabel());
762 outLabels.add(rule.getLabel());
767 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
768 traversal.out(outLabels.toArray(new String[outLabels.size()]));
769 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
770 traversal.in(inLabels.toArray(new String[inLabels.size()]));
772 traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
773 __.in(inLabels.toArray(new String[inLabels.size()])));
778 this.createContainerQuery(inObj);
785 * @param outObj the out type
786 * @param inObj the in type
787 * @throws NoEdgeRuleFoundException
788 * @throws AAIException
790 private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
791 throws AAIException {
792 String outType = outObj.getDbName();
793 String inType = inObj.getDbName();
795 if (outObj.isContainer()) {
796 outType = outObj.getChildDBName();
798 if (inObj.isContainer()) {
799 inType = inObj.getChildDBName();
802 markParentBoundary();
803 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
804 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
807 if (labels == null) {
808 rules.putAll(edgeRules.getRules(qB.build()));
810 for (String label : labels) {
811 rules.putAll(edgeRules.getRules(qB.label(label).build()));
814 } catch (EdgeRuleNotFoundException e) {
815 throw new NoEdgeRuleFoundException(e);
818 final List<String> inLabels = new ArrayList<>();
819 final List<String> outLabels = new ArrayList<>();
821 for (EdgeRule rule : rules.values()) {
822 if (labels != null && !labels.contains(rule.getLabel())) {
825 if (Direction.IN.equals(rule.getDirection())) {
826 inLabels.add(rule.getLabel());
828 outLabels.add(rule.getLabel());
833 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
834 traversal.outE(outLabels.toArray(new String[outLabels.size()]));
835 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
836 traversal.inE(inLabels.toArray(new String[inLabels.size()]));
838 traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])),
839 __.inE(inLabels.toArray(new String[inLabels.size()])));
844 public QueryBuilder<E> limit(long amount) {
845 traversal.limit(amount);
853 public <E2> E2 getQuery() {
854 return (E2) this.traversal;
861 public QueryBuilder<E> getParentQuery() {
862 return this.parentQuery != null
864 : cloneQueryAtStep(parentStepIndex);
868 public QueryBuilder<E> getContainerQuery() {
870 if (this.parentStepIndex == 0) {
871 return removeQueryStepsBetween(0, containerStepIndex);
873 return this.containerQuery;
881 public void markParentBoundary() {
882 this.parentQuery = cloneQueryAtStep(stepIndex);
883 parentStepIndex = stepIndex;
887 public void markContainer() {
888 this.containerQuery = cloneQueryAtStep(stepIndex);
889 containerStepIndex = stepIndex;
896 public Vertex getStart() {
900 protected int getParentStepIndex() {
901 return parentStepIndex;
904 protected int getContainerStepIndex() {
905 return containerStepIndex;
908 protected int getStepIndex() {
919 protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
921 protected void executeQuery() {
923 Admin<Vertex, Vertex> admin;
925 this.completeTraversal = traversal.asAdmin();
927 admin = source.V().asAdmin();
928 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
930 this.completeTraversal = (Admin<Vertex, E>) admin;
937 public boolean hasNext() {
938 if (this.completeTraversal == null) {
942 return this.completeTraversal.hasNext();
947 if (this.completeTraversal == null) {
951 return this.completeTraversal.next();
955 public List<E> toList() {
956 if (this.completeTraversal == null) {
959 return this.completeTraversal.toList();
962 protected QueryBuilder<Edge> has(String key, String value) {
963 traversal.has(key, value);
965 return (QueryBuilder<Edge>) this;