2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright © 2018 IBM.
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=========================================================
22 package org.onap.aai.query.builder;
24 import com.google.common.base.Joiner;
25 import com.google.common.collect.ArrayListMultimap;
26 import com.google.common.collect.Multimap;
28 import org.apache.tinkerpop.gremlin.process.traversal.Path;
29 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
30 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
31 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
32 import org.apache.tinkerpop.gremlin.structure.Direction;
33 import org.apache.tinkerpop.gremlin.structure.Edge;
34 import org.apache.tinkerpop.gremlin.structure.Vertex;
35 import org.onap.aai.db.props.AAIProperties;
36 import org.onap.aai.exceptions.AAIException;
37 import org.onap.aai.introspection.Introspector;
38 import org.onap.aai.introspection.Loader;
39 import org.onap.aai.restcore.search.GremlinGroovyShell;
40 import org.onap.aai.schema.enums.ObjectMetadata;
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.serialization.db.exceptions.NoEdgeRuleFoundException;
50 * The Class GremlinQueryBuilder.
52 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
54 private static final String ARGUMENT2 = "#!#argument#!#";
55 private static final String HAS = ".has('";
56 private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell();
57 private GraphTraversal<?, ?> completeTraversal = null;
58 protected List<String> list = null;
61 * Instantiates a new gremlin query builder.
63 * @param loader the loader
65 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
66 super(loader, source);
67 list = new ArrayList<>();
71 * Instantiates a new gremlin query builder.
73 * @param loader the loader
74 * @param start the start
76 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
77 super(loader, source, start);
78 list = new ArrayList<>();
82 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
83 // TODO not implemented because this is implementation is no longer used
84 this.createKeyQuery(obj);
85 this.createContainerQuery(obj);
86 return (QueryBuilder<Vertex>) this;
93 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
96 if (value != null && !(value instanceof String) ) {
97 term = value.toString();
99 term = "'" + value + "'";
101 list.add(HAS + key + "', " + term + ")");
103 return (QueryBuilder<Vertex>) this;
110 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
111 list.add(HAS + key + "', " + value + ")");
113 return (QueryBuilder<Vertex>) this;
117 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
119 if(value!=null && !"".equals(value)) {
120 boolean bValue = false;
121 if(value instanceof String){
122 bValue = Boolean.valueOf(value.toString());
123 } else if(value instanceof Boolean){
124 bValue = (Boolean) value;
127 list.add(HAS + key + "', " + bValue + ")");
130 return (QueryBuilder<Vertex>) this;
137 public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
139 String predicate = "P.within(#!#argument#!#)";
140 List<String> arguments = new ArrayList<>();
141 for (Object item : values) {
142 if (item != null && !(item instanceof String)) {
143 arguments.add(item.toString());
145 arguments.add("'" + item + "'");
148 String argument = Joiner.on(",").join(arguments);
149 predicate = predicate.replace(ARGUMENT2, argument);
150 list.add(HAS + key + "', " + predicate + ")");
152 return (QueryBuilder<Vertex>) this;
159 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
161 list.add(HAS + key + "')");
163 return (QueryBuilder<Vertex>) this;
170 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
172 list.add(".hasNot('" + key + "')");
174 return (QueryBuilder<Vertex>) this;
181 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
184 String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
185 if (value != null && !(value instanceof String) ) {
186 term = value.toString();
188 term = "'" + value + "'";
190 predicate = predicate.replace(ARGUMENT2, term);
191 list.add(HAS + key + "', " + predicate + ")");
193 return (QueryBuilder<Vertex>) this;
200 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
203 String predicate = "P.neq(#!#argument#!#)";
204 if (value != null && !(value instanceof String) ) {
205 term = value.toString();
207 term = "'" + value + "'";
209 predicate = predicate.replace(ARGUMENT2, term);
210 list.add(HAS + key + "', " + predicate + ")");
212 return (QueryBuilder<Vertex>) this;
219 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
221 String predicate = "P.without(#!#argument#!#)";
222 List<String> arguments = new ArrayList<>();
223 for (Object item : values) {
224 if (item != null && !(item instanceof String)) {
225 arguments.add(item.toString());
227 arguments.add("'" + item + "'");
230 String argument = Joiner.on(",").join(arguments);
231 predicate = predicate.replace(ARGUMENT2, argument);
232 list.add(HAS + key + "', " + predicate + ")");
234 return (QueryBuilder<Vertex>) this;
238 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
239 String predicate = "P.gte(#!#argument1#!#)";
241 if (value != null && !(value instanceof String) ) {
242 term = value.toString();
244 term = "'" + value + "'";
246 predicate = predicate.replace("#!#argument1#!#", term);
247 list.add(HAS + key + "', " + predicate + ")");
249 return (QueryBuilder<Vertex>) this;
253 public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
254 String predicate = "P.lte(#!#argument1#!#)";
256 if (value != null && !(value instanceof String) ) {
257 term = value.toString();
259 term = "'" + value + "'";
261 predicate = predicate.replace("#!#argument1#!#", term);
262 list.add(HAS + key + "', " + predicate + ")");
264 return (QueryBuilder<Vertex>) this;
274 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
276 return (QueryBuilder<Vertex>) this;
283 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
285 for (Map.Entry<String, String> es : map.entrySet()) {
286 list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
289 list.add(".has('aai-node-type', '" + type + "')");
291 return (QueryBuilder<Vertex>) this;
298 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
299 Set<String> keys = obj.getKeys();
301 for (String key : keys) {
303 this.getVerticesByProperty(key, obj.<Object>getValue(key));
306 return (QueryBuilder<Vertex>) this;
310 * @throws NoEdgeRuleFoundException
311 * @throws AAIException
315 public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException {
316 String parentName = parent.getDbName();
317 String childName = child.getDbName();
318 if (parent.isContainer()) {
319 parentName = parent.getChildDBName();
321 if (child.isContainer()) {
322 childName = child.getChildDBName();
324 this.edgeQueryToVertex(type, parentName, childName, null);
330 public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException{
331 String parentName = parent.getDbName();
332 String childName = child.getDbName();
333 if (parent.isContainer()) {
334 parentName = parent.getChildDBName();
336 if (child.isContainer()) {
337 childName = child.getChildDBName();
339 this.edgeQueryToVertex(type, parentName, childName, null, true);
348 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in, List<String> labels) throws AAIException {
349 String parentName = out.getDbName();
350 String childName = in.getDbName();
351 if (out.isContainer()) {
352 parentName = out.getChildDBName();
354 if (in.isContainer()) {
355 childName = in.getChildDBName();
357 this.edgeQueryToVertex(type, parentName, childName, labels);
358 return (QueryBuilder<Vertex>) this;
362 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType, List<String> labels) throws AAIException {
363 this.edgeQuery(type, outNodeType, inNodeType, labels);
364 return (QueryBuilder<Edge>)this;
367 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
368 this.edgeQueryToVertex(type, outType, inType, labels, false);
374 * @param outType the out type
375 * @param inType the in type
376 * @throws NoEdgeRuleFoundException
377 * @throws AAIException
379 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels, boolean isPrivateEdge) throws AAIException {
380 markParentBoundary();
381 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
382 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
385 if (labels == null) {
386 rules.putAll(edgeRules.getRules(qB.build()));
388 for (String label : labels) {
389 rules.putAll(edgeRules.getRules(qB.label(label).build()));
392 } catch (EdgeRuleNotFoundException e) {
393 throw new NoEdgeRuleFoundException(e);
396 final List<String> inLabels = new ArrayList<>();
397 final List<String> outLabels = new ArrayList<>();
399 for (EdgeRule rule : rules.values()) {
400 if (labels != null && !labels.contains(rule.getLabel())) {
403 if (Direction.IN.equals(rule.getDirection())) {
404 inLabels.add(rule.getLabel());
405 if(inType.equals(outType)) {//code to handle when a type edges to itself, to add both in and out
406 outLabels.add(rule.getLabel());
409 outLabels.add(rule.getLabel());
410 if(inType.equals(outType)) {//code to handle when a type edges to itself, to add both in and out
411 inLabels.add(rule.getLabel());
417 if(inLabels.isEmpty() && outLabels.isEmpty()) {
418 throw new NoEdgeRuleFoundException("no " + type.toString() + " edge rule between " + outType + " and " + inType );
419 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
420 list.add(".out('" + String.join("','", outLabels) + "')");
421 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
422 list.add(".in('" + String.join("','", inLabels) + "')");
424 list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('" + String.join("','", outLabels) + "'))");
427 list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
435 * @param outType the out type
436 * @param inType the in type
437 * @throws NoEdgeRuleFoundException
438 * @throws AAIException
440 private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
441 markParentBoundary();
442 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
443 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
445 if (labels == null) {
446 rules.putAll(edgeRules.getRules(qB.build()));
448 for (String label : labels) {
449 rules.putAll(edgeRules.getRules(qB.label(label).build()));
452 } catch (EdgeRuleNotFoundException e) {
453 throw new NoEdgeRuleFoundException(e);
456 final List<String> inLabels = new ArrayList<>();
457 final List<String> outLabels = new ArrayList<>();
459 for (EdgeRule rule : rules.values()) {
460 if (labels != null && !labels.contains(rule.getLabel())) {
463 if (Direction.IN.equals(rule.getDirection())) {
464 inLabels.add(rule.getLabel());
466 outLabels.add(rule.getLabel());
471 if(inLabels.isEmpty() && outLabels.isEmpty()) {
472 throw new NoEdgeRuleFoundException("no " + type.toString() + " edge rule between " + outType + " and " + inType );
473 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
474 list.add(".outE('" + String.join("','", outLabels) + "')");
475 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
476 list.add(".inE('" + String.join("','", inLabels) + "')");
478 list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('" + String.join("','", outLabels) + "'))");
485 public QueryBuilder<E> limit(long amount) {
486 list.add(".limit(" + amount + ")");
493 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
494 String type = obj.getChildDBName();
495 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
496 if (abstractType != null) {
497 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
498 String[] wrapped = new String[inheritors.length];
499 StringBuilder command = new StringBuilder();
500 command.append("P.within(");
501 for (int i = 0; i < inheritors.length; i++) {
502 wrapped[i] = "'" + inheritors[i] + "'";
504 command.append(Joiner.on(",").join(wrapped));
506 list.add(".has('aai-node-type', " + command + ")");
509 list.add(".has('aai-node-type', '" + type + "')");
512 this.markContainer();
513 return (QueryBuilder<Vertex>) this;
517 public QueryBuilder<E> union(QueryBuilder<E>... builder) {
518 markParentBoundary();
519 String[] traversals = new String[builder.length];
520 StringBuilder command = new StringBuilder();
521 for (int i = 0; i < builder.length; i++) {
522 traversals[i] = "__" + builder[i].getQuery();
524 command.append(".union(");
525 command.append(Joiner.on(",").join(traversals));
527 list.add(command.toString());
534 public QueryBuilder<E> where(QueryBuilder<E>... builder) {
535 markParentBoundary();
536 List<String> traversals = new ArrayList<>();
537 for (int i = 0; i < builder.length; i++) {
538 traversals.add(".where(__" + builder[i].getQuery() + ")");
541 list.addAll(traversals);
548 public QueryBuilder<E> or(QueryBuilder<E>... builder) {
549 markParentBoundary();
550 String[] traversals = new String[builder.length];
551 StringBuilder command = new StringBuilder();
552 for (int i = 0; i < builder.length; i++) {
553 traversals[i] = "__" + builder[i].getQuery();
555 command.append(".or(");
556 command.append(Joiner.on(",").join(traversals));
558 list.add(command.toString());
565 public QueryBuilder<E> store(String name) {
566 this.list.add(".store('"+ name + "')");
573 public QueryBuilder<E> cap(String name) {
574 this.list.add(".cap('"+ name + "')");
581 public QueryBuilder<E> unfold() {
582 this.list.add(".unfold()");
589 public QueryBuilder<E> dedup() {
590 this.list.add(".dedup()");
597 public QueryBuilder<E> emit() {
598 this.list.add(".emit()");
605 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
606 this.list.add(".repeat(__" + builder.getQuery() + ")");
613 public QueryBuilder<E> until(QueryBuilder<E> builder) {
614 this.list.add(".until(__" + builder.getQuery() + ")");
621 public QueryBuilder<E> groupCount() {
622 this.list.add(".groupCount()");
629 public QueryBuilder<E> both() {
630 this.list.add(".both()");
637 public QueryBuilder<Tree> tree() {
638 this.list.add(".tree()");
641 return (QueryBuilder<Tree>)this;
645 public QueryBuilder<E> by(String name) {
646 this.list.add(".by('"+ name + "')");
656 public QueryBuilder<E> simplePath(){
657 this.list.add(".simplePath()");
666 public QueryBuilder<Path> path(){
667 this.list.add(".path()");
669 return (QueryBuilder<Path>)this;
673 public QueryBuilder<Edge> outE() {
674 this.list.add(".outE()");
677 return (QueryBuilder<Edge>)this;
681 public QueryBuilder<Edge> inE() {
682 this.list.add(".inE()");
685 return (QueryBuilder<Edge>)this;
689 public QueryBuilder<Vertex> outV() {
690 this.list.add(".outV()");
693 return (QueryBuilder<Vertex>)this;
697 public QueryBuilder<Vertex> inV() {
698 this.list.add(".inV()");
701 return (QueryBuilder<Vertex>)this;
705 public QueryBuilder<E> not(QueryBuilder<E> builder) {
706 this.list.add(".not(" + "__" + builder.getQuery() + ")");
713 public QueryBuilder<E> as(String name) {
714 this.list.add(".as('" + name + "')");
721 public QueryBuilder<E> select(String name) {
722 this.list.add(".select('" + name + "')");
729 public QueryBuilder<E> select(String... names) {
730 String stepString = ".select('";
731 for(int i = 0; i<names.length; i++) {
732 stepString = stepString + names[i] +"'";
733 if(i!=(names.length-1)) {
734 stepString = stepString + ",'";
737 stepString = stepString + ")";
738 this.list.add(stepString);
747 public QueryBuilder<E> getParentQuery() {
748 return cloneQueryAtStep(parentStepIndex);
752 public QueryBuilder<E> getContainerQuery() {
753 return cloneQueryAtStep(containerStepIndex);
760 public <T2> T2 getQuery() {
761 StringBuilder sb = new StringBuilder();
763 for (String piece : this.list) {
767 return (T2)sb.toString();
774 public void markParentBoundary() {
775 parentStepIndex = stepIndex;
779 public void markContainer() {
780 this.containerStepIndex = stepIndex;
787 public Vertex getStart() {
791 protected int getParentStepIndex() {
792 return parentStepIndex;
795 protected int getContainerStepIndex() {
796 return containerStepIndex;
799 protected int getStepIndex() {
803 private void executeQuery() {
804 String queryString = "g" + Joiner.on("").join(list);
805 Map<String, Object> params = new HashMap<>();
806 if (this.start == null) {
807 params.put("g", source.V());
809 params.put("g", source.V(this.start));
811 this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
814 public boolean hasNext() {
815 if (this.completeTraversal == null) {
819 return this.completeTraversal.hasNext();
824 if (this.completeTraversal == null) {
828 return (E)this.completeTraversal.next();
832 public List<E> toList() {
833 if (this.completeTraversal == null) {
837 return (List<E>)this.completeTraversal.toList();
840 protected QueryBuilder<Edge> has(String key, String value) {
841 this.list.add(HAS + key + "','" + value + "')");
843 return (QueryBuilder<Edge>)this;