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 com.google.common.base.Joiner;
26 import com.google.common.collect.ArrayListMultimap;
27 import com.google.common.collect.Multimap;
31 import org.apache.tinkerpop.gremlin.process.traversal.Path;
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.step.util.Tree;
35 import org.apache.tinkerpop.gremlin.structure.Direction;
36 import org.apache.tinkerpop.gremlin.structure.Edge;
37 import org.apache.tinkerpop.gremlin.structure.Vertex;
38 import org.onap.aai.db.props.AAIProperties;
39 import org.onap.aai.edges.EdgeRule;
40 import org.onap.aai.edges.EdgeRuleQuery;
41 import org.onap.aai.edges.enums.EdgeType;
42 import org.onap.aai.edges.exceptions.EdgeRuleNotFoundException;
43 import org.onap.aai.exceptions.AAIException;
44 import org.onap.aai.introspection.Introspector;
45 import org.onap.aai.introspection.Loader;
46 import org.onap.aai.restcore.search.GremlinGroovyShell;
47 import org.onap.aai.schema.enums.ObjectMetadata;
48 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
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<>();
89 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
90 // TODO not implemented because this is implementation is no longer used
91 this.createKeyQuery(obj);
92 this.createContainerQuery(obj);
93 return (QueryBuilder<Vertex>) this;
97 protected void vertexHas(String key, Object value) {
98 list.add(HAS + key + "', " + value + ")");
102 protected void vertexHasNot(String key) {
103 list.add(".hasNot('" + key + "')");
108 protected void vertexHas(String key) {
109 list.add(HAS + key + "')");
116 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
119 if (value != null && !(value instanceof String)) {
120 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();
127 } else if (value != null && value instanceof String) {
128 String valueString = value.toString();
130 if (valueString.indexOf('\'') != -1) {
131 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
133 LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
134 term = "'" + value + "'";
136 term = "'" + value + "'";
138 this.vertexHas(key, term);
140 return (QueryBuilder<Vertex>) this;
147 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
148 this.vertexHas(key, value);
150 return (QueryBuilder<Vertex>) this;
154 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
156 if (value != null && !"".equals(value)) {
157 boolean bValue = false;
158 if (value instanceof String) {
159 bValue = Boolean.valueOf(value.toString());
160 } else if (value instanceof Boolean) {
161 bValue = (Boolean) value;
164 this.vertexHas(key, bValue);
167 return (QueryBuilder<Vertex>) this;
174 public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
176 String predicate = "P.within(#!#argument#!#)";
177 List<String> arguments = new ArrayList<>();
178 for (Object item : values) {
179 if (item != null && !(item instanceof String)) {
180 arguments.add(item.toString());
182 arguments.add("'" + item + "'");
185 String argument = Joiner.on(",").join(arguments);
186 predicate = predicate.replace(ARGUMENT2, argument);
187 this.vertexHas(key, predicate);
189 return (QueryBuilder<Vertex>) this;
196 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
197 ArrayList<String> arguments = new ArrayList<>(Arrays.asList(value.split(",")));
198 // add the single quotes
199 for (int i = 0; i < arguments.size(); i++) {
200 if (arguments.get(i) != null && !arguments.get(i).startsWith("'") && !arguments.get(i).endsWith("'")) {
201 arguments.set(i, "'" + arguments.get(i).trim() + "'");
203 arguments.set(i, arguments.get(i).trim());
206 String predicate = "P.within(#!#argument#!#)";
207 String argument = Joiner.on(",").join(arguments);
208 predicate = predicate.replace(ARGUMENT2, argument);
209 this.vertexHas(key, predicate);
211 return (QueryBuilder<Vertex>) this;
218 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
221 return (QueryBuilder<Vertex>) this;
228 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
229 this.vertexHasNot(key);
231 return (QueryBuilder<Vertex>) this;
238 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
240 String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
241 if (value != null && !(value instanceof String)) {
242 term = value.toString();
244 term = "'" + value + "'";
246 predicate = predicate.replace(ARGUMENT2, term);
247 this.vertexHas(key, predicate);
249 return (QueryBuilder<Vertex>) this;
256 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
259 String predicate = "P.neq(#!#argument#!#)";
260 if (value != null && !(value instanceof String)) {
261 term = value.toString();
263 term = "'" + value + "'";
265 predicate = predicate.replace(ARGUMENT2, term);
266 this.vertexHas(key, predicate);
268 return (QueryBuilder<Vertex>) this;
275 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
277 String predicate = "P.without(#!#argument#!#)";
278 List<String> arguments = new ArrayList<>();
279 for (Object item : values) {
280 if (item != null && !(item instanceof String)) {
281 arguments.add(item.toString());
283 arguments.add("'" + item + "'");
286 String argument = Joiner.on(",").join(arguments);
287 predicate = predicate.replace(ARGUMENT2, argument);
288 this.vertexHas(key, predicate);
290 return (QueryBuilder<Vertex>) this;
294 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
295 String predicate = "P.gte(#!#argument1#!#)";
297 if (value != null && !(value instanceof String)) {
298 term = value.toString();
300 term = "'" + value + "'";
302 predicate = predicate.replace("#!#argument1#!#", term);
303 this.vertexHas(key, predicate);
305 return (QueryBuilder<Vertex>) this;
309 public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
310 String predicate = "P.lte(#!#argument1#!#)";
312 if (value != null && !(value instanceof String)) {
313 term = value.toString();
315 term = "'" + value + "'";
317 predicate = predicate.replace("#!#argument1#!#", term);
318 this.vertexHas(key, predicate);
320 return (QueryBuilder<Vertex>) this;
327 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
329 return (QueryBuilder<Vertex>) this;
336 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
338 for (Map.Entry<String, String> es : map.entrySet()) {
339 // TODO what is this and where is it used - need to check
340 list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
343 list.add(".has('aai-node-type', '" + type + "')");
345 return (QueryBuilder<Vertex>) this;
352 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
353 Set<String> keys = obj.getKeys();
355 for (String key : keys) {
357 this.getVerticesByProperty(key, obj.<Object>getValue(key));
360 return (QueryBuilder<Vertex>) this;
364 * @throws NoEdgeRuleFoundException
365 * @throws AAIException
369 public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
370 throws AAIException {
371 String parentName = parent.getDbName();
372 String childName = child.getDbName();
373 if (parent.isContainer()) {
374 parentName = parent.getChildDBName();
376 if (child.isContainer()) {
377 childName = child.getChildDBName();
379 this.edgeQueryToVertex(type, parentName, childName, null);
385 public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
386 throws AAIException {
387 String parentName = parent.getDbName();
388 String childName = child.getDbName();
389 if (parent.isContainer()) {
390 parentName = parent.getChildDBName();
392 if (child.isContainer()) {
393 childName = child.getChildDBName();
395 this.edgeQueryToVertex(type, parentName, childName, null, true);
404 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
405 List<String> labels) throws AAIException {
406 String parentName = out.getDbName();
407 String childName = in.getDbName();
408 if (out.isContainer()) {
409 parentName = out.getChildDBName();
411 if (in.isContainer()) {
412 childName = in.getChildDBName();
414 this.edgeQueryToVertex(type, parentName, childName, labels);
415 return (QueryBuilder<Vertex>) this;
418 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
419 List<String> labels) throws AAIException {
420 this.edgeQuery(type, outNodeType, inNodeType, labels);
421 return (QueryBuilder<Edge>) this;
424 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels)
425 throws AAIException {
426 this.edgeQueryToVertex(type, outType, inType, labels, false);
432 * @param outType the out type
433 * @param inType the in type
434 * @throws NoEdgeRuleFoundException
435 * @throws AAIException
437 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels,
438 boolean isPrivateEdge) throws AAIException {
439 markParentBoundary();
440 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
441 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
444 if (labels == null) {
445 rules.putAll(edgeRules.getRules(qB.build()));
447 for (String label : labels) {
448 rules.putAll(edgeRules.getRules(qB.label(label).build()));
451 } catch (EdgeRuleNotFoundException e) {
452 throw new NoEdgeRuleFoundException(e);
455 final List<String> inLabels = new ArrayList<>();
456 final List<String> outLabels = new ArrayList<>();
458 for (EdgeRule rule : rules.values()) {
459 if (labels != null && !labels.contains(rule.getLabel())) {
462 if (Direction.IN.equals(rule.getDirection())) {
463 inLabels.add(rule.getLabel());
464 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
465 outLabels.add(rule.getLabel());
468 outLabels.add(rule.getLabel());
469 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
470 inLabels.add(rule.getLabel());
476 if (inLabels.isEmpty() && outLabels.isEmpty()) {
477 throw new NoEdgeRuleFoundException(
478 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
479 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
480 list.add(".out('" + String.join("','", outLabels) + "')");
481 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
482 list.add(".in('" + String.join("','", inLabels) + "')");
484 list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('"
485 + String.join("','", outLabels) + "'))");
488 list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
496 * @param outType the out type
497 * @param inType the in type
498 * @throws NoEdgeRuleFoundException
499 * @throws AAIException
501 private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
502 markParentBoundary();
503 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
504 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
506 if (labels == null) {
507 rules.putAll(edgeRules.getRules(qB.build()));
509 for (String label : labels) {
510 rules.putAll(edgeRules.getRules(qB.label(label).build()));
513 } catch (EdgeRuleNotFoundException e) {
514 throw new NoEdgeRuleFoundException(e);
517 final List<String> inLabels = new ArrayList<>();
518 final List<String> outLabels = new ArrayList<>();
520 for (EdgeRule rule : rules.values()) {
521 if (labels != null && !labels.contains(rule.getLabel())) {
524 if (Direction.IN.equals(rule.getDirection())) {
525 inLabels.add(rule.getLabel());
527 outLabels.add(rule.getLabel());
532 if (inLabels.isEmpty() && outLabels.isEmpty()) {
533 throw new NoEdgeRuleFoundException(
534 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
535 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
536 list.add(".outE('" + String.join("','", outLabels) + "')");
537 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
538 list.add(".inE('" + String.join("','", inLabels) + "')");
540 list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('"
541 + String.join("','", outLabels) + "'))");
549 public QueryBuilder<E> limit(long amount) {
550 list.add(".limit(" + amount + ")");
558 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
559 String type = obj.getChildDBName();
560 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
561 if (abstractType != null) {
562 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
563 String[] wrapped = new String[inheritors.length];
564 StringBuilder command = new StringBuilder();
565 command.append("P.within(");
566 for (int i = 0; i < inheritors.length; i++) {
567 wrapped[i] = "'" + inheritors[i] + "'";
569 command.append(Joiner.on(",").join(wrapped));
571 list.add(".has('aai-node-type', " + command + ")");
574 list.add(".has('aai-node-type', '" + type + "')");
577 this.markContainerIndex();
578 return (QueryBuilder<Vertex>) this;
582 public QueryBuilder<E> union(QueryBuilder<E>... builder) {
583 markParentBoundary();
584 String[] traversals = new String[builder.length];
585 StringBuilder command = new StringBuilder();
586 for (int i = 0; i < builder.length; i++) {
587 traversals[i] = "__" + builder[i].getQuery();
589 command.append(".union(");
590 command.append(Joiner.on(",").join(traversals));
592 list.add(command.toString());
599 public QueryBuilder<E> where(QueryBuilder<E>... builder) {
600 markParentBoundary();
601 List<String> traversals = new ArrayList<>();
602 for (int i = 0; i < builder.length; i++) {
603 traversals.add(".where(__" + builder[i].getQuery() + ")");
606 list.addAll(traversals);
612 public QueryBuilder<E> or(QueryBuilder<E>... builder) {
613 markParentBoundary();
614 String[] traversals = new String[builder.length];
615 StringBuilder command = new StringBuilder();
616 for (int i = 0; i < builder.length; i++) {
617 traversals[i] = "__" + builder[i].getQuery();
619 command.append(".or(");
620 command.append(Joiner.on(",").join(traversals));
622 list.add(command.toString());
629 public QueryBuilder<E> store(String name) {
630 this.list.add(".store('" + name + "')");
637 public QueryBuilder<E> cap(String name) {
638 this.list.add(".cap('" + name + "')");
645 public QueryBuilder<E> unfold() {
646 this.list.add(".unfold()");
653 public QueryBuilder<E> fold() {
654 this.list.add(".fold()");
660 public QueryBuilder<E> id() {
661 this.list.add(".id()");
667 public QueryBuilder<E> dedup() {
668 this.list.add(".dedup()");
675 public QueryBuilder<E> emit() {
676 this.list.add(".emit()");
683 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
684 this.list.add(".repeat(__" + builder.getQuery() + ")");
691 public QueryBuilder<E> until(QueryBuilder<E> builder) {
692 this.list.add(".until(__" + builder.getQuery() + ")");
699 public QueryBuilder<E> groupCount() {
700 this.list.add(".groupCount()");
707 public QueryBuilder<E> both() {
708 this.list.add(".both()");
715 public QueryBuilder<Tree> tree() {
716 this.list.add(".tree()");
719 return (QueryBuilder<Tree>) this;
723 public QueryBuilder<E> by(String name) {
724 this.list.add(".by('" + name + "')");
731 public QueryBuilder<E> valueMap() {
732 this.list.add(".valueMap()");
739 public QueryBuilder<E> valueMap(String... names) {
740 String stepString = ".valueMap('";
741 for (int i = 0; i < names.length; i++) {
742 stepString = stepString + names[i] + "'";
743 if (i != (names.length - 1)) {
744 stepString = stepString + ",'";
747 stepString = stepString + ")";
748 this.list.add(stepString);
758 public QueryBuilder<E> simplePath() {
759 this.list.add(".simplePath()");
768 public QueryBuilder<Path> path() {
769 this.list.add(".path()");
771 return (QueryBuilder<Path>) this;
775 public QueryBuilder<Edge> outE() {
776 this.list.add(".outE()");
779 return (QueryBuilder<Edge>) this;
783 public QueryBuilder<Edge> inE() {
784 this.list.add(".inE()");
787 return (QueryBuilder<Edge>) this;
791 public QueryBuilder<Vertex> outV() {
792 this.list.add(".outV()");
795 return (QueryBuilder<Vertex>) this;
799 public QueryBuilder<Vertex> inV() {
800 this.list.add(".inV()");
803 return (QueryBuilder<Vertex>) this;
807 public QueryBuilder<E> not(QueryBuilder<E> builder) {
808 this.list.add(".not(" + "__" + builder.getQuery() + ")");
815 public QueryBuilder<E> as(String name) {
816 this.list.add(".as('" + name + "')");
823 public QueryBuilder<E> select(String name) {
824 this.list.add(".select('" + name + "')");
831 public QueryBuilder<E> select(String... names) {
832 String stepString = ".select('";
833 for (int i = 0; i < names.length; i++) {
834 stepString = stepString + names[i] + "'";
835 if (i != (names.length - 1)) {
836 stepString = stepString + ",'";
839 stepString = stepString + ")";
840 this.list.add(stepString);
850 public QueryBuilder<E> getParentQuery() {
851 return cloneQueryAtStep(parentStepIndex);
855 public QueryBuilder<E> getContainerQuery() {
856 return cloneQueryAtStep(containerStepIndex);
863 public <T2> T2 getQuery() {
864 StringBuilder sb = new StringBuilder();
866 for (String piece : this.list) {
870 return (T2) sb.toString();
877 public void markParentBoundary() {
878 parentStepIndex = stepIndex;
882 public void markContainerIndex() {
883 this.containerStepIndex = stepIndex;
890 public Vertex getStart() {
894 protected int getParentStepIndex() {
895 return parentStepIndex;
898 protected int getContainerStepIndex() {
899 return containerStepIndex;
902 protected int getStepIndex() {
906 private void executeQuery() {
907 String queryString = "g" + Joiner.on("").join(list);
908 Map<String, Object> params = new HashMap<>();
909 if (this.start == null) {
910 params.put("g", source.V());
912 params.put("g", source.V(this.start));
914 this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
918 public boolean hasNext() {
919 if (this.completeTraversal == null) {
923 return this.completeTraversal.hasNext();
928 if (this.completeTraversal == null) {
932 return (E) this.completeTraversal.next();
936 public List<E> toList() {
937 if (this.completeTraversal == null) {
941 return (List<E>) this.completeTraversal.toList();
944 protected QueryBuilder<Edge> has(String key, String value) {
945 this.list.add(HAS + key + "','" + value + "')");
947 return (QueryBuilder<Edge>) this;
951 * This is required for the subgraphstrategies to work