| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 |
| * <p> |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * <p> |
| * 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. |
| */ |
| package org.apache.atlas.discovery; |
| |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import org.apache.atlas.AtlasErrorCode; |
| import org.apache.atlas.exception.AtlasBaseException; |
| import org.apache.atlas.model.discovery.SearchParameters; |
| import org.apache.atlas.model.instance.AtlasEntity; |
| import org.apache.atlas.model.typedef.AtlasClassificationDef; |
| import org.apache.atlas.repository.Constants; |
| import org.apache.atlas.repository.graph.GraphHelper; |
| import org.apache.atlas.repository.graphdb.AtlasEdge; |
| import org.apache.atlas.repository.graphdb.AtlasGraph; |
| import org.apache.atlas.repository.graphdb.AtlasGraphQuery; |
| import org.apache.atlas.repository.graphdb.AtlasVertex; |
| import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2; |
| import org.apache.atlas.repository.store.graph.v2.EntityGraphRetriever; |
| import org.apache.atlas.type.AtlasArrayType; |
| import org.apache.atlas.type.AtlasBuiltInTypes; |
| import org.apache.atlas.type.AtlasClassificationType; |
| import org.apache.atlas.type.AtlasEntityType; |
| import org.apache.atlas.type.AtlasRelationshipType; |
| import org.apache.atlas.type.AtlasStructType; |
| import org.apache.atlas.type.AtlasStructType.AtlasAttribute; |
| import org.apache.atlas.type.AtlasType; |
| import org.apache.atlas.type.AtlasTypeRegistry; |
| import org.apache.atlas.util.AtlasRepositoryConfiguration; |
| import org.apache.commons.collections.CollectionUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Base64; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import static org.apache.atlas.discovery.SearchProcessor.ALL_TYPE_QUERY; |
| import static org.apache.atlas.model.discovery.SearchParameters.ALL_CLASSIFICATIONS; |
| import static org.apache.atlas.model.discovery.SearchParameters.ALL_CLASSIFICATION_TYPES; |
| import static org.apache.atlas.model.discovery.SearchParameters.ALL_ENTITY_TYPES; |
| import static org.apache.atlas.model.discovery.SearchParameters.FilterCriteria; |
| import static org.apache.atlas.model.discovery.SearchParameters.NO_CLASSIFICATIONS; |
| import static org.apache.atlas.model.discovery.SearchParameters.WILDCARD_CLASSIFICATIONS; |
| |
| /* |
| * Search context captures elements required for performing a basic search |
| * For every search request the search context will determine the execution sequence of the search processor(s) and the |
| * possible chaining of processor(s) |
| */ |
| public class SearchContext { |
| private static final Logger LOG = LoggerFactory.getLogger(SearchContext.class); |
| |
| private final AtlasTypeRegistry typeRegistry; |
| private final AtlasGraph graph; |
| private final Set<AtlasEntityType> entityTypes; |
| private final Set<String> indexedKeys; |
| private Set<String> edgeIndexKeys; |
| private final Set<String> entityAttributes; |
| private final Set<String> relationAttributes; |
| private final SearchParameters searchParameters; |
| private final Set<AtlasClassificationType> classificationTypes; |
| private final Set<AtlasRelationshipType> relationshipTypes; |
| private final Set<String> classificationNames; |
| private final Set<String> typeAndSubTypes; |
| private final Set<String> classificationTypeAndSubTypes; |
| private final String typeAndSubTypesQryStr; |
| private final String classificationTypeAndSubTypesQryStr; |
| private boolean terminateSearch = false; |
| private SearchProcessor searchProcessor; |
| private Integer marker; |
| private boolean hasRelationshipAttributes = false; |
| |
| public final static AtlasClassificationType MATCH_ALL_WILDCARD_CLASSIFICATION = new AtlasClassificationType(new AtlasClassificationDef(WILDCARD_CLASSIFICATIONS)); |
| public final static AtlasClassificationType MATCH_ALL_CLASSIFIED = new AtlasClassificationType(new AtlasClassificationDef(ALL_CLASSIFICATIONS)); |
| public final static AtlasClassificationType MATCH_ALL_NOT_CLASSIFIED = new AtlasClassificationType(new AtlasClassificationDef(NO_CLASSIFICATIONS)); |
| public final static AtlasClassificationType MATCH_ALL_CLASSIFICATION_TYPES = AtlasClassificationType.getClassificationRoot(); |
| public final static AtlasEntityType MATCH_ALL_ENTITY_TYPES = AtlasEntityType.getEntityRoot(); |
| public final static String TYPENAME_DELIMITER = ","; |
| |
| |
| public SearchContext(SearchParameters searchParameters, AtlasTypeRegistry typeRegistry, AtlasGraph graph, Set<String> indexedKeys) throws AtlasBaseException { |
| this.searchParameters = searchParameters; |
| this.typeRegistry = typeRegistry; |
| this.graph = graph; |
| this.indexedKeys = indexedKeys; |
| this.entityAttributes = new HashSet<>(); |
| this.relationAttributes = new HashSet<>(); |
| this.entityTypes = getEntityTypes(searchParameters.getTypeName()); |
| this.classificationNames = getClassificationNames(searchParameters.getClassification()); |
| this.classificationTypes = getClassificationTypes(this.classificationNames); |
| this.relationshipTypes = getRelationshipTypes(searchParameters.getRelationshipName()); |
| |
| AtlasVertex glossaryTermVertex = getGlossaryTermVertex(searchParameters.getTermName()); |
| |
| // Validate if the term exists |
| if (StringUtils.isNotEmpty(searchParameters.getTermName()) && glossaryTermVertex == null) { |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_GLOSSARY_TERM, searchParameters.getTermName()); |
| } |
| |
| // Invalid attributes or unsupported attribute in a type, will raise an exception with 400 error code |
| if (CollectionUtils.isNotEmpty(entityTypes)) { |
| for (AtlasEntityType entityType : entityTypes) { |
| validateAttributes(entityType, searchParameters.getEntityFilters()); |
| |
| validateAttributes(entityType, searchParameters.getSortBy()); |
| } |
| } |
| |
| //Wildcard tag with filter will raise an exception with 400 error code |
| if (CollectionUtils.isNotEmpty(classificationNames) && hasAttributeFilter(searchParameters.getTagFilters())) { |
| for (String classificationName : classificationNames){ |
| //in case of '*' , filters are allowed, but |
| //in case of regex 'PI*', filters are not allowed ( if present in any of the requested tag) |
| if (classificationName.contains(WILDCARD_CLASSIFICATIONS) && !classificationName.equals(WILDCARD_CLASSIFICATIONS)) { |
| throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "TagFilters specified with wildcard tag name"); |
| } |
| } |
| } |
| |
| // Invalid attributes will raise an exception with 400 error code |
| if (CollectionUtils.isNotEmpty(classificationTypes)) { |
| for (AtlasClassificationType classificationType : classificationTypes) { |
| validateAttributes(classificationType, searchParameters.getTagFilters()); |
| } |
| } |
| |
| // Invalid relationship attributes will raise an exception with 400 error code |
| if (CollectionUtils.isNotEmpty(relationshipTypes)) { |
| for (AtlasRelationshipType relationshipType : relationshipTypes) { |
| validateAttributes(relationshipType, searchParameters.getRelationshipFilters()); |
| } |
| } |
| |
| if (StringUtils.isNotEmpty(searchParameters.getMarker())) { |
| marker = MarkerUtil.decodeMarker(searchParameters); |
| } |
| |
| //remove other types if builtin type is present |
| filterStructTypes(); |
| |
| //validate 'attributes' field |
| validateAttributes(); |
| |
| //gather all classifications and its corresponding subtypes |
| Set<String> classificationTypeAndSubTypes = new HashSet<>(); |
| String classificationTypeAndSubTypesQryStr = null; |
| |
| if (CollectionUtils.isNotEmpty(classificationTypes) && classificationTypes.iterator().next() != MATCH_ALL_NOT_CLASSIFIED ) { |
| for (AtlasClassificationType classificationType : classificationTypes) { |
| |
| if (classificationType == MATCH_ALL_CLASSIFICATION_TYPES) { |
| classificationTypeAndSubTypes = Collections.emptySet(); |
| classificationTypeAndSubTypesQryStr = ALL_TYPE_QUERY; |
| break; |
| } else { |
| Set<String> allTypes = searchParameters.getIncludeSubClassifications() ? classificationType.getTypeAndAllSubTypes() : Collections.singleton(classificationType.getTypeName()); |
| classificationTypeAndSubTypes.addAll(allTypes); } |
| } |
| |
| if (CollectionUtils.isNotEmpty(classificationTypeAndSubTypes)) { |
| classificationTypeAndSubTypesQryStr = AtlasAttribute.escapeIndexQueryValue(classificationTypeAndSubTypes, true); |
| } |
| } else { |
| classificationTypeAndSubTypes = Collections.emptySet(); |
| classificationTypeAndSubTypesQryStr = ""; |
| } |
| this.classificationTypeAndSubTypes = classificationTypeAndSubTypes; |
| this.classificationTypeAndSubTypesQryStr = classificationTypeAndSubTypesQryStr; |
| |
| //gather all types and its corresponding subtypes |
| Set<String> typeAndSubTypes = new HashSet<>(); |
| String typeAndSubTypesQryStr = null; |
| |
| if (CollectionUtils.isNotEmpty(entityTypes)) { |
| for (AtlasEntityType entityType : entityTypes) { |
| |
| if (entityType.equals(MATCH_ALL_ENTITY_TYPES)) { |
| typeAndSubTypes = Collections.emptySet(); |
| typeAndSubTypesQryStr = ALL_TYPE_QUERY; |
| break; |
| } else { |
| Set<String> allTypes = searchParameters.getIncludeSubTypes() ? entityType.getTypeAndAllSubTypes() : Collections.singleton(entityType.getTypeName()); |
| typeAndSubTypes.addAll(allTypes); |
| } |
| } |
| |
| if (CollectionUtils.isNotEmpty(typeAndSubTypes)) { |
| typeAndSubTypesQryStr = AtlasAttribute.escapeIndexQueryValue(typeAndSubTypes, true); |
| } |
| } else { |
| typeAndSubTypes = Collections.emptySet(); |
| typeAndSubTypesQryStr = ""; |
| } |
| this.typeAndSubTypes = typeAndSubTypes; |
| this.typeAndSubTypesQryStr = typeAndSubTypesQryStr; |
| |
| if (glossaryTermVertex != null) { |
| addProcessor(new TermSearchProcessor(this, getAssignedEntities(glossaryTermVertex))); |
| } |
| |
| if (needFullTextProcessor()) { |
| if (AtlasRepositoryConfiguration.isFreeTextSearchEnabled()) { |
| LOG.debug("Using Free Text index based search."); |
| |
| addProcessor(new FreeTextSearchProcessor(this)); |
| } else { |
| LOG.debug("Using Full Text index based search."); |
| |
| addProcessor(new FullTextSearchProcessor(this)); |
| } |
| } |
| |
| if (needClassificationProcessor()) { |
| addProcessor(new ClassificationSearchProcessor(this)); |
| } |
| |
| if (needEntityProcessor()) { |
| addProcessor(new EntitySearchProcessor(this)); |
| } |
| } |
| |
| public SearchParameters getSearchParameters() { return searchParameters; } |
| |
| public AtlasTypeRegistry getTypeRegistry() { return typeRegistry; } |
| |
| public AtlasGraph getGraph() { return graph; } |
| |
| public Set<String> getIndexedKeys() { return indexedKeys; } |
| |
| public void setEdgeIndexKeys(Set<String> edgeIndexKeys) { |
| this.edgeIndexKeys = edgeIndexKeys; |
| } |
| |
| public Set<String> getEdgeIndexKeys() { return edgeIndexKeys; } |
| |
| public Set<String> getEntityAttributes() { return entityAttributes; } |
| |
| public Set<String> getRelationAttributes() { return relationAttributes; } |
| |
| public Set<AtlasClassificationType> getClassificationTypes() { return classificationTypes; } |
| |
| public Set<String> getEntityTypeNames() { return typeAndSubTypes; } |
| |
| public Set<String> getClassificationTypeNames() { return classificationTypeAndSubTypes; } |
| |
| public String getEntityTypesQryStr() { return typeAndSubTypesQryStr; } |
| |
| public String getClassificationTypesQryStr() { return classificationTypeAndSubTypesQryStr; } |
| |
| public Set<AtlasEntityType> getEntityTypes() { return entityTypes; } |
| |
| public SearchProcessor getSearchProcessor() { return searchProcessor; } |
| |
| public Set<String> getClassificationNames() {return classificationNames;} |
| |
| public Integer getMarker() { return marker; } |
| |
| public Set<AtlasRelationshipType> getRelationshipTypes() { return relationshipTypes; } |
| |
| public boolean includeEntityType(String entityType) { |
| return typeAndSubTypes.isEmpty() || typeAndSubTypes.contains(entityType); |
| } |
| |
| public boolean includeClassificationTypes(Collection<String> traitNames) { |
| final boolean ret; |
| |
| if (classificationTypes.iterator().next() == MATCH_ALL_NOT_CLASSIFIED) { |
| ret = CollectionUtils.isEmpty(traitNames); |
| } else if (classificationTypes.iterator().next() == MATCH_ALL_CLASSIFICATION_TYPES) { |
| ret = CollectionUtils.isNotEmpty(traitNames); |
| } else { |
| ret = CollectionUtils.containsAny(classificationTypeAndSubTypes, traitNames); |
| } |
| |
| return ret; |
| } |
| |
| public boolean terminateSearch() { return terminateSearch; } |
| |
| public void terminateSearch(boolean terminateSearch) { this.terminateSearch = terminateSearch; } |
| |
| public StringBuilder toString(StringBuilder sb) { |
| if (sb == null) { |
| sb = new StringBuilder(); |
| } |
| |
| sb.append("searchParameters="); |
| |
| if (searchParameters != null) { |
| searchParameters.toString(sb); |
| } |
| |
| return sb; |
| } |
| |
| @Override |
| public String toString() { |
| return toString(new StringBuilder()).toString(); |
| } |
| |
| boolean needFullTextProcessor() { |
| return StringUtils.isNotEmpty(searchParameters.getQuery()); |
| } |
| |
| boolean needRelationshipProcessor() { |
| return CollectionUtils.isNotEmpty(relationshipTypes); |
| } |
| |
| boolean needClassificationProcessor() { |
| return (CollectionUtils.isNotEmpty(classificationTypes) && (CollectionUtils.isEmpty(entityTypes) || hasAttributeFilter(searchParameters.getTagFilters()))) || isWildCardSearch() ; |
| } |
| |
| boolean isWildCardSearch () { |
| if (CollectionUtils.isNotEmpty(classificationNames)) { |
| return classificationNames.stream().anyMatch(classification -> classification.contains(WILDCARD_CLASSIFICATIONS)); |
| } |
| return false; |
| } |
| |
| boolean needEntityProcessor() { |
| return CollectionUtils.isNotEmpty(entityTypes); |
| } |
| |
| private void validateAttributes(final AtlasStructType structType, final FilterCriteria filterCriteria) throws AtlasBaseException { |
| if (filterCriteria != null) { |
| FilterCriteria.Condition condition = filterCriteria.getCondition(); |
| |
| if (condition != null && CollectionUtils.isNotEmpty(filterCriteria.getCriterion())) { |
| for (FilterCriteria criteria : filterCriteria.getCriterion()) { |
| validateAttributes(structType, criteria); |
| } |
| } else { |
| String attributeName = filterCriteria.getAttributeName(); |
| validateAttributes(structType, attributeName); |
| } |
| } |
| } |
| |
| private void validateAttributes(final AtlasStructType structType, final String... attributeNames) throws AtlasBaseException { |
| for (String attributeName : attributeNames) { |
| if (StringUtils.isNotEmpty(attributeName) && (structType == null || structType.getAttributeType(attributeName) == null)) { |
| if (structType == null) { |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_TYPENAME, "NULL"); |
| } |
| |
| String name = structType.getTypeName(); |
| if (name.equals(MATCH_ALL_ENTITY_TYPES.getTypeName())) { |
| name = ALL_ENTITY_TYPES; |
| } else if (name.equals(MATCH_ALL_CLASSIFICATION_TYPES.getTypeName())) { |
| name = ALL_CLASSIFICATION_TYPES; |
| } |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_ATTRIBUTE, attributeName, name); |
| } |
| } |
| } |
| |
| private void validateAttributes() throws AtlasBaseException { |
| Set<String> attributes = searchParameters.getAttributes(); |
| if (CollectionUtils.isNotEmpty(attributes) && CollectionUtils.isNotEmpty(entityTypes)) { |
| |
| AtlasEntityType entityType = entityTypes.iterator().next(); |
| for (String attr : attributes) { |
| AtlasAttribute attribute = entityType.getAttribute(attr); |
| |
| if (attribute == null) { |
| attribute = entityType.getRelationshipAttribute(attr, null); |
| hasRelationshipAttributes = attribute != null; |
| } |
| |
| if (attribute == null) { |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_ATTRIBUTE, attr, entityType.getTypeName()); |
| } |
| } |
| } |
| } |
| |
| public boolean excludeHeaderAttributes() { |
| if (CollectionUtils.isNotEmpty(entityTypes) && |
| searchParameters.getExcludeHeaderAttributes() && |
| CollectionUtils.isNotEmpty(searchParameters.getAttributes()) && |
| !hasRelationshipAttributes){ |
| return true; |
| } |
| |
| return false; |
| } |
| |
| public boolean hasAttributeFilter(FilterCriteria filterCriteria) { |
| return filterCriteria != null && |
| (CollectionUtils.isNotEmpty(filterCriteria.getCriterion()) || StringUtils.isNotEmpty(filterCriteria.getAttributeName())); |
| } |
| |
| private void addProcessor(SearchProcessor processor) { |
| if (searchProcessor == null) { |
| searchProcessor = processor; |
| } else { |
| searchProcessor.addProcessor(processor); |
| } |
| } |
| |
| private AtlasClassificationType getClassificationType(String classificationName) { |
| AtlasClassificationType ret; |
| |
| if (StringUtils.equals(classificationName, MATCH_ALL_WILDCARD_CLASSIFICATION.getTypeName())) { |
| ret = MATCH_ALL_WILDCARD_CLASSIFICATION; |
| } else if (StringUtils.equals(classificationName, MATCH_ALL_CLASSIFIED.getTypeName())) { |
| ret = MATCH_ALL_CLASSIFIED; |
| } else if (StringUtils.equals(classificationName, MATCH_ALL_NOT_CLASSIFIED.getTypeName())) { |
| ret = MATCH_ALL_NOT_CLASSIFIED; |
| } else if (StringUtils.equals(classificationName, ALL_CLASSIFICATION_TYPES)){ |
| ret = MATCH_ALL_CLASSIFICATION_TYPES; |
| } else { |
| ret = typeRegistry.getClassificationTypeByName(classificationName); |
| } |
| |
| return ret; |
| } |
| |
| private Set<AtlasClassificationType> getClassificationTypes(Set<String> classificationNames) { |
| if (CollectionUtils.isNotEmpty(classificationNames)) { |
| return classificationNames.stream().map(n -> |
| getClassificationType(n)).filter(Objects::nonNull).collect(Collectors.toSet()); |
| } |
| |
| return null; |
| } |
| |
| private Set<String> getClassificationNames(String classification) throws AtlasBaseException { |
| Set<String> classificationNames = new HashSet<>(); |
| |
| if (StringUtils.isNotEmpty(classification)) { |
| String[] types = classification.split(TYPENAME_DELIMITER); |
| Set<String> names = new HashSet<>(Arrays.asList(types)); |
| |
| names.forEach(name -> { |
| AtlasClassificationType type = getClassificationType(name); |
| if (type != null || name.contains(WILDCARD_CLASSIFICATIONS)) { |
| classificationNames.add(name); |
| } |
| }); |
| |
| // Validate if the classification exists |
| if (CollectionUtils.isEmpty(classificationNames)) { |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_CLASSIFICATION, classification); |
| |
| } else if (classificationNames.size() != names.size()) { |
| names.removeAll(classificationNames); |
| |
| LOG.info("Could not search for {} , invalid classifications", String.join(TYPENAME_DELIMITER, names)); |
| } |
| } |
| |
| return classificationNames; |
| } |
| |
| private AtlasEntityType getEntityType(String entityName) { |
| return StringUtils.equals(entityName, ALL_ENTITY_TYPES) ? MATCH_ALL_ENTITY_TYPES : |
| typeRegistry.getEntityTypeByName(entityName); |
| } |
| |
| private Set<AtlasEntityType> getEntityTypes(String typeName) throws AtlasBaseException { |
| |
| Set<AtlasEntityType> entityTypes = null; |
| //split multiple typeNames by comma |
| if (StringUtils.isNotEmpty(typeName)) { |
| |
| String[] types = typeName.split(TYPENAME_DELIMITER); |
| Set<String> typeNames = new HashSet<>(Arrays.asList(types)); |
| entityTypes = typeNames.stream().map(n -> |
| getEntityType(n)).filter(Objects::nonNull).collect(Collectors.toSet()); |
| |
| // Validate if the type name is incorrect |
| if (CollectionUtils.isEmpty(entityTypes)) { |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_TYPENAME,typeName); |
| |
| } else if (entityTypes.size() != typeNames.size()) { |
| Set<String> validEntityTypes = new HashSet<>(); |
| for (AtlasEntityType entityType : entityTypes) { |
| String name = entityType.getTypeName(); |
| if (name.equals(MATCH_ALL_ENTITY_TYPES.getTypeName())) { |
| validEntityTypes.add(ALL_ENTITY_TYPES); |
| continue; |
| } |
| validEntityTypes.add(entityType.getTypeName()); |
| } |
| |
| typeNames.removeAll(validEntityTypes); |
| |
| LOG.info("Could not search for {} , invalid typeNames", String.join(TYPENAME_DELIMITER, typeNames)); |
| } |
| } |
| |
| return entityTypes; |
| } |
| |
| private void filterStructTypes(){ |
| //if typeName contains ALL_ENTITY_TYPES, remove others as OR condition will not effect any other |
| if (CollectionUtils.isNotEmpty(entityTypes) && entityTypes.contains(MATCH_ALL_ENTITY_TYPES)) { |
| entityTypes.clear(); |
| entityTypes.add(MATCH_ALL_ENTITY_TYPES); |
| } |
| |
| //No Builtin Classification can be together |
| if (CollectionUtils.isNotEmpty(classificationTypes)) { |
| if (classificationTypes.contains(MATCH_ALL_NOT_CLASSIFIED)) { |
| classificationTypes.clear(); |
| classificationTypes.add(MATCH_ALL_NOT_CLASSIFIED); |
| |
| classificationNames.clear(); |
| classificationNames.add(MATCH_ALL_NOT_CLASSIFIED.getTypeName()); |
| } else if (classificationTypes.contains(MATCH_ALL_WILDCARD_CLASSIFICATION) || classificationTypes.contains(MATCH_ALL_CLASSIFICATION_TYPES) || classificationTypes.contains(MATCH_ALL_CLASSIFIED)) { |
| classificationTypes.clear(); |
| classificationTypes.add(MATCH_ALL_CLASSIFICATION_TYPES); |
| |
| classificationNames.clear(); |
| classificationNames.add(ALL_CLASSIFICATION_TYPES); |
| } |
| } |
| } |
| |
| |
| private AtlasVertex getGlossaryTermVertex(String termName) { |
| AtlasVertex ret = null; |
| |
| if (StringUtils.isNotEmpty(termName)) { |
| AtlasEntityType termType = getTermEntityType(); |
| AtlasAttribute attrName = termType.getAttribute(TermSearchProcessor.ATLAS_GLOSSARY_TERM_ATTR_QNAME); |
| AtlasGraphQuery query = graph.query().has(Constants.ENTITY_TYPE_PROPERTY_KEY, termType.getTypeName()) |
| .has(attrName.getVertexPropertyName(), termName) |
| .has(Constants.STATE_PROPERTY_KEY, AtlasEntity.Status.ACTIVE.name()); |
| |
| Iterator<AtlasVertex> results = query.vertices().iterator(); |
| |
| ret = results.hasNext() ? results.next() : null; |
| } |
| |
| return ret; |
| } |
| |
| private List<AtlasVertex> getAssignedEntities(AtlasVertex glossaryTerm) { |
| List<AtlasVertex> ret = new ArrayList<>(); |
| AtlasEntityType termType = getTermEntityType(); |
| AtlasAttribute attr = termType.getRelationshipAttribute(TermSearchProcessor.ATLAS_GLOSSARY_TERM_ATTR_ASSIGNED_ENTITIES, EntityGraphRetriever.TERM_RELATION_NAME); |
| Iterator<AtlasEdge> edges = GraphHelper.getEdgesForLabel(glossaryTerm, attr.getRelationshipEdgeLabel(), attr.getRelationshipEdgeDirection()); |
| |
| boolean excludeDeletedEntities = searchParameters.getExcludeDeletedEntities(); |
| if (edges != null) { |
| while (edges.hasNext()) { |
| AtlasEdge edge = edges.next(); |
| |
| AtlasVertex inVertex = edge.getInVertex(); |
| if (excludeDeletedEntities && AtlasGraphUtilsV2.getState(inVertex) == AtlasEntity.Status.DELETED) { |
| continue; |
| } |
| ret.add(inVertex); |
| } |
| } |
| |
| return ret; |
| } |
| |
| private AtlasEntityType getTermEntityType() { |
| return typeRegistry.getEntityTypeByName(TermSearchProcessor.ATLAS_GLOSSARY_TERM_ENTITY_TYPE); |
| } |
| |
| private Set<AtlasRelationshipType> getRelationshipTypes(String relationship) throws AtlasBaseException { |
| Set<AtlasRelationshipType> relationshipTypes = null; |
| //split multiple typeNames by comma |
| if (StringUtils.isNotEmpty(relationship)) { |
| |
| String[] types = relationship.split(TYPENAME_DELIMITER); |
| Set<String> typeNames = new HashSet<>(Arrays.asList(types)); |
| relationshipTypes = typeNames.stream().map(n -> |
| typeRegistry.getRelationshipTypeByName(n)).filter(Objects::nonNull).collect(Collectors.toSet()); |
| |
| // Validate if the type name is incorrect |
| if (CollectionUtils.isEmpty(relationshipTypes)) { |
| throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_TYPENAME,relationship); |
| |
| } else if (relationshipTypes.size() != typeNames.size()) { |
| Set<String> validEntityTypes = new HashSet<>(); |
| for (AtlasRelationshipType entityType : relationshipTypes) { |
| validEntityTypes.add(entityType.getTypeName()); |
| } |
| |
| typeNames.removeAll(validEntityTypes); |
| |
| LOG.info("Could not search for {} , invalid typeNames", String.join(TYPENAME_DELIMITER, typeNames)); |
| } |
| } |
| |
| return relationshipTypes; |
| } |
| |
| public static class MarkerUtil { |
| private final static int IDX_HASH_CODE = 0; |
| private final static int IDX_OFFSET = 1; |
| |
| private final static String MARKER_DELIMITER = ":"; |
| |
| @VisibleForTesting |
| final static String MARKER_START = "*"; |
| |
| @VisibleForTesting |
| final static int MARKER_END = -1; |
| |
| public static String getNextEncMarker(SearchParameters searchParameters, Integer nextOffset) { |
| if (nextOffset == null) { |
| return null; |
| } |
| |
| if (nextOffset == MARKER_END) { |
| return String.valueOf(nextOffset); |
| } |
| |
| String value = searchParameters.hashCode() + MARKER_DELIMITER + nextOffset; |
| return Base64.getEncoder().encodeToString(value.getBytes()); |
| } |
| |
| public static Integer decodeMarker(SearchParameters searchParameters) throws AtlasBaseException { |
| if (searchParameters == null || searchParameters.getOffset() > 0) { |
| throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Marker can be used only if offset=0."); |
| } |
| |
| String encodedMarker = searchParameters.getMarker(); |
| if (StringUtils.equals(encodedMarker, MARKER_START)) { |
| return 0; |
| } |
| |
| try { |
| byte[] inputMarkerBytes = Base64.getDecoder().decode(encodedMarker); |
| String inputMarker = new String(inputMarkerBytes); |
| if (StringUtils.isEmpty(inputMarker) || !inputMarker.contains(MARKER_DELIMITER)) { |
| throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Invalid marker found! Marker does not contain delimiter: " + MARKER_DELIMITER); |
| } |
| |
| String[] str = inputMarker.split(MARKER_DELIMITER); |
| if (str == null || str.length != 2) { |
| throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Invalid marker found! Decoding using delimiter did not yield correct result!"); |
| } |
| |
| int hashCode = Integer.parseInt(str[IDX_HASH_CODE]); |
| int currentHashCode = searchParameters.hashCode(); |
| if (hashCode == currentHashCode && Integer.parseInt(str[IDX_OFFSET]) >= 0) { |
| return Integer.parseInt(str[IDX_OFFSET]); |
| } |
| |
| throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Invalid Marker! Parsing resulted in error."); |
| } catch (Exception e) { |
| throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "Invalid marker!"); |
| } |
| } |
| } |
| } |