sheetalshah1007 commented on code in PR #490:
URL: https://github.com/apache/atlas/pull/490#discussion_r2832518690


##########
client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java:
##########
@@ -353,7 +353,19 @@ public void deleteAtlasTypeDefs(AtlasTypesDef typesDef) 
throws AtlasServiceExcep
     }
 
     public void deleteTypeByName(String typeName) throws AtlasServiceException 
{
-        callAPI(API_V2.DELETE_TYPE_DEF_BY_NAME, (Class) null, null, typeName);
+        callAPI(API_V2.DELETE_TYPE_DEF_BY_NAME, (Class<?>) null, null, 
typeName);

Review Comment:
   The if-else with duplicate API calls can be avoided; consider combining the 
if-else logic since both overloaded methods call the same API - just with 
different query params.



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -271,15 +285,29 @@ public AtlasVertex preDeleteByName(String name) throws 
AtlasBaseException {
             throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_NOT_FOUND, 
name);
         }
 
-        checkBusinessMetadataRef(existingDef.getName());
+        if (!forceDelete) {
+            boolean hasIndexableAttribute = hasIndexableAttribute(existingDef);
 
-        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByName({}): 
{}", name, ret);
+            if (!hasIndexableAttribute) {

Review Comment:
   The hasIndexableAttribute check could be expensive if there are many 
attributes. 
      Consider caching this in the BusinessMetadataDef or TypeRegistry.



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -271,15 +285,29 @@ public AtlasVertex preDeleteByName(String name) throws 
AtlasBaseException {
             throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_NOT_FOUND, 
name);
         }
 
-        checkBusinessMetadataRef(existingDef.getName());
+        if (!forceDelete) {

Review Comment:
   The logic at lines 288-296 and 322-330 (in preDeleteByGuid) is duplicated. 
      Extract to a helper method: validateDeletion(AtlasBusinessMetadataDef 
def, boolean forceDelete)



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) {
+        if (graph == null || CollectionUtils.isEmpty(allApplicableTypes)) {
+            return false;
+        }
 
-        SearchParameters.FilterCriteria entityFilters = new 
SearchParameters.FilterCriteria();
+        try {
+            List<String> typesList = new ArrayList<>(allApplicableTypes);
 
-        
entityFilters.setCondition(SearchParameters.FilterCriteria.Condition.OR);
-        entityFilters.setCriterion(Collections.singletonList(criteria));
+            // 1. To Check if the BM property exists on a vertex where the 
direct type matches
+            Iterable<AtlasVertex> verticesDirect = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.ENTITY_TYPE_PROPERTY_KEY, typesList)
+                    .vertices();

Review Comment:
   Consider adding a limit (e.g., .limit(1)) since we only need to know if ANY 
exist



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) {
+        if (graph == null || CollectionUtils.isEmpty(allApplicableTypes)) {
+            return false;
+        }
 
-        SearchParameters.FilterCriteria entityFilters = new 
SearchParameters.FilterCriteria();
+        try {
+            List<String> typesList = new ArrayList<>(allApplicableTypes);
 
-        
entityFilters.setCondition(SearchParameters.FilterCriteria.Condition.OR);
-        entityFilters.setCriterion(Collections.singletonList(criteria));
+            // 1. To Check if the BM property exists on a vertex where the 
direct type matches
+            Iterable<AtlasVertex> verticesDirect = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.ENTITY_TYPE_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        SearchParameters searchParameters = new SearchParameters();
+            if (verticesDirect != null && verticesDirect.iterator().hasNext()) 
{
+                return true;
+            }
 
-        
searchParameters.setTypeName(String.join(SearchContext.TYPENAME_DELIMITER, 
applicableTypes));
-        searchParameters.setExcludeDeletedEntities(true);
-        searchParameters.setIncludeSubClassifications(false);
-        searchParameters.setEntityFilters(entityFilters);
-        searchParameters.setAttributes(Collections.singleton(attrName));
-        searchParameters.setOffset(0);
-        searchParameters.setLimit(1);
+            // 2. To Check if the BM property exists on a vertex where it 
inherits from one of parent Types
+            // This is crucial for Case 6 (Parent -> Child)
+            Iterable<AtlasVertex> verticesInherited = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.SUPER_TYPES_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        AtlasSearchResult atlasSearchResult = 
entityDiscoveryService.searchWithParameters(searchParameters);
+            if (verticesInherited != null && 
verticesInherited.iterator().hasNext()) {
+                return true;
+            }
+        } catch (Exception e) {

Review Comment:
   **CRITICAL:** Catching all exceptions and returning true treats temporary 
system errors (network issues, DB timeouts) the same as actual reference 
blockers. Any temporary failure makes deletion fail with 
"`TYPE_HAS_REFERENCES`" error - even when no references exist.  Users can't 
tell if they need to clean up references or just retry. Throw an exception with 
a clear "system error" message instead of silently returning true.
   
   
   



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) {
+        if (graph == null || CollectionUtils.isEmpty(allApplicableTypes)) {

Review Comment:
   **CRITICAL**: Remove the graph null check at line 451. Returning false when 
graph is null means "safe to delete" without actually verifying references - 
this is dangerous. AtlasGraph is dependency-injected and cannot be null in 
production.



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -258,8 +266,14 @@ public AtlasBusinessMetadataDef updateByGuid(String guid, 
AtlasBusinessMetadataD
         return ret;
     }
 
+    @Override
     public AtlasVertex preDeleteByName(String name) throws AtlasBaseException {
-        LOG.debug("==> AtlasBusinessMetadataDefStoreV2.preDeleteByName({})", 
name);
+        return preDeleteByName(name, false);
+    }
+
+    @Override
+    public AtlasVertex preDeleteByName(String name, boolean forceDelete) 
throws AtlasBaseException {
+        LOG.debug("==> AtlasBusinessMetadataDefStoreV2.preDeleteByName({}, 
{})", name, forceDelete);
 
         AtlasBusinessMetadataDef existingDef = 
typeRegistry.getBusinessMetadataDefByName(name);
 

Review Comment:
   Add a warning log when force-delete is used; something like "Force-deleting 
BusinessMetadata '{}'. Skipping validation - orphaned references may remain.", 
name"



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -291,15 +319,35 @@ public AtlasVertex preDeleteByGuid(String guid) throws 
AtlasBaseException {
             throw new AtlasBaseException(AtlasErrorCode.TYPE_GUID_NOT_FOUND, 
guid);
         }
 
-        if (existingDef != null) {
+        if (existingDef != null && !forceDelete) {
+            boolean hasIndexableAttribute = hasIndexableAttribute(existingDef);
+
+            if (!hasIndexableAttribute) {

Review Comment:
   The hasIndexableAttribute check could be expensive if there are many 
attributes. 
      Consider caching this in the BusinessMetadataDef or TypeRegistry.



##########
intg/src/main/java/org/apache/atlas/AtlasErrorCode.java:
##########
@@ -257,7 +257,9 @@ public enum AtlasErrorCode {
     IMPORT_REGISTRATION_FAILED(500, "ATLAS-500-00-020", "Failed to register 
import request"),
     IMPORT_FAILED(500, "ATLAS-500-00-021", "Import with id {0} failed"),
     ABORT_IMPORT_FAILED(500, "ATLAS-500-00-022", "Failed to abort import with 
id {0}"),
-    IMPORT_QUEUEING_FAILED(500, "ATLAS-500-00-023", "Failed to add import with 
id {0} to request queue, please try again later");
+    IMPORT_QUEUEING_FAILED(500, "ATLAS-500-00-023", "Failed to add import with 
id {0} to request queue, please try again later"),
+
+    NON_INDEXABLE_BM_DELETE_NOT_ALLOWED(400, "ATLAS-400-00-106", "Deletion not 
allowed for non-indexable Business Metadata ''{0}'' without force-delete. 
Please use the force-delete parameter to remove.");

Review Comment:
   the message could be more user-friendly by explaining WHAT force-delete does.
   Suggested message:
   "Deletion not allowed for non-indexable Business Metadata ''{0}'' without 
force parameter.
   Non-indexable attributes cannot be efficiently validated for references. Use 
force=true to
   skip validation and delete (Warning: may leave orphaned references)."



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -271,15 +285,29 @@ public AtlasVertex preDeleteByName(String name) throws 
AtlasBaseException {
             throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_NOT_FOUND, 
name);
         }
 
-        checkBusinessMetadataRef(existingDef.getName());
+        if (!forceDelete) {
+            boolean hasIndexableAttribute = hasIndexableAttribute(existingDef);
 
-        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByName({}): 
{}", name, ret);
+            if (!hasIndexableAttribute) {
+                LOG.warn("Deletion blocked for non-indexable Business Metadata 
'{}' without force-delete flag", name);
+                throw new 
AtlasBaseException(AtlasErrorCode.NON_INDEXABLE_BM_DELETE_NOT_ALLOWED, name);
+            }
+            checkBusinessMetadataRef(existingDef.getName());
+        }
+
+        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByName({}, 
{}): {}", name, forceDelete, ret);
 
         return ret;
     }
 
+    @Override
     public AtlasVertex preDeleteByGuid(String guid) throws AtlasBaseException {
-        LOG.debug("==> AtlasBusinessMetadataDefStoreV2.preDeleteByGuid({})", 
guid);
+        return preDeleteByGuid(guid, false);
+    }
+
+    @Override
+    public AtlasVertex preDeleteByGuid(String guid, boolean forceDelete) 
throws AtlasBaseException {
+        LOG.debug("==> AtlasBusinessMetadataDefStoreV2.preDeleteByGuid({}, 
{})", guid, forceDelete);
 
         AtlasBusinessMetadataDef existingDef = 
typeRegistry.getBusinessMetadataDefByGuid(guid);
 

Review Comment:
   Add a warning log when force-delete is used; something like "Force-deleting 
BusinessMetadata '{}'. Skipping validation - orphaned references may remain.", 
name"



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasDefStore.java:
##########
@@ -45,9 +45,25 @@ public interface AtlasDefStore<T extends AtlasBaseTypeDef> {
 
     AtlasVertex preDeleteByName(String name) throws AtlasBaseException;
 
-    void deleteByName(String name, AtlasVertex preDeleteResult) throws 
AtlasBaseException;
-
     AtlasVertex preDeleteByGuid(String guid) throws AtlasBaseException;
 
+    void deleteByName(String name, AtlasVertex preDeleteResult) throws 
AtlasBaseException;
+
     void deleteByGuid(String guid, AtlasVertex preDeleteResult) throws 
AtlasBaseException;
+
+    default AtlasVertex preDeleteByName(String name, boolean forceDelete) 
throws AtlasBaseException {
+        return preDeleteByName(name);
+    }
+
+    default void deleteByName(String name, AtlasVertex preDeleteResult, 
boolean forceDelete) throws AtlasBaseException {
+        deleteByName(name, preDeleteResult);
+    }
+
+    default AtlasVertex preDeleteByGuid(String guid, boolean forceDelete) 
throws AtlasBaseException {
+        return preDeleteByGuid(guid);
+    }
+
+    default void deleteByGuid(String guid, AtlasVertex preDeleteResult, 
boolean forceDelete) throws AtlasBaseException {
+        deleteByGuid(guid, preDeleteResult);
+    }
 }

Review Comment:
   Good use of default methods for backward compatibility.
   
   SUGGESTION: Add debug logging when force-delete flag is ignored. 
Force-delete only applies to BusinessMetadata (fixes slow array deletion); the 
other 5 type stores (Enum, Struct, Classification, Entity, Relationship) don't 
need it, so these default methods just ignore the flag - which is fine.
   Adding a log like "Force-delete flag ignored for {typeName} - 
BusinessMetadata-only feature" clarifies the design and helps debug deletion 
issues without any breaking changes.



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -291,15 +319,35 @@ public AtlasVertex preDeleteByGuid(String guid) throws 
AtlasBaseException {
             throw new AtlasBaseException(AtlasErrorCode.TYPE_GUID_NOT_FOUND, 
guid);
         }
 
-        if (existingDef != null) {
+        if (existingDef != null && !forceDelete) {
+            boolean hasIndexableAttribute = hasIndexableAttribute(existingDef);
+
+            if (!hasIndexableAttribute) {
+                LOG.warn("Deletion blocked for non-indexable Business Metadata 
'{}' without force-delete flag", existingDef.getName());
+                throw new 
AtlasBaseException(AtlasErrorCode.NON_INDEXABLE_BM_DELETE_NOT_ALLOWED, 
existingDef.getName());
+            }
             checkBusinessMetadataRef(existingDef.getName());
         }
 
-        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByGuid({}): 
ret={}", guid, ret);
+        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByGuid({}, 
{}): ret={}", guid, forceDelete, ret);
 
         return ret;
     }
 
+    private boolean hasIndexableAttribute(AtlasBusinessMetadataDef bmDef) {
+        if (bmDef == null || 
CollectionUtils.isEmpty(bmDef.getAttributeDefs())) {
+            return false;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
bmDef.getAttributeDefs()) {
+            if (attributeDef.getIsIndexable()) {

Review Comment:
   ****CRITICAL**: Logic is inverted - checks for good attributes instead of 
problematic ones.**
   
   Example: BM with attr1 (String, indexable) + attr2 (Array, non-indexable)
   - Current: hasIndexableAttribute() = true → normal delete allowed → SLOW 
(still checks array)
   - Should be: **hasNonIndexableAttribute() = true → force-delete required**
   
   The check should block normal deletion if ANY attribute is non-indexable, 
not only if ALL attributes are non-indexable.



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) {
+        if (graph == null || CollectionUtils.isEmpty(allApplicableTypes)) {
+            return false;
+        }
 
-        SearchParameters.FilterCriteria entityFilters = new 
SearchParameters.FilterCriteria();
+        try {
+            List<String> typesList = new ArrayList<>(allApplicableTypes);
 
-        
entityFilters.setCondition(SearchParameters.FilterCriteria.Condition.OR);
-        entityFilters.setCriterion(Collections.singletonList(criteria));
+            // 1. To Check if the BM property exists on a vertex where the 
direct type matches
+            Iterable<AtlasVertex> verticesDirect = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.ENTITY_TYPE_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        SearchParameters searchParameters = new SearchParameters();
+            if (verticesDirect != null && verticesDirect.iterator().hasNext()) 
{
+                return true;
+            }
 
-        
searchParameters.setTypeName(String.join(SearchContext.TYPENAME_DELIMITER, 
applicableTypes));
-        searchParameters.setExcludeDeletedEntities(true);
-        searchParameters.setIncludeSubClassifications(false);
-        searchParameters.setEntityFilters(entityFilters);
-        searchParameters.setAttributes(Collections.singleton(attrName));
-        searchParameters.setOffset(0);
-        searchParameters.setLimit(1);
+            // 2. To Check if the BM property exists on a vertex where it 
inherits from one of parent Types
+            // This is crucial for Case 6 (Parent -> Child)
+            Iterable<AtlasVertex> verticesInherited = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.SUPER_TYPES_PROPERTY_KEY, typesList)
+                    .vertices();

Review Comment:
   Consider adding a limit (e.g., .limit(1)) since we only need to know if ANY 
exist



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());

Review Comment:
   Current problem: Error "TYPE_HAS_REFERENCES" doesn't say which attribute is 
blocking deletion or where to look.
   
   Suggestion: Add detailed logging before throwing the error for easier 
debugging. 
   ```
   LOG.error("Cannot delete BusinessMetadata '{}' - attribute '{}' (vertex 
property: '{}') 
   has references in entity types: {}", bmDef.getName(), 
attributeDef.getName(), 
   vertexPropertyName, allApplicableTypes);
   
   ```
   This way logs would show exactly which attribute, property name, and entity 
types have references. 
   Using this information, user can run a targeted query and find the exact 
references



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) {
+        if (graph == null || CollectionUtils.isEmpty(allApplicableTypes)) {
+            return false;
+        }
 
-        SearchParameters.FilterCriteria entityFilters = new 
SearchParameters.FilterCriteria();
+        try {
+            List<String> typesList = new ArrayList<>(allApplicableTypes);
 
-        
entityFilters.setCondition(SearchParameters.FilterCriteria.Condition.OR);
-        entityFilters.setCriterion(Collections.singletonList(criteria));
+            // 1. To Check if the BM property exists on a vertex where the 
direct type matches
+            Iterable<AtlasVertex> verticesDirect = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.ENTITY_TYPE_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        SearchParameters searchParameters = new SearchParameters();
+            if (verticesDirect != null && verticesDirect.iterator().hasNext()) 
{
+                return true;
+            }
 
-        
searchParameters.setTypeName(String.join(SearchContext.TYPENAME_DELIMITER, 
applicableTypes));
-        searchParameters.setExcludeDeletedEntities(true);
-        searchParameters.setIncludeSubClassifications(false);
-        searchParameters.setEntityFilters(entityFilters);
-        searchParameters.setAttributes(Collections.singleton(attrName));
-        searchParameters.setOffset(0);
-        searchParameters.setLimit(1);
+            // 2. To Check if the BM property exists on a vertex where it 
inherits from one of parent Types
+            // This is crucial for Case 6 (Parent -> Child)
+            Iterable<AtlasVertex> verticesInherited = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.SUPER_TYPES_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        AtlasSearchResult atlasSearchResult = 
entityDiscoveryService.searchWithParameters(searchParameters);
+            if (verticesInherited != null && 
verticesInherited.iterator().hasNext()) {
+                return true;
+            }
+        } catch (Exception e) {
+            LOG.error("Error occurred while querying graph for references of 
property: {}", vertexPropertyName, e);
+            return true;
+        }
+        return false;
+    }
+
+    private Set<String> getApplicableTypesWithSubTypes(Set<String> 
applicableTypes) throws AtlasBaseException {

Review Comment:
   Suggestions:
   1. Add JavaDoc explaining WHY sub-types are needed as this is an important 
business logic -  without JavaDoc, developers won't understand the inheritance 
scenario.
   
   2. Consider caching the result in TypeRegistry to avoid repeated calculations
   



##########
webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java:
##########


Review Comment:
   Missing test coverage: Test the actual use case - non-indexable BM deletion



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);

Review Comment:
   `getApplicableTypesWithSubTypes() `gets called multiple times for the same 
BM definition.
   
   Optimization: 
   Calculate `allApplicableTypes` once at the BM level and pass it down:
   - Move `getApplicableTypesWithSubTypes` call to `checkBusinessMetadataRef`
   - Pass the expanded types to `validateAttributeReferences`
   



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);
+        }
+
+        if (isPresent) {
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) {
+        if (graph == null || CollectionUtils.isEmpty(allApplicableTypes)) {
+            return false;
+        }
 
-        SearchParameters.FilterCriteria entityFilters = new 
SearchParameters.FilterCriteria();
+        try {
+            List<String> typesList = new ArrayList<>(allApplicableTypes);
 
-        
entityFilters.setCondition(SearchParameters.FilterCriteria.Condition.OR);
-        entityFilters.setCriterion(Collections.singletonList(criteria));
+            // 1. To Check if the BM property exists on a vertex where the 
direct type matches
+            Iterable<AtlasVertex> verticesDirect = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.ENTITY_TYPE_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        SearchParameters searchParameters = new SearchParameters();
+            if (verticesDirect != null && verticesDirect.iterator().hasNext()) 
{
+                return true;
+            }
 
-        
searchParameters.setTypeName(String.join(SearchContext.TYPENAME_DELIMITER, 
applicableTypes));
-        searchParameters.setExcludeDeletedEntities(true);
-        searchParameters.setIncludeSubClassifications(false);
-        searchParameters.setEntityFilters(entityFilters);
-        searchParameters.setAttributes(Collections.singleton(attrName));
-        searchParameters.setOffset(0);
-        searchParameters.setLimit(1);
+            // 2. To Check if the BM property exists on a vertex where it 
inherits from one of parent Types
+            // This is crucial for Case 6 (Parent -> Child)
+            Iterable<AtlasVertex> verticesInherited = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null)
+                    .in(Constants.SUPER_TYPES_PROPERTY_KEY, typesList)
+                    .vertices();
 
-        AtlasSearchResult atlasSearchResult = 
entityDiscoveryService.searchWithParameters(searchParameters);
+            if (verticesInherited != null && 
verticesInherited.iterator().hasNext()) {
+                return true;
+            }
+        } catch (Exception e) {
+            LOG.error("Error occurred while querying graph for references of 
property: {}", vertexPropertyName, e);
+            return true;
+        }
+        return false;
+    }

Review Comment:
    The two queries (for direct and inherited type match) could be combined 
into one to make it more efficient



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java:
##########
@@ -539,14 +539,20 @@ public AtlasTypesDef createUpdateTypesDef(AtlasTypesDef 
typesToCreate, AtlasType
     @Override
     @GraphTransaction
     public void deleteTypesDef(AtlasTypesDef typesDef) throws 
AtlasBaseException {
+        deleteTypesDef(typesDef, false);
+    }
+
+    @GraphTransaction
+    public void deleteTypesDef(AtlasTypesDef typesDef, boolean forceDelete) 
throws AtlasBaseException {
         if (LOG.isDebugEnabled()) {

Review Comment:
   LOG.isDebugEnabled() check can be removed



##########
repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java:
##########
@@ -363,44 +411,91 @@ private AtlasBusinessMetadataDef 
toBusinessMetadataDef(AtlasVertex vertex) throw
     private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
         AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            validateAttributeReferences(businessMetadataDef, attributeDef);
+        }
+    }
 
-            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
attributeDefs) {
-                String      qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(businessMetadataDef, 
attributeDef.getName());
-                String      vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(businessMetadataDef, 
attributeDef, qualifiedName);
-                Set<String> applicableTypes    = 
AtlasJson.fromJson(attributeDef.getOption(AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES),
 Set.class);
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef) throws AtlasBaseException {
+        String applicableTypesStr = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypes = StringUtils.isBlank(applicableTypesStr) 
? null : AtlasJson.fromJson(applicableTypesStr, Set.class);
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
-            }
+        if (CollectionUtils.isEmpty(applicableTypes)) {
+            return;
+        }
+
+        Set<String> allApplicableTypes = 
getApplicableTypesWithSubTypes(applicableTypes);
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+
+        long startTime = System.currentTimeMillis();
+
+        boolean isPresent = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.info("Reference check for attribute {} took {} ms. Found: {}",
+                    attributeDef.getName(), (System.currentTimeMillis() - 
startTime), isPresent);

Review Comment:
   Code/logging related to time capture can be removed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to