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=========================================================
21 package org.onap.aai.query.builder;
23 import com.google.common.collect.ArrayListMultimap;
24 import com.google.common.collect.Multimap;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
30 import java.util.Optional;
33 import org.apache.tinkerpop.gremlin.process.traversal.P;
34 import org.apache.tinkerpop.gremlin.process.traversal.Path;
35 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
36 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
37 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
38 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
39 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
40 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
41 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
42 import org.apache.tinkerpop.gremlin.structure.Direction;
43 import org.apache.tinkerpop.gremlin.structure.Edge;
44 import org.apache.tinkerpop.gremlin.structure.Vertex;
45 import org.onap.aai.db.props.AAIProperties;
46 import org.onap.aai.edges.EdgeRule;
47 import org.onap.aai.edges.EdgeRuleQuery;
48 import org.onap.aai.edges.enums.EdgeType;
49 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
50 import org.onap.aai.exceptions.AAIException;
51 import org.onap.aai.introspection.Introspector;
52 import org.onap.aai.introspection.Loader;
53 import org.onap.aai.schema.enums.ObjectMetadata;
54 import org.onap.aai.schema.enums.PropertyMetadata;
55 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
58 * The Class GraphTraversalBuilder.
60 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
62 protected GraphTraversal<Vertex, E> traversal = null;
63 protected Admin<Vertex, E> completeTraversal = null;
66 * Instantiates a new graph traversal builder.
68 * @param loader the loader
70 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
71 super(loader, source);
72 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
77 * Instantiates a new graph traversal builder.
79 * @param loader the loader
80 * @param start the start
82 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
83 super(loader, source, start);
85 traversal = (GraphTraversal<Vertex, E>) __.__(start);
93 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
95 // correct value call because the index is registered as an Integer
96 this.vertexHas(key, this.correctObjectType(value));
98 return (QueryBuilder<Vertex>) this;
102 protected void vertexHas(String key, Object value) {
103 traversal.has(key, value);
107 protected void vertexHasNot(String key) {
108 traversal.hasNot(key);
112 protected void vertexHas(String key) {
116 // TODO: Remove this once we test this - at this point i dont thib this is required
117 // because predicare is an object
120 * protected void vertexHas(final String key, final P<?> predicate) {
121 * traversal.has(key, predicate);
129 public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
131 // this is because the index is registered as an Integer
132 List<Object> correctedValues = new ArrayList<>();
133 for (Object item : values) {
134 correctedValues.add(this.correctObjectType(item));
137 this.vertexHas(key, P.within(correctedValues));
139 return (QueryBuilder<Vertex>) this;
145 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
146 ArrayList<String> values = new ArrayList<>(Arrays.asList(value.split(",")));
147 int size = values.size();
148 for (int i = 0; i < size; i++) {
149 values.set(i, values.get(i).trim());
151 this.vertexHas(key, P.within(values));
154 return (QueryBuilder<Vertex>) this;
161 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
163 // correct value call because the index is registered as an Integer
164 // TODO Check if this needs to be in QB and add these as internal
165 this.vertexHas(key, org.janusgraph.core.attribute.Text.textPrefix(value));
168 return (QueryBuilder<Vertex>) this;
175 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
178 return (QueryBuilder<Vertex>) this;
185 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
186 this.vertexHasNot(key);
188 return (QueryBuilder<Vertex>) this;
195 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
197 // correct value call because the index is registered as an Integer
198 this.vertexHas(key, P.neq(this.correctObjectType(value)));
200 return (QueryBuilder<Vertex>) this;
207 public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
209 // this is because the index is registered as an Integer
210 List<Object> correctedValues = new ArrayList<>();
211 for (Object item : values) {
212 correctedValues.add(this.correctObjectType(item));
215 this.vertexHas(key, P.without(correctedValues));
217 return (QueryBuilder<Vertex>) this;
221 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) {
222 this.vertexHas(key, P.gte(this.correctObjectType(value)));
225 return (QueryBuilder<Vertex>) this;
229 public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) {
230 this.vertexHas(key, P.lte(this.correctObjectType(value)));
233 return (QueryBuilder<Vertex>) this;
240 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
241 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
243 return (QueryBuilder<Vertex>) this;
250 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
252 for (Map.Entry<String, String> es : map.entrySet()) {
253 this.vertexHas(es.getKey(), es.getValue());
256 traversal.has(AAIProperties.NODE_TYPE, type);
258 return (QueryBuilder<Vertex>) this;
262 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
264 if (value != null && !"".equals(value)) {
265 boolean bValue = false;
267 if (value instanceof String) {// "true"
268 bValue = Boolean.valueOf(value.toString());
269 } else if (value instanceof Boolean) {// true
270 bValue = (Boolean) value;
273 this.vertexHas(key, bValue);
276 return (QueryBuilder<Vertex>) this;
283 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
284 Set<String> keys = obj.getKeys();
286 for (String key : keys) {
287 val = obj.getValue(key);
288 Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
289 if (metadata.isPresent()) {
290 // use the db name for the field rather than the object model
291 key = metadata.get();
294 // this is because the index is registered as an Integer
295 if (val.getClass().equals(Long.class)) {
296 this.vertexHas(key, Integer.valueOf(val.toString()));
298 this.vertexHas(key, val);
303 return (QueryBuilder<Vertex>) this;
307 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
308 this.createKeyQuery(obj);
309 allPropertiesQuery(obj);
310 this.createContainerQuery(obj);
311 return (QueryBuilder<Vertex>) this;
314 private void allPropertiesQuery(Introspector obj) {
315 Set<String> props = obj.getProperties();
316 Set<String> keys = obj.getKeys();
318 for (String prop : props) {
319 if (obj.isSimpleType(prop) && !keys.contains(prop)) {
320 val = obj.getValue(prop);
322 Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
323 if (metadata.isPresent()) {
324 // use the db name for the field rather than the object model
325 prop = metadata.get();
327 // this is because the index is registered as an Integer
328 if (val.getClass().equals(Long.class)) {
329 this.vertexHas(prop, Integer.valueOf(val.toString()));
331 this.vertexHas(prop, val);
344 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
345 String type = obj.getChildDBName();
346 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
347 if (abstractType != null) {
348 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
349 traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
351 traversal.has(AAIProperties.NODE_TYPE, type);
355 return (QueryBuilder<Vertex>) this;
359 * @throws NoEdgeRuleFoundException
360 * @throws AAIException
364 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
365 throws AAIException {
366 createTraversal(type, parent, child, false);
367 return (QueryBuilder<Vertex>) this;
372 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
373 throws AAIException {
374 this.createTraversal(type, parent, child, true);
375 return (QueryBuilder<Vertex>) this;
378 private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge)
379 throws AAIException {
380 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
381 if ("true".equals(isAbstractType)) {
382 markParentBoundary();
383 traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
386 this.edgeQueryToVertex(type, parent, child, null);
394 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
395 List<String> labels) throws AAIException {
396 this.edgeQueryToVertex(type, out, in, labels);
397 return (QueryBuilder<Vertex>) this;
400 private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent,
401 Introspector child, boolean isPrivateEdge) throws AAIException {
402 String childName = child.getDbName();
403 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
404 String[] inheritors = inheritorMetadata.split(",");
405 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
407 for (int i = 0; i < inheritors.length; i++) {
408 String inheritor = inheritors[i];
409 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
410 if (edgeRules.hasRule(qB.build())) {
411 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
413 rules = edgeRules.getRules(qB.edgeType(type).build());
414 } catch (EdgeRuleNotFoundException e) {
415 throw new NoEdgeRuleFoundException(e);
418 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
420 final List<String> inLabels = new ArrayList<>();
421 final List<String> outLabels = new ArrayList<>();
423 rules.values().forEach(rule -> {
424 if (rule.getDirection().equals(Direction.IN)) {
425 inLabels.add(rule.getLabel());
427 outLabels.add(rule.getLabel());
431 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
432 innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
433 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
434 innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
436 innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
437 __.in(inLabels.toArray(new String[inLabels.size()])));
440 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
441 unionTraversals.add(innerTraversal);
445 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
448 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
449 List<String> labels) throws AAIException {
450 Introspector outObj = loader.introspectorFromName(outNodeType);
451 Introspector inObj = loader.introspectorFromName(inNodeType);
452 this.edgeQuery(type, outObj, inObj, labels);
454 return (QueryBuilder<Edge>) this;
461 public QueryBuilder<E> union(QueryBuilder... builder) {
462 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
463 for (int i = 0; i < builder.length; i++) {
464 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
466 this.traversal.union(traversals);
476 public QueryBuilder<E> where(QueryBuilder... builder) {
477 for (int i = 0; i < builder.length; i++) {
478 this.traversal.where((GraphTraversal<Vertex, Vertex>) builder[i].getQuery());
489 public QueryBuilder<E> or(QueryBuilder... builder) {
490 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
491 for (int i = 0; i < builder.length; i++) {
492 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
494 this.traversal.or(traversals);
501 public QueryBuilder<E> store(String name) {
503 this.traversal.store(name);
510 public QueryBuilder<E> cap(String name) {
511 this.traversal.cap(name);
518 public QueryBuilder<E> unfold() {
519 this.traversal.unfold();
526 public QueryBuilder<E> dedup() {
528 this.traversal.dedup();
535 public QueryBuilder<E> emit() {
537 this.traversal.emit();
545 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
547 this.traversal.repeat((GraphTraversal<Vertex, E>) builder.getQuery());
554 public QueryBuilder<E> until(QueryBuilder<E> builder) {
555 this.traversal.until((GraphTraversal<Vertex, E>) builder.getQuery());
562 public QueryBuilder<E> groupCount() {
563 this.traversal.groupCount();
570 public QueryBuilder<E> both() {
571 this.traversal.both();
578 public QueryBuilder<Tree> tree() {
580 this.traversal.tree();
583 return (QueryBuilder<Tree>) this;
587 public QueryBuilder<E> by(String name) {
588 this.traversal.by(name);
595 public QueryBuilder<E> valueMap() {
596 this.traversal.valueMap();
603 public QueryBuilder<E> valueMap(String... names) {
604 this.traversal.valueMap(names);
614 public QueryBuilder<E> simplePath() {
615 this.traversal.simplePath();
624 public QueryBuilder<Path> path() {
625 this.traversal.path();
627 return (QueryBuilder<Path>) this;
631 public QueryBuilder<Edge> outE() {
632 this.traversal.outE();
634 return (QueryBuilder<Edge>) this;
638 public QueryBuilder<Edge> inE() {
639 this.traversal.inE();
641 return (QueryBuilder<Edge>) this;
645 public QueryBuilder<Vertex> outV() {
646 this.traversal.outV();
648 return (QueryBuilder<Vertex>) this;
652 public QueryBuilder<Vertex> inV() {
653 this.traversal.inV();
655 return (QueryBuilder<Vertex>) this;
659 public QueryBuilder<E> as(String name) {
660 this.traversal.as(name);
667 public QueryBuilder<E> not(QueryBuilder<E> builder) {
668 this.traversal.not(builder.getQuery());
675 public QueryBuilder<E> select(String name) {
676 this.traversal.select(name);
684 public QueryBuilder<E> select(String... names) {
685 if (names.length == 1) {
686 this.traversal.select(names[0]);
687 } else if (names.length == 2) {
688 this.traversal.select(names[0], names[1]);
689 } else if (names.length > 2) {
690 String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
691 this.traversal.select(names[0], names[1], otherNames);
702 * @param outObj the out type
703 * @param inObj the in type
704 * @throws NoEdgeRuleFoundException
705 * @throws AAIException
707 private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
708 throws AAIException {
709 String outType = outObj.getDbName();
710 String inType = inObj.getDbName();
712 if (outObj.isContainer()) {
713 outType = outObj.getChildDBName();
715 if (inObj.isContainer()) {
716 inType = inObj.getChildDBName();
718 markParentBoundary();
719 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
720 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
722 if (labels == null) {
724 rules.putAll(edgeRules.getRules(qB.build()));
725 } catch (EdgeRuleNotFoundException e) {
726 // is ok per original functioning of this section
727 // TODO add "best try" sort of flag to the EdgeRuleQuery
728 // to indicate if the exception should be thrown or not
731 for (String label : labels) {
733 rules.putAll(edgeRules.getRules(qB.label(label).build()));
734 } catch (EdgeRuleNotFoundException e) {
735 throw new NoEdgeRuleFoundException(e);
738 if (rules.isEmpty()) {
739 throw new NoEdgeRuleFoundException(
740 "No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
744 final List<String> inLabels = new ArrayList<>();
745 final List<String> outLabels = new ArrayList<>();
747 for (EdgeRule rule : rules.values()) {
748 if (labels != null && !labels.contains(rule.getLabel())) {
751 if (Direction.IN.equals(rule.getDirection())) {
752 inLabels.add(rule.getLabel());
754 outLabels.add(rule.getLabel());
759 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
760 traversal.out(outLabels.toArray(new String[outLabels.size()]));
761 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
762 traversal.in(inLabels.toArray(new String[inLabels.size()]));
764 traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
765 __.in(inLabels.toArray(new String[inLabels.size()])));
770 this.createContainerQuery(inObj);
777 * @param outObj the out type
778 * @param inObj the in type
779 * @throws NoEdgeRuleFoundException
780 * @throws AAIException
782 private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
783 throws AAIException {
784 String outType = outObj.getDbName();
785 String inType = inObj.getDbName();
787 if (outObj.isContainer()) {
788 outType = outObj.getChildDBName();
790 if (inObj.isContainer()) {
791 inType = inObj.getChildDBName();
794 markParentBoundary();
795 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
796 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
799 if (labels == null) {
800 rules.putAll(edgeRules.getRules(qB.build()));
802 for (String label : labels) {
803 rules.putAll(edgeRules.getRules(qB.label(label).build()));
806 } catch (EdgeRuleNotFoundException e) {
807 throw new NoEdgeRuleFoundException(e);
810 final List<String> inLabels = new ArrayList<>();
811 final List<String> outLabels = new ArrayList<>();
813 for (EdgeRule rule : rules.values()) {
814 if (labels != null && !labels.contains(rule.getLabel())) {
817 if (Direction.IN.equals(rule.getDirection())) {
818 inLabels.add(rule.getLabel());
820 outLabels.add(rule.getLabel());
825 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
826 traversal.outE(outLabels.toArray(new String[outLabels.size()]));
827 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
828 traversal.inE(inLabels.toArray(new String[inLabels.size()]));
830 traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])),
831 __.inE(inLabels.toArray(new String[inLabels.size()])));
836 public QueryBuilder<E> limit(long amount) {
837 traversal.limit(amount);
845 public <E2> E2 getQuery() {
846 return (E2) this.traversal;
853 public QueryBuilder<E> getParentQuery() {
855 return cloneQueryAtStep(parentStepIndex);
859 public QueryBuilder<E> getContainerQuery() {
861 if (this.parentStepIndex == 0) {
862 return removeQueryStepsBetween(0, containerStepIndex);
864 return cloneQueryAtStep(containerStepIndex);
872 public void markParentBoundary() {
873 parentStepIndex = stepIndex;
877 public void markContainer() {
878 containerStepIndex = stepIndex;
885 public Vertex getStart() {
889 protected int getParentStepIndex() {
890 return parentStepIndex;
893 protected int getContainerStepIndex() {
894 return containerStepIndex;
897 protected int getStepIndex() {
908 protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
910 protected void executeQuery() {
912 Admin<Vertex, Vertex> admin;
914 this.completeTraversal = traversal.asAdmin();
916 admin = source.V().asAdmin();
917 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
919 this.completeTraversal = (Admin<Vertex, E>) admin;
926 public boolean hasNext() {
927 if (this.completeTraversal == null) {
931 return this.completeTraversal.hasNext();
936 if (this.completeTraversal == null) {
940 return this.completeTraversal.next();
944 public List<E> toList() {
945 if (this.completeTraversal == null) {
948 return this.completeTraversal.toList();
951 protected QueryBuilder<Edge> has(String key, String value) {
952 traversal.has(key, value);
954 return (QueryBuilder<Edge>) this;