2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.openecomp.aai.dbgraphgen;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.LinkedHashSet;
26 import java.util.List;
30 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
31 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
33 import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
34 import org.apache.tinkerpop.gremlin.structure.Element;
35 import org.apache.tinkerpop.gremlin.structure.Vertex;
37 import org.openecomp.aai.db.props.AAIProperties;
38 import org.openecomp.aai.exceptions.AAIException;
39 import org.openecomp.aai.introspection.Loader;
40 import org.openecomp.aai.serialization.db.DBSerializer;
41 import org.openecomp.aai.serialization.engines.TransactionalGraphEngine;
42 import org.openecomp.aai.util.AAIConfig;
43 import com.att.eelf.configuration.EELFLogger;
44 import com.att.eelf.configuration.EELFManager;
49 * Database-level Search-Utility class that uses edge-tags to help it navigate the graph.
51 public class DbSearchWithTags{
53 private EELFLogger LOGGER = EELFManager.getInstance().getLogger(DbSearchWithTags.class);
55 private TransactionalGraphEngine engine;
57 protected DbSearchWithTags() {
60 public DbSearchWithTags(Loader loader, TransactionalGraphEngine engine, DBSerializer serializer) {
67 * @param transId the trans id
68 * @param fromAppId the from app id
69 * @param edgeTag the edge tag
70 * @param searchRootType - root collection point when doing the search
71 * @param displayRootType - if they want more data than would be included using the searchRootType, this
72 * lets them specify it.
73 * @param searchDirectionStr the search direction str
74 * @param initialFilterHash the initial filter hash
75 * @param secondaryFilterHash the secondary filter hash
76 * @param pruneLevel either "none", "searchLevel" or "displayLevel".
77 * @param retNodeType could be either "all" for the full map, or a single nodeType
78 * @param trimList list of nodes to "stop" at when collecting data
79 * @return List<resultSet>
80 * @throws AAIException the AAI exception
82 public Tree<Vertex> runEdgeTagQuery( String transId, String fromAppId,
84 String searchRootType,
85 Map<String,Object> initialFilterHash,
86 Map<String,Object> secondaryFilterHash)
89 final String tag = edgeTag;
90 final String reverseTag = edgeTag + "-REV";
91 //max levels is not used at this time, but may be used again
92 int maxLevels = 50; // default value
93 String maxString = AAIConfig.get("aai.edgeTag.proc.max.levels");
94 if( maxString != null && !maxString.equals("") ){
96 int maxVal = Integer.parseInt(maxString);
99 catch ( Exception nfe ){
100 // Don't worry, we will leave "maxLevels" set to the default value it was initialized with
104 // First, we need to use the intialFilter plus the edgeTag to identify a set of search-root-nodes
105 HashMap <String, Vertex> searchRootHash = identifyTopNodeSet( transId, fromAppId,
106 edgeTag, searchRootType, initialFilterHash, maxLevels );
109 Set<String> keySet = searchRootHash.keySet();
110 Iterator<String> itr = keySet.iterator();
111 String[] arrayOfVertices = new String[keySet.size()];
113 while (itr.hasNext()) {
114 arrayOfVertices[i] = itr.next();
117 //start from all vertices provided
118 //repeat checking the out edge for the tag and the in edge for reverse tag
119 //emit all vertices not already seen and start again
120 //return a tree structure of the vertices and edges touched by this traversal
121 Tree<Element> resultTree = this.engine.asAdmin().getReadOnlyTraversalSource().V(arrayOfVertices)
122 .emit().repeat(__.union(__.outE().has(tag, true), __.inE().has(reverseTag, true)).otherV()).tree().next();
124 //the resulting tree includes the edges because of our query, we'll need to remove them
125 Tree<Vertex> sanitizedTree = removeUnwantedItems(resultTree);
127 //if we have secondary filters then check each tree returned for matches
128 if (!secondaryFilterHash.isEmpty()) {
129 filterOutResultTrees(sanitizedTree, secondaryFilterHash);
131 return sanitizedTree;
133 }// End of runEdgeTagQuery()
136 * Identify top node set.
138 * @param transId the trans id
139 * @param fromAppId the from app id
140 * @param edgeTag the edge tag
141 * @param topNodeType the top node type
142 * @param initialFilterHash the initial filter hash
143 * @param maxLevels the max levels
144 * @return List<titanVertex>
145 * @throws AAIException the AAI exception
147 public HashMap<String, Vertex> identifyTopNodeSet( String transId, String fromAppId,
148 String edgeTag, String topNodeType, Map<String,Object> initialFilterHash, int maxLevels )
149 throws AAIException {
151 final String tag = edgeTag;
152 final String reverseTag = edgeTag + "-REV";
153 HashMap <String, Vertex> topVertHash = new HashMap <>();
155 // Given the filter, we want to select all the nodes of the type the filter tells us that have the
156 // property they gave us.
157 // Then looping through those start points, we will look "up" and then "down" to find the set of target/top nodes.
159 if( initialFilterHash == null || initialFilterHash.isEmpty() ){
160 throw new AAIException("AAI_6118", " initialFilterHash is required for identifyInitialNodeSet() call. \n");
163 // NOTE: we're expecting the filter to have a format like this: "nodeType.parameterName:parameterValue"
164 Iterator <?> it = initialFilterHash.entrySet().iterator();
165 // -- DEBUG -- for now we only deal with ONE initial filter parameter
166 // it would be easy enough to deal with multiple parameters if they all
167 // applied to the same nodeType.
168 String propNodeTypeDotName = "";
169 String initNodeType = "";
170 String initPropName = "";
172 String extraChecks = "";
176 Map.Entry<?,?> propEntry = (Map.Entry<?,?>) it.next();
177 propNodeTypeDotName = (propEntry.getKey()).toString();
178 propVal = (propEntry.getValue()).toString();
181 GraphTraversalSource source = this.engine.asAdmin().getReadOnlyTraversalSource();
182 GraphTraversal<Vertex, Vertex> g;
184 int periodLoc = propNodeTypeDotName.indexOf(".");
185 if( periodLoc <= 0 ){
186 throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + propNodeTypeDotName + "]. Expected format = [nodeName.paramName]\n");
189 initNodeType = propNodeTypeDotName.substring(0,periodLoc);
190 initPropName = propNodeTypeDotName.substring(periodLoc + 1);
192 //there used to be logic here that would do something special for generic-vnf.vnf-name and vserver.vserver-name
193 //it would attempt a search as they sent it, if nothing came back, try as all upper, try as all lower, then fail
194 //here it checks whether something comes back or not, if not change the case and try again
195 if( (initNodeType.equals("generic-vnf") && initPropName.equals("vnf-name"))
196 || (initNodeType.equals("vserver") && initPropName.equals("vserver-name")) ){
197 if (!this.checkKludgeCase(initNodeType, initPropName, propVal)) {
198 if (this.checkKludgeCase(initNodeType, initPropName, propVal.toUpperCase())) {
199 propVal = propVal.toUpperCase();
201 if (this.checkKludgeCase(initNodeType, initPropName, propVal)) {
202 propVal = propVal.toLowerCase();
207 g = source.V().has(AAIProperties.NODE_TYPE, initNodeType).has(initPropName, propVal);
209 //search all around bounded by our edge tag for start nodes that match our topNodeType
210 if (!topNodeType.equals(initNodeType)) {
212 g.union(__.<Vertex>start().until(__.has(AAIProperties.NODE_TYPE, topNodeType))
213 .repeat(__.union(__.inE().has(reverseTag, true), __.outE().has(tag, true)).otherV()),
214 __.<Vertex>start().until(__.has(AAIProperties.NODE_TYPE, topNodeType))
215 .repeat(__.union(__.inE().has(tag, true), __.outE().has(reverseTag, true)).otherV())).dedup();
218 List<Vertex> results = g.toList();
220 results.forEach(v -> {
221 topVertHash.put(v.id().toString(), v);
224 if( topVertHash.isEmpty() ){
225 // No Vertex was found - throw a not-found exception
226 throw new AAIException("AAI_6114", "No Node of type " + topNodeType + " found for properties: " + initialFilterHash.toString() + extraChecks);
232 }// End identifyInitialNodeSet()
235 * This is a carryover from the previous version.
236 * We may be able to remove this.
243 private boolean checkKludgeCase(String nodeType, String propName, String propValue) {
244 return this.engine.getQueryBuilder().getVerticesByIndexedProperty(AAIProperties.NODE_TYPE, nodeType).getVerticesByProperty(propName, propValue).hasNext();
248 * This method starts from the top of the tree object and checks each nested tree.
249 * If that tree does not contain a node (or nodes) that match the filterHash, remove it
253 * @throws AAIException
255 private void filterOutResultTrees(Tree<Vertex> tree, Map<String,Object> filterHash) throws AAIException {
256 Set<Vertex> topLevelKeys = new LinkedHashSet<>(tree.keySet());
257 for (Vertex topLevel : topLevelKeys) {
258 if (!this.checkVertexWithFilters(topLevel, filterHash)) {
259 if (!filterOutResultTreesHelper(tree.get(topLevel), filterHash)) {
260 //if we never found anything to satisfy our filter, remove the entire result tree
261 tree.remove(topLevel);
268 * Checks all vertices of a tree with the provided filterHash
273 * @throws AAIException
275 private boolean filterOutResultTreesHelper(Tree<Vertex> tree, Map<String,Object> filterHash) throws AAIException {
277 Set<Vertex> keys = tree.keySet();
279 for (Vertex v : keys) {
280 if (checkVertexWithFilters(v, filterHash)) {
283 if (filterOutResultTreesHelper(tree.get(v), filterHash)) {
292 * Checks whether a vertex matches the filterHash provided
297 * @throws AAIException
299 private boolean checkVertexWithFilters(Vertex v, Map<String,Object> filterHash) throws AAIException {
300 Iterator <?> it = filterHash.entrySet().iterator();
302 while( it.hasNext() ){
303 Map.Entry<?,?> filtEntry = (Map.Entry<?,?>) it.next();
304 String propNodeTypeDotName = (filtEntry.getKey()).toString();
305 String value = (filtEntry.getValue()).toString();
307 int periodLoc = propNodeTypeDotName.indexOf(".");
308 if( periodLoc <= 0 ){
309 throw new AAIException("AAI_6120", "Bad filter param key passed in: [" + propNodeTypeDotName + "]. Expected format = [nodeName.paramName]\n");
312 String nodeType = propNodeTypeDotName.substring(0,periodLoc);
313 String propertyName = propNodeTypeDotName.substring(periodLoc + 1);
314 String nt = v.<String>property("aai-node-type").orElse(null);
315 if( nt.equals( nodeType ) ){
316 if( propertyName.equals("vertex-id") ){
317 // vertex-id can't be gotten the same way as other properties
318 String thisVtxId = v.id().toString();
319 if( thisVtxId.equals(value) ){
324 Object thisValObj = v.property(propertyName).orElse(null);
325 if( thisValObj != null ){
326 String thisVal = thisValObj.toString();
327 if( thisVal.equals(value) ){
340 * Removes every other tree from the originalTree provided.
341 * It is designed to specifically handle removing unwanted edges from the originalTree
342 * @param originalTree
345 private Tree<Vertex> removeUnwantedItems(Tree<Element> originalTree) {
347 Tree<Vertex> newTree = new Tree<>();
348 Set<Element> keys = originalTree.keySet();
349 for (Element element : keys) {
350 newTree.put((Vertex)element, removeUnwantedItemsHelper(originalTree.get(element).getTreesAtDepth(2)));
358 private Tree<Vertex> removeUnwantedItemsHelper(List<Tree<Element>> originalTrees) {
359 Tree<Vertex> newTree = new Tree<>();
360 for (Tree<Element> tree : originalTrees) {
361 newTree.addTree(removeUnwantedItems(tree));