2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Modifications Copyright © 2024 DEUTSCHE TELEKOM AG.
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 java.io.UnsupportedEncodingException;
27 import java.util.Iterator;
28 import java.util.List;
31 import jakarta.ws.rs.core.MultivaluedMap;
33 import org.apache.tinkerpop.gremlin.process.traversal.Path;
34 import org.apache.tinkerpop.gremlin.process.traversal.Pop;
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.Edge;
38 import org.apache.tinkerpop.gremlin.structure.Vertex;
39 import org.onap.aai.config.SpringContextAware;
40 import org.onap.aai.db.props.AAIProperties;
41 import org.onap.aai.edges.EdgeIngestor;
42 import org.onap.aai.edges.enums.AAIDirection;
43 import org.onap.aai.edges.enums.EdgeProperty;
44 import org.onap.aai.edges.enums.EdgeType;
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.parsers.query.QueryParser;
49 import org.onap.aai.parsers.query.QueryParserStrategy;
50 import org.onap.aai.query.entities.PaginationResult;
51 import org.springframework.context.ApplicationContext;
54 * The Class QueryBuilder.
56 public abstract class QueryBuilder<E> implements Iterator<E> {
58 protected final GraphTraversalSource source;
59 protected QueryParserStrategy factory = null;
60 protected Loader loader = null;
61 protected EdgeIngestor edgeRules;
62 protected boolean optimize = false;
63 protected Vertex start = null;
65 protected int parentStepIndex = 0;
66 protected int containerStepIndex = 0;
67 protected int stepIndex = 0;
70 * Instantiates a new query builder.
72 * @param loader the loader
74 public QueryBuilder(Loader loader, GraphTraversalSource source) {
81 * Instantiates a new query builder.
83 * @param loader the loader
84 * @param start the start
86 public QueryBuilder(Loader loader, GraphTraversalSource source, Vertex start) {
93 public void changeLoader(Loader loader) {
98 * Creates a new {@link QueryBuilder} that contains the traversal up to the provided index.
102 protected abstract QueryBuilder<E> cloneQueryAtStep(int index);
105 * Gets the vertices by indexed property.
108 * @param value the value
109 * @return the vertices by indexed property
111 public QueryBuilder<Vertex> getVerticesByIndexedProperty(String key, Object value) {
112 return this.getVerticesByProperty(key, value);
116 * Gets the vertices by property.
119 * @param value the value
120 * @return the vertices by property
122 public abstract QueryBuilder<Vertex> getVerticesByProperty(String key, Object value);
125 * Gets the edges by property.
128 * @param value the value
129 * @return the vertices by property
131 public QueryBuilder<Edge> getEdgesByProperty(String key, Object value) {
132 return this.has(key, value.toString());
136 * filters by all the values for this property
140 * @return vertices that match these values
142 public QueryBuilder<Vertex> getVerticesByIndexedProperty(String key, List<?> values) {
143 return this.getVerticesByProperty(key, values);
147 * filters by all the values for this property
151 * @return vertices that match these values
153 public abstract QueryBuilder<Vertex> getVerticesByProperty(String key, List<?> values);
156 * filters by all the values for this property
159 * @param value in comma delimited string format
160 * @return vertices that match these values
162 public abstract QueryBuilder<Vertex> getVerticesByCommaSeperatedValue(String key, String value);
165 * Gets the vertices that have this property key.
168 * @return the vertices by property
170 public abstract QueryBuilder<Vertex> getVerticesByProperty(String key);
173 * Gets the vertices that do not have this property key.
176 * @return the vertices by property
178 public abstract QueryBuilder<Vertex> getVerticesExcludeByProperty(String key);
181 * filters by elements that start with the value for this property
185 * @return vertices that match these values
187 public abstract QueryBuilder<Vertex> getVerticesStartsWithProperty(String key, Object value);
190 * Gets the vertices that are excluded by property.
193 * @param value the value
194 * @return the vertices by property
196 public abstract QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, Object value);
199 * filters by all the values for this property and excludes the vertices
203 * @return vertices that match these values
205 public QueryBuilder<Vertex> getVerticesExcludeByIndexedProperty(String key, List<?> values) {
206 return this.getVerticesExcludeByProperty(key, values);
210 * filters by all the values for this property and excludes the vertices
214 * @return vertices that match these values
216 public abstract QueryBuilder<Vertex> getVerticesExcludeByProperty(String key, List<?> values);
219 * filters by all the values greater than for this property
223 * @return vertices that match these values
225 public abstract QueryBuilder<Vertex> getVerticesGreaterThanProperty(String key, Object value);
228 * filters by all the values less than for this property
232 * @return vertices that match these values
235 public abstract QueryBuilder<Vertex> getVerticesLessThanProperty(String key, Object value);
238 * Gets the child vertices from parent.
240 * @param parentKey the parent key
241 * @param parentValue the parent value
242 * @param childType the child type
243 * @return the child vertices from parent
245 public abstract QueryBuilder<Vertex> getChildVerticesFromParent(String parentKey, String parentValue,
249 * Gets the typed vertices by map.
251 * @param type the type
253 * @return the typed vertices by map
255 public abstract QueryBuilder<Vertex> getTypedVerticesByMap(String type, Map<String, String> map);
258 * Creates the DB query.
261 * @return the query builder
263 public QueryBuilder<Vertex> createDBQuery(Introspector obj) {
264 this.createKeyQuery(obj);
265 this.createContainerQuery(obj);
266 return (QueryBuilder<Vertex>) this;
270 * Creates the key query.
273 * @return the query builder
275 public abstract QueryBuilder<Vertex> createKeyQuery(Introspector obj);
278 * Creates the container query.<br>
279 * A container query is a query that will return a collection of objects:
281 * /cloud-infrastructure/complexes/complex/key1
283 * namespace container object
286 * @param obj the Introspector into the db schema
287 * @return the query builder
289 public abstract QueryBuilder<Vertex> createContainerQuery(Introspector obj);
292 * Creates the edge traversal.
294 * @param parent the parent
295 * @param child the child
296 * @return the query builder
298 public abstract QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Introspector parent, Introspector child)
301 public abstract QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, Object value);
303 public QueryBuilder<Vertex> getVerticesByBooleanProperty(String key, MissingOptionalParameter value) {
304 return (QueryBuilder<Vertex>) this;
308 * Creates the private edge traversal.
310 * @param parent the parent
311 * @param child the child
312 * @return the query builder
314 public abstract QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, Introspector parent,
315 Introspector child) throws AAIException;
318 * Creates the edge traversal.
320 * @param parent the parent
321 * @param child the child
322 * @return the query builder
324 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, Vertex parent, Introspector child)
325 throws AAIException {
326 String nodeType = parent.<String>property(AAIProperties.NODE_TYPE).orElse(null);
327 this.createEdgeTraversal(type, nodeType, child.getDbName());
328 return (QueryBuilder<Vertex>) this;
337 * @throws AAIException
339 public QueryBuilder<Vertex> createEdgeTraversal(EdgeType type, String outNodeType, String inNodeType)
340 throws AAIException {
341 Introspector out = loader.introspectorFromName(outNodeType);
342 Introspector in = loader.introspectorFromName(inNodeType);
344 return createEdgeTraversal(type, out, in);
353 * @throws AAIException
355 public QueryBuilder<Vertex> createEdgeTraversal(String edgeType, String outNodeType, String inNodeType)
356 throws AAIException {
358 * When the optional parameter edgetype is sent it is a string that needs to be converted to Enum
360 EdgeType type = EdgeType.valueOf(edgeType);
361 Introspector out = loader.introspectorFromName(outNodeType);
362 Introspector in = loader.introspectorFromName(inNodeType);
364 return createEdgeTraversal(type, out, in);
373 * @throws AAIException
375 public QueryBuilder<Vertex> createEdgeTraversal(MissingOptionalParameter edgeType, String outNodeType,
376 String inNodeType) throws AAIException {
378 * When no optional parameter edgetype is sent get all edges between the 2 nodetypes
380 return this.createEdgeTraversal(outNodeType, inNodeType);
389 * @throws AAIException
391 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(MissingOptionalParameter edgeType, String outNodeType,
392 String inNodeType, List<String> labels) throws AAIException {
394 * When no optional parameter edgetype is sent get all edges between the 2 nodetypes
396 return this.createEdgeTraversalWithLabels(outNodeType, inNodeType, labels);
399 public QueryBuilder<Vertex> createEdgeTraversal(String outNodeType, String inNodeType) throws AAIException {
401 Introspector out = loader.introspectorFromName(outNodeType);
402 Introspector in = loader.introspectorFromName(inNodeType);
404 QueryBuilder<Vertex> cousinBuilder = null;
405 QueryBuilder<Vertex> treeBuilder = null;
406 QueryBuilder<Vertex> queryBuilder = null;
409 cousinBuilder = this.newInstance().createEdgeTraversal(EdgeType.COUSIN, out, in);
410 } catch (AAIException e) {
413 if (cousinBuilder != null) {
415 treeBuilder = this.newInstance().createEdgeTraversal(EdgeType.TREE, out, in);
416 } catch (AAIException e) {
418 if (treeBuilder != null) {
419 queryBuilder = this.union(new QueryBuilder[] {cousinBuilder, treeBuilder});
421 queryBuilder = this.union(new QueryBuilder[] {cousinBuilder});
424 treeBuilder = this.newInstance().createEdgeTraversal(EdgeType.TREE, out, in);
425 queryBuilder = this.union(new QueryBuilder[] {treeBuilder});
431 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(String outNodeType, String inNodeType,
432 List<String> labels) throws AAIException {
434 Introspector out = loader.introspectorFromName(outNodeType);
435 Introspector in = loader.introspectorFromName(inNodeType);
437 QueryBuilder<Vertex> cousinBuilder = null;
438 QueryBuilder<Vertex> treeBuilder = null;
439 QueryBuilder<Vertex> queryBuilder = null;
442 cousinBuilder = this.newInstance().createEdgeTraversalWithLabels(EdgeType.COUSIN, out, in, labels);
443 } catch (AAIException e) {
446 if (cousinBuilder != null) {
448 treeBuilder = this.newInstance().createEdgeTraversalWithLabels(EdgeType.TREE, out, in, labels);
449 } catch (AAIException e) {
451 if (treeBuilder != null) {
452 queryBuilder = this.union(new QueryBuilder[] {cousinBuilder, treeBuilder});
454 queryBuilder = this.union(new QueryBuilder[] {cousinBuilder});
457 treeBuilder = this.newInstance().createEdgeTraversalWithLabels(EdgeType.TREE, out, in, labels);
458 queryBuilder = this.union(new QueryBuilder[] {treeBuilder});
464 public QueryBuilder<Vertex> createPrivateEdgeTraversal(EdgeType type, String outNodeType, String inNodeType)
465 throws AAIException {
466 Introspector out = loader.introspectorFromName(outNodeType);
467 Introspector in = loader.introspectorFromName(inNodeType);
468 return createPrivateEdgeTraversal(type, out, in);
478 * @throws AAIException
480 public QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, String outNodeType, String inNodeType,
481 List<String> labels) throws AAIException {
482 Introspector out = loader.introspectorFromName(outNodeType);
483 Introspector in = loader.introspectorFromName(inNodeType);
485 return createEdgeTraversalWithLabels(type, out, in, labels);
496 public abstract QueryBuilder<Vertex> createEdgeTraversalWithLabels(EdgeType type, Introspector out, Introspector in,
497 List<String> labels) throws AAIException;
500 * This method and it's overloaded counterpart are conditional statements.
501 * This method creates an edge traversal step if an optional property is present
502 * This is necessary in cases such as "if the Optional Property 1 is sent,
503 * find all Nodes of type A with edges to Nodes of type B with property 1,
504 * otherwise, simply find all nodes of type A".
511 * @throws AAIException
513 public QueryBuilder<Vertex> createEdgeTraversalIfParameterIsPresent(EdgeType type, String outNodeType,
514 String inNodeType, Object value) throws AAIException {
515 return this.createEdgeTraversal(type, outNodeType, inNodeType);
519 * This method and it's overloaded counterpart are conditional statements.
520 * This method skips an edge traversal step if there is an optional property missing.
521 * This is necessary in cases such as "if the Optional Property 1 is sent,
522 * find all Nodes of type A with edges to Nodes of type B with property 1,
523 * otherwise, simply find all nodes of type A".
530 * @throws AAIException
532 public QueryBuilder<Vertex> createEdgeTraversalIfParameterIsPresent(EdgeType type, String outNodeType,
533 String inNodeType, MissingOptionalParameter value) throws AAIException {
534 return (QueryBuilder<Vertex>) this;
543 * @throws AAIException
545 public QueryBuilder<Edge> getEdgesBetween(EdgeType type, String outNodeType, String inNodeType)
546 throws AAIException {
547 this.getEdgesBetweenWithLabels(type, outNodeType, inNodeType, null);
549 return (QueryBuilder<Edge>) this;
560 * @throws AAIException
562 public abstract QueryBuilder<Edge> getEdgesBetweenWithLabels(EdgeType type, String outNodeType, String inNodeType,
563 List<String> labels) throws AAIException;
566 * Creates the query from URI.
569 * @return the query parser
570 * @throws UnsupportedEncodingException the unsupported encoding exception
571 * @throws AAIException the AAI exception
573 public abstract QueryParser createQueryFromURI(URI uri) throws UnsupportedEncodingException, AAIException;
576 * Creates the query from URI.
579 * @param queryParams the query params
580 * @return the query parser
581 * @throws UnsupportedEncodingException the unsupported encoding exception
582 * @throws AAIException the AAI exception
584 public abstract QueryParser createQueryFromURI(URI uri, MultivaluedMap<String, String> queryParams)
585 throws UnsupportedEncodingException, AAIException;
588 * Creates a queryparser from a given object name.
590 * @param objName - name of the object type as it appears in the database
593 public abstract QueryParser createQueryFromObjectName(String objName);
596 * Creates the query from relationship.
598 * @param relationship the relationship
599 * @return the query parser
600 * @throws UnsupportedEncodingException the unsupported encoding exception
601 * @throws AAIException the AAI exception
603 public abstract QueryParser createQueryFromRelationship(Introspector relationship)
604 throws UnsupportedEncodingException, AAIException;
607 * Gets the parent query.
609 * @return the parent query
611 public abstract QueryBuilder<E> getParentQuery();
618 public abstract <E2> E2 getQuery();
623 public abstract void markParentBoundary();
625 public abstract QueryBuilder<E> limit(long amount);
630 * @param start the start
631 * @return the query builder
633 public abstract QueryBuilder<E> newInstance(Vertex start);
638 * @return the query builder
640 public abstract QueryBuilder<E> newInstance();
647 public abstract Vertex getStart();
649 protected Object correctObjectType(Object obj) {
651 if (obj != null && obj.getClass().equals(Long.class)) {
652 return Integer.valueOf(obj.toString());
659 * uses all fields in the introspector to create a query
664 public abstract QueryBuilder<Vertex> exactMatchQuery(Introspector obj);
667 * lets you join any number of QueryBuilders
668 * <b>be careful about starting with a union it will not use indexes</b>
673 public abstract QueryBuilder<E> union(QueryBuilder<E>... builder);
675 public abstract QueryBuilder<E> where(QueryBuilder<E>... builder);
677 public abstract QueryBuilder<E> or(QueryBuilder<E>... builder);
679 public abstract QueryBuilder<E> store(String name);
681 public abstract QueryBuilder<E> cap(String name);
683 public abstract QueryBuilder<E> unfold();
685 public abstract QueryBuilder<E> fold();
687 public abstract QueryBuilder<E> id();
689 public abstract QueryBuilder<E> dedup();
691 public abstract QueryBuilder<E> emit();
693 public abstract QueryBuilder<E> repeat(QueryBuilder<E> builder);
695 public abstract QueryBuilder<Edge> outE();
697 public abstract QueryBuilder<Edge> inE();
699 public abstract QueryBuilder<Vertex> inV();
701 public abstract QueryBuilder<Vertex> outV();
703 public abstract QueryBuilder<E> not(QueryBuilder<E> builder);
705 public abstract QueryBuilder<E> as(String name);
707 public abstract QueryBuilder<E> select(String name);
709 public abstract QueryBuilder<E> select(Pop pop, String name);
711 public abstract QueryBuilder<E> select(String... names);
713 public abstract QueryBuilder<E> until(QueryBuilder<E> builder);
715 public abstract QueryBuilder<E> groupCount();
717 public abstract QueryBuilder<E> by(String name);
719 public abstract QueryBuilder<E> valueMap();
721 public abstract QueryBuilder<E> valueMap(String... names);
723 public abstract QueryBuilder<E> both();
725 public abstract QueryBuilder<Tree> tree();
728 * Used to prevent the traversal from repeating its path through the graph.
729 * See http://tinkerpop.apache.org/docs/3.0.1-incubating/#simplepath-step for more info.
731 * @return a QueryBuilder with the simplePath step appended to its traversal
733 public abstract QueryBuilder<E> simplePath();
737 * @return QueryBuilder with the path step appended to its traversal
739 public abstract QueryBuilder<Path> path();
741 public abstract void markContainer();
743 public abstract QueryBuilder<E> getContainerQuery();
745 public abstract List<E> toList();
748 * Paginate the resulting list.
749 * This is a final step that returns a PaginationResult.
750 * @param pageable object that contains page and page size
751 * @return returns a page of the results and the total count.
753 public abstract PaginationResult<E> toPaginationResult(Pageable pageable);
756 * Sort the resulting list.
757 * @param sort object that contains the property to sort by and the direction
758 * @return returns the QueryBuilder for further query building
760 public abstract QueryBuilder<E> sort(Sort sort);
764 * Used to skip step if there is an optional property missing.
770 public QueryBuilder<Vertex> getVerticesByProperty(String key, MissingOptionalParameter value) {
771 return (QueryBuilder<Vertex>) this;
775 * TODO the edge direction is hardcoded here, make it more generic
776 * Returns the parent edge of the vertex
780 public QueryBuilder<Edge> getParentEdge() {
781 this.outE().has(EdgeProperty.CONTAINS.toString(), AAIDirection.IN.toString());
782 return (QueryBuilder<Edge>) this;
786 * TODO the edge direction is hardcoded here, make it more generic
787 * Returns the parent vertex of the vertex
791 public QueryBuilder<Vertex> getParentVertex() {
792 this.getParentEdge().inV();
793 return (QueryBuilder<Vertex>) this;
796 protected abstract QueryBuilder<Edge> has(String key, String value);
798 protected void initEdgeIngestor() {
799 // TODO proper spring wiring, but that requires a lot of refactoring so for now we have this
800 ApplicationContext ctx = SpringContextAware.getApplicationContext();
801 EdgeIngestor ei = ctx.getBean(EdgeIngestor.class);
805 protected void setEdgeIngestor(EdgeIngestor ei) {
809 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, Object value) {
810 return getVerticesByProperty(key, value);
813 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key) {
814 return getVerticesByProperty(key);
817 public QueryBuilder<Vertex> getVerticesByNumberProperty(String key, MissingOptionalParameter value) {
818 return getVerticesByProperty(key, value);
821 protected abstract void vertexHas(String key, Object value);
823 protected abstract void vertexHasNot(String key);
825 protected abstract void vertexHas(String key);
827 // TODO: This probably is not required but need to test
828 // protected abstract void vertexHas(final String key, final P<?> predicate);