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.Pop;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
35 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
36 import org.apache.tinkerpop.gremlin.structure.Direction;
37 import org.apache.tinkerpop.gremlin.structure.Edge;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39 import org.onap.aai.db.props.AAIProperties;
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.exceptions.AAIException;
45 import org.onap.aai.introspection.Introspector;
46 import org.onap.aai.introspection.Loader;
47 import org.onap.aai.query.entities.PaginationResult;
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;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 * The Class GremlinQueryBuilder.
57 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
59 private static final String ARGUMENT2 = "#!#argument#!#";
60 private static final String HAS = ".has('";
61 private static final String SINGLE_QUOTE = "'";
62 private static final String ESCAPE_SINGLE_QUOTE = "\\\'";
63 private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell();
64 private GraphTraversal<?, ?> completeTraversal = null;
65 protected List<String> list = null;
67 private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class);
70 * Instantiates a new gremlin query builder.
72 * @param loader the loader
74 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
75 super(loader, source);
76 list = new ArrayList<>();
80 * Instantiates a new gremlin query builder.
82 * @param loader the loader
83 * @param start the start
85 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
86 super(loader, source, start);
87 list = new ArrayList<>();
91 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
92 // TODO not implemented because this is implementation is no longer used
93 this.createKeyQuery(obj);
94 this.createContainerQuery(obj);
95 return (QueryBuilder<Vertex>) this;
99 protected void vertexHas(String key, Object value) {
100 list.add(HAS + key + "', " + value + ")");
104 protected void vertexHasNot(String key) {
105 list.add(".hasNot('" + key + "')");
110 protected void vertexHas(String key) {
111 list.add(HAS + key + "')");
118 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
121 if (value != null && !(value instanceof String)) {
122 String valueString = value.toString();
124 if (valueString.indexOf('\'') != -1) {
125 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
127 LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
128 term = value.toString();
129 } else if (value != null && value instanceof String) {
130 String valueString = value.toString();
132 if (valueString.indexOf('\'') != -1) {
133 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
135 LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
136 term = "'" + value + "'";
138 term = "'" + value + "'";
140 this.vertexHas(key, term);
142 return (QueryBuilder<Vertex>) this;
149 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
150 this.vertexHas(key, value);
152 return (QueryBuilder<Vertex>) this;
156 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
158 if (value != null && !"".equals(value)) {
159 boolean bValue = false;
160 if (value instanceof String) {
161 bValue = Boolean.valueOf(value.toString());
162 } else if (value instanceof Boolean) {
163 bValue = (Boolean) value;
166 this.vertexHas(key, bValue);
169 return (QueryBuilder<Vertex>) this;
176 public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
178 String predicate = "P.within(#!#argument#!#)";
179 List<String> arguments = new ArrayList<>();
180 for (Object item : values) {
181 if (item != null && !(item instanceof String)) {
182 arguments.add(item.toString());
184 arguments.add("'" + item + "'");
187 String argument = Joiner.on(",").join(arguments);
188 predicate = predicate.replace(ARGUMENT2, argument);
189 this.vertexHas(key, predicate);
191 return (QueryBuilder<Vertex>) this;
198 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
199 ArrayList<String> arguments = new ArrayList<>(Arrays.asList(value.split(",")));
200 // add the single quotes
201 for (int i = 0; i < arguments.size(); i++) {
202 if (arguments.get(i) != null && !arguments.get(i).startsWith("'") && !arguments.get(i).endsWith("'")) {
203 arguments.set(i, "'" + arguments.get(i).trim() + "'");
205 arguments.set(i, arguments.get(i).trim());
208 String predicate = "P.within(#!#argument#!#)";
209 String argument = Joiner.on(",").join(arguments);
210 predicate = predicate.replace(ARGUMENT2, argument);
211 this.vertexHas(key, predicate);
213 return (QueryBuilder<Vertex>) this;
220 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
223 return (QueryBuilder<Vertex>) this;
230 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
231 this.vertexHasNot(key);
233 return (QueryBuilder<Vertex>) this;
240 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
242 String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
243 if (value != null && !(value instanceof String)) {
244 term = value.toString();
246 term = "'" + value + "'";
248 predicate = predicate.replace(ARGUMENT2, term);
249 this.vertexHas(key, predicate);
251 return (QueryBuilder<Vertex>) this;
258 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
261 String predicate = "P.neq(#!#argument#!#)";
262 if (value != null && !(value instanceof String)) {
263 term = value.toString();
265 term = "'" + value + "'";
267 predicate = predicate.replace(ARGUMENT2, term);
268 this.vertexHas(key, predicate);
270 return (QueryBuilder<Vertex>) this;
277 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
279 String predicate = "P.without(#!#argument#!#)";
280 List<String> arguments = new ArrayList<>();
281 for (Object item : values) {
282 if (item != null && !(item instanceof String)) {
283 arguments.add(item.toString());
285 arguments.add("'" + item + "'");
288 String argument = Joiner.on(",").join(arguments);
289 predicate = predicate.replace(ARGUMENT2, argument);
290 this.vertexHas(key, predicate);
292 return (QueryBuilder<Vertex>) this;
296 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
297 String predicate = "P.gte(#!#argument1#!#)";
299 if (value != null && !(value instanceof String)) {
300 term = value.toString();
302 term = "'" + value + "'";
304 predicate = predicate.replace("#!#argument1#!#", term);
305 this.vertexHas(key, predicate);
307 return (QueryBuilder<Vertex>) this;
311 public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
312 String predicate = "P.lte(#!#argument1#!#)";
314 if (value != null && !(value instanceof String)) {
315 term = value.toString();
317 term = "'" + value + "'";
319 predicate = predicate.replace("#!#argument1#!#", term);
320 this.vertexHas(key, predicate);
322 return (QueryBuilder<Vertex>) this;
329 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
331 return (QueryBuilder<Vertex>) this;
338 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
340 for (Map.Entry<String, String> es : map.entrySet()) {
341 // TODO what is this and where is it used - need to check
342 list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
345 list.add(".has('aai-node-type', '" + type + "')");
347 return (QueryBuilder<Vertex>) this;
354 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
355 Set<String> keys = obj.getKeys();
357 for (String key : keys) {
359 this.getVerticesByProperty(key, obj.<Object>getValue(key));
362 return (QueryBuilder<Vertex>) this;
366 * @throws NoEdgeRuleFoundException
367 * @throws AAIException
371 public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
372 throws AAIException {
373 String parentName = parent.getDbName();
374 String childName = child.getDbName();
375 if (parent.isContainer()) {
376 parentName = parent.getChildDBName();
378 if (child.isContainer()) {
379 childName = child.getChildDBName();
381 this.edgeQueryToVertex(type, parentName, childName, null);
387 public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
388 throws AAIException {
389 String parentName = parent.getDbName();
390 String childName = child.getDbName();
391 if (parent.isContainer()) {
392 parentName = parent.getChildDBName();
394 if (child.isContainer()) {
395 childName = child.getChildDBName();
397 this.edgeQueryToVertex(type, parentName, childName, null, true);
406 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
407 List<String> labels) throws AAIException {
408 String parentName = out.getDbName();
409 String childName = in.getDbName();
410 if (out.isContainer()) {
411 parentName = out.getChildDBName();
413 if (in.isContainer()) {
414 childName = in.getChildDBName();
416 this.edgeQueryToVertex(type, parentName, childName, labels);
417 return (QueryBuilder<Vertex>) this;
420 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
421 List<String> labels) throws AAIException {
422 this.edgeQuery(type, outNodeType, inNodeType, labels);
423 return (QueryBuilder<Edge>) this;
426 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels)
427 throws AAIException {
428 this.edgeQueryToVertex(type, outType, inType, labels, false);
434 * @param outType the out type
435 * @param inType the in type
436 * @throws NoEdgeRuleFoundException
437 * @throws AAIException
439 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels,
440 boolean isPrivateEdge) throws AAIException {
441 markParentBoundary();
442 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
443 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
446 if (labels == null) {
447 rules.putAll(edgeRules.getRules(qB.build()));
449 for (String label : labels) {
450 rules.putAll(edgeRules.getRules(qB.label(label).build()));
453 } catch (EdgeRuleNotFoundException e) {
454 throw new NoEdgeRuleFoundException(e);
457 final List<String> inLabels = new ArrayList<>();
458 final List<String> outLabels = new ArrayList<>();
460 for (EdgeRule rule : rules.values()) {
461 if (labels != null && !labels.contains(rule.getLabel())) {
464 if (Direction.IN.equals(rule.getDirection())) {
465 inLabels.add(rule.getLabel());
466 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
467 outLabels.add(rule.getLabel());
470 outLabels.add(rule.getLabel());
471 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
472 inLabels.add(rule.getLabel());
478 if (inLabels.isEmpty() && outLabels.isEmpty()) {
479 throw new NoEdgeRuleFoundException(
480 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
481 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
482 list.add(".out('" + String.join("','", outLabels) + "')");
483 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
484 list.add(".in('" + String.join("','", inLabels) + "')");
486 list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('"
487 + String.join("','", outLabels) + "'))");
490 list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
498 * @param outType the out type
499 * @param inType the in type
500 * @throws NoEdgeRuleFoundException
501 * @throws AAIException
503 private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
504 markParentBoundary();
505 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
506 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
508 if (labels == null) {
509 rules.putAll(edgeRules.getRules(qB.build()));
511 for (String label : labels) {
512 rules.putAll(edgeRules.getRules(qB.label(label).build()));
515 } catch (EdgeRuleNotFoundException e) {
516 throw new NoEdgeRuleFoundException(e);
519 final List<String> inLabels = new ArrayList<>();
520 final List<String> outLabels = new ArrayList<>();
522 for (EdgeRule rule : rules.values()) {
523 if (labels != null && !labels.contains(rule.getLabel())) {
526 if (Direction.IN.equals(rule.getDirection())) {
527 inLabels.add(rule.getLabel());
529 outLabels.add(rule.getLabel());
534 if (inLabels.isEmpty() && outLabels.isEmpty()) {
535 throw new NoEdgeRuleFoundException(
536 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
537 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
538 list.add(".outE('" + String.join("','", outLabels) + "')");
539 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
540 list.add(".inE('" + String.join("','", inLabels) + "')");
542 list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('"
543 + String.join("','", outLabels) + "'))");
551 public QueryBuilder<E> limit(long amount) {
552 list.add(".limit(" + amount + ")");
560 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
561 String type = obj.getChildDBName();
562 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
563 if (abstractType != null) {
564 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
565 String[] wrapped = new String[inheritors.length];
566 StringBuilder command = new StringBuilder();
567 command.append("P.within(");
568 for (int i = 0; i < inheritors.length; i++) {
569 wrapped[i] = "'" + inheritors[i] + "'";
571 command.append(Joiner.on(",").join(wrapped));
573 list.add(".has('aai-node-type', " + command + ")");
576 list.add(".has('aai-node-type', '" + type + "')");
579 this.markContainer();
580 return (QueryBuilder<Vertex>) this;
584 public QueryBuilder<E> union(QueryBuilder<E>... builder) {
585 markParentBoundary();
586 String[] traversals = new String[builder.length];
587 StringBuilder command = new StringBuilder();
588 for (int i = 0; i < builder.length; i++) {
589 traversals[i] = "__" + builder[i].getQuery();
591 command.append(".union(");
592 command.append(Joiner.on(",").join(traversals));
594 list.add(command.toString());
601 public QueryBuilder<E> where(QueryBuilder<E>... builder) {
602 markParentBoundary();
603 List<String> traversals = new ArrayList<>();
604 for (int i = 0; i < builder.length; i++) {
605 traversals.add(".where(__" + builder[i].getQuery() + ")");
608 list.addAll(traversals);
614 public QueryBuilder<E> or(QueryBuilder<E>... builder) {
615 markParentBoundary();
616 String[] traversals = new String[builder.length];
617 StringBuilder command = new StringBuilder();
618 for (int i = 0; i < builder.length; i++) {
619 traversals[i] = "__" + builder[i].getQuery();
621 command.append(".or(");
622 command.append(Joiner.on(",").join(traversals));
624 list.add(command.toString());
631 public QueryBuilder<E> store(String name) {
632 this.list.add(".store('" + name + "')");
639 public QueryBuilder<E> cap(String name) {
640 this.list.add(".cap('" + name + "')");
647 public QueryBuilder<E> unfold() {
648 this.list.add(".unfold()");
655 public QueryBuilder<E> fold() {
656 this.list.add(".fold()");
662 public QueryBuilder<E> id() {
663 this.list.add(".id()");
669 public QueryBuilder<E> dedup() {
670 this.list.add(".dedup()");
677 public QueryBuilder<E> emit() {
678 this.list.add(".emit()");
685 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
686 this.list.add(".repeat(__" + builder.getQuery() + ")");
693 public QueryBuilder<E> until(QueryBuilder<E> builder) {
694 this.list.add(".until(__" + builder.getQuery() + ")");
701 public QueryBuilder<E> groupCount() {
702 this.list.add(".groupCount()");
709 public QueryBuilder<E> both() {
710 this.list.add(".both()");
717 public QueryBuilder<Tree> tree() {
718 this.list.add(".tree()");
721 return (QueryBuilder<Tree>) this;
725 public QueryBuilder<E> by(String name) {
726 this.list.add(".by('" + name + "')");
733 public QueryBuilder<E> valueMap() {
734 this.list.add(".valueMap()");
741 public QueryBuilder<E> valueMap(String... names) {
742 String stepString = ".valueMap('";
743 for (int i = 0; i < names.length; i++) {
744 stepString = stepString + names[i] + "'";
745 if (i != (names.length - 1)) {
746 stepString = stepString + ",'";
749 stepString = stepString + ")";
750 this.list.add(stepString);
760 public QueryBuilder<E> simplePath() {
761 this.list.add(".simplePath()");
770 public QueryBuilder<Path> path() {
771 this.list.add(".path()");
773 return (QueryBuilder<Path>) this;
777 public QueryBuilder<Edge> outE() {
778 this.list.add(".outE()");
781 return (QueryBuilder<Edge>) this;
785 public QueryBuilder<Edge> inE() {
786 this.list.add(".inE()");
789 return (QueryBuilder<Edge>) this;
793 public QueryBuilder<Vertex> outV() {
794 this.list.add(".outV()");
797 return (QueryBuilder<Vertex>) this;
801 public QueryBuilder<Vertex> inV() {
802 this.list.add(".inV()");
805 return (QueryBuilder<Vertex>) this;
809 public QueryBuilder<E> not(QueryBuilder<E> builder) {
810 this.list.add(".not(" + "__" + builder.getQuery() + ")");
817 public QueryBuilder<E> as(String name) {
818 this.list.add(".as('" + name + "')");
825 public QueryBuilder<E> select(String name) {
826 this.list.add(".select('" + name + "')");
833 public QueryBuilder<E> select(Pop pop, String name) {
834 this.list.add(".select(Pop." + pop.toString() + ",'" + name + "')");
841 public QueryBuilder<E> select(String... names) {
842 String stepString = ".select('";
843 for (int i = 0; i < names.length; i++) {
844 stepString = stepString + names[i] + "'";
845 if (i != (names.length - 1)) {
846 stepString = stepString + ",'";
849 stepString = stepString + ")";
850 this.list.add(stepString);
860 public QueryBuilder<E> getParentQuery() {
861 return cloneQueryAtStep(parentStepIndex);
865 public QueryBuilder<E> getContainerQuery() {
866 return cloneQueryAtStep(containerStepIndex);
873 public <T2> T2 getQuery() {
874 StringBuilder sb = new StringBuilder();
876 for (String piece : this.list) {
880 return (T2) sb.toString();
887 public void markParentBoundary() {
888 parentStepIndex = stepIndex;
892 public void markContainer() {
893 this.containerStepIndex = stepIndex;
900 public Vertex getStart() {
904 protected int getParentStepIndex() {
905 return parentStepIndex;
908 protected int getContainerStepIndex() {
909 return containerStepIndex;
912 protected int getStepIndex() {
916 private void executeQuery() {
917 String queryString = "g" + Joiner.on("").join(list);
918 Map<String, Object> params = new HashMap<>();
919 if (this.start == null) {
920 params.put("g", source.V());
922 params.put("g", source.V(this.start));
924 this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
928 public boolean hasNext() {
929 if (this.completeTraversal == null) {
933 return this.completeTraversal.hasNext();
938 if (this.completeTraversal == null) {
942 return (E) this.completeTraversal.next();
946 public List<E> toList() {
947 if (this.completeTraversal == null) {
951 return (List<E>) this.completeTraversal.toList();
954 protected QueryBuilder<Edge> has(String key, String value) {
955 this.list.add(HAS + key + "','" + value + "')");
957 return (QueryBuilder<Edge>) this;
961 public PaginationResult<E> toPaginationResult(Pageable pageable) {
962 // TODO Auto-generated method stub
963 throw new UnsupportedOperationException("Unimplemented method 'toPaginationResult'");
967 public QueryBuilder<E> sort(Sort sort) {
968 // TODO Auto-generated method stub
969 throw new UnsupportedOperationException("Unimplemented method 'sort'");