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;
28 import org.apache.tinkerpop.gremlin.process.traversal.P;
29 import org.apache.tinkerpop.gremlin.process.traversal.Path;
30 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
31 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
35 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
36 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
37 import org.apache.tinkerpop.gremlin.structure.Direction;
38 import org.apache.tinkerpop.gremlin.structure.Edge;
39 import org.apache.tinkerpop.gremlin.structure.Vertex;
40 import org.onap.aai.db.props.AAIProperties;
41 import org.onap.aai.edges.EdgeRule;
42 import org.onap.aai.edges.EdgeRuleQuery;
43 import org.onap.aai.edges.enums.EdgeType;
44 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
45 import org.onap.aai.exceptions.AAIException;
46 import org.onap.aai.introspection.Introspector;
47 import org.onap.aai.introspection.Loader;
48 import org.onap.aai.schema.enums.ObjectMetadata;
49 import org.onap.aai.schema.enums.PropertyMetadata;
50 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
53 * The Class GraphTraversalBuilder.
55 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
57 protected GraphTraversal<Vertex, E> traversal = null;
58 protected Admin<Vertex, E> completeTraversal = null;
61 * Instantiates a new graph traversal builder.
63 * @param loader the loader
65 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
66 super(loader, source);
67 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
72 * Instantiates a new graph traversal builder.
74 * @param loader the loader
75 * @param start the start
77 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
78 super(loader, source, start);
80 traversal = (GraphTraversal<Vertex, E>) __.__(start);
88 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
90 // correct value call because the index is registered as an Integer
91 this.vertexHas(key, this.correctObjectType(value));
93 return (QueryBuilder<Vertex>) this;
97 protected void vertexHas(String key, Object value) {
98 traversal.has(key, value);
102 protected void vertexHasNot(String key) {
103 traversal.hasNot(key);
107 protected void vertexHas(String key) {
111 // TODO: Remove this once we test this - at this point i dont thib this is required
112 // because predicare is an object
115 * protected void vertexHas(final String key, final P<?> predicate) {
116 * traversal.has(key, predicate);
124 public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
126 // this is because the index is registered as an Integer
127 List<Object> correctedValues = new ArrayList<>();
128 for (Object item : values) {
129 correctedValues.add(this.correctObjectType(item));
132 this.vertexHas(key, P.within(correctedValues));
134 return (QueryBuilder<Vertex>) this;
140 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
141 ArrayList<String> values = new ArrayList<>(Arrays.asList(value.split(",")));
142 int size = values.size();
143 for (int i = 0; i < size; i++) {
144 values.set(i, values.get(i).trim());
146 this.vertexHas(key, P.within(values));
149 return (QueryBuilder<Vertex>) this;
156 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
158 // correct value call because the index is registered as an Integer
159 // TODO Check if this needs to be in QB and add these as internal
160 this.vertexHas(key, org.janusgraph.core.attribute.Text.textPrefix(value));
163 return (QueryBuilder<Vertex>) this;
170 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
173 return (QueryBuilder<Vertex>) this;
180 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
181 this.vertexHasNot(key);
183 return (QueryBuilder<Vertex>) this;
190 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
192 // correct value call because the index is registered as an Integer
193 this.vertexHas(key, P.neq(this.correctObjectType(value)));
195 return (QueryBuilder<Vertex>) this;
202 public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
204 // this is because the index is registered as an Integer
205 List<Object> correctedValues = new ArrayList<>();
206 for (Object item : values) {
207 correctedValues.add(this.correctObjectType(item));
210 this.vertexHas(key, P.without(correctedValues));
212 return (QueryBuilder<Vertex>) this;
216 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) {
217 this.vertexHas(key, P.gte(this.correctObjectType(value)));
220 return (QueryBuilder<Vertex>) this;
224 public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) {
225 this.vertexHas(key, P.lte(this.correctObjectType(value)));
228 return (QueryBuilder<Vertex>) this;
235 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
236 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
238 return (QueryBuilder<Vertex>) this;
245 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
247 for (Map.Entry<String, String> es : map.entrySet()) {
248 this.vertexHas(es.getKey(), es.getValue());
251 traversal.has(AAIProperties.NODE_TYPE, type);
253 return (QueryBuilder<Vertex>) this;
257 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
259 if (value != null && !"".equals(value)) {
260 boolean bValue = false;
262 if (value instanceof String) {// "true"
263 bValue = Boolean.valueOf(value.toString());
264 } else if (value instanceof Boolean) {// true
265 bValue = (Boolean) value;
268 this.vertexHas(key, bValue);
271 return (QueryBuilder<Vertex>) this;
278 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
279 Set<String> keys = obj.getKeys();
281 for (String key : keys) {
282 val = obj.getValue(key);
283 Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
284 if (metadata.isPresent()) {
285 // use the db name for the field rather than the object model
286 key = metadata.get();
289 // this is because the index is registered as an Integer
290 if (val.getClass().equals(Long.class)) {
291 this.vertexHas(key, new Integer(val.toString()));
293 this.vertexHas(key, val);
298 return (QueryBuilder<Vertex>) this;
302 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
303 this.createKeyQuery(obj);
304 allPropertiesQuery(obj);
305 this.createContainerQuery(obj);
306 return (QueryBuilder<Vertex>) this;
309 private void allPropertiesQuery(Introspector obj) {
310 Set<String> props = obj.getProperties();
311 Set<String> keys = obj.getKeys();
313 for (String prop : props) {
314 if (obj.isSimpleType(prop) && !keys.contains(prop)) {
315 val = obj.getValue(prop);
317 Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
318 if (metadata.isPresent()) {
319 // use the db name for the field rather than the object model
320 prop = metadata.get();
322 // this is because the index is registered as an Integer
323 if (val.getClass().equals(Long.class)) {
324 this.vertexHas(prop, new Integer(val.toString()));
326 this.vertexHas(prop, val);
339 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
340 String type = obj.getChildDBName();
341 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
342 if (abstractType != null) {
343 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
344 traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
346 traversal.has(AAIProperties.NODE_TYPE, type);
350 return (QueryBuilder<Vertex>) this;
354 * @throws NoEdgeRuleFoundException
355 * @throws AAIException
359 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
360 throws AAIException {
361 createTraversal(type, parent, child, false);
362 return (QueryBuilder<Vertex>) this;
367 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
368 throws AAIException {
369 this.createTraversal(type, parent, child, true);
370 return (QueryBuilder<Vertex>) this;
373 private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge)
374 throws AAIException {
375 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
376 if ("true".equals(isAbstractType)) {
377 markParentBoundary();
378 traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
381 this.edgeQueryToVertex(type, parent, child, null);
389 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
390 List<String> labels) throws AAIException {
391 this.edgeQueryToVertex(type, out, in, labels);
392 return (QueryBuilder<Vertex>) this;
395 private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent,
396 Introspector child, boolean isPrivateEdge) throws AAIException {
397 String childName = child.getDbName();
398 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
399 String[] inheritors = inheritorMetadata.split(",");
400 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
402 for (int i = 0; i < inheritors.length; i++) {
403 String inheritor = inheritors[i];
404 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
405 if (edgeRules.hasRule(qB.build())) {
406 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
408 rules = edgeRules.getRules(qB.edgeType(type).build());
409 } catch (EdgeRuleNotFoundException e) {
410 throw new NoEdgeRuleFoundException(e);
413 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
415 final List<String> inLabels = new ArrayList<>();
416 final List<String> outLabels = new ArrayList<>();
418 rules.values().forEach(rule -> {
419 if (rule.getDirection().equals(Direction.IN)) {
420 inLabels.add(rule.getLabel());
422 outLabels.add(rule.getLabel());
426 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
427 innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
428 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
429 innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
431 innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
432 __.in(inLabels.toArray(new String[inLabels.size()])));
435 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
436 unionTraversals.add(innerTraversal);
440 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
443 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
444 List<String> labels) throws AAIException {
445 Introspector outObj = loader.introspectorFromName(outNodeType);
446 Introspector inObj = loader.introspectorFromName(inNodeType);
447 this.edgeQuery(type, outObj, inObj, labels);
449 return (QueryBuilder<Edge>) this;
456 public QueryBuilder<E> union(QueryBuilder... builder) {
457 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
458 for (int i = 0; i < builder.length; i++) {
459 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
461 this.traversal.union(traversals);
471 public QueryBuilder<E> where(QueryBuilder... builder) {
472 for (int i = 0; i < builder.length; i++) {
473 this.traversal.where((GraphTraversal<Vertex, Vertex>) builder[i].getQuery());
484 public QueryBuilder<E> or(QueryBuilder... builder) {
485 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
486 for (int i = 0; i < builder.length; i++) {
487 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
489 this.traversal.or(traversals);
496 public QueryBuilder<E> store(String name) {
498 this.traversal.store(name);
505 public QueryBuilder<E> cap(String name) {
506 this.traversal.cap(name);
513 public QueryBuilder<E> unfold() {
514 this.traversal.unfold();
521 public QueryBuilder<E> dedup() {
523 this.traversal.dedup();
530 public QueryBuilder<E> emit() {
532 this.traversal.emit();
540 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
542 this.traversal.repeat((GraphTraversal<Vertex, E>) builder.getQuery());
549 public QueryBuilder<E> until(QueryBuilder<E> builder) {
550 this.traversal.until((GraphTraversal<Vertex, E>) builder.getQuery());
557 public QueryBuilder<E> groupCount() {
558 this.traversal.groupCount();
565 public QueryBuilder<E> both() {
566 this.traversal.both();
573 public QueryBuilder<Tree> tree() {
575 this.traversal.tree();
578 return (QueryBuilder<Tree>) this;
582 public QueryBuilder<E> by(String name) {
583 this.traversal.by(name);
590 public QueryBuilder<E> valueMap() {
591 this.traversal.valueMap();
598 public QueryBuilder<E> valueMap(String... names) {
599 this.traversal.valueMap(names);
609 public QueryBuilder<E> simplePath() {
610 this.traversal.simplePath();
619 public QueryBuilder<Path> path() {
620 this.traversal.path();
622 return (QueryBuilder<Path>) this;
626 public QueryBuilder<Edge> outE() {
627 this.traversal.outE();
629 return (QueryBuilder<Edge>) this;
633 public QueryBuilder<Edge> inE() {
634 this.traversal.inE();
636 return (QueryBuilder<Edge>) this;
640 public QueryBuilder<Vertex> outV() {
641 this.traversal.outV();
643 return (QueryBuilder<Vertex>) this;
647 public QueryBuilder<Vertex> inV() {
648 this.traversal.inV();
650 return (QueryBuilder<Vertex>) this;
654 public QueryBuilder<E> as(String name) {
655 this.traversal.as(name);
662 public QueryBuilder<E> not(QueryBuilder<E> builder) {
663 this.traversal.not(builder.getQuery());
670 public QueryBuilder<E> select(String name) {
671 this.traversal.select(name);
679 public QueryBuilder<E> select(String... names) {
680 if (names.length == 1) {
681 this.traversal.select(names[0]);
682 } else if (names.length == 2) {
683 this.traversal.select(names[0], names[1]);
684 } else if (names.length > 2) {
685 String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
686 this.traversal.select(names[0], names[1], otherNames);
697 * @param outObj the out type
698 * @param inObj the in type
699 * @throws NoEdgeRuleFoundException
700 * @throws AAIException
702 private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
703 throws AAIException {
704 String outType = outObj.getDbName();
705 String inType = inObj.getDbName();
707 if (outObj.isContainer()) {
708 outType = outObj.getChildDBName();
710 if (inObj.isContainer()) {
711 inType = inObj.getChildDBName();
713 markParentBoundary();
714 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
715 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
717 if (labels == null) {
719 rules.putAll(edgeRules.getRules(qB.build()));
720 } catch (EdgeRuleNotFoundException e) {
721 // is ok per original functioning of this section
722 // TODO add "best try" sort of flag to the EdgeRuleQuery
723 // to indicate if the exception should be thrown or not
726 for (String label : labels) {
728 rules.putAll(edgeRules.getRules(qB.label(label).build()));
729 } catch (EdgeRuleNotFoundException e) {
730 throw new NoEdgeRuleFoundException(e);
733 if (rules.isEmpty()) {
734 throw new NoEdgeRuleFoundException(
735 "No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
739 final List<String> inLabels = new ArrayList<>();
740 final List<String> outLabels = new ArrayList<>();
742 for (EdgeRule rule : rules.values()) {
743 if (labels != null && !labels.contains(rule.getLabel())) {
746 if (Direction.IN.equals(rule.getDirection())) {
747 inLabels.add(rule.getLabel());
749 outLabels.add(rule.getLabel());
754 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
755 traversal.out(outLabels.toArray(new String[outLabels.size()]));
756 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
757 traversal.in(inLabels.toArray(new String[inLabels.size()]));
759 traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
760 __.in(inLabels.toArray(new String[inLabels.size()])));
765 this.createContainerQuery(inObj);
772 * @param outObj the out type
773 * @param inObj the in type
774 * @throws NoEdgeRuleFoundException
775 * @throws AAIException
777 private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
778 throws AAIException {
779 String outType = outObj.getDbName();
780 String inType = inObj.getDbName();
782 if (outObj.isContainer()) {
783 outType = outObj.getChildDBName();
785 if (inObj.isContainer()) {
786 inType = inObj.getChildDBName();
789 markParentBoundary();
790 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
791 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
794 if (labels == null) {
795 rules.putAll(edgeRules.getRules(qB.build()));
797 for (String label : labels) {
798 rules.putAll(edgeRules.getRules(qB.label(label).build()));
801 } catch (EdgeRuleNotFoundException e) {
802 throw new NoEdgeRuleFoundException(e);
805 final List<String> inLabels = new ArrayList<>();
806 final List<String> outLabels = new ArrayList<>();
808 for (EdgeRule rule : rules.values()) {
809 if (labels != null && !labels.contains(rule.getLabel())) {
812 if (Direction.IN.equals(rule.getDirection())) {
813 inLabels.add(rule.getLabel());
815 outLabels.add(rule.getLabel());
820 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
821 traversal.outE(outLabels.toArray(new String[outLabels.size()]));
822 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
823 traversal.inE(inLabels.toArray(new String[inLabels.size()]));
825 traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])),
826 __.inE(inLabels.toArray(new String[inLabels.size()])));
831 public QueryBuilder<E> limit(long amount) {
832 traversal.limit(amount);
840 public <E2> E2 getQuery() {
841 return (E2) this.traversal;
848 public QueryBuilder<E> getParentQuery() {
850 return cloneQueryAtStep(parentStepIndex);
854 public QueryBuilder<E> getContainerQuery() {
856 if (this.parentStepIndex == 0) {
857 return removeQueryStepsBetween(0, containerStepIndex);
859 return cloneQueryAtStep(containerStepIndex);
867 public void markParentBoundary() {
868 parentStepIndex = stepIndex;
872 public void markContainer() {
873 containerStepIndex = stepIndex;
880 public Vertex getStart() {
884 protected int getParentStepIndex() {
885 return parentStepIndex;
888 protected int getContainerStepIndex() {
889 return containerStepIndex;
892 protected int getStepIndex() {
903 protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
905 protected void executeQuery() {
909 this.completeTraversal = traversal.asAdmin();
911 admin = source.V().asAdmin();
912 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
914 this.completeTraversal = (Admin<Vertex, E>) admin;
921 public boolean hasNext() {
922 if (this.completeTraversal == null) {
926 return this.completeTraversal.hasNext();
931 if (this.completeTraversal == null) {
935 return this.completeTraversal.next();
939 public List<E> toList() {
940 if (this.completeTraversal == null) {
943 return this.completeTraversal.toList();
946 protected QueryBuilder<Edge> has(String key, String value) {
947 traversal.has(key, value);
949 return (QueryBuilder<Edge>) this;