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;
51 * The Class GremlinQueryBuilder.
53 public abstract class GremlinQueryBuilder<E> extends QueryBuilder<E> {
55 private static final String ARGUMENT2 = "#!#argument#!#";
56 private static final String HAS = ".has('";
57 private GremlinGroovyShell gremlinGroovy = new GremlinGroovyShell();
58 private GraphTraversal<?, ?> completeTraversal = null;
59 protected List<String> list = null;
62 * Instantiates a new gremlin query builder.
64 * @param loader the loader
66 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source) {
67 super(loader, source);
68 list = new ArrayList<>();
72 * Instantiates a new gremlin query builder.
74 * @param loader the loader
75 * @param start the start
77 public GremlinQueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
78 super(loader, source, start);
79 list = new ArrayList<>();
83 public QueryBuilder<Vertex> exactMatchQuery(Introspector obj) {
84 // TODO not implemented because this is implementation is no longer used
85 this.createKeyQuery(obj);
86 this.createContainerQuery(obj);
87 return (QueryBuilder<Vertex>) this;
94 public QueryBuilder<Vertex> getVerticesByProperty(String key, Object value) {
97 if (value != null && !(value instanceof String)) {
98 term = value.toString();
100 term = "'" + value + "'";
102 list.add(HAS + key + "', " + term + ")");
104 return (QueryBuilder<Vertex>) this;
111 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
112 list.add(HAS + key + "', " + value + ")");
114 return (QueryBuilder<Vertex>) this;
118 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value) {
120 if (value != null && !"".equals(value)) {
121 boolean bValue = false;
122 if (value instanceof String) {
123 bValue = Boolean.valueOf(value.toString());
124 } else if (value instanceof Boolean) {
125 bValue = (Boolean) value;
128 list.add(HAS + key + "', " + bValue + ")");
131 return (QueryBuilder<Vertex>) this;
138 public QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values) {
140 String predicate = "P.within(#!#argument#!#)";
141 List<String> arguments = new ArrayList<>();
142 for (Object item : values) {
143 if (item != null && !(item instanceof String)) {
144 arguments.add(item.toString());
146 arguments.add("'" + item + "'");
149 String argument = Joiner.on(",").join(arguments);
150 predicate = predicate.replace(ARGUMENT2, argument);
151 list.add(HAS + key + "', " + predicate + ")");
153 return (QueryBuilder<Vertex>) this;
160 public QueryBuilder<Vertex> getVerticesByProperty(String key) {
162 list.add(HAS + key + "')");
164 return (QueryBuilder<Vertex>) this;
171 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key) {
173 list.add(".hasNot('" + key + "')");
175 return (QueryBuilder<Vertex>) this;
182 public QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value) {
185 String predicate = "org.janusgraph.core.attribute.Text.textPrefix(#!#argument#!#)";
186 if (value != null && !(value instanceof String)) {
187 term = value.toString();
189 term = "'" + value + "'";
191 predicate = predicate.replace(ARGUMENT2, term);
192 list.add(HAS + key + "', " + predicate + ")");
194 return (QueryBuilder<Vertex>) this;
201 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value) {
204 String predicate = "P.neq(#!#argument#!#)";
205 if (value != null && !(value instanceof String)) {
206 term = value.toString();
208 term = "'" + value + "'";
210 predicate = predicate.replace(ARGUMENT2, term);
211 list.add(HAS + key + "', " + predicate + ")");
213 return (QueryBuilder<Vertex>) this;
220 public QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values) {
222 String predicate = "P.without(#!#argument#!#)";
223 List<String> arguments = new ArrayList<>();
224 for (Object item : values) {
225 if (item != null && !(item instanceof String)) {
226 arguments.add(item.toString());
228 arguments.add("'" + item + "'");
231 String argument = Joiner.on(",").join(arguments);
232 predicate = predicate.replace(ARGUMENT2, argument);
233 list.add(HAS + key + "', " + predicate + ")");
235 return (QueryBuilder<Vertex>) this;
239 public QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value) {
240 String predicate = "P.gte(#!#argument1#!#)";
242 if (value != null && !(value instanceof String)) {
243 term = value.toString();
245 term = "'" + value + "'";
247 predicate = predicate.replace("#!#argument1#!#", term);
248 list.add(HAS + key + "', " + predicate + ")");
250 return (QueryBuilder<Vertex>) this;
254 public QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value) {
255 String predicate = "P.lte(#!#argument1#!#)";
257 if (value != null && !(value instanceof String)) {
258 term = value.toString();
260 term = "'" + value + "'";
262 predicate = predicate.replace("#!#argument1#!#", term);
263 list.add(HAS + key + "', " + predicate + ")");
265 return (QueryBuilder<Vertex>) this;
272 public QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue, String childType) {
274 return (QueryBuilder<Vertex>) this;
281 public QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map) {
283 for (Map.Entry<String, String> es : map.entrySet()) {
284 list.add(HAS + es.getKey() + "', '" + es.getValue() + "')");
287 list.add(".has('aai-node-type', '" + type + "')");
289 return (QueryBuilder<Vertex>) this;
296 public QueryBuilder<Vertex> createKeyQuery(Introspector obj) {
297 Set<String> keys = obj.getKeys();
299 for (String key : keys) {
301 this.getVerticesByProperty(key, obj.<Object>getValue(key));
304 return (QueryBuilder<Vertex>) this;
308 * @throws NoEdgeRuleFoundException
309 * @throws AAIException
313 public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
314 throws AAIException {
315 String parentName = parent.getDbName();
316 String childName = child.getDbName();
317 if (parent.isContainer()) {
318 parentName = parent.getChildDBName();
320 if (child.isContainer()) {
321 childName = child.getChildDBName();
323 this.edgeQueryToVertex(type, parentName, childName, null);
329 public QueryBuilder createPrivateEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
330 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,
349 List<String> labels) throws AAIException {
350 String parentName = out.getDbName();
351 String childName = in.getDbName();
352 if (out.isContainer()) {
353 parentName = out.getChildDBName();
355 if (in.isContainer()) {
356 childName = in.getChildDBName();
358 this.edgeQueryToVertex(type, parentName, childName, labels);
359 return (QueryBuilder<Vertex>) this;
362 public QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
363 List<String> labels) throws AAIException {
364 this.edgeQuery(type, outNodeType, inNodeType, labels);
365 return (QueryBuilder<Edge>) this;
368 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels)
369 throws AAIException {
370 this.edgeQueryToVertex(type, outType, inType, labels, false);
376 * @param outType the out type
377 * @param inType the in type
378 * @throws NoEdgeRuleFoundException
379 * @throws AAIException
381 private void edgeQueryToVertex(EdgeType type, String outType, String inType, List<String> labels,
382 boolean isPrivateEdge) throws AAIException {
383 markParentBoundary();
384 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
385 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type).setPrivate(isPrivateEdge);
388 if (labels == null) {
389 rules.putAll(edgeRules.getRules(qB.build()));
391 for (String label : labels) {
392 rules.putAll(edgeRules.getRules(qB.label(label).build()));
395 } catch (EdgeRuleNotFoundException e) {
396 throw new NoEdgeRuleFoundException(e);
399 final List<String> inLabels = new ArrayList<>();
400 final List<String> outLabels = new ArrayList<>();
402 for (EdgeRule rule : rules.values()) {
403 if (labels != null && !labels.contains(rule.getLabel())) {
406 if (Direction.IN.equals(rule.getDirection())) {
407 inLabels.add(rule.getLabel());
408 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
409 outLabels.add(rule.getLabel());
412 outLabels.add(rule.getLabel());
413 if (inType.equals(outType)) {// code to handle when a type edges to itself, to add both in and out
414 inLabels.add(rule.getLabel());
420 if (inLabels.isEmpty() && outLabels.isEmpty()) {
421 throw new NoEdgeRuleFoundException(
422 "no " + type.toString() + " edge rule between " + outType + " and " + inType);
423 } else if (inLabels.isEmpty() && !outLabels.isEmpty()) {
424 list.add(".out('" + String.join("','", outLabels) + "')");
425 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
426 list.add(".in('" + String.join("','", inLabels) + "')");
428 list.add(".union(__.in('" + String.join("','", inLabels) + "')" + ", __.out('"
429 + String.join("','", outLabels) + "'))");
432 list.add(HAS + AAIProperties.NODE_TYPE + "', '" + inType + "')");
440 * @param outType the out type
441 * @param inType the in type
442 * @throws NoEdgeRuleFoundException
443 * @throws AAIException
445 private void edgeQuery(EdgeType type, String outType, String inType, List<String> labels) throws AAIException {
446 markParentBoundary();
447 Multimap<String, EdgeRule> rules = ArrayListMultimap.create();
448 EdgeRuleQuery.Builder qB = new EdgeRuleQuery.Builder(outType, inType).edgeType(type);
450 if (labels == null) {
451 rules.putAll(edgeRules.getRules(qB.build()));
453 for (String label : labels) {
454 rules.putAll(edgeRules.getRules(qB.label(label).build()));
457 } catch (EdgeRuleNotFoundException e) {
458 throw new NoEdgeRuleFoundException(e);
461 final List<String> inLabels = new ArrayList<>();
462 final List<String> outLabels = new ArrayList<>();
464 for (EdgeRule rule : rules.values()) {
465 if (labels != null && !labels.contains(rule.getLabel())) {
468 if (Direction.IN.equals(rule.getDirection())) {
469 inLabels.add(rule.getLabel());
471 outLabels.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(".outE('" + String.join("','", outLabels) + "')");
481 } else if (outLabels.isEmpty() && !inLabels.isEmpty()) {
482 list.add(".inE('" + String.join("','", inLabels) + "')");
484 list.add(".union(__.inE('" + String.join("','", inLabels) + "')" + ", __.outE('"
485 + String.join("','", outLabels) + "'))");
493 public QueryBuilder<E> limit(long amount) {
494 list.add(".limit(" + amount + ")");
502 public QueryBuilder<Vertex> createContainerQuery(Introspector obj) {
503 String type = obj.getChildDBName();
504 String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT);
505 if (abstractType != null) {
506 String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(",");
507 String[] wrapped = new String[inheritors.length];
508 StringBuilder command = new StringBuilder();
509 command.append("P.within(");
510 for (int i = 0; i < inheritors.length; i++) {
511 wrapped[i] = "'" + inheritors[i] + "'";
513 command.append(Joiner.on(",").join(wrapped));
515 list.add(".has('aai-node-type', " + command + ")");
518 list.add(".has('aai-node-type', '" + type + "')");
521 this.markContainer();
522 return (QueryBuilder<Vertex>) this;
526 public QueryBuilder<E> union(QueryBuilder<E>... builder) {
527 markParentBoundary();
528 String[] traversals = new String[builder.length];
529 StringBuilder command = new StringBuilder();
530 for (int i = 0; i < builder.length; i++) {
531 traversals[i] = "__" + builder[i].getQuery();
533 command.append(".union(");
534 command.append(Joiner.on(",").join(traversals));
536 list.add(command.toString());
543 public QueryBuilder<E> where(QueryBuilder<E>... builder) {
544 markParentBoundary();
545 List<String> traversals = new ArrayList<>();
546 for (int i = 0; i < builder.length; i++) {
547 traversals.add(".where(__" + builder[i].getQuery() + ")");
550 list.addAll(traversals);
556 public QueryBuilder<E> or(QueryBuilder<E>... builder) {
557 markParentBoundary();
558 String[] traversals = new String[builder.length];
559 StringBuilder command = new StringBuilder();
560 for (int i = 0; i < builder.length; i++) {
561 traversals[i] = "__" + builder[i].getQuery();
563 command.append(".or(");
564 command.append(Joiner.on(",").join(traversals));
566 list.add(command.toString());
573 public QueryBuilder<E> store(String name) {
574 this.list.add(".store('" + name + "')");
581 public QueryBuilder<E> cap(String name) {
582 this.list.add(".cap('" + name + "')");
589 public QueryBuilder<E> unfold() {
590 this.list.add(".unfold()");
597 public QueryBuilder<E> dedup() {
598 this.list.add(".dedup()");
605 public QueryBuilder<E> emit() {
606 this.list.add(".emit()");
613 public QueryBuilder<E> repeat(QueryBuilder<E> builder) {
614 this.list.add(".repeat(__" + builder.getQuery() + ")");
621 public QueryBuilder<E> until(QueryBuilder<E> builder) {
622 this.list.add(".until(__" + builder.getQuery() + ")");
629 public QueryBuilder<E> groupCount() {
630 this.list.add(".groupCount()");
637 public QueryBuilder<E> both() {
638 this.list.add(".both()");
645 public QueryBuilder<Tree> tree() {
646 this.list.add(".tree()");
649 return (QueryBuilder<Tree>) this;
653 public QueryBuilder<E> by(String name) {
654 this.list.add(".by('" + name + "')");
664 public QueryBuilder<E> simplePath() {
665 this.list.add(".simplePath()");
674 public QueryBuilder<Path> path() {
675 this.list.add(".path()");
677 return (QueryBuilder<Path>) this;
681 public QueryBuilder<Edge> outE() {
682 this.list.add(".outE()");
685 return (QueryBuilder<Edge>) this;
689 public QueryBuilder<Edge> inE() {
690 this.list.add(".inE()");
693 return (QueryBuilder<Edge>) this;
697 public QueryBuilder<Vertex> outV() {
698 this.list.add(".outV()");
701 return (QueryBuilder<Vertex>) this;
705 public QueryBuilder<Vertex> inV() {
706 this.list.add(".inV()");
709 return (QueryBuilder<Vertex>) this;
713 public QueryBuilder<E> not(QueryBuilder<E> builder) {
714 this.list.add(".not(" + "__" + builder.getQuery() + ")");
721 public QueryBuilder<E> as(String name) {
722 this.list.add(".as('" + name + "')");
729 public QueryBuilder<E> select(String name) {
730 this.list.add(".select('" + name + "')");
737 public QueryBuilder<E> select(String... names) {
738 String stepString = ".select('";
739 for (int i = 0; i < names.length; i++) {
740 stepString = stepString + names[i] + "'";
741 if (i != (names.length - 1)) {
742 stepString = stepString + ",'";
745 stepString = stepString + ")";
746 this.list.add(stepString);
756 public QueryBuilder<E> getParentQuery() {
757 return cloneQueryAtStep(parentStepIndex);
761 public QueryBuilder<E> getContainerQuery() {
762 return cloneQueryAtStep(containerStepIndex);
769 public <T2> T2 getQuery() {
770 StringBuilder sb = new StringBuilder();
772 for (String piece : this.list) {
776 return (T2) sb.toString();
783 public void markParentBoundary() {
784 parentStepIndex = stepIndex;
788 public void markContainer() {
789 this.containerStepIndex = stepIndex;
796 public Vertex getStart() {
800 protected int getParentStepIndex() {
801 return parentStepIndex;
804 protected int getContainerStepIndex() {
805 return containerStepIndex;
808 protected int getStepIndex() {
812 private void executeQuery() {
813 String queryString = "g" + Joiner.on("").join(list);
814 Map<String, Object> params = new HashMap<>();
815 if (this.start == null) {
816 params.put("g", source.V());
818 params.put("g", source.V(this.start));
820 this.completeTraversal = this.gremlinGroovy.executeTraversal(queryString, params);
824 public boolean hasNext() {
825 if (this.completeTraversal == null) {
829 return this.completeTraversal.hasNext();
834 if (this.completeTraversal == null) {
838 return (E) this.completeTraversal.next();
842 public List<E> toList() {
843 if (this.completeTraversal == null) {
847 return (List<E>) this.completeTraversal.toList();
850 protected QueryBuilder<Edge> has(String key, String value) {
851 this.list.add(HAS + key + "','" + value + "')");
853 return (QueryBuilder<Edge>) this;