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=========================================================
20 package org.onap.aai.query.builder;
22 import org.apache.tinkerpop.gremlin.process.traversal.P;
23 import org.apache.tinkerpop.gremlin.process.traversal.Path;
24 import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
25 import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin;
26 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
27 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
28 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
29 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
30 import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
31 import org.apache.tinkerpop.gremlin.structure.Direction;
32 import org.apache.tinkerpop.gremlin.structure.Edge;
33 import org.apache.tinkerpop.gremlin.structure.Vertex;
34 import org.onap.aai.db.props.AAIProperties;
35 import org.onap.aai.exceptions.AAIException;
36 import org.onap.aai.introspection.Introspector;
37 import org.onap.aai.introspection.Loader;
38 import org.onap.aai.schema.enums.ObjectMetadata;
39 import org.onap.aai.schema.enums.PropertyMetadata;
40 import org.onap.aai.edges.EdgeRule;
41 import org.onap.aai.edges.EdgeRuleQuery;
42 import org.onap.aai.edges.enums.EdgeType;
43 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
44 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
46 import com.google.common.collect.ArrayListMultimap;
47 import com.google.common.collect.Multimap;
52 * The Class GraphTraversalBuilder.
54 public abstract class GraphTraversalBuilder<E> extends QueryBuilder<E> {
56 protected GraphTraversal<Vertex, E> traversal = null;
57 protected Admin<Vertex, E> completeTraversal = null;
60 * Instantiates a new graph traversal builder.
62 * @param loader the loader
64 public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) {
65 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 traversal.has(key, this.correctObjectType(value));
94 return (QueryBuilder<Vertex>) this;
101 public QueryBuilder<Vertex> getVerticesByProperty(final String key, final List<?> values) {
103 //this is because the index is registered as an Integer
104 List<Object> correctedValues = new ArrayList<>();
105 for (Object item : values) {
106 correctedValues.add(this.correctObjectType(item));
109 traversal.has(key, P.within(correctedValues));
112 return (QueryBuilder<Vertex>) this;
119 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
121 // correct value call because the index is registered as an Integer
122 traversal.has(key, org.janusgraph.core.attribute.Text.textPrefix(value));
125 return (QueryBuilder<Vertex>) this;
132 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
136 return (QueryBuilder<Vertex>) this;
143 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
145 traversal.hasNot(key);
147 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;
205 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
206 traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType);
208 return (QueryBuilder<Vertex>) this;
215 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
217 for (Map.Entry<String, String> es : map.entrySet()) {
218 traversal.has(es.getKey(), es.getValue());
221 traversal.has(AAIProperties.NODE_TYPE, type);
223 return (QueryBuilder<Vertex>) this;
227 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
229 if(value!=null && !"".equals(value)) {
230 boolean bValue = false;
232 if(value instanceof String){//"true"
233 bValue = Boolean.valueOf(value.toString());
234 } else if(value instanceof Boolean){//true
235 bValue = (Boolean) value;
238 traversal.has(key, bValue);
241 return (QueryBuilder<Vertex>) this;
248 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
249 Set<String> keys = obj.getKeys();
251 for (String key : keys) {
252 val = obj.getValue(key);
253 Optional<String> metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS);
254 if (metadata.isPresent()) {
255 //use the db name for the field rather than the object model
256 key = metadata.get();
259 //this is because the index is registered as an Integer
260 if (val.getClass().equals(Long.class)) {
261 traversal.has(key,new Integer(val.toString()));
263 traversal.has(key, val);
268 return (QueryBuilder<Vertex>) this;
272 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
273 this.createKeyQuery(obj);
274 allPropertiesQuery(obj);
275 this.createContainerQuery(obj);
276 return (QueryBuilder<Vertex>) this;
279 private void allPropertiesQuery(Introspector obj) {
280 Set<String> props = obj.getProperties();
281 Set<String> keys = obj.getKeys();
283 for (String prop : props) {
284 if (obj.isSimpleType(prop) && !keys.contains(prop)) {
285 val = obj.getValue(prop);
287 Optional<String> metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS);
288 if (metadata.isPresent()) {
289 //use the db name for the field rather than the object model
290 prop = metadata.get();
292 //this is because the index is registered as an Integer
293 if (val.getClass().equals(Long.class)) {
294 traversal.has(prop,new Integer(val.toString()));
296 traversal.has(prop, val);
309 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
310 String type = obj.getChildDBName();
311 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
312 if (abstractType != null) {
313 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
314 traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors));
316 traversal.has(AAIProperties.NODE_TYPE, type);
320 return (QueryBuilder<Vertex>) this;
324 * @throws NoEdgeRuleFoundException
325 * @throws AAIException
329 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
330 createTraversal(type, parent, child, false);
331 return (QueryBuilder<Vertex>) this;
336 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
337 this.createTraversal(type, parent, child, true);
338 return (QueryBuilder<Vertex>) this;
341 private void createTraversal(EdgeType type, Introspector parent, Introspector child, boolean isPrivateEdge) throws AAIException {
342 String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT);
343 if ("true".equals(isAbstractType)) {
344 markParentBoundary();
345 traversal.union(handleAbstractEdge(type, parent, child, isPrivateEdge));
348 this.edgeQueryToVertex(type, parent, child, null);
357 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException {
358 this.edgeQueryToVertex(type, out, in, labels);
359 return (QueryBuilder<Vertex>) this;
362 private Traversal<Vertex, Vertex>[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child, boolean isPrivateEdge) throws AAIException {
363 String childName = child.getDbName();
364 String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS);
365 String[] inheritors = inheritorMetadata.split(",");
366 List<Traversal<Vertex, Vertex>> unionTraversals = new ArrayList<>(inheritors.length);
368 for (int i = 0; i < inheritors.length; i++) {
369 String inheritor = inheritors[i];
370 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(inheritor, childName);
371 if (edgeRules.hasRule(qB.build())) {
372 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
374 rules = edgeRules.getRules(qB.edgeType(type).build());
375 } catch (EdgeRuleNotFoundException e) {
376 throw new NoEdgeRuleFoundException(e);
379 GraphTraversal<Vertex, Vertex> innerTraversal = __.start();
381 final List<String> inLabels = new ArrayList<>();
382 final List<String> outLabels = new ArrayList<>();
384 rules.values().forEach(rule -> {
385 if (rule.getDirection().equals(Direction.IN)) {
386 inLabels.add(rule.getLabel());
388 outLabels.add(rule.getLabel());
392 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
393 innerTraversal.out(outLabels.toArray(new String[outLabels.size()]));
394 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
395 innerTraversal.in(inLabels.toArray(new String[inLabels.size()]));
397 innerTraversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
400 innerTraversal.has(AAIProperties.NODE_TYPE, childName);
401 unionTraversals.add(innerTraversal);
405 return unionTraversals.toArray(new Traversal[unionTraversals.size()]);
408 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List<String> labels) throws AAIException {
409 Introspector outObj = loader.introspectorFromName(outNodeType);
410 Introspector inObj = loader.introspectorFromName(inNodeType);
411 this.edgeQuery(type, outObj, inObj, labels);
413 return (QueryBuilder<Edge>)this;
421 public QueryBuilder<E> union(QueryBuilder... builder) {
422 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
423 for (int i = 0; i < builder.length; i++) {
424 traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
426 this.traversal.union(traversals);
436 public QueryBuilder<E> where(QueryBuilder... builder) {
437 for (int i = 0; i < builder.length; i++) {
438 this.traversal.where((GraphTraversal<Vertex, Vertex>)builder[i].getQuery());
449 public QueryBuilder<E> or(QueryBuilder... builder) {
450 GraphTraversal<Vertex, Vertex>[] traversals = new GraphTraversal[builder.length];
451 for (int i = 0; i < builder.length; i++) {
452 traversals[i] = (GraphTraversal<Vertex, Vertex>)builder[i].getQuery();
454 this.traversal.or(traversals);
461 public QueryBuilder<E> store(String name) {
463 this.traversal.store(name);
470 public QueryBuilder<E> cap(String name) {
471 this.traversal.cap(name);
478 public QueryBuilder<E> unfold() {
479 this.traversal.unfold();
486 public QueryBuilder<E> dedup() {
488 this.traversal.dedup();
495 public QueryBuilder<E> emit() {
497 this.traversal.emit();
505 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
507 this.traversal.repeat((GraphTraversal<Vertex, E>)builder.getQuery());
514 public QueryBuilder<E> until(QueryBuilder<E> builder) {
515 this.traversal.until((GraphTraversal<Vertex,E>)builder.getQuery());
522 public QueryBuilder<E> groupCount() {
523 this.traversal.groupCount();
530 public QueryBuilder<E> both() {
531 this.traversal.both();
538 public QueryBuilder<Tree> tree() {
540 this.traversal.tree();
543 return (QueryBuilder<Tree>)this;
547 public QueryBuilder<E> by(String name) {
548 this.traversal.by(name);
558 public QueryBuilder<E> simplePath(){
559 this.traversal.simplePath();
568 public QueryBuilder<Path> path(){
569 this.traversal.path();
571 return (QueryBuilder<Path>)this;
575 public QueryBuilder<Edge> outE() {
576 this.traversal.outE();
578 return (QueryBuilder<Edge>)this;
582 public QueryBuilder<Edge> inE() {
583 this.traversal.inE();
585 return (QueryBuilder<Edge>)this;
589 public QueryBuilder<Vertex> outV() {
590 this.traversal.outV();
592 return (QueryBuilder<Vertex>)this;
596 public QueryBuilder<Vertex> inV() {
597 this.traversal.inV();
599 return (QueryBuilder<Vertex>)this;
603 public QueryBuilder<E> as(String name) {
604 this.traversal.as(name);
611 public QueryBuilder<E> not(QueryBuilder<E> builder) {
612 this.traversal.not(builder.getQuery());
619 public QueryBuilder<E> select(String name) {
620 this.traversal.select(name);
628 public QueryBuilder<E> select(String... names) {
629 if(names.length == 1) {
630 this.traversal.select(names[0]);
632 else if(names.length == 2) {
633 this.traversal.select(names[0], names[1]);
635 else if(names.length > 2){
636 String[] otherNames = Arrays.copyOfRange(names, 2, names.length);
637 this.traversal.select(names[0], names[1], otherNames);
648 * @param outObj the out type
649 * @param inObj the in type
650 * @throws NoEdgeRuleFoundException
651 * @throws AAIException
653 private void edgeQueryToVertex(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
654 String outType = outObj.getDbName();
655 String inType = inObj.getDbName();
657 if (outObj.isContainer()) {
658 outType = outObj.getChildDBName();
660 if (inObj.isContainer()) {
661 inType = inObj.getChildDBName();
663 markParentBoundary();
664 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
665 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
667 if (labels == null) {
669 rules.putAll(edgeRules.getRules(qB.build()));
670 } catch (EdgeRuleNotFoundException e) {
671 //is ok per original functioning of this section
672 //TODO add "best try" sort of flag to the EdgeRuleQuery
673 // to indicate if the exception should be thrown or not
676 for (String label : labels) {
678 rules.putAll(edgeRules.getRules(qB.label(label).build()));
679 } catch (EdgeRuleNotFoundException e) {
680 throw new NoEdgeRuleFoundException(e);
683 if (rules.isEmpty()) {
684 throw new NoEdgeRuleFoundException("No edge rules found for " + outType + " and " + inType + " of type " + type.toString());
689 final List<String> inLabels = new ArrayList<>();
690 final List<String> outLabels = new ArrayList<>();
692 for (EdgeRule rule : rules.values()) {
693 if (labels != null && !labels.contains(rule.getLabel())) {
696 if (Direction.IN.equals(rule.getDirection())) {
697 inLabels.add(rule.getLabel());
699 outLabels.add(rule.getLabel());
704 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
705 traversal.out(outLabels.toArray(new String[outLabels.size()]));
706 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
707 traversal.in(inLabels.toArray(new String[inLabels.size()]));
709 traversal.union(__.out(outLabels.toArray(new String[outLabels.size()])), __.in(inLabels.toArray(new String[inLabels.size()])));
714 this.createContainerQuery(inObj);
721 * @param outObj the out type
722 * @param inObj the in type
723 * @throws NoEdgeRuleFoundException
724 * @throws AAIException
726 private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj, List<String> labels) throws AAIException {
727 String outType = outObj.getDbName();
728 String inType = inObj.getDbName();
730 if (outObj.isContainer()) {
731 outType = outObj.getChildDBName();
733 if (inObj.isContainer()) {
734 inType = inObj.getChildDBName();
737 markParentBoundary();
738 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
739 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
742 if (labels == null) {
743 rules.putAll(edgeRules.getRules(qB.build()));
745 for (String label : labels) {
746 rules.putAll(edgeRules.getRules(qB.label(label).build()));
749 } catch (EdgeRuleNotFoundException e) {
750 throw new NoEdgeRuleFoundException(e);
753 final List<String> inLabels = new ArrayList<>();
754 final List<String> outLabels = new ArrayList<>();
756 for (EdgeRule rule : rules.values()) {
757 if (labels != null && !labels.contains(rule.getLabel())) {
760 if (Direction.IN.equals(rule.getDirection())) {
761 inLabels.add(rule.getLabel());
763 outLabels.add(rule.getLabel());
768 if (inLabels.isEmpty() && !outLabels.isEmpty()) {
769 traversal.outE(outLabels.toArray(new String[outLabels.size()]));
770 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
771 traversal.inE(inLabels.toArray(new String[inLabels.size()]));
773 traversal.union(__.outE(outLabels.toArray(new String[outLabels.size()])), __.inE(inLabels.toArray(new String[inLabels.size()])));
778 public QueryBuilder<E> limit(long amount) {
779 traversal.limit(amount);
787 public <E2> E2 getQuery() {
788 return (E2)this.traversal;
795 public QueryBuilder<E> getParentQuery() {
797 return cloneQueryAtStep(parentStepIndex);
801 public QueryBuilder<E> getContainerQuery() {
803 if (this.parentStepIndex == 0) {
804 return removeQueryStepsBetween(0, containerStepIndex);
806 return cloneQueryAtStep(containerStepIndex);
814 public void markParentBoundary() {
815 parentStepIndex = stepIndex;
819 public void markContainer() {
820 containerStepIndex = stepIndex;
828 public Vertex getStart() {
832 protected int getParentStepIndex() {
833 return parentStepIndex;
836 protected int getContainerStepIndex() {
837 return containerStepIndex;
840 protected int getStepIndex() {
851 protected abstract QueryBuilder<E> removeQueryStepsBetween(int start, int end);
853 protected void executeQuery() {
857 this.completeTraversal = traversal.asAdmin();
859 admin = source.V().asAdmin();
860 TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin);
862 this.completeTraversal = (Admin<Vertex, E>) admin;
869 public boolean hasNext() {
870 if (this.completeTraversal == null) {
874 return this.completeTraversal.hasNext();
879 if (this.completeTraversal == null) {
883 return this.completeTraversal.next();
887 public List<E> toList() {
888 if (this.completeTraversal == null) {
891 return this.completeTraversal.toList();
894 protected QueryBuilder<Edge> has(String key, String value) {
895 traversal.has(key, value);
897 return (QueryBuilder<Edge>)this;