Commit f622751d by Madhan Neethiraj

ATLAS-2561: basic-search update to enable search by glossary-term

parent 0dad9529
...@@ -91,7 +91,7 @@ public enum AtlasErrorCode { ...@@ -91,7 +91,7 @@ public enum AtlasErrorCode {
RELATIONSHIPDEF_INVALID(400, "ATLAS-400-00-044", "Invalid relationshipDef: {0}"), RELATIONSHIPDEF_INVALID(400, "ATLAS-400-00-044", "Invalid relationshipDef: {0}"),
RELATIONSHIP_INVALID_ENDTYPE(400, "ATLAS-400-00-045", "Invalid entity-type for relationship attribute ‘{0}’: entity specified (guid={1}) is of type ‘{2}’, but expected type is ‘{3}’"), RELATIONSHIP_INVALID_ENDTYPE(400, "ATLAS-400-00-045", "Invalid entity-type for relationship attribute ‘{0}’: entity specified (guid={1}) is of type ‘{2}’, but expected type is ‘{3}’"),
UNKNOWN_CLASSIFICATION(400, "ATLAS-400-00-046", "{0}: Unknown/invalid classification"), UNKNOWN_CLASSIFICATION(400, "ATLAS-400-00-046", "{0}: Unknown/invalid classification"),
INVALID_SEARCH_PARAMS(400, "ATLAS-400-00-047", "No search parameter was found. One of the following MUST be specified in the request; typeName, classification or queryText"), INVALID_SEARCH_PARAMS(400, "ATLAS-400-00-047", "No search parameter was found. One of the following MUST be specified in the request; typeName, classification, termName or queryText"),
INVALID_RELATIONSHIP_ATTRIBUTE(400, "ATLAS-400-00-048", "Expected attribute {0} to be a relationship but found type {1}"), INVALID_RELATIONSHIP_ATTRIBUTE(400, "ATLAS-400-00-048", "Expected attribute {0} to be a relationship but found type {1}"),
INVALID_RELATIONSHIP_TYPE(400, "ATLAS-400-00-049", "Invalid entity type '{0}', guid '{1}' in relationship search"), INVALID_RELATIONSHIP_TYPE(400, "ATLAS-400-00-049", "Invalid entity type '{0}', guid '{1}' in relationship search"),
INVALID_IMPORT_ATTRIBUTE_TYPE_CHANGED(400, "ATLAS-400-00-050", "Attribute {0}.{1} is of type {2}. Import has this attribute type as {3}"), INVALID_IMPORT_ATTRIBUTE_TYPE_CHANGED(400, "ATLAS-400-00-050", "Attribute {0}.{1} is of type {2}. Import has this attribute type as {3}"),
...@@ -138,6 +138,7 @@ public enum AtlasErrorCode { ...@@ -138,6 +138,7 @@ public enum AtlasErrorCode {
GLOSSARY_QUALIFIED_NAME_CANT_BE_DERIVED(400, "ATLAS-400-00-078", "Attributes qualifiedName and displayName are missing. Failed to derive a unique name for Glossary"), GLOSSARY_QUALIFIED_NAME_CANT_BE_DERIVED(400, "ATLAS-400-00-078", "Attributes qualifiedName and displayName are missing. Failed to derive a unique name for Glossary"),
GLOSSARY_TERM_QUALIFIED_NAME_CANT_BE_DERIVED(400, "ATLAS-400-00-079", "Attributes qualifiedName, displayName & glossary name are missing. Failed to derive a unique name for Glossary term"), GLOSSARY_TERM_QUALIFIED_NAME_CANT_BE_DERIVED(400, "ATLAS-400-00-079", "Attributes qualifiedName, displayName & glossary name are missing. Failed to derive a unique name for Glossary term"),
GLOSSARY_CATEGORY_QUALIFIED_NAME_CANT_BE_DERIVED(400, "ATLAS-400-00-07A", "Attributes qualifiedName, displayName & glossary name are missing. Failed to derive a unique name for Glossary category"), GLOSSARY_CATEGORY_QUALIFIED_NAME_CANT_BE_DERIVED(400, "ATLAS-400-00-07A", "Attributes qualifiedName, displayName & glossary name are missing. Failed to derive a unique name for Glossary category"),
UNKNOWN_GLOSSARY_TERM(400, "ATLAS-400-00-07B", "{0}: Unknown/invalid glossary term"),
UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to perform {1}"), UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to perform {1}"),
......
...@@ -43,6 +43,7 @@ public class SearchParameters implements Serializable { ...@@ -43,6 +43,7 @@ public class SearchParameters implements Serializable {
private String query; private String query;
private String typeName; private String typeName;
private String classification; private String classification;
private String termName;
private boolean excludeDeletedEntities; private boolean excludeDeletedEntities;
private boolean includeClassificationAttributes; private boolean includeClassificationAttributes;
private boolean includeSubTypes = true; private boolean includeSubTypes = true;
...@@ -86,6 +87,22 @@ public class SearchParameters implements Serializable { ...@@ -86,6 +87,22 @@ public class SearchParameters implements Serializable {
/** /**
* *
* @return termName to search on
*/
public String getTermName() {
return termName;
}
/**
* Set the classification/tag to search on
* @param termName classification/tag name
*/
public void setTermName(String termName) {
this.termName = termName;
}
/**
*
* @return Classification/tag to search on * @return Classification/tag to search on
*/ */
public String getClassification() { public String getClassification() {
...@@ -249,6 +266,7 @@ public class SearchParameters implements Serializable { ...@@ -249,6 +266,7 @@ public class SearchParameters implements Serializable {
Objects.equals(query, that.query) && Objects.equals(query, that.query) &&
Objects.equals(typeName, that.typeName) && Objects.equals(typeName, that.typeName) &&
Objects.equals(classification, that.classification) && Objects.equals(classification, that.classification) &&
Objects.equals(termName, that.termName) &&
Objects.equals(entityFilters, that.entityFilters) && Objects.equals(entityFilters, that.entityFilters) &&
Objects.equals(tagFilters, that.tagFilters) && Objects.equals(tagFilters, that.tagFilters) &&
Objects.equals(attributes, that.attributes); Objects.equals(attributes, that.attributes);
...@@ -256,7 +274,7 @@ public class SearchParameters implements Serializable { ...@@ -256,7 +274,7 @@ public class SearchParameters implements Serializable {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(query, typeName, classification, excludeDeletedEntities, includeClassificationAttributes, return Objects.hash(query, typeName, classification, termName, excludeDeletedEntities, includeClassificationAttributes,
limit, offset, entityFilters, tagFilters, attributes); limit, offset, entityFilters, tagFilters, attributes);
} }
...@@ -269,6 +287,7 @@ public class SearchParameters implements Serializable { ...@@ -269,6 +287,7 @@ public class SearchParameters implements Serializable {
sb.append("query='").append(query).append('\''); sb.append("query='").append(query).append('\'');
sb.append(", typeName='").append(typeName).append('\''); sb.append(", typeName='").append(typeName).append('\'');
sb.append(", classification='").append(classification).append('\''); sb.append(", classification='").append(classification).append('\'');
sb.append(", termName='").append(termName).append('\'');
sb.append(", excludeDeletedEntities=").append(excludeDeletedEntities); sb.append(", excludeDeletedEntities=").append(excludeDeletedEntities);
sb.append(", includeClassificationAttributes=").append(includeClassificationAttributes); sb.append(", includeClassificationAttributes=").append(includeClassificationAttributes);
sb.append(", limit=").append(limit); sb.append(", limit=").append(limit);
......
...@@ -22,16 +22,28 @@ import org.apache.atlas.AtlasErrorCode; ...@@ -22,16 +22,28 @@ import org.apache.atlas.AtlasErrorCode;
import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.discovery.SearchParameters; import org.apache.atlas.model.discovery.SearchParameters;
import org.apache.atlas.model.discovery.SearchParameters.FilterCriteria; import org.apache.atlas.model.discovery.SearchParameters.FilterCriteria;
import org.apache.atlas.model.instance.AtlasEntity;
import org.apache.atlas.model.typedef.AtlasClassificationDef; 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.AtlasEdgeDirection;
import org.apache.atlas.repository.graphdb.AtlasGraph; 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.type.AtlasClassificationType; import org.apache.atlas.type.AtlasClassificationType;
import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasEntityType;
import org.apache.atlas.type.AtlasStructType; import org.apache.atlas.type.AtlasStructType;
import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.type.AtlasTypeRegistry;
import org.apache.atlas.v1.model.instance.Id;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
/* /*
...@@ -73,12 +85,23 @@ public class SearchContext { ...@@ -73,12 +85,23 @@ public class SearchContext {
throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_CLASSIFICATION, classificationName); throw new AtlasBaseException(AtlasErrorCode.UNKNOWN_CLASSIFICATION, classificationName);
} }
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 will raise an exception with 400 error code // Invalid attributes will raise an exception with 400 error code
validateAttributes(entityType, searchParameters.getEntityFilters()); validateAttributes(entityType, searchParameters.getEntityFilters());
// Invalid attributes will raise an exception with 400 error code // Invalid attributes will raise an exception with 400 error code
validateAttributes(classificationType, searchParameters.getTagFilters()); validateAttributes(classificationType, searchParameters.getTagFilters());
if (glossaryTermVertex != null) {
addProcessor(new TermSearchProcessor(this, getAssignedEntities(glossaryTermVertex)));
}
if (needFullTextProcessor()) { if (needFullTextProcessor()) {
addProcessor(new FullTextSearchProcessor(this)); addProcessor(new FullTextSearchProcessor(this));
} }
...@@ -185,4 +208,43 @@ public class SearchContext { ...@@ -185,4 +208,43 @@ public class SearchContext {
return ret; return ret;
} }
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);
Iterator<AtlasEdge> edges = GraphHelper.getEdgesForLabel(glossaryTerm, attr.getRelationshipEdgeLabel(), attr.getRelationshipEdgeDirection());
if (edges != null) {
while (edges.hasNext()) {
AtlasEdge edge = edges.next();
ret.add(edge.getInVertex());
}
}
return ret;
}
private AtlasEntityType getTermEntityType() {
return typeRegistry.getEntityTypeByName(TermSearchProcessor.ATLAS_GLOSSARY_TERM_ENTITY_TYPE);
}
} }
/**
* 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 org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.utils.AtlasPerfTracer;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class TermSearchProcessor extends SearchProcessor {
private static final Logger LOG = LoggerFactory.getLogger(TermSearchProcessor.class);
private static final Logger PERF_LOG = AtlasPerfTracer.getPerfLogger("TermSearchProcessor");
public static final String ATLAS_GLOSSARY_TERM_ENTITY_TYPE = "__AtlasGlossaryTerm";
public static final String ATLAS_GLOSSARY_TERM_ATTR_QNAME = "qualifiedName";
public static final String ATLAS_GLOSSARY_TERM_ATTR_ASSIGNED_ENTITIES = "assignedEntities";
final List<AtlasVertex> assignedEntities;
public TermSearchProcessor(SearchContext context, List<AtlasVertex> assignedEntities) {
super(context);
this.assignedEntities = assignedEntities;
}
@Override
public List<AtlasVertex> execute() {
if (LOG.isDebugEnabled()) {
LOG.debug("==> TermSearchProcessor.execute({})", context);
}
List<AtlasVertex> ret = new ArrayList<>();
AtlasPerfTracer perf = null;
if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "TermSearchProcessor.execute(" + context + ")");
}
try {
if (CollectionUtils.isNotEmpty(assignedEntities)) {
final int startIdx = context.getSearchParameters().getOffset();
final int limit = context.getSearchParameters().getLimit();
final List<AtlasVertex> tmpList = new ArrayList<>(assignedEntities);
super.filter(tmpList);
collectResultVertices(ret, startIdx, limit, 0, tmpList);
}
} finally {
AtlasPerfTracer.log(perf);
}
if (LOG.isDebugEnabled()) {
LOG.debug("<== TermSearchProcessor.execute({}): ret.size()={}", context, ret.size());
}
return ret;
}
@Override
public void filter(List<AtlasVertex> entityVertices) {
if (LOG.isDebugEnabled()) {
LOG.debug("==> TermSearchProcessor.filter({})", entityVertices.size());
}
if (CollectionUtils.isNotEmpty(entityVertices)) {
if (CollectionUtils.isEmpty(assignedEntities)) {
entityVertices.clear();
} else {
CollectionUtils.filter(entityVertices, new Predicate() {
@Override
public boolean evaluate(Object o) {
if (o instanceof AtlasVertex) {
AtlasVertex entityVertex = (AtlasVertex) o;
for (AtlasVertex assignedEntity : assignedEntities) {
if (assignedEntity.getId().equals(entityVertex.getId())) {
return true;
}
}
}
return false;
}
});
}
}
super.filter(entityVertices);
if (LOG.isDebugEnabled()) {
LOG.debug("<== TermSearchProcessor.filter(): ret.size()={}", entityVertices.size());
}
}
}
...@@ -290,7 +290,7 @@ public class DiscoveryREST { ...@@ -290,7 +290,7 @@ public class DiscoveryREST {
throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "TagFilters specified without tag name"); throw new AtlasBaseException(AtlasErrorCode.BAD_REQUEST, "TagFilters specified without tag name");
} }
if (StringUtils.isEmpty(parameters.getTypeName()) && StringUtils.isEmpty(parameters.getClassification()) && StringUtils.isEmpty(parameters.getQuery())) { if (StringUtils.isEmpty(parameters.getTypeName()) && StringUtils.isEmpty(parameters.getClassification()) && StringUtils.isEmpty(parameters.getQuery()) && StringUtils.isEmpty(parameters.getTermName())) {
throw new AtlasBaseException(AtlasErrorCode.INVALID_SEARCH_PARAMS); throw new AtlasBaseException(AtlasErrorCode.INVALID_SEARCH_PARAMS);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment