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.restcore.search.GremlinGroovyShell;
48 import org.onap.aai.schema.enums.ObjectMetadata;
49 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 * The Class GremlinQueryBuilder.
56 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
58 private static final String ARGUMENT2 = "#!#argument#!#";
59 private static final String HAS = ".has('";
60 private static final String SINGLE_QUOTE = "'";
61 private static final String ESCAPE_SINGLE_QUOTE = "\\\'";
62 private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell();
63 private GraphTraversal<?, ?> completeTraversal = null;
64 protected List<String> list = null;
66 private static final Logger LOGGER = LoggerFactory.getLogger(QueryBuilder.class);
69 * Instantiates a new gremlin query builder.
71 * @param loader the loader
73 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
74 super(loader, source);
75 list = new ArrayList<>();
79 * Instantiates a new gremlin query builder.
81 * @param loader the loader
82 * @param start the start
84 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
85 super(loader, source, start);
86 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();
123 if (valueString.indexOf('\'') != -1) {
124 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
126 LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
127 term = value.toString();
128 } else if (value != null && value instanceof String) {
129 String valueString = value.toString();
131 if (valueString.indexOf('\'') != -1) {
132 value = valueString.replace(SINGLE_QUOTE, ESCAPE_SINGLE_QUOTE);
134 LOGGER.trace("Inside getVerticesByProperty(): key = {}, value = {}", key, value);
135 term = "'" + value + "'";
137 term = "'" + value + "'";
139 this.vertexHas(key, term);
141 return (QueryBuilder<Vertex>) this;
148 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
149 this.vertexHas(key, value);
151 return (QueryBuilder<Vertex>) this;
155 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
157 if (value != null && !"".equals(value)) {
158 boolean bValue = false;
159 if (value instanceof String) {
160 bValue = Boolean.valueOf(value.toString());
161 } else if (value instanceof Boolean) {
162 bValue = (Boolean) value;
165 this.vertexHas(key, bValue);
168 return (QueryBuilder<Vertex>) this;
175 public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
177 String predicate = "P.within(#!#argument#!#)";
178 List<String> arguments = new ArrayList<>();
179 for (Object item : values) {
180 if (item != null && !(item instanceof String)) {
181 arguments.add(item.toString());
183 arguments.add("'" + item + "'");
186 String argument = Joiner.on(",").join(arguments);
187 predicate = predicate.replace(ARGUMENT2, argument);
188 this.vertexHas(key, predicate);
190 return (QueryBuilder<Vertex>) this;
197 public QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value) {
198 ArrayList<String> arguments = new ArrayList<>(Arrays.asList(value.split(",")));
199 // add the single quotes
200 for (int i = 0; i < arguments.size(); i++) {
201 if (arguments.get(i) != null && !arguments.get(i).startsWith("'") && !arguments.get(i).endsWith("'")) {
202 arguments.set(i, "'" + arguments.get(i).trim() + "'");
204 arguments.set(i, arguments.get(i).trim());
207 String predicate = "P.within(#!#argument#!#)";
208 String argument = Joiner.on(",").join(arguments);
209 predicate = predicate.replace(ARGUMENT2, argument);
210 this.vertexHas(key, predicate);
212 return (QueryBuilder<Vertex>) this;
219 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
222 return (QueryBuilder<Vertex>) this;
229 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
230 this.vertexHasNot(key);
232 return (QueryBuilder<Vertex>) this;
239 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
241 String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
242 if (value != null && !(value instanceof String)) {
243 term = value.toString();
245 term = "'" + value + "'";
247 predicate = predicate.replace(ARGUMENT2, term);
248 this.vertexHas(key, predicate);
250 return (QueryBuilder<Vertex>) this;
257 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
260 String predicate = "P.neq(#!#argument#!#)";
261 if (value != null && !(value instanceof String)) {
262 term = value.toString();
264 term = "'" + value + "'";
266 predicate = predicate.replace(ARGUMENT2, term);
267 this.vertexHas(key, predicate);
269 return (QueryBuilder<Vertex>) this;
276 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
278 String predicate = "P.without(#!#argument#!#)";
279 List<String> arguments = new ArrayList<>();
280 for (Object item : values) {
281 if (item != null && !(item instanceof String)) {
282 arguments.add(item.toString());
284 arguments.add("'" + item + "'");
287 String argument = Joiner.on(",").join(arguments);
288 predicate = predicate.replace(ARGUMENT2, argument);
289 this.vertexHas(key, predicate);
291 return (QueryBuilder<Vertex>) this;
295 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
296 String predicate = "P.gte(#!#argument1#!#)";
298 if (value != null && !(value instanceof String)) {
299 term = value.toString();
301 term = "'" + value + "'";
303 predicate = predicate.replace("#!#argument1#!#", term);
304 this.vertexHas(key, predicate);
306 return (QueryBuilder<Vertex>) this;
310 public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
311 String predicate = "P.lte(#!#argument1#!#)";
313 if (value != null && !(value instanceof String)) {
314 term = value.toString();
316 term = "'" + value + "'";
318 predicate = predicate.replace("#!#argument1#!#", term);
319 this.vertexHas(key, predicate);
321 return (QueryBuilder<Vertex>) this;
328 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
330 return (QueryBuilder<Vertex>) this;
337 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
339 for (Map.Entry<String, String> es : map.entrySet()) {
340 // TODO what is this and where is it used - need to check
341 list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
344 list.add(".has('aai-node-type', '" + type + "')");
346 return (QueryBuilder<Vertex>) this;
353 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
354 Set<String> keys = obj.getKeys();
356 for (String key : keys) {
358 this.getVerticesByProperty(key, obj.<Object>getValue(key));
361 return (QueryBuilder<Vertex>) this;
365 * @throws NoEdgeRuleFoundException
366 * @throws AAIException
370 public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
371 throws AAIException {
372 String parentName = parent.getDbName();
373 String childName = child.getDbName();
374 if (parent.isContainer()) {
375 parentName = parent.getChildDBName();
377 if (child.isContainer()) {
378 childName = child.getChildDBName();
380 this.edgeQueryToVertex(type, parentName, childName, null);
386 public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
387 throws AAIException {
388 String parentName = parent.getDbName();
389 String childName = child.getDbName();
390 if (parent.isContainer()) {
391 parentName = parent.getChildDBName();
393 if (child.isContainer()) {
394 childName = child.getChildDBName();
396 this.edgeQueryToVertex(type, parentName, childName, null, true);
405 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
406 List<String> labels) throws AAIException {
407 String parentName = out.getDbName();
408 String childName = in.getDbName();
409 if (out.isContainer()) {
410 parentName = out.getChildDBName();
412 if (in.isContainer()) {
413 childName = in.getChildDBName();
415 this.edgeQueryToVertex(type, parentName, childName, labels);
416 return (QueryBuilder<Vertex>) this;
419 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
420 List<String> labels) throws AAIException {
421 this.edgeQuery(type, outNodeType, inNodeType, labels);
422 return (QueryBuilder<Edge>) this;
425 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels)
426 throws AAIException {
427 this.edgeQueryToVertex(type, outType, inType, labels, false);
433 * @param outType the out type
434 * @param inType the in type
435 * @throws NoEdgeRuleFoundException
436 * @throws AAIException
438 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels,
439 boolean isPrivateEdge) throws AAIException {
440 markParentBoundary();
441 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
442 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
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());
465 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
466 outLabels.add(rule.getLabel());
469 outLabels.add(rule.getLabel());
470 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
471 inLabels.add(rule.getLabel());
477 if (inLabels.isEmpty() && outLabels.isEmpty()) {
478 throw new NoEdgeRuleFoundException(
479 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
480 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
481 list.add(".out('" + String.join("','", outLabels) + "')");
482 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
483 list.add(".in('" + String.join("','", inLabels) + "')");
485 list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('"
486 + String.join("','", outLabels) + "'))");
489 list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
497 * @param outType the out type
498 * @param inType the in type
499 * @throws NoEdgeRuleFoundException
500 * @throws AAIException
502 private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
503 markParentBoundary();
504 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
505 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
507 if (labels == null) {
508 rules.putAll(edgeRules.getRules(qB.build()));
510 for (String label : labels) {
511 rules.putAll(edgeRules.getRules(qB.label(label).build()));
514 } catch (EdgeRuleNotFoundException e) {
515 throw new NoEdgeRuleFoundException(e);
518 final List<String> inLabels = new ArrayList<>();
519 final List<String> outLabels = new ArrayList<>();
521 for (EdgeRule rule : rules.values()) {
522 if (labels != null && !labels.contains(rule.getLabel())) {
525 if (Direction.IN.equals(rule.getDirection())) {
526 inLabels.add(rule.getLabel());
528 outLabels.add(rule.getLabel());
533 if (inLabels.isEmpty() && outLabels.isEmpty()) {
534 throw new NoEdgeRuleFoundException(
535 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
536 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
537 list.add(".outE('" + String.join("','", outLabels) + "')");
538 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
539 list.add(".inE('" + String.join("','", inLabels) + "')");
541 list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('"
542 + String.join("','", outLabels) + "'))");
550 public QueryBuilder<E> limit(long amount) {
551 list.add(".limit(" + amount + ")");
559 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
560 String type = obj.getChildDBName();
561 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
562 if (abstractType != null) {
563 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
564 String[] wrapped = new String[inheritors.length];
565 StringBuilder command = new StringBuilder();
566 command.append("P.within(");
567 for (int i = 0; i < inheritors.length; i++) {
568 wrapped[i] = "'" + inheritors[i] + "'";
570 command.append(Joiner.on(",").join(wrapped));
572 list.add(".has('aai-node-type', " + command + ")");
575 list.add(".has('aai-node-type', '" + type + "')");
578 this.markContainer();
579 return (QueryBuilder<Vertex>) this;
583 public QueryBuilder<E> union(QueryBuilder<E>... builder) {
584 markParentBoundary();
585 String[] traversals = new String[builder.length];
586 StringBuilder command = new StringBuilder();
587 for (int i = 0; i < builder.length; i++) {
588 traversals[i] = "__" + builder[i].getQuery();
590 command.append(".union(");
591 command.append(Joiner.on(",").join(traversals));
593 list.add(command.toString());
600 public QueryBuilder<E> where(QueryBuilder<E>... builder) {
601 markParentBoundary();
602 List<String> traversals = new ArrayList<>();
603 for (int i = 0; i < builder.length; i++) {
604 traversals.add(".where(__" + builder[i].getQuery() + ")");
607 list.addAll(traversals);
613 public QueryBuilder<E> or(QueryBuilder<E>... builder) {
614 markParentBoundary();
615 String[] traversals = new String[builder.length];
616 StringBuilder command = new StringBuilder();
617 for (int i = 0; i < builder.length; i++) {
618 traversals[i] = "__" + builder[i].getQuery();
620 command.append(".or(");
621 command.append(Joiner.on(",").join(traversals));
623 list.add(command.toString());
630 public QueryBuilder<E> store(String name) {
631 this.list.add(".store('" + name + "')");
638 public QueryBuilder<E> cap(String name) {
639 this.list.add(".cap('" + name + "')");
646 public QueryBuilder<E> unfold() {
647 this.list.add(".unfold()");
654 public QueryBuilder<E> fold() {
655 this.list.add(".fold()");
661 public QueryBuilder<E> id() {
662 this.list.add(".id()");
668 public QueryBuilder<E> dedup() {
669 this.list.add(".dedup()");
676 public QueryBuilder<E> emit() {
677 this.list.add(".emit()");
684 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
685 this.list.add(".repeat(__" + builder.getQuery() + ")");
692 public QueryBuilder<E> until(QueryBuilder<E> builder) {
693 this.list.add(".until(__" + builder.getQuery() + ")");
700 public QueryBuilder<E> groupCount() {
701 this.list.add(".groupCount()");
708 public QueryBuilder<E> both() {
709 this.list.add(".both()");
716 public QueryBuilder<Tree> tree() {
717 this.list.add(".tree()");
720 return (QueryBuilder<Tree>) this;
724 public QueryBuilder<E> by(String name) {
725 this.list.add(".by('" + name + "')");
732 public QueryBuilder<E> valueMap() {
733 this.list.add(".valueMap()");
740 public QueryBuilder<E> valueMap(String... names) {
741 String stepString = ".valueMap('";
742 for (int i = 0; i < names.length; i++) {
743 stepString = stepString + names[i] + "'";
744 if (i != (names.length - 1)) {
745 stepString = stepString + ",'";
748 stepString = stepString + ")";
749 this.list.add(stepString);
759 public QueryBuilder<E> simplePath() {
760 this.list.add(".simplePath()");
769 public QueryBuilder<Path> path() {
770 this.list.add(".path()");
772 return (QueryBuilder<Path>) this;
776 public QueryBuilder<Edge> outE() {
777 this.list.add(".outE()");
780 return (QueryBuilder<Edge>) this;
784 public QueryBuilder<Edge> inE() {
785 this.list.add(".inE()");
788 return (QueryBuilder<Edge>) this;
792 public QueryBuilder<Vertex> outV() {
793 this.list.add(".outV()");
796 return (QueryBuilder<Vertex>) this;
800 public QueryBuilder<Vertex> inV() {
801 this.list.add(".inV()");
804 return (QueryBuilder<Vertex>) this;
808 public QueryBuilder<E> not(QueryBuilder<E> builder) {
809 this.list.add(".not(" + "__" + builder.getQuery() + ")");
816 public QueryBuilder<E> as(String name) {
817 this.list.add(".as('" + name + "')");
824 public QueryBuilder<E> select(String name) {
825 this.list.add(".select('" + name + "')");
832 public QueryBuilder<E> select(Pop pop, String name) {
833 this.list.add(".select(Pop." + pop.toString() + ",'" + name + "')");
840 public QueryBuilder<E> select(String... names) {
841 String stepString = ".select('";
842 for (int i = 0; i < names.length; i++) {
843 stepString = stepString + names[i] + "'";
844 if (i != (names.length - 1)) {
845 stepString = stepString + ",'";
848 stepString = stepString + ")";
849 this.list.add(stepString);
859 public QueryBuilder<E> getParentQuery() {
860 return cloneQueryAtStep(parentStepIndex);
864 public QueryBuilder<E> getContainerQuery() {
865 return cloneQueryAtStep(containerStepIndex);
872 public <T2> T2 getQuery() {
873 StringBuilder sb = new StringBuilder();
875 for (String piece : this.list) {
879 return (T2) sb.toString();
886 public void markParentBoundary() {
887 parentStepIndex = stepIndex;
891 public void markContainer() {
892 this.containerStepIndex = stepIndex;
899 public Vertex getStart() {
903 protected int getParentStepIndex() {
904 return parentStepIndex;
907 protected int getContainerStepIndex() {
908 return containerStepIndex;
911 protected int getStepIndex() {
915 private void executeQuery() {
916 String queryString = "g" + Joiner.on("").join(list);
917 Map<String, Object> params = new HashMap<>();
918 if (this.start == null) {
919 params.put("g", source.V());
921 params.put("g", source.V(this.start));
923 this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
927 public boolean hasNext() {
928 if (this.completeTraversal == null) {
932 return this.completeTraversal.hasNext();
937 if (this.completeTraversal == null) {
941 return (E) this.completeTraversal.next();
945 public List<E> toList() {
946 if (this.completeTraversal == null) {
950 return (List<E>) this.completeTraversal.toList();
953 protected QueryBuilder<Edge> has(String key, String value) {
954 this.list.add(HAS + key + "','" + value + "')");
956 return (QueryBuilder<Edge>) this;
960 * This is required for the subgraphstrategies to work