/*- * ============LICENSE_START======================================================= * org.openecomp.aai * ================================================================================ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. * ============LICENSE_END========================================================= */ package org.openecomp.aai.query.builder; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; import java.util.Set; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.Traversal.Admin; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Vertex; import org.openecomp.aai.db.props.AAIProperties; import org.openecomp.aai.exceptions.AAIException; import org.openecomp.aai.introspection.Introspector; import org.openecomp.aai.introspection.Loader; import org.openecomp.aai.schema.enums.ObjectMetadata; import org.openecomp.aai.schema.enums.PropertyMetadata; import org.openecomp.aai.serialization.db.EdgeRule; import org.openecomp.aai.serialization.db.EdgeRules; import org.openecomp.aai.serialization.db.EdgeType; import org.openecomp.aai.serialization.db.exceptions.NoEdgeRuleFoundException; /** * The Class GraphTraversalBuilder. */ public abstract class GraphTraversalBuilder extends QueryBuilder { protected GraphTraversal traversal = null; protected Admin completeTraversal = null; private EdgeRules edgeRules = EdgeRules.getInstance(); protected int parentStepIndex = 0; protected int containerStepIndex = 0; protected int stepIndex = 0; /** * Instantiates a new graph traversal builder. * * @param loader the loader */ public GraphTraversalBuilder(Loader loader, GraphTraversalSource source) { super(loader, source); traversal = __.start(); } /** * Instantiates a new graph traversal builder. * * @param loader the loader * @param start the start */ public GraphTraversalBuilder(Loader loader, GraphTraversalSource source, Vertex start) { super(loader, source, start); traversal = __.start(); } /** * @{inheritDoc} */ @Override public QueryBuilder getVerticesByIndexedProperty(String key, Object value) { return this.getVerticesByProperty(key, value); } /** * @{inheritDoc} */ @Override public QueryBuilder getVerticesByIndexedProperty(String key, List values) { return this.getVerticesByProperty(key, values); } /** * @{inheritDoc} */ @Override public QueryBuilder getVerticesByProperty(String key, Object value) { //this is because the index is registered as an Integer value = this.correctObjectType(value); traversal.has(key, value); stepIndex++; return this; } /** * @{inheritDoc} */ @Override public QueryBuilder getVerticesByProperty(final String key, final List values) { //this is because the index is registered as an Integer List correctedValues = new ArrayList<>(); for (Object item : values) { correctedValues.add(this.correctObjectType(item)); } traversal.has(key, P.within(correctedValues)); stepIndex++; return this; } /** * @{inheritDoc} */ @Override public QueryBuilder getChildVerticesFromParent(String parentKey, String parentValue, String childType) { traversal.has(parentKey, parentValue).has(AAIProperties.NODE_TYPE, childType); stepIndex++; return this; } /** * @{inheritDoc} */ @Override public QueryBuilder getTypedVerticesByMap(String type, LinkedHashMap map) { for (String key : map.keySet()) { traversal.has(key, map.get(key)); stepIndex++; } traversal.has(AAIProperties.NODE_TYPE, type); stepIndex++; return this; } /** * @{inheritDoc} */ @Override public QueryBuilder createDBQuery(Introspector obj) { this.createKeyQuery(obj); this.createContainerQuery(obj); return this; } /** * @{inheritDoc} */ @Override public QueryBuilder createKeyQuery(Introspector obj) { Set keys = obj.getKeys(); Object val; for (String key : keys) { val = obj.getValue(key); Optional metadata = obj.getPropertyMetadata(key, PropertyMetadata.DB_ALIAS); if (metadata.isPresent()) { //use the db name for the field rather than the object model key = metadata.get(); } if (val != null) { //this is because the index is registered as an Integer if (val.getClass().equals(Long.class)) { traversal.has(key,new Integer(val.toString())); } else { traversal.has(key, val); } stepIndex++; } } return this; } @Override public QueryBuilder exactMatchQuery(Introspector obj) { this.createKeyQuery(obj); allPropertiesQuery(obj); this.createContainerQuery(obj); return this; } private void allPropertiesQuery(Introspector obj) { Set props = obj.getProperties(); Set keys = obj.getKeys(); Object val; for (String prop : props) { if (obj.isSimpleType(prop) && !keys.contains(prop)) { val = obj.getValue(prop); if (val != null) { Optional metadata = obj.getPropertyMetadata(prop, PropertyMetadata.DB_ALIAS); if (metadata.isPresent()) { //use the db name for the field rather than the object model prop = metadata.get(); } //this is because the index is registered as an Integer if (val != null && val.getClass().equals(Long.class)) { traversal.has(prop,new Integer(val.toString())); } else { traversal.has(prop, val); } stepIndex++; } } } } /** * @{inheritDoc} */ @Override public QueryBuilder createContainerQuery(Introspector obj) { String type = obj.getChildDBName(); String abstractType = obj.getMetadata(ObjectMetadata.ABSTRACT); if (abstractType != null) { String[] inheritors = obj.getMetadata(ObjectMetadata.INHERITORS).split(","); traversal.has(AAIProperties.NODE_TYPE, P.within(inheritors)); } else { traversal.has(AAIProperties.NODE_TYPE, type); } stepIndex++; markContainer(); return this; } /** * @throws NoEdgeRuleFoundException * @throws AAIException * @{inheritDoc} */ @Override public QueryBuilder createEdgeTraversal(EdgeType type, Introspector parent, Introspector child) throws AAIException, NoEdgeRuleFoundException { String isAbstractType = parent.getMetadata(ObjectMetadata.ABSTRACT); if ("true".equals(isAbstractType)) { markParentBoundary(); traversal.union(handleAbstractEdge(type, parent, child)); stepIndex += 1; } else { this.edgeQuery(type, parent, child); } return this; } private Traversal[] handleAbstractEdge(EdgeType type, Introspector abstractParent, Introspector child) throws AAIException, NoEdgeRuleFoundException { String childName = child.getDbName(); String inheritorMetadata = abstractParent.getMetadata(ObjectMetadata.INHERITORS); String[] inheritors = inheritorMetadata.split(","); Traversal[] unionTraversals = new Traversal[inheritors.length]; int traversalIndex = 0; for (int i = 0; i < inheritors.length; i++) { String inheritor = inheritors[i]; if (edgeRules.hasEdgeRule(inheritor, childName) || edgeRules.hasEdgeRule(childName, inheritor)) { EdgeRule rule = edgeRules.getEdgeRule(type, inheritor, childName); GraphTraversal innerTraversal = __.start(); if (rule.getDirection().equals(Direction.OUT)) { innerTraversal.out(rule.getLabel()); } else { innerTraversal.in(rule.getLabel()); } innerTraversal.has(AAIProperties.NODE_TYPE, childName); unionTraversals[traversalIndex] = innerTraversal; traversalIndex++; } } if (traversalIndex < inheritors.length) { Traversal[] temp = Arrays.copyOfRange(unionTraversals, 0, traversalIndex); unionTraversals = temp; } return unionTraversals; } /** * @throws NoEdgeRuleFoundException * @throws AAIException * @{inheritDoc} */ @Override public QueryBuilder createEdgeTraversal(EdgeType type, Vertex parent, Introspector child) throws AAIException, NoEdgeRuleFoundException { String nodeType = parent.property(AAIProperties.NODE_TYPE).orElse(null); Introspector parentObj = loader.introspectorFromName(nodeType); this.edgeQuery(type, parentObj, child); return this; } /** * @{inheritDoc} */ @Override public QueryBuilder union(QueryBuilder... builder) { GraphTraversal[] traversals = new GraphTraversal[builder.length]; for (int i = 0; i < builder.length; i++) { traversals[i] = (GraphTraversal)builder[i].getQuery(); } this.traversal.union(traversals); stepIndex++; return this; } /** * @{inheritDoc} */ @Override public QueryBuilder where(QueryBuilder... builder) { GraphTraversal[] traversals = new GraphTraversal[builder.length]; for (int i = 0; i < builder.length; i++) { this.traversal.where((GraphTraversal)builder[i].getQuery()); stepIndex++; } return this; } /** * Edge query. * * @param outType the out type * @param inType the in type * @throws NoEdgeRuleFoundException * @throws AAIException */ private void edgeQuery(EdgeType type, Introspector outObj, Introspector inObj) throws AAIException, NoEdgeRuleFoundException { String outType = outObj.getDbName(); String inType = inObj.getDbName(); if (outObj.isContainer()) { outType = outObj.getChildDBName(); } if (inObj.isContainer()) { inType = inObj.getChildDBName(); } markParentBoundary(); EdgeRule rule = edgeRules.getEdgeRule(type, outType, inType); if (rule.getDirection().equals(Direction.OUT)) { traversal.out(rule.getLabel()); } else { traversal.in(rule.getLabel()); } stepIndex++; this.createContainerQuery(inObj); } @Override public QueryBuilder limit(long amount) { traversal.limit(amount); return this; } /** * @{inheritDoc} */ @Override public T getQuery() { return (T)this.traversal; } /** * @{inheritDoc} */ @Override public QueryBuilder getParentQuery() { return cloneQueryAtStep(parentStepIndex); } @Override public QueryBuilder getContainerQuery() { if (this.parentStepIndex == 0) { return removeQueryStepsBetween(0, containerStepIndex); } else { return cloneQueryAtStep(containerStepIndex); } } /** * @{inheritDoc} */ @Override public void markParentBoundary() { parentStepIndex = stepIndex; } @Override public void markContainer() { containerStepIndex = stepIndex; } /** * @{inheritDoc} */ @Override public Vertex getStart() { return this.start; } protected int getParentStepIndex() { return parentStepIndex; } protected int getContainerStepIndex() { return containerStepIndex; } protected int getStepIndex() { return stepIndex; } protected abstract QueryBuilder cloneQueryAtStep(int index); /** * end is exclusive * * @param start * @param end * @return */ protected abstract QueryBuilder removeQueryStepsBetween(int start, int end); private void executeQuery() { Admin admin; if (start != null) { admin = source.V(start).asAdmin(); } else { admin = source.V().asAdmin(); } TraversalHelper.insertTraversal(admin.getEndStep(), traversal.asAdmin(), admin); this.completeTraversal = admin; } @Override public boolean hasNext() { if (this.completeTraversal == null) { executeQuery(); } return this.completeTraversal.hasNext(); } @Override public Vertex next() { if (this.completeTraversal == null) { executeQuery(); } return this.completeTraversal.next(); } @Override public List toList() { if (this.completeTraversal == null) { executeQuery(); } return this.completeTraversal.toList(); } }