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=========================================================
23 package org.onap.aai.query.builder;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27 import com.google.common.base.Joiner;
28 import com.google.common.collect.ArrayListMultimap;
29 import com.google.common.collect.Multimap;
33 import org.apache.tinkerpop.gremlin.process.traversal.Path;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
35 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
36 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
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.restcore.search.GremlinGroovyShell;
49 import org.onap.aai.schema.enums.ObjectMetadata;
50 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
53 * The Class GremlinQueryBuilder.
55 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
57 private static final String ARGUMENT2 = "#!#argument#!#";
58 private static final String HAS = ".has('";
59 private static final String SINGLE_QUOTE = "'";
60 private static final String ESCAPE_SINGLE_QUOTE = "\\'";
61 private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell();
62 private GraphTraversal<?, ?> completeTraversal = null;
63 protected List<String> list = null;
65 private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class);
68 * Instantiates a new gremlin query builder.
70 * @param loader the loader
72 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
73 super(loader, source);
74 list = new ArrayList<>();
78 * Instantiates a new gremlin query builder.
80 * @param loader the loader
81 * @param start the start
83 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
84 super(loader, source, start);
85 list = new ArrayList<>();
90 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
91 // TODO not implemented because this is implementation is no longer used
92 this.createKeyQuery(obj);
93 this.createContainerQuery(obj);
94 return (QueryBuilder<Vertex>) this;
98 protected void vertexHas(String key, Object value) {
99 list.add(HAS + key + "', " + value + ")");
103 protected void vertexHasNot(String key) {
104 list.add(".hasNot('" + key + "')");
109 protected void vertexHas(String key) {
110 list.add(HAS + key + "')");
117 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
120 if (value != null && !(value instanceof String)) {
121 String valueString = value.toString();
122 if (valueString.indexOf('\'') != -1) {
123 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
125 LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
126 term = value.toString();
128 term = "'" + value + "'";
130 this.vertexHas(key, term);
132 return (QueryBuilder<Vertex>) this;
139 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
140 this.vertexHas(key, value);
142 return (QueryBuilder<Vertex>) this;
146 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
148 if (value != null && !"".equals(value)) {
149 boolean bValue = false;
150 if (value instanceof String) {
151 bValue = Boolean.valueOf(value.toString());
152 } else if (value instanceof Boolean) {
153 bValue = (Boolean) value;
156 this.vertexHas(key, bValue);
159 return (QueryBuilder<Vertex>) this;
166 public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
168 String predicate = "P.within(#!#argument#!#)";
169 List<String> arguments = new ArrayList<>();
170 for (Object item : values) {
171 if (item != null && !(item instanceof String)) {
172 arguments.add(item.toString());
174 arguments.add("'" + item + "'");
177 String argument = Joiner.on(",").join(arguments);
178 predicate = predicate.replace(ARGUMENT2, argument);
179 this.vertexHas(key, predicate);
181 return (QueryBuilder<Vertex>) this;
188 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
189 ArrayList<String> arguments = new ArrayList<>(Arrays.asList(value.split(",")));
190 //add the single quotes
191 for (int i = 0; i < arguments.size(); i++) {
192 if(arguments.get(i) != null && !arguments.get(i).startsWith("'")
193 && !arguments.get(i).endsWith("'")) {
194 arguments.set(i,"'" + arguments.get(i).trim() + "'");
197 arguments.set(i, arguments.get(i).trim());
200 String predicate = "P.within(#!#argument#!#)";
201 String argument = Joiner.on(",").join(arguments);
202 predicate = predicate.replace(ARGUMENT2, argument);
203 this.vertexHas(key, predicate);
205 return (QueryBuilder<Vertex>) this;
212 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
215 return (QueryBuilder<Vertex>) this;
222 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
223 this.vertexHasNot(key);
225 return (QueryBuilder<Vertex>) this;
232 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
234 String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
235 if (value != null && !(value instanceof String)) {
236 term = value.toString();
238 term = "'" + value + "'";
240 predicate = predicate.replace(ARGUMENT2, term);
241 this.vertexHas(key, predicate);
243 return (QueryBuilder<Vertex>) this;
250 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
253 String predicate = "P.neq(#!#argument#!#)";
254 if (value != null && !(value instanceof String)) {
255 term = value.toString();
257 term = "'" + value + "'";
259 predicate = predicate.replace(ARGUMENT2, term);
260 this.vertexHas(key, predicate);
262 return (QueryBuilder<Vertex>) this;
269 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
271 String predicate = "P.without(#!#argument#!#)";
272 List<String> arguments = new ArrayList<>();
273 for (Object item : values) {
274 if (item != null && !(item instanceof String)) {
275 arguments.add(item.toString());
277 arguments.add("'" + item + "'");
280 String argument = Joiner.on(",").join(arguments);
281 predicate = predicate.replace(ARGUMENT2, argument);
282 this.vertexHas(key, predicate);
284 return (QueryBuilder<Vertex>) this;
288 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
289 String predicate = "P.gte(#!#argument1#!#)";
291 if (value != null && !(value instanceof String)) {
292 term = value.toString();
294 term = "'" + value + "'";
296 predicate = predicate.replace("#!#argument1#!#", term);
297 this.vertexHas(key, predicate);
299 return (QueryBuilder<Vertex>) this;
303 public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
304 String predicate = "P.lte(#!#argument1#!#)";
306 if (value != null && !(value instanceof String)) {
307 term = value.toString();
309 term = "'" + value + "'";
311 predicate = predicate.replace("#!#argument1#!#", term);
312 this.vertexHas(key, predicate);
314 return (QueryBuilder<Vertex>) this;
321 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
323 return (QueryBuilder<Vertex>) this;
330 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
332 for (Map.Entry<String, String> es : map.entrySet()) {
333 //TODO what is this and where is it used - need to check
334 list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
337 list.add(".has('aai-node-type', '" + type + "')");
339 return (QueryBuilder<Vertex>) this;
346 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
347 Set<String> keys = obj.getKeys();
349 for (String key : keys) {
351 this.getVerticesByProperty(key, obj.<Object>getValue(key));
354 return (QueryBuilder<Vertex>) this;
358 * @throws NoEdgeRuleFoundException
359 * @throws AAIException
363 public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
364 throws AAIException {
365 String parentName = parent.getDbName();
366 String childName = child.getDbName();
367 if (parent.isContainer()) {
368 parentName = parent.getChildDBName();
370 if (child.isContainer()) {
371 childName = child.getChildDBName();
373 this.edgeQueryToVertex(type, parentName, childName, null);
379 public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
380 throws AAIException {
381 String parentName = parent.getDbName();
382 String childName = child.getDbName();
383 if (parent.isContainer()) {
384 parentName = parent.getChildDBName();
386 if (child.isContainer()) {
387 childName = child.getChildDBName();
389 this.edgeQueryToVertex(type, parentName, childName, null, true);
398 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
399 List<String> labels) throws AAIException {
400 String parentName = out.getDbName();
401 String childName = in.getDbName();
402 if (out.isContainer()) {
403 parentName = out.getChildDBName();
405 if (in.isContainer()) {
406 childName = in.getChildDBName();
408 this.edgeQueryToVertex(type, parentName, childName, labels);
409 return (QueryBuilder<Vertex>) this;
412 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
413 List<String> labels) throws AAIException {
414 this.edgeQuery(type, outNodeType, inNodeType, labels);
415 return (QueryBuilder<Edge>) this;
418 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels)
419 throws AAIException {
420 this.edgeQueryToVertex(type, outType, inType, labels, false);
426 * @param outType the out type
427 * @param inType the in type
428 * @throws NoEdgeRuleFoundException
429 * @throws AAIException
431 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels,
432 boolean isPrivateEdge) throws AAIException {
433 markParentBoundary();
434 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
435 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
438 if (labels == null) {
439 rules.putAll(edgeRules.getRules(qB.build()));
441 for (String label : labels) {
442 rules.putAll(edgeRules.getRules(qB.label(label).build()));
445 } catch (EdgeRuleNotFoundException e) {
446 throw new NoEdgeRuleFoundException(e);
449 final List<String> inLabels = new ArrayList<>();
450 final List<String> outLabels = new ArrayList<>();
452 for (EdgeRule rule : rules.values()) {
453 if (labels != null && !labels.contains(rule.getLabel())) {
456 if (Direction.IN.equals(rule.getDirection())) {
457 inLabels.add(rule.getLabel());
458 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
459 outLabels.add(rule.getLabel());
462 outLabels.add(rule.getLabel());
463 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
464 inLabels.add(rule.getLabel());
470 if (inLabels.isEmpty() && outLabels.isEmpty()) {
471 throw new NoEdgeRuleFoundException(
472 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
473 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
474 list.add(".out('" + String.join("','", outLabels) + "')");
475 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
476 list.add(".in('" + String.join("','", inLabels) + "')");
478 list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('"
479 + String.join("','", outLabels) + "'))");
482 list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
490 * @param outType the out type
491 * @param inType the in type
492 * @throws NoEdgeRuleFoundException
493 * @throws AAIException
495 private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
496 markParentBoundary();
497 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
498 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
500 if (labels == null) {
501 rules.putAll(edgeRules.getRules(qB.build()));
503 for (String label : labels) {
504 rules.putAll(edgeRules.getRules(qB.label(label).build()));
507 } catch (EdgeRuleNotFoundException e) {
508 throw new NoEdgeRuleFoundException(e);
511 final List<String> inLabels = new ArrayList<>();
512 final List<String> outLabels = new ArrayList<>();
514 for (EdgeRule rule : rules.values()) {
515 if (labels != null && !labels.contains(rule.getLabel())) {
518 if (Direction.IN.equals(rule.getDirection())) {
519 inLabels.add(rule.getLabel());
521 outLabels.add(rule.getLabel());
526 if (inLabels.isEmpty() && outLabels.isEmpty()) {
527 throw new NoEdgeRuleFoundException(
528 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
529 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
530 list.add(".outE('" + String.join("','", outLabels) + "')");
531 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
532 list.add(".inE('" + String.join("','", inLabels) + "')");
534 list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('"
535 + String.join("','", outLabels) + "'))");
543 public QueryBuilder<E> limit(long amount) {
544 list.add(".limit(" + amount + ")");
552 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
553 String type = obj.getChildDBName();
554 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
555 if (abstractType != null) {
556 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
557 String[] wrapped = new String[inheritors.length];
558 StringBuilder command = new StringBuilder();
559 command.append("P.within(");
560 for (int i = 0; i < inheritors.length; i++) {
561 wrapped[i] = "'" + inheritors[i] + "'";
563 command.append(Joiner.on(",").join(wrapped));
565 list.add(".has('aai-node-type', " + command + ")");
568 list.add(".has('aai-node-type', '" + type + "')");
571 this.markContainer();
572 return (QueryBuilder<Vertex>) this;
576 public QueryBuilder<E> union(QueryBuilder<E>... builder) {
577 markParentBoundary();
578 String[] traversals = new String[builder.length];
579 StringBuilder command = new StringBuilder();
580 for (int i = 0; i < builder.length; i++) {
581 traversals[i] = "__" + builder[i].getQuery();
583 command.append(".union(");
584 command.append(Joiner.on(",").join(traversals));
586 list.add(command.toString());
593 public QueryBuilder<E> where(QueryBuilder<E>... builder) {
594 markParentBoundary();
595 List<String> traversals = new ArrayList<>();
596 for (int i = 0; i < builder.length; i++) {
597 traversals.add(".where(__" + builder[i].getQuery() + ")");
600 list.addAll(traversals);
606 public QueryBuilder<E> or(QueryBuilder<E>... builder) {
607 markParentBoundary();
608 String[] traversals = new String[builder.length];
609 StringBuilder command = new StringBuilder();
610 for (int i = 0; i < builder.length; i++) {
611 traversals[i] = "__" + builder[i].getQuery();
613 command.append(".or(");
614 command.append(Joiner.on(",").join(traversals));
616 list.add(command.toString());
623 public QueryBuilder<E> store(String name) {
624 this.list.add(".store('" + name + "')");
631 public QueryBuilder<E> cap(String name) {
632 this.list.add(".cap('" + name + "')");
639 public QueryBuilder<E> unfold() {
640 this.list.add(".unfold()");
647 public QueryBuilder<E> fold() {
648 this.list.add(".fold()");
654 public QueryBuilder<E> id() {
655 this.list.add(".id()");
661 public QueryBuilder<E> dedup() {
662 this.list.add(".dedup()");
669 public QueryBuilder<E> emit() {
670 this.list.add(".emit()");
677 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
678 this.list.add(".repeat(__" + builder.getQuery() + ")");
685 public QueryBuilder<E> until(QueryBuilder<E> builder) {
686 this.list.add(".until(__" + builder.getQuery() + ")");
693 public QueryBuilder<E> groupCount() {
694 this.list.add(".groupCount()");
701 public QueryBuilder<E> both() {
702 this.list.add(".both()");
709 public QueryBuilder<Tree> tree() {
710 this.list.add(".tree()");
713 return (QueryBuilder<Tree>) this;
717 public QueryBuilder<E> by(String name) {
718 this.list.add(".by('" + name + "')");
725 public QueryBuilder<E> valueMap() {
726 this.list.add(".valueMap()");
733 public QueryBuilder<E> valueMap(String... names) {
734 String stepString = ".valueMap('";
735 for (int i = 0; i < names.length; i++) {
736 stepString = stepString + names[i] + "'";
737 if (i != (names.length - 1)) {
738 stepString = stepString + ",'";
741 stepString = stepString + ")";
742 this.list.add(stepString);
753 public QueryBuilder<E> simplePath() {
754 this.list.add(".simplePath()");
763 public QueryBuilder<Path> path() {
764 this.list.add(".path()");
766 return (QueryBuilder<Path>) this;
770 public QueryBuilder<Edge> outE() {
771 this.list.add(".outE()");
774 return (QueryBuilder<Edge>) this;
778 public QueryBuilder<Edge> inE() {
779 this.list.add(".inE()");
782 return (QueryBuilder<Edge>) this;
786 public QueryBuilder<Vertex> outV() {
787 this.list.add(".outV()");
790 return (QueryBuilder<Vertex>) this;
794 public QueryBuilder<Vertex> inV() {
795 this.list.add(".inV()");
798 return (QueryBuilder<Vertex>) this;
802 public QueryBuilder<E> not(QueryBuilder<E> builder) {
803 this.list.add(".not(" + "__" + builder.getQuery() + ")");
810 public QueryBuilder<E> as(String name) {
811 this.list.add(".as('" + name + "')");
818 public QueryBuilder<E> select(String name) {
819 this.list.add(".select('" + name + "')");
826 public QueryBuilder<E> select(String... names) {
827 String stepString = ".select('";
828 for (int i = 0; i < names.length; i++) {
829 stepString = stepString + names[i] + "'";
830 if (i != (names.length - 1)) {
831 stepString = stepString + ",'";
834 stepString = stepString + ")";
835 this.list.add(stepString);
845 public QueryBuilder<E> getParentQuery() {
846 return cloneQueryAtStep(parentStepIndex);
850 public QueryBuilder<E> getContainerQuery() {
851 return cloneQueryAtStep(containerStepIndex);
858 public <T2> T2 getQuery() {
859 StringBuilder sb = new StringBuilder();
861 for (String piece : this.list) {
865 return (T2) sb.toString();
872 public void markParentBoundary() {
873 parentStepIndex = stepIndex;
877 public void markContainer() {
878 this.containerStepIndex = stepIndex;
885 public Vertex getStart() {
889 protected int getParentStepIndex() {
890 return parentStepIndex;
893 protected int getContainerStepIndex() {
894 return containerStepIndex;
897 protected int getStepIndex() {
901 private void executeQuery() {
902 String queryString = "g" + Joiner.on("").join(list);
903 Map<String, Object> params = new HashMap<>();
904 if (this.start == null) {
905 params.put("g", source.V());
907 params.put("g", source.V(this.start));
909 this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
913 public boolean hasNext() {
914 if (this.completeTraversal == null) {
918 return this.completeTraversal.hasNext();
923 if (this.completeTraversal == null) {
927 return (E) this.completeTraversal.next();
931 public List<E> toList() {
932 if (this.completeTraversal == null) {
936 return (List<E>) this.completeTraversal.toList();
939 protected QueryBuilder<Edge> has(String key, String value) {
940 this.list.add(HAS + key + "','" + value + "')");
942 return (QueryBuilder<Edge>) this;
946 * This is required for the subgraphstrategies to work