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);
68 traversal = (GraphTraversal<Vertex, E>) __.<E>start();
73 * Instantiates a new graph traversal builder.
75 * @param loader the loader
76 * @param start the start
78 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
79 super(loader, source, start);
81 traversal = (GraphTraversal<Vertex, E>) __.__(start);
89 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
91 // correct value call because the index is registered as an Integer
92 traversal.has(key, this.correctObjectType(value));
95 return (QueryBuilder<Vertex>) this;
102 public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
104 // this is because the index is registered as an Integer
105 List<Object> correctedValues = new ArrayList<>();
106 for (Object item : values) {
107 correctedValues.add(this.correctObjectType(item));
110 traversal.has(key, P.within(correctedValues));
113 return (QueryBuilder<Vertex>) this;
120 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
122 // correct value call because the index is registered as an Integer
123 traversal.has(key, org.janusgraph.core.attribute.Text.textPrefix(value));
126 return (QueryBuilder<Vertex>) this;
133 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
137 return (QueryBuilder<Vertex>) this;
144 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
146 traversal.hasNot(key);
148 return (QueryBuilder<Vertex>) this;
155 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
157 // correct value call because the index is registered as an Integer
158 traversal.has(key, P.neq(this.correctObjectType(value)));
161 return (QueryBuilder<Vertex>) this;
168 public QueryBuilder<Vertex> getVerticesExcludeByProperty(final String key, final List<?> values) {
170 // this is because the index is registered as an Integer
171 List<Object> correctedValues = new ArrayList<>();
172 for (Object item : values) {
173 correctedValues.add(this.correctObjectType(item));
176 traversal.has(key, P.without(correctedValues));
179 return (QueryBuilder<Vertex>) this;
183 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(final String key, Object value) {
185 traversal.has(key, P.gte(this.correctObjectType(value)));
188 return (QueryBuilder<Vertex>) this;
192 public QueryBuilder<Vertex> getVerticesLessThanProperty(final String key, Object value) {
194 traversal.has(key, P.lte(this.correctObjectType(value)));
197 return (QueryBuilder<Vertex>) this;
204 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
205 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
207 return (QueryBuilder<Vertex>) this;
214 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
216 for (Map.Entry<String, String> es : map.entrySet()) {
217 traversal.has(es.getKey(), es.getValue());
220 traversal.has(AAIProperties.NODE_TYPE, type);
222 return (QueryBuilder<Vertex>) this;
226 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
228 if (value != null && !"".equals(value)) {
229 boolean bValue = false;
231 if (value instanceof String) {// "true"
232 bValue = Boolean.valueOf(value.toString());
233 } else if (value instanceof Boolean) {// true
234 bValue = (Boolean) value;
237 traversal.has(key, bValue);
240 return (QueryBuilder<Vertex>) this;
247 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
248 Set<String> keys = obj.getKeys();
250 for (String key : keys) {
251 val = obj.getValue(key);
252 Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
253 if (metadata.isPresent()) {
254 // use the db name for the field rather than the object model
255 key = metadata.get();
258 // this is because the index is registered as an Integer
259 if (val.getClass().equals(Long.class)) {
260 traversal.has(key, new Integer(val.toString()));
262 traversal.has(key, val);
267 return (QueryBuilder<Vertex>) this;
271 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
272 this.createKeyQuery(obj);
273 allPropertiesQuery(obj);
274 this.createContainerQuery(obj);
275 return (QueryBuilder<Vertex>) this;
278 private void allPropertiesQuery(Introspector obj) {
279 Set<String> props = obj.getProperties();
280 Set<String> keys = obj.getKeys();
282 for (String prop : props) {
283 if (obj.isSimpleType(prop) && !keys.contains(prop)) {
284 val = obj.getValue(prop);
286 Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
287 if (metadata.isPresent()) {
288 // use the db name for the field rather than the object model
289 prop = metadata.get();
291 // this is because the index is registered as an Integer
292 if (val.getClass().equals(Long.class)) {
293 traversal.has(prop, new Integer(val.toString()));
295 traversal.has(prop, val);
308 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
309 String type = obj.getChildDBName();
310 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
311 if (abstractType != null) {
312 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
313 traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
315 traversal.has(AAIProperties.NODE_TYPE, type);
319 return (QueryBuilder<Vertex>) this;
323 * @throws NoEdgeRuleFoundException
324 * @throws AAIException
328 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
329 throws AAIException {
330 createTraversal(type, parent, child, false);
331 return (QueryBuilder<Vertex>) this;
336 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
337 throws AAIException {
338 this.createTraversal(type, parent, child, true);
339 return (QueryBuilder<Vertex>) this;
342 private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge)
343 throws AAIException {
344 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
345 if ("true".equals(isAbstractType)) {
346 markParentBoundary();
347 traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
350 this.edgeQueryToVertex(type, parent, child, null);
359 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
360 List<String> labels) throws AAIException {
361 this.edgeQueryToVertex(type, out, in, labels);
362 return (QueryBuilder<Vertex>) this;
365 private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent,
366 Introspector child, boolean isPrivateEdge) throws AAIException {
367 String childName = child.getDbName();
368 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
369 String[] inheritors = inheritorMetadata.split(",");
370 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
372 for (int i = 0; i < inheritors.length; i++) {
373 String inheritor = inheritors[i];
374 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
375 if (edgeRules.hasRule(qB.build())) {
376 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
378 rules = edgeRules.getRules(qB.edgeType(type).build());
379 } catch (EdgeRuleNotFoundException e) {
380 throw new NoEdgeRuleFoundException(e);
383 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
385 final List<String> inLabels = new ArrayList<>();
386 final List<String> outLabels = new ArrayList<>();
388 rules.values().forEach(rule -> {
389 if (rule.getDirection().equals(Direction.IN)) {
390 inLabels.add(rule.getLabel());
392 outLabels.add(rule.getLabel());
396 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
397 innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
398 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
399 innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
401 innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
402 __.in(inLabels.toArray(new String[inLabels.size()])));
405 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
406 unionTraversals.add(innerTraversal);
410 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
413 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
414 List<String> labels) throws AAIException {
415 Introspector outObj = loader.introspectorFromName(outNodeType);
416 Introspector inObj = loader.introspectorFromName(inNodeType);
417 this.edgeQuery(type, outObj, inObj, labels);
419 return (QueryBuilder<Edge>) this;
426 public QueryBuilder<E> union(QueryBuilder... builder) {
427 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
428 for (int i = 0; i < builder.length; i++) {
429 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
431 this.traversal.union(traversals);
441 public QueryBuilder<E> where(QueryBuilder... builder) {
442 for (int i = 0; i < builder.length; i++) {
443 this.traversal.where((GraphTraversal<Vertex, Vertex>) builder[i].getQuery());
454 public QueryBuilder<E> or(QueryBuilder... builder) {
455 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
456 for (int i = 0; i < builder.length; i++) {
457 traversals[i] = (GraphTraversal<Vertex, Vertex>) builder[i].getQuery();
459 this.traversal.or(traversals);
466 public QueryBuilder<E> store(String name) {
468 this.traversal.store(name);
475 public QueryBuilder<E> cap(String name) {
476 this.traversal.cap(name);
483 public QueryBuilder<E> unfold() {
484 this.traversal.unfold();
491 public QueryBuilder<E> dedup() {
493 this.traversal.dedup();
500 public QueryBuilder<E> emit() {
502 this.traversal.emit();
510 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
512 this.traversal.repeat((GraphTraversal<Vertex, E>) builder.getQuery());
519 public QueryBuilder<E> until(QueryBuilder<E> builder) {
520 this.traversal.until((GraphTraversal<Vertex, E>) builder.getQuery());
527 public QueryBuilder<E> groupCount() {
528 this.traversal.groupCount();
535 public QueryBuilder<E> both() {
536 this.traversal.both();
543 public QueryBuilder<Tree> tree() {
545 this.traversal.tree();
548 return (QueryBuilder<Tree>) this;
552 public QueryBuilder<E> by(String name) {
553 this.traversal.by(name);
563 public QueryBuilder<E> simplePath() {
564 this.traversal.simplePath();
573 public QueryBuilder<Path> path() {
574 this.traversal.path();
576 return (QueryBuilder<Path>) this;
580 public QueryBuilder<Edge> outE() {
581 this.traversal.outE();
583 return (QueryBuilder<Edge>) this;
587 public QueryBuilder<Edge> inE() {
588 this.traversal.inE();
590 return (QueryBuilder<Edge>) this;
594 public QueryBuilder<Vertex> outV() {
595 this.traversal.outV();
597 return (QueryBuilder<Vertex>) this;
601 public QueryBuilder<Vertex> inV() {
602 this.traversal.inV();
604 return (QueryBuilder<Vertex>) this;
608 public QueryBuilder<E> as(String name) {
609 this.traversal.as(name);
616 public QueryBuilder<E> not(QueryBuilder<E> builder) {
617 this.traversal.not(builder.getQuery());
624 public QueryBuilder<E> select(String name) {
625 this.traversal.select(name);
633 public QueryBuilder<E> select(String... names) {
634 if (names.length == 1) {
635 this.traversal.select(names[0]);
636 } else if (names.length == 2) {
637 this.traversal.select(names[0], names[1]);
638 } else if (names.length > 2) {
639 String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
640 this.traversal.select(names[0], names[1], otherNames);
651 * @param outObj the out type
652 * @param inObj the in type
653 * @throws NoEdgeRuleFoundException
654 * @throws AAIException
656 private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
657 throws AAIException {
658 String outType = outObj.getDbName();
659 String inType = inObj.getDbName();
661 if (outObj.isContainer()) {
662 outType = outObj.getChildDBName();
664 if (inObj.isContainer()) {
665 inType = inObj.getChildDBName();
667 markParentBoundary();
668 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
669 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
671 if (labels == null) {
673 rules.putAll(edgeRules.getRules(qB.build()));
674 } catch (EdgeRuleNotFoundException e) {
675 // is ok per original functioning of this section
676 // TODO add "best try" sort of flag to the EdgeRuleQuery
677 // to indicate if the exception should be thrown or not
680 for (String label : labels) {
682 rules.putAll(edgeRules.getRules(qB.label(label).build()));
683 } catch (EdgeRuleNotFoundException e) {
684 throw new NoEdgeRuleFoundException(e);
687 if (rules.isEmpty()) {
688 throw new NoEdgeRuleFoundException(
689 "No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
693 final List<String> inLabels = new ArrayList<>();
694 final List<String> outLabels = new ArrayList<>();
696 for (EdgeRule rule : rules.values()) {
697 if (labels != null && !labels.contains(rule.getLabel())) {
700 if (Direction.IN.equals(rule.getDirection())) {
701 inLabels.add(rule.getLabel());
703 outLabels.add(rule.getLabel());
708 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
709 traversal.out(outLabels.toArray(new String[outLabels.size()]));
710 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
711 traversal.in(inLabels.toArray(new String[inLabels.size()]));
713 traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])),
714 __.in(inLabels.toArray(new String[inLabels.size()])));
719 this.createContainerQuery(inObj);
726 * @param outObj the out type
727 * @param inObj the in type
728 * @throws NoEdgeRuleFoundException
729 * @throws AAIException
731 private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels)
732 throws AAIException {
733 String outType = outObj.getDbName();
734 String inType = inObj.getDbName();
736 if (outObj.isContainer()) {
737 outType = outObj.getChildDBName();
739 if (inObj.isContainer()) {
740 inType = inObj.getChildDBName();
743 markParentBoundary();
744 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
745 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
748 if (labels == null) {
749 rules.putAll(edgeRules.getRules(qB.build()));
751 for (String label : labels) {
752 rules.putAll(edgeRules.getRules(qB.label(label).build()));
755 } catch (EdgeRuleNotFoundException e) {
756 throw new NoEdgeRuleFoundException(e);
759 final List<String> inLabels = new ArrayList<>();
760 final List<String> outLabels = new ArrayList<>();
762 for (EdgeRule rule : rules.values()) {
763 if (labels != null && !labels.contains(rule.getLabel())) {
766 if (Direction.IN.equals(rule.getDirection())) {
767 inLabels.add(rule.getLabel());
769 outLabels.add(rule.getLabel());
774 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
775 traversal.outE(outLabels.toArray(new String[outLabels.size()]));
776 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
777 traversal.inE(inLabels.toArray(new String[inLabels.size()]));
779 traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])),
780 __.inE(inLabels.toArray(new String[inLabels.size()])));
785 public QueryBuilder<E> limit(long amount) {
786 traversal.limit(amount);
794 public <E2> E2 getQuery() {
795 return (E2) this.traversal;
802 public QueryBuilder<E> getParentQuery() {
804 return cloneQueryAtStep(parentStepIndex);
808 public QueryBuilder<E> getContainerQuery() {
810 if (this.parentStepIndex == 0) {
811 return removeQueryStepsBetween(0, containerStepIndex);
813 return cloneQueryAtStep(containerStepIndex);
821 public void markParentBoundary() {
822 parentStepIndex = stepIndex;
826 public void markContainer() {
827 containerStepIndex = stepIndex;
834 public Vertex getStart() {
838 protected int getParentStepIndex() {
839 return parentStepIndex;
842 protected int getContainerStepIndex() {
843 return containerStepIndex;
846 protected int getStepIndex() {
857 protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
859 protected void executeQuery() {
863 this.completeTraversal = traversal.asAdmin();
865 admin = source.V().asAdmin();
866 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
868 this.completeTraversal = (Admin<Vertex, E>) admin;
875 public boolean hasNext() {
876 if (this.completeTraversal == null) {
880 return this.completeTraversal.hasNext();
885 if (this.completeTraversal == null) {
889 return this.completeTraversal.next();
893 public List<E> toList() {
894 if (this.completeTraversal == null) {
897 return this.completeTraversal.toList();
900 protected QueryBuilder<Edge> has(String key, String value) {
901 traversal.has(key, value);
903 return (QueryBuilder<Edge>) this;