This is an automated email from the ASF dual-hosted git repository. pinal pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/master by this push: new 37c242755 ATLAS-4671: Basic Search : Exclude Header attributes of entities from the response 37c242755 is described below commit 37c242755cdf5320c2eafce925d613061e584e6a Author: Pinal Shah <pinal.s...@freestoneinfotech.com> AuthorDate: Wed Sep 14 15:36:39 2022 +0530 ATLAS-4671: Basic Search : Exclude Header attributes of entities from the response Signed-off-by: Pinal Shah <pinal.s...@freestoneinfotech.com> --- .../model/discovery/QuickSearchParameters.java | 9 ++ .../atlas/model/discovery/SearchParameters.java | 14 ++- .../atlas/discovery/EntityDiscoveryService.java | 27 +++++ .../org/apache/atlas/discovery/SearchContext.java | 38 +++++++ .../atlas/discovery/AtlasDiscoveryServiceTest.java | 117 +++++++++++++++++++++ .../search-parameters/combination-filters.json | 12 +-- .../json/search-parameters/entity-filters.json | 13 --- 7 files changed, 210 insertions(+), 20 deletions(-) diff --git a/intg/src/main/java/org/apache/atlas/model/discovery/QuickSearchParameters.java b/intg/src/main/java/org/apache/atlas/model/discovery/QuickSearchParameters.java index 79f5aae0d..dcad83c01 100644 --- a/intg/src/main/java/org/apache/atlas/model/discovery/QuickSearchParameters.java +++ b/intg/src/main/java/org/apache/atlas/model/discovery/QuickSearchParameters.java @@ -50,6 +50,7 @@ public class QuickSearchParameters implements Serializable { private Set<String> attributes; private String sortBy; private SortOrder sortOrder; + private boolean excludeHeaderAttributes; /** * for framework use. @@ -158,4 +159,12 @@ public class QuickSearchParameters implements Serializable { public void setSortOrder(SortOrder sortOrder) { this.sortOrder = sortOrder; } + + public boolean getExcludeHeaderAttributes() { + return excludeHeaderAttributes; + } + + public void setExcludeHeaderAttributes(boolean excludeHeaderAttributes) { + this.excludeHeaderAttributes = excludeHeaderAttributes; + } } diff --git a/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java b/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java index 78fb4a48f..8e68d0e82 100644 --- a/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java +++ b/intg/src/main/java/org/apache/atlas/model/discovery/SearchParameters.java @@ -50,6 +50,8 @@ public class SearchParameters implements Serializable { private boolean includeClassificationAttributes; private boolean includeSubTypes = true; private boolean includeSubClassifications = true; + private boolean excludeHeaderAttributes = false; + private int limit; private int offset; private String marker; @@ -258,6 +260,14 @@ public class SearchParameters implements Serializable { this.tagFilters = tagFilters; } + public boolean getExcludeHeaderAttributes() { + return excludeHeaderAttributes; + } + + public void setExcludeHeaderAttributes(boolean excludeHeaderAttributes) { + this.excludeHeaderAttributes = excludeHeaderAttributes; + } + /** * Attribute values included in the results * @return @@ -307,6 +317,7 @@ public class SearchParameters implements Serializable { includeClassificationAttributes == that.includeClassificationAttributes && includeSubTypes == that.includeSubTypes && includeSubClassifications == that.includeSubClassifications && + excludeHeaderAttributes == that.excludeHeaderAttributes && limit == that.limit && offset == that.offset && Objects.equals(query, that.query) && @@ -323,7 +334,7 @@ public class SearchParameters implements Serializable { @Override public int hashCode() { return Objects.hash(query, typeName, classification, termName, includeSubTypes, includeSubClassifications, - excludeDeletedEntities, includeClassificationAttributes, limit, offset, entityFilters, + excludeDeletedEntities, includeClassificationAttributes, excludeHeaderAttributes, limit, offset, entityFilters, tagFilters, attributes, sortBy, sortOrder); } @@ -341,6 +352,7 @@ public class SearchParameters implements Serializable { sb.append(", includeSubClassifications='").append(includeSubClassifications).append('\''); sb.append(", excludeDeletedEntities=").append(excludeDeletedEntities); sb.append(", includeClassificationAttributes=").append(includeClassificationAttributes); + sb.append(", excludeHeaderAttributes=").append(excludeHeaderAttributes); sb.append(", limit=").append(limit); sb.append(", offset=").append(offset); sb.append(", entityFilters=").append(entityFilters); diff --git a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java index 8fbc22fa0..4b113dbef 100644 --- a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java +++ b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java @@ -468,6 +468,32 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { ret.setNextMarker(nextMarker); } + //If excludeHeaderAttributes is true, only primitive attributes requested in 'attributes' field will be sent in the response + Set<String> attributes = searchParameters.getAttributes(); + if (searchContext.excludeHeaderAttributes()) { + + AtlasSearchResult.AttributeSearchResult attributeSearchResult = new AtlasSearchResult.AttributeSearchResult(); + attributeSearchResult.setName(new ArrayList<>(attributes)); + + Collection<List<Object>> values = new ArrayList<>(); + for (AtlasVertex vertex : resultList) { + List<Object> row = new ArrayList<>(); + + for (String attrName : attributes) { + AtlasEntityType entityType = searchContext.getEntityTypes().iterator().next(); + AtlasAttribute attribute = entityType.getAttribute(attrName); + Object value = vertex.getProperty(attribute.getVertexPropertyName(), Object.class); + + row.add(value != null ? value : StringUtils.EMPTY); + } + values.add(row); + } + attributeSearchResult.setValues(new ArrayList<>(values)); + + ret.setAttributes(attributeSearchResult); + return ret; + } + // By default any attribute that shows up in the search parameter should be sent back in the response // If additional values are requested then the entityAttributes will be a superset of the all search attributes // and the explicitly requested attribute(s) @@ -922,6 +948,7 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { searchParameters.setAttributes(quickSearchParameters.getAttributes()); searchParameters.setSortBy(quickSearchParameters.getSortBy()); searchParameters.setSortOrder(quickSearchParameters.getSortOrder()); + searchParameters.setExcludeHeaderAttributes(quickSearchParameters.getExcludeHeaderAttributes()); return searchParameters; } diff --git a/repository/src/main/java/org/apache/atlas/discovery/SearchContext.java b/repository/src/main/java/org/apache/atlas/discovery/SearchContext.java index 01954d07e..b8976e079 100644 --- a/repository/src/main/java/org/apache/atlas/discovery/SearchContext.java +++ b/repository/src/main/java/org/apache/atlas/discovery/SearchContext.java @@ -32,10 +32,13 @@ 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.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; @@ -86,6 +89,7 @@ public class SearchContext { 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)); @@ -146,6 +150,9 @@ public class SearchContext { //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; @@ -345,6 +352,37 @@ public class SearchContext { } } + 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())); diff --git a/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java b/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java index fbc739652..ecb398a44 100644 --- a/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java +++ b/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java @@ -31,6 +31,7 @@ import org.apache.atlas.model.discovery.AtlasAggregationEntry; import org.apache.atlas.model.instance.AtlasClassification; import org.apache.atlas.model.instance.AtlasEntity; import org.apache.atlas.model.instance.AtlasEntityHeader; +import org.apache.atlas.model.instance.AtlasObjectId; import org.apache.atlas.model.instance.EntityMutationResponse; import org.apache.atlas.repository.graph.AtlasGraphProvider; import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream; @@ -42,6 +43,7 @@ import org.testng.annotations.*; import javax.inject.Inject; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -929,6 +931,109 @@ public class AtlasDiscoveryServiceTest extends BasicTestSetup { Assert.assertTrue(list.get(0).getDisplayText().equalsIgnoreCase("time_id")); } + //test excludeHeaderAttributes + @Test + public void excludeHeaderAttributesStringAttr() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + params.setExcludeHeaderAttributes(true); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition("tableType", Operator.EQ, "Managed"); + params.setEntityFilters(filterCriteria); + params.setSortBy("name"); + params.setAttributes(new HashSet<String>() {{ add("name");}}); + params.setLimit(1); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + AtlasSearchResult.AttributeSearchResult expected = new AtlasSearchResult.AttributeSearchResult(); + expected.setName(Arrays.asList("name")); + expected.setValues(Arrays.asList(Arrays.asList("log_fact_daily_mv"))); + assertSearchResult(searchResult,expected); + } + + @Test + public void excludeHeaderAttributesRelationAttr() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + params.setExcludeHeaderAttributes(true); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition("name", Operator.EQ, "time_dim"); + params.setEntityFilters(filterCriteria); + params.setAttributes(new HashSet<String>() {{ add("name"); add("db");}}); + params.setLimit(1); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + + assertNotNull(searchResult); + assertNotNull(searchResult.getEntities()); + assertNotNull(searchResult.getReferredEntities()); + } + + @Test + public void excludeHeaderAttributesSystemAttr() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + params.setExcludeHeaderAttributes(true); + params.setAttributes(new HashSet<String>() {{ add("name"); add("__state");}}); + params.setLimit(1); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition("tableType", Operator.EQ, "Managed"); + params.setEntityFilters(filterCriteria); + params.setSortBy("name"); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + AtlasSearchResult.AttributeSearchResult expected = new AtlasSearchResult.AttributeSearchResult(); + expected.setName(Arrays.asList("name","__state")); + expected.setValues(Arrays.asList(Arrays.asList("log_fact_daily_mv","ACTIVE"))); + assertSearchResult(searchResult,expected); + } + + @Test + public void excludeHeaderAttributesAllEntityTypeSysAttr() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE+","+ALL_ENTITY_TYPES); + params.setExcludeHeaderAttributes(true); + params.setAttributes(new HashSet<String>() {{ add("__state");}}); + params.setLimit(2); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + AtlasSearchResult.AttributeSearchResult expected = new AtlasSearchResult.AttributeSearchResult(); + expected.setName(Arrays.asList("__state")); + expected.setValues(Arrays.asList(Arrays.asList("ACTIVE"), Arrays.asList("ACTIVE"))); + assertSearchResult(searchResult,expected); + } + @Test + public void excludeHeaderAttributesAllEntityTypeSysAttrs() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE+","+ALL_ENTITY_TYPES); + params.setExcludeHeaderAttributes(true); + params.setAttributes(new HashSet<String>() {{ add("__state"); add("__guid");}}); + params.setLimit(2); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + assertEquals(searchResult.getAttributes().getValues().size(), 2); + } + + @Test(expectedExceptions = AtlasBaseException.class, expectedExceptionsMessageRegExp = "Attribute name not found for type __ENTITY_ROOT") + public void excludeHeaderAttributesAllEntityType() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE+","+ALL_ENTITY_TYPES); + params.setExcludeHeaderAttributes(true); + params.setAttributes(new HashSet<String>() {{ add("name");}}); + params.setLimit(1); + + discoveryService.searchWithParameters(params); + } + + @Test(expectedExceptions = AtlasBaseException.class, expectedExceptionsMessageRegExp = "Attribute name1 not found for type hive_table") + public void excludeHeaderAttributesInvalidAttr() throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + params.setExcludeHeaderAttributes(true); + params.setAttributes(new HashSet<String>() {{ add("name1");}}); + params.setLimit(1); + + discoveryService.searchWithParameters(params); + } + + private String gethiveTableSalesFactGuid() throws AtlasBaseException { if (salesFactGuid == null) { SearchParameters params = new SearchParameters(); @@ -958,6 +1063,18 @@ public class AtlasDiscoveryServiceTest extends BasicTestSetup { } } + private void assertSearchResult(AtlasSearchResult searchResult, AtlasSearchResult.AttributeSearchResult expected) { + assertNotNull(searchResult); + AtlasSearchResult.AttributeSearchResult result = searchResult.getAttributes(); + assertNotNull(result); + assertTrue(result.getName().containsAll(expected.getName())); + int i = 0; + for (List<Object> value : result.getValues()) { + assertTrue(value.containsAll(expected.getValues().get(i))); + i++; + } + } + private void assertAggregationMetrics(AtlasQuickSearchResult searchResult) { Map<String, List<AtlasAggregationEntry>> agg = searchResult.getAggregationMetrics(); Assert.assertTrue(CollectionUtils.isNotEmpty(agg.get("__typeName"))); diff --git a/webapp/src/test/resources/json/search-parameters/combination-filters.json b/webapp/src/test/resources/json/search-parameters/combination-filters.json index dc52d33d1..f65e2bae3 100644 --- a/webapp/src/test/resources/json/search-parameters/combination-filters.json +++ b/webapp/src/test/resources/json/search-parameters/combination-filters.json @@ -10,7 +10,7 @@ "offset": 0, "entityFilters": null, "tagFilters": null, - "attributes": [""] + "attributes": [] }, "expectedCount": 1 }, @@ -25,7 +25,7 @@ "offset": 0, "entityFilters": null, "tagFilters": null, - "attributes": [""] + "attributes": [] }, "expectedCount": 0 }, @@ -129,7 +129,7 @@ ] }, "tagFilters": null, - "attributes": [""] + "attributes": [] }, "expectedCount": 1 }, @@ -144,7 +144,7 @@ "offset": 0, "entityFilters": null, "tagFilters": null, - "attributes": [""] + "attributes": [] }, "expectedCount": 1 }, @@ -159,7 +159,7 @@ "offset": 0, "entityFilters": null, "tagFilters": null, - "attributes": [""] + "attributes": [] }, "expectedCount": 3 }, @@ -178,7 +178,7 @@ "attributeValue" : "+" }, "tagFilters": null, - "attributes": [""] + "attributes": [] }, "expectedCount": 1 } diff --git a/webapp/src/test/resources/json/search-parameters/entity-filters.json b/webapp/src/test/resources/json/search-parameters/entity-filters.json index 93d2d7d36..255ed8adb 100644 --- a/webapp/src/test/resources/json/search-parameters/entity-filters.json +++ b/webapp/src/test/resources/json/search-parameters/entity-filters.json @@ -15,7 +15,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -38,7 +37,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -59,7 +57,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 2 @@ -81,7 +78,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 1 @@ -113,7 +109,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -145,7 +140,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -177,7 +171,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -199,7 +192,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -231,7 +223,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 1 @@ -263,7 +254,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 18 @@ -295,7 +285,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -327,7 +316,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3 @@ -359,7 +347,6 @@ }, "tagFilters": null, "attributes": [ - "" ] }, "expectedCount": 3