This is an automated email from the ASF dual-hosted git repository.

pinal-shah 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 ae973e76d ATLAS-4988: BusinessMetadata with attribute of data type 
Array takes time to Delete. (#490)
ae973e76d is described below

commit ae973e76db131c86417e91c9b43102c0677fe436
Author: UmeshPatil-1 <[email protected]>
AuthorDate: Fri May 8 16:39:41 2026 +0530

    ATLAS-4988: BusinessMetadata with attribute of data type Array takes time 
to Delete. (#490)
---
 .../main/java/org/apache/atlas/AtlasClientV2.java  |  26 ++-
 .../java/org/apache/atlas/AtlasClientV2Test.java   |  22 +++
 .../main/java/org/apache/atlas/AtlasErrorCode.java |   1 +
 .../atlas/type/AtlasBusinessMetadataType.java      |  43 +++++
 .../test/java/org/apache/atlas/TestUtilsV2.java    |  15 ++
 .../repository/store/graph/AtlasDefStore.java      |  20 ++-
 .../store/graph/AtlasTypeDefGraphStore.java        |  30 ++--
 .../store/graph/v2/AtlasAbstractDefStoreV2.java    |  24 ++-
 .../graph/v2/AtlasBusinessMetadataDefStoreV2.java  | 186 ++++++++++++++++-----
 .../store/graph/v2/AtlasTypeDefGraphStoreV2.java   |  18 +-
 .../org/apache/atlas/store/AtlasTypeDefStore.java  |   4 +-
 .../store/graph/AtlasTypeDefGraphStoreTest.java    |   8 +-
 .../v2/AtlasBusinessMetadataDefStoreV2Test.java    |  79 +++++++++
 .../java/org/apache/atlas/web/rest/TypesREST.java  |  41 +++--
 .../atlas/web/adapters/TypeDefsRESTTest.java       |  20 +--
 .../org/apache/atlas/web/rest/TypesRESTTest.java   |  71 ++++++--
 16 files changed, 486 insertions(+), 122 deletions(-)

diff --git a/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java 
b/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
index 31687acda..04e4376d6 100644
--- a/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
+++ b/client/client-v2/src/main/java/org/apache/atlas/AtlasClientV2.java
@@ -349,11 +349,33 @@ public class AtlasClientV2 extends AtlasBaseClient {
      * @param typesDef A composite object that captures all types to be deleted
      */
     public void deleteAtlasTypeDefs(AtlasTypesDef typesDef) throws 
AtlasServiceException {
-        callAPI(API_V2.DELETE_TYPE_DEFS, (Class<?>) null, 
AtlasType.toJson(typesDef));
+        deleteAtlasTypeDefs(typesDef, false);
+    }
+
+    public void deleteAtlasTypeDefs(AtlasTypesDef typesDef, boolean 
forceDelete) throws AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = null;
+
+        if (forceDelete) {
+            queryParams = new MultivaluedMapImpl();
+            queryParams.add("force", "true");
+        }
+
+        callAPI(API_V2.DELETE_TYPE_DEFS, (Class<?>) null, 
AtlasType.toJson(typesDef), queryParams);
     }
 
     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);
+    }
+
+    public void deleteTypeByName(String typeName, boolean forceDelete) throws 
AtlasServiceException {
+        MultivaluedMap<String, String> queryParams = null;
+
+        if (forceDelete) {
+            queryParams = new MultivaluedMapImpl();
+            queryParams.add("force", "true");
+        }
+
+        callAPI(API_V2.DELETE_TYPE_DEF_BY_NAME, (Class<?>) null, queryParams, 
typeName);
     }
 
     // Entity APIs
diff --git 
a/client/client-v2/src/test/java/org/apache/atlas/AtlasClientV2Test.java 
b/client/client-v2/src/test/java/org/apache/atlas/AtlasClientV2Test.java
index be180b56a..f4942d7f5 100644
--- a/client/client-v2/src/test/java/org/apache/atlas/AtlasClientV2Test.java
+++ b/client/client-v2/src/test/java/org/apache/atlas/AtlasClientV2Test.java
@@ -82,6 +82,7 @@ import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -808,6 +809,7 @@ public class AtlasClientV2Test {
         private Object mockResponse;
         private Class<?> expectedReturnType;
         private boolean shouldThrowException;
+        private javax.ws.rs.core.MultivaluedMap<String, String> 
lastQueryParams;
 
         public TestableAtlasClientV2() {
             super(mock(WebResource.class), mock(Configuration.class));
@@ -823,8 +825,13 @@ public class AtlasClientV2Test {
             this.shouldThrowException = shouldThrow;
         }
 
+        public javax.ws.rs.core.MultivaluedMap<String, String> 
getLastQueryParams() {
+            return lastQueryParams;
+        }
+
         @Override
         public <T> T callAPI(API api, Class<T> responseType, Object 
requestObject, String... params) throws AtlasServiceException {
+            lastQueryParams = null;
             return handleCallAPI(responseType);
         }
 
@@ -838,11 +845,13 @@ public class AtlasClientV2Test {
 
         @Override
         public <T> T callAPI(API api, Class<T> responseType, 
javax.ws.rs.core.MultivaluedMap<String, String> queryParams, String... params) 
throws AtlasServiceException {
+            lastQueryParams = queryParams;
             return handleCallAPI(responseType);
         }
 
         @Override
         public <T> T callAPI(API api, Class<T> responseType, Object 
requestObject, javax.ws.rs.core.MultivaluedMap<String, String> queryParams, 
String... params) throws AtlasServiceException {
+            lastQueryParams = queryParams;
             return handleCallAPI(responseType);
         }
 
@@ -1053,6 +1062,19 @@ public class AtlasClientV2Test {
         // Should not throw exception
         client.deleteAtlasTypeDefs(input);
         assertTrue(true);
+        assertNull(client.getLastQueryParams());
+    }
+
+    @Test
+    public void testDeleteAtlasTypeDefsRealExecutionWithForceDelete() throws 
Exception {
+        TestableAtlasClientV2 client = new TestableAtlasClientV2();
+        client.setMockResponse(null, Object.class);
+
+        AtlasTypesDef input = new AtlasTypesDef();
+        client.deleteAtlasTypeDefs(input, true);
+
+        assertNotNull(client.getLastQueryParams());
+        assertEquals(client.getLastQueryParams().getFirst("force"), "true");
     }
 
     @Test
diff --git a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java 
b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
index ca7220243..a5aa58a1b 100644
--- a/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
+++ b/intg/src/main/java/org/apache/atlas/AtlasErrorCode.java
@@ -183,6 +183,7 @@ public enum AtlasErrorCode {
     BLANK_NAME_ATTRIBUTE(400, "ATLAS-400-00-104", "Name Attribute can't be 
empty!"),
     BLANK_VALUE_ATTRIBUTE(400, "ATLAS-400-00-105", "Value Attribute can't be 
empty!"),
     INVALID_RELATIONSHIP_LABEL(400, "ATLAS-400-00-106", "Invalid relationship 
label {0}. The referenced entity type {1} could not be resolved from the type 
registry."),
+    NON_INDEXABLE_BM_DELETE_NOT_ALLOWED(400, "ATLAS-400-00-107", "Deletion not 
allowed for non-indexable Business Metadata ''{0}'' without force=true. 
Non-indexable attributes cannot be validated efficiently for references; use 
force=true to skip validation and delete (warning: orphaned references may 
remain)."),
 
     UNAUTHORIZED_ACCESS(403, "ATLAS-403-00-001", "{0} is not authorized to 
perform {1}"),
 
diff --git 
a/intg/src/main/java/org/apache/atlas/type/AtlasBusinessMetadataType.java 
b/intg/src/main/java/org/apache/atlas/type/AtlasBusinessMetadataType.java
index ccea47c72..ed29bdd7a 100644
--- a/intg/src/main/java/org/apache/atlas/type/AtlasBusinessMetadataType.java
+++ b/intg/src/main/java/org/apache/atlas/type/AtlasBusinessMetadataType.java
@@ -44,12 +44,18 @@ public class AtlasBusinessMetadataType extends 
AtlasStructType {
 
     private final AtlasBusinessMetadataDef businessMetadataDef;
 
+    private boolean hasNonIndexableAttributes;
+
     public AtlasBusinessMetadataType(AtlasBusinessMetadataDef 
businessMetadataDef) {
         super(businessMetadataDef);
 
         this.businessMetadataDef = businessMetadataDef;
     }
 
+    public boolean hasNonIndexableAttributes() {
+        return hasNonIndexableAttributes;
+    }
+
     @Override
     public AtlasStruct createDefaultValue() {
         return null;  // there is no runtime instance for businessMetadataDef, 
so return null
@@ -71,6 +77,8 @@ public class AtlasBusinessMetadataType extends 
AtlasStructType {
 
         Map<String, AtlasBusinessAttribute> a = new HashMap<>();
 
+        hasNonIndexableAttributes = false;
+
         for (AtlasAttribute attribute : super.allAttributes.values()) {
             AtlasAttributeDef attributeDef = attribute.getAttributeDef();
             String            attrName     = attribute.getName();
@@ -115,10 +123,16 @@ public class AtlasBusinessMetadataType extends 
AtlasStructType {
                 bmAttribute = new AtlasBusinessAttribute(attribute, 
entityTypes);
             }
 
+            if (!hasNonIndexableAttributes && !attributeDef.getIsIndexable()) {
+                hasNonIndexableAttributes = true;
+            }
+
             a.put(attrName, bmAttribute);
         }
 
         super.allAttributes = Collections.unmodifiableMap(a);
+        LOG.debug("CACHE-POPULATE [Phase1] BM='{}' 
hasNonIndexableAttributes={} totalAttributes={}",
+                getTypeName(), hasNonIndexableAttributes, a.size());
     }
 
     @Override
@@ -137,6 +151,29 @@ public class AtlasBusinessMetadataType extends 
AtlasStructType {
         }
     }
 
+    @Override
+    void resolveReferencesPhase3(AtlasTypeRegistry typeRegistry) throws 
AtlasBaseException {
+        super.resolveReferencesPhase3(typeRegistry);
+
+        for (AtlasAttribute attribute : super.allAttributes.values()) {
+            AtlasBusinessAttribute bmAttribute = (AtlasBusinessAttribute) 
attribute;
+            Set<AtlasEntityType>   entityTypes = 
bmAttribute.getApplicableEntityTypes();
+
+            if (CollectionUtils.isNotEmpty(entityTypes)) {
+                Set<String> expandedTypeNames = new HashSet<>();
+
+                for (AtlasEntityType entityType : entityTypes) {
+                    expandedTypeNames.add(entityType.getTypeName());
+                    expandedTypeNames.addAll(entityType.getAllSubTypes());
+                }
+
+                bmAttribute.applicableEntityTypeNamesWithSubTypes = 
Collections.unmodifiableSet(expandedTypeNames);
+                LOG.debug("CACHE-POPULATE [Phase3] BM='{}' attr='{}' 
expandedTypes={}",
+                        getTypeName(), bmAttribute.getName(), 
expandedTypeNames);
+            }
+        }
+    }
+
     public AtlasBusinessMetadataDef getBusinessMetadataDef() {
         return businessMetadataDef;
     }
@@ -146,6 +183,8 @@ public class AtlasBusinessMetadataType extends 
AtlasStructType {
         private final int                  maxStringLength;
         private final String               validPattern;
 
+        private Set<String> applicableEntityTypeNamesWithSubTypes = 
Collections.emptySet();
+
         public AtlasBusinessAttribute(AtlasAttribute attribute, 
Set<AtlasEntityType> applicableEntityTypes) {
             super(attribute);
 
@@ -171,6 +210,10 @@ public class AtlasBusinessMetadataType extends 
AtlasStructType {
             return applicableEntityTypes;
         }
 
+        public Set<String> getApplicableEntityTypeNamesWithSubTypes() {
+            return applicableEntityTypeNamesWithSubTypes;
+        }
+
         public String getValidPattern() {
             return validPattern;
         }
diff --git a/intg/src/test/java/org/apache/atlas/TestUtilsV2.java 
b/intg/src/test/java/org/apache/atlas/TestUtilsV2.java
index 27ad72bf8..27fb27d1c 100755
--- a/intg/src/test/java/org/apache/atlas/TestUtilsV2.java
+++ b/intg/src/test/java/org/apache/atlas/TestUtilsV2.java
@@ -625,6 +625,11 @@ public final class TestUtilsV2 {
                 createOptionalAttrDef("attrSuperBoolean", 
AtlasBusinessMetadataDef.ATLAS_TYPE_BOOLEAN, options, description),
                 createOptionalAttrDef("attrSuperString", 
AtlasBusinessMetadataDef.ATLAS_TYPE_STRING, options, description));
 
+        setBusinessMetadataAttributesIndexable(bmNoApplicableTypes);
+        setBusinessMetadataAttributesIndexable(bmWithAllTypes);
+        setBusinessMetadataAttributesIndexable(bmWithAllTypesMV);
+        setBusinessMetadataAttributesIndexable(bmWithSuperType);
+
         AtlasTypesDef ret = AtlasTypeUtil.getTypesDef(new ArrayList<>(),
                 new ArrayList<>(), new ArrayList<>(),
                 new ArrayList<>(), new ArrayList<>(),
@@ -635,6 +640,16 @@ public final class TestUtilsV2 {
         return ret;
     }
 
+    private static void 
setBusinessMetadataAttributesIndexable(AtlasBusinessMetadataDef def) {
+        if (def == null || CollectionUtils.isEmpty(def.getAttributeDefs())) {
+            return;
+        }
+
+        for (AtlasAttributeDef ad : def.getAttributeDefs()) {
+            ad.setIsIndexable(true);
+        }
+    }
+
     public static AtlasTypesDef defineHiveTypes() {
         String description = "_description";
         AtlasEntityDef superTypeDefinition =
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasDefStore.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasDefStore.java
index a956eecb4..59828fd09 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasDefStore.java
+++ 
b/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);
+    }
 }
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java
index 4776912cd..06a5bcee9 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java
@@ -539,15 +539,19 @@ public abstract class AtlasTypeDefGraphStore implements 
AtlasTypeDefStore {
     @Override
     @GraphTransaction
     public void deleteTypesDef(AtlasTypesDef typesDef) throws 
AtlasBaseException {
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("==> AtlasTypeDefGraphStore.deleteTypesDef(enums={}, 
structs={}, classfications={}, entities={}, relationships={}, 
businessMetadataDefs={})",
-                    CollectionUtils.size(typesDef.getEnumDefs()),
-                    CollectionUtils.size(typesDef.getStructDefs()),
-                    CollectionUtils.size(typesDef.getClassificationDefs()),
-                    CollectionUtils.size(typesDef.getEntityDefs()),
-                    CollectionUtils.size(typesDef.getRelationshipDefs()),
-                    CollectionUtils.size(typesDef.getBusinessMetadataDefs()));
-        }
+        deleteTypesDef(typesDef, false);
+    }
+
+    @GraphTransaction
+    public void deleteTypesDef(AtlasTypesDef typesDef, boolean forceDelete) 
throws AtlasBaseException {
+        LOG.debug("==> AtlasTypeDefGraphStore.deleteTypesDef(enums={}, 
structs={}, classfications={}, entities={}, relationships={}, 
businessMetadataDefs={}, forceDelete={})",
+                CollectionUtils.size(typesDef.getEnumDefs()),
+                CollectionUtils.size(typesDef.getStructDefs()),
+                CollectionUtils.size(typesDef.getClassificationDefs()),
+                CollectionUtils.size(typesDef.getEntityDefs()),
+                CollectionUtils.size(typesDef.getRelationshipDefs()),
+                CollectionUtils.size(typesDef.getBusinessMetadataDefs()),
+                forceDelete);
 
         AtlasTransientTypeRegistry ttr = 
lockTypeRegistryAndReleasePostCommit();
 
@@ -678,9 +682,9 @@ public abstract class AtlasTypeDefGraphStore implements 
AtlasTypeDefStore {
         if (CollectionUtils.isNotEmpty(typesDef.getBusinessMetadataDefs())) {
             for (AtlasBusinessMetadataDef businessMetadataDef : 
typesDef.getBusinessMetadataDefs()) {
                 if (StringUtils.isNotBlank(businessMetadataDef.getGuid())) {
-                    
businessMetadataDefStore.deleteByGuid(businessMetadataDef.getGuid(), null);
+                    
businessMetadataDefStore.deleteByGuid(businessMetadataDef.getGuid(), null, 
forceDelete);
                 } else {
-                    
businessMetadataDefStore.deleteByName(businessMetadataDef.getName(), null);
+                    
businessMetadataDefStore.deleteByName(businessMetadataDef.getName(), null, 
forceDelete);
                 }
             }
         }
@@ -777,7 +781,7 @@ public abstract class AtlasTypeDefGraphStore implements 
AtlasTypeDefStore {
 
     @Override
     @GraphTransaction
-    public void deleteTypeByName(String typeName) throws AtlasBaseException {
+    public void deleteTypeByName(String typeName, boolean forceDelete) throws 
AtlasBaseException {
         AtlasType atlasType = typeRegistry.getType(typeName);
 
         if (atlasType == null) {
@@ -801,7 +805,7 @@ public abstract class AtlasTypeDefGraphStore implements 
AtlasTypeDefStore {
             typesDef.setStructDefs(Collections.singletonList((AtlasStructDef) 
baseTypeDef));
         }
 
-        deleteTypesDef(typesDef);
+        deleteTypesDef(typesDef, forceDelete);
     }
 
     @Override
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasAbstractDefStoreV2.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasAbstractDefStoreV2.java
index 609275c4b..49bbb5096 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasAbstractDefStoreV2.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasAbstractDefStoreV2.java
@@ -148,24 +148,34 @@ abstract class AtlasAbstractDefStoreV2<T extends 
AtlasBaseTypeDef> implements At
 
     @Override
     public void deleteByName(String name, AtlasVertex preDeleteResult) throws 
AtlasBaseException {
-        LOG.debug("==> AtlasAbstractDefStoreV1.deleteByName({}, {})", name, 
preDeleteResult);
+        deleteByName(name, preDeleteResult, false);
+    }
+
+    @Override
+    public void deleteByGuid(String guid, AtlasVertex preDeleteResult) throws 
AtlasBaseException {
+        deleteByGuid(guid, preDeleteResult, false);
+    }
+
+    @Override
+    public void deleteByName(String name, AtlasVertex preDeleteResult, boolean 
forceDelete) throws AtlasBaseException {
+        LOG.debug("==> AtlasAbstractDefStoreV2.deleteByName({}, {}, {})", 
name, preDeleteResult, forceDelete);
 
-        AtlasVertex vertex = (preDeleteResult == null) ? preDeleteByName(name) 
: preDeleteResult;
+        AtlasVertex vertex = (preDeleteResult == null) ? preDeleteByName(name, 
forceDelete) : preDeleteResult;
 
         typeDefStore.deleteTypeVertex(vertex);
 
-        LOG.debug("<== AtlasAbstractDefStoreV1.deleteByName({}, {})", name, 
preDeleteResult);
+        LOG.debug("<== AtlasAbstractDefStoreV2.deleteByName({}, {}, {})", 
name, preDeleteResult, forceDelete);
     }
 
     @Override
-    public void deleteByGuid(String guid, AtlasVertex preDeleteResult) throws 
AtlasBaseException {
-        LOG.debug("==> AtlasAbstractDefStoreV1.deleteByGuid({}, {})", guid, 
preDeleteResult);
+    public void deleteByGuid(String guid, AtlasVertex preDeleteResult, boolean 
forceDelete) throws AtlasBaseException {
+        LOG.debug("==> AtlasAbstractDefStoreV2.deleteByGuid({}, {}, {})", 
guid, preDeleteResult, forceDelete);
 
-        AtlasVertex vertex = (preDeleteResult == null) ? preDeleteByGuid(guid) 
: preDeleteResult;
+        AtlasVertex vertex = (preDeleteResult == null) ? preDeleteByGuid(guid, 
forceDelete) : preDeleteResult;
 
         typeDefStore.deleteTypeVertex(vertex);
 
-        LOG.debug("<== AtlasAbstractDefStoreV1.deleteByGuid({}, {})", guid, 
preDeleteResult);
+        LOG.debug("<== AtlasAbstractDefStoreV2.deleteByGuid({}, {}, {})", 
guid, preDeleteResult, forceDelete);
     }
 
     public boolean isInvalidTypeDefName(String typeName) {
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java
index 3543c2322..c53c3a78e 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2.java
@@ -21,23 +21,21 @@ import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.authorize.AtlasAuthorizationUtils;
 import org.apache.atlas.authorize.AtlasPrivilege;
 import org.apache.atlas.authorize.AtlasTypeAccessRequest;
-import org.apache.atlas.discovery.EntityDiscoveryService;
-import org.apache.atlas.discovery.SearchContext;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.model.TypeCategory;
-import org.apache.atlas.model.discovery.AtlasSearchResult;
-import org.apache.atlas.model.discovery.SearchParameters;
 import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
 import org.apache.atlas.model.typedef.AtlasBusinessMetadataDef;
 import org.apache.atlas.model.typedef.AtlasStructDef;
 import org.apache.atlas.repository.Constants;
+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.AtlasBusinessMetadataType;
+import org.apache.atlas.type.AtlasEntityType;
 import org.apache.atlas.type.AtlasStructType;
 import org.apache.atlas.type.AtlasType;
 import org.apache.atlas.type.AtlasTypeRegistry;
 import org.apache.atlas.typesystem.types.DataTypes;
-import org.apache.atlas.utils.AtlasJson;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -47,6 +45,7 @@ import javax.inject.Inject;
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -56,13 +55,13 @@ import static 
org.apache.atlas.model.typedef.AtlasBusinessMetadataDef.ATTR_OPTIO
 public class AtlasBusinessMetadataDefStoreV2 extends 
AtlasAbstractDefStoreV2<AtlasBusinessMetadataDef> {
     private static final Logger LOG = 
LoggerFactory.getLogger(AtlasBusinessMetadataDefStoreV2.class);
 
-    private final EntityDiscoveryService entityDiscoveryService;
+    private final AtlasGraph graph;
 
     @Inject
-    public AtlasBusinessMetadataDefStoreV2(AtlasTypeDefGraphStoreV2 
typeDefStore, AtlasTypeRegistry typeRegistry, EntityDiscoveryService 
entityDiscoveryService) {
+    public AtlasBusinessMetadataDefStoreV2(AtlasTypeDefGraphStoreV2 
typeDefStore, AtlasTypeRegistry typeRegistry, AtlasGraph graph) {
         super(typeDefStore, typeRegistry);
 
-        this.entityDiscoveryService = entityDiscoveryService;
+        this.graph = graph;
     }
 
     @Override
@@ -258,8 +257,14 @@ public class AtlasBusinessMetadataDefStoreV2 extends 
AtlasAbstractDefStoreV2<Atl
         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);
 
@@ -271,15 +276,21 @@ public class AtlasBusinessMetadataDefStoreV2 extends 
AtlasAbstractDefStoreV2<Atl
             throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_NOT_FOUND, 
name);
         }
 
-        checkBusinessMetadataRef(existingDef.getName());
+        validateDeletion(existingDef, forceDelete, name);
 
-        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByName({}): 
{}", name, ret);
+        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);
 
@@ -291,15 +302,49 @@ public class AtlasBusinessMetadataDefStoreV2 extends 
AtlasAbstractDefStoreV2<Atl
             throw new AtlasBaseException(AtlasErrorCode.TYPE_GUID_NOT_FOUND, 
guid);
         }
 
-        if (existingDef != null) {
-            checkBusinessMetadataRef(existingDef.getName());
-        }
+        validateDeletion(existingDef, forceDelete, guid);
 
-        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByGuid({}): 
ret={}", guid, ret);
+        LOG.debug("<== AtlasBusinessMetadataDefStoreV2.preDeleteByGuid({}, 
{}): ret={}", guid, forceDelete, ret);
 
         return ret;
     }
 
+    private void validateDeletion(AtlasBusinessMetadataDef 
businessMetadataDef, boolean forceDelete, String identifier) throws 
AtlasBaseException {
+        if (businessMetadataDef == null) {
+            return;
+        }
+
+        if (forceDelete) {
+            LOG.warn("Force-deleting BusinessMetadata '{}'. Skipping 
validation - orphaned references may remain.", businessMetadataDef.getName());
+            return;
+        }
+
+        if (hasNonIndexableAttribute(businessMetadataDef)) {
+            LOG.warn("Deletion blocked for non-indexable Business Metadata 
'{}' without force-delete flag", businessMetadataDef.getName());
+            throw new 
AtlasBaseException(AtlasErrorCode.NON_INDEXABLE_BM_DELETE_NOT_ALLOWED, 
businessMetadataDef.getName());
+        }
+
+        checkBusinessMetadataRef(businessMetadataDef, identifier);
+    }
+
+    private boolean hasNonIndexableAttribute(AtlasBusinessMetadataDef 
businessMetadataDef) {
+        AtlasBusinessMetadataType bmType = 
typeRegistry.getBusinessMetadataTypeByName(businessMetadataDef.getName());
+
+        if (bmType != null && bmType.hasNonIndexableAttributes()) {
+            return true;
+        }
+
+        if 
(CollectionUtils.isNotEmpty(businessMetadataDef.getAttributeDefs())) {
+            for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+                if (!attributeDef.getIsIndexable()) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
     @Override
     public void validateType(AtlasBaseTypeDef typeDef) throws 
AtlasBaseException {
         super.validateType(typeDef);
@@ -360,47 +405,100 @@ public class AtlasBusinessMetadataDefStoreV2 extends 
AtlasAbstractDefStoreV2<Atl
         return ret;
     }
 
-    private void checkBusinessMetadataRef(String typeName) throws 
AtlasBaseException {
-        AtlasBusinessMetadataDef businessMetadataDef = 
typeRegistry.getBusinessMetadataDefByName(typeName);
+    private void checkBusinessMetadataRef(AtlasBusinessMetadataDef 
businessMetadataDef, String identifier) throws AtlasBaseException {
+        if (businessMetadataDef == null || 
CollectionUtils.isEmpty(businessMetadataDef.getAttributeDefs())) {
+            return;
+        }
 
-        if (businessMetadataDef != null) {
-            List<AtlasStructDef.AtlasAttributeDef> attributeDefs = 
businessMetadataDef.getAttributeDefs();
+        AtlasBusinessMetadataType bmType = 
typeRegistry.getBusinessMetadataTypeByName(businessMetadataDef.getName());
 
-            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);
+        for (AtlasStructDef.AtlasAttributeDef attributeDef : 
businessMetadataDef.getAttributeDefs()) {
+            Set<String> allApplicableTypes;
 
-                if (CollectionUtils.isNotEmpty(applicableTypes) && 
isBusinessAttributePresent(vertexPropertyName, applicableTypes)) {
-                    throw new 
AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, typeName);
-                }
+            if (bmType != null) {
+                AtlasBusinessMetadataType.AtlasBusinessAttribute bmAttr =
+                        (AtlasBusinessMetadataType.AtlasBusinessAttribute) 
bmType.getAttribute(attributeDef.getName());
+                Set<String> cachedTypes = bmAttr != null ? 
bmAttr.getApplicableEntityTypeNamesWithSubTypes() : Collections.emptySet();
+
+                allApplicableTypes = CollectionUtils.isNotEmpty(cachedTypes) ? 
cachedTypes : getApplicableTypesWithSubTypes(attributeDef);
+            } else {
+                allApplicableTypes = 
getApplicableTypesWithSubTypes(attributeDef);
             }
+
+            LOG.debug("REF-CHECK [ApplicableTypes] BM='{}' attr='{}' types={}",
+                    businessMetadataDef.getName(), attributeDef.getName(), 
allApplicableTypes);
+
+            if (CollectionUtils.isEmpty(allApplicableTypes)) {
+                continue;
+            }
+
+            validateAttributeReferences(businessMetadataDef, attributeDef, 
allApplicableTypes, identifier);
         }
     }
 
-    private boolean isBusinessAttributePresent(String attrName, Set<String> 
applicableTypes) throws AtlasBaseException {
-        SearchParameters.FilterCriteria criteria = new 
SearchParameters.FilterCriteria();
+    private void validateAttributeReferences(AtlasBusinessMetadataDef bmDef, 
AtlasStructDef.AtlasAttributeDef attributeDef,
+                                             Set<String> allApplicableTypes, 
String identifier) throws AtlasBaseException {
+        String qualifiedName      = 
AtlasStructType.AtlasAttribute.getQualifiedAttributeName(bmDef, 
attributeDef.getName());
+        String vertexPropertyName = 
AtlasStructType.AtlasAttribute.generateVertexPropertyName(bmDef, attributeDef, 
qualifiedName);
+        boolean isPresent         = 
isBusinessAttributePresentInGraph(vertexPropertyName, allApplicableTypes);
 
-        criteria.setAttributeName(attrName);
-        criteria.setOperator(SearchParameters.Operator.NOT_EMPTY);
+        if (isPresent) {
+            LOG.warn("Cannot delete BusinessMetadata '{}' (request='{}') - 
attribute '{}' (vertex property: '{}') has references in entity types: {}",
+                    bmDef.getName(), identifier, attributeDef.getName(), 
vertexPropertyName, allApplicableTypes);
+            throw new AtlasBaseException(AtlasErrorCode.TYPE_HAS_REFERENCES, 
bmDef.getName());
+        }
+    }
 
-        SearchParameters.FilterCriteria entityFilters = new 
SearchParameters.FilterCriteria();
+    private boolean isBusinessAttributePresentInGraph(String 
vertexPropertyName, Set<String> allApplicableTypes) throws AtlasBaseException {
+        if (CollectionUtils.isEmpty(allApplicableTypes)) {
+            return false;
+        }
 
-        
entityFilters.setCondition(SearchParameters.FilterCriteria.Condition.OR);
-        entityFilters.setCriterion(Collections.singletonList(criteria));
+        try {
+            List<String> typesList = new ArrayList<>(allApplicableTypes);
 
-        SearchParameters searchParameters = new SearchParameters();
+            AtlasGraphQuery query = graph.query()
+                    .has(vertexPropertyName, 
AtlasGraphQuery.ComparisionOperator.NOT_EQUAL, (Object) null);
 
-        
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);
+            List<AtlasGraphQuery> orConditions = new ArrayList<>();
+            
orConditions.add(query.createChildQuery().in(Constants.ENTITY_TYPE_PROPERTY_KEY,
 typesList));
+            
orConditions.add(query.createChildQuery().in(Constants.SUPER_TYPES_PROPERTY_KEY,
 typesList));
 
-        AtlasSearchResult atlasSearchResult = 
entityDiscoveryService.searchWithParameters(searchParameters);
+            query.or(orConditions);
 
-        return CollectionUtils.isNotEmpty(atlasSearchResult.getEntities());
+            Iterable<AtlasVertex> vertices = query.vertices(1);
+
+            if (vertices != null && vertices.iterator().hasNext()) {
+                return true;
+            }
+        } catch (Exception e) {
+            throw new AtlasBaseException(AtlasErrorCode.INTERNAL_ERROR,
+                    e, String.format("failed to validate BusinessMetadata 
references for property %s", vertexPropertyName));
+        }
+        return false;
+    }
+
+    /**
+     * Expands configured applicable entity types to include all concrete 
sub-types.
+     * Without this expansion, deletion checks can miss references present 
only on inherited child entity types.
+     */
+    private Set<String> 
getApplicableTypesWithSubTypes(AtlasStructDef.AtlasAttributeDef attributeDef) {
+        String      applicableTypesJson = 
attributeDef.getOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
+        Set<String> applicableTypeNames = 
StringUtils.isBlank(applicableTypesJson) ? null : 
AtlasType.fromJson(applicableTypesJson, Set.class);
+
+        if (CollectionUtils.isEmpty(applicableTypeNames)) {
+            return Collections.emptySet();
+        }
+
+        Set<String> result = new HashSet<>(applicableTypeNames);
+
+        for (String typeName : applicableTypeNames) {
+            AtlasEntityType entityType = 
typeRegistry.getEntityTypeByName(typeName);
+
+            if (entityType != null) {
+                result.addAll(entityType.getAllSubTypes());
+            }
+        }
+        return result;
     }
 }
diff --git 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasTypeDefGraphStoreV2.java
 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasTypeDefGraphStoreV2.java
index 57146521a..09f4dcc9b 100644
--- 
a/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasTypeDefGraphStoreV2.java
+++ 
b/repository/src/main/java/org/apache/atlas/repository/store/graph/v2/AtlasTypeDefGraphStoreV2.java
@@ -21,7 +21,6 @@ import com.google.common.annotations.VisibleForTesting;
 import org.apache.atlas.AtlasErrorCode;
 import org.apache.atlas.RequestContext;
 import org.apache.atlas.annotation.GraphTransaction;
-import org.apache.atlas.discovery.EntityDiscoveryService;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.listener.TypeDefChangeListener;
 import org.apache.atlas.model.typedef.AtlasBaseTypeDef;
@@ -64,7 +63,7 @@ import static 
org.apache.atlas.repository.Constants.VERTEX_TYPE_PROPERTY_KEY;
 import static 
org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2.VERTEX_TYPE;
 
 /**
- * Graph persistence store for TypeDef - v1
+ * Graph persistence store for TypeDef - v2
  */
 @Singleton
 @Component
@@ -73,16 +72,13 @@ public class AtlasTypeDefGraphStoreV2 extends 
AtlasTypeDefGraphStore {
 
     protected final AtlasGraph atlasGraph;
 
-    private final EntityDiscoveryService entityDiscoveryService;
-
     @Inject
-    public AtlasTypeDefGraphStoreV2(AtlasTypeRegistry typeRegistry, 
List<TypeDefChangeListener> typeDefChangeListeners, AtlasGraph atlasGraph, 
EntityDiscoveryService entityDiscoveryService) {
+    public AtlasTypeDefGraphStoreV2(AtlasTypeRegistry typeRegistry, 
List<TypeDefChangeListener> typeDefChangeListeners, AtlasGraph atlasGraph) {
         super(typeRegistry, typeDefChangeListeners);
 
-        this.atlasGraph             = atlasGraph;
-        this.entityDiscoveryService = entityDiscoveryService;
+        this.atlasGraph = atlasGraph;
 
-        LOG.debug("<== AtlasTypeDefGraphStoreV1()");
+        LOG.debug("<== AtlasTypeDefGraphStoreV2()");
     }
 
     public static String getCurrentUser() {
@@ -177,17 +173,17 @@ public class AtlasTypeDefGraphStoreV2 extends 
AtlasTypeDefGraphStore {
 
     @Override
     protected AtlasDefStore<AtlasBusinessMetadataDef> 
getBusinessMetadataDefStore(AtlasTypeRegistry typeRegistry) {
-        return new AtlasBusinessMetadataDefStoreV2(this, typeRegistry, 
this.entityDiscoveryService);
+        return new AtlasBusinessMetadataDefStoreV2(this, typeRegistry, 
this.atlasGraph);
     }
 
     @Override
     @GraphTransaction
     public void init() throws AtlasBaseException {
-        LOG.info("==> AtlasTypeDefGraphStoreV1.init()");
+        LOG.info("==> AtlasTypeDefGraphStoreV2.init()");
 
         super.init();
 
-        LOG.info("<== AtlasTypeDefGraphStoreV1.init()");
+        LOG.info("<== AtlasTypeDefGraphStoreV2.init()");
     }
 
     AtlasGraph getAtlasGraph() {
diff --git 
a/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java 
b/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java
index 792f714ad..64d8cf1c0 100644
--- a/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java
+++ b/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java
@@ -102,6 +102,8 @@ public interface AtlasTypeDefStore {
 
     void deleteTypesDef(AtlasTypesDef typesDef) throws AtlasBaseException;
 
+    void deleteTypesDef(AtlasTypesDef typesDef, boolean forceDelete) throws 
AtlasBaseException;
+
     AtlasTypesDef searchTypesDef(SearchFilter searchFilter) throws 
AtlasBaseException;
 
     /* Generic operation */
@@ -109,7 +111,7 @@ public interface AtlasTypeDefStore {
 
     AtlasBaseTypeDef getByGuid(String guid) throws AtlasBaseException;
 
-    void deleteTypeByName(String typeName) throws AtlasBaseException;
+    void deleteTypeByName(String typeName, boolean forceDelete) throws 
AtlasBaseException;
 
     void notifyLoadCompletion();
 }
diff --git 
a/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java
 
b/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java
index f84d1f461..90603af03 100644
--- 
a/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java
+++ 
b/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java
@@ -369,10 +369,10 @@ public class AtlasTypeDefGraphStoreTest extends 
AtlasTestBase {
             AtlasTypesDef typesDef            = 
TestResourceFileUtils.readObjectFromJson(".", hivedbV2Json, 
AtlasTypesDef.class);
 
             typeDefStore.createTypesDef(typesDef);
-            typeDefStore.deleteTypeByName(hiveDB2);
-            typeDefStore.deleteTypeByName(relationshipDefName);
-            typeDefStore.deleteTypeByName(hostEntityDef);
-            typeDefStore.deleteTypeByName(clusterEntityDef);
+            typeDefStore.deleteTypeByName(hiveDB2, false);
+            typeDefStore.deleteTypeByName(relationshipDefName, false);
+            typeDefStore.deleteTypeByName(hostEntityDef, false);
+            typeDefStore.deleteTypeByName(clusterEntityDef, false);
         } catch (AtlasBaseException e) {
             fail("Deletion should've succeeded");
         }
diff --git 
a/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2Test.java
 
b/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2Test.java
index a93ff8040..7f0fa866c 100644
--- 
a/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2Test.java
+++ 
b/repository/src/test/java/org/apache/atlas/repository/store/graph/v2/AtlasBusinessMetadataDefStoreV2Test.java
@@ -59,6 +59,7 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
 
 /* Please note that for these tests, since the typeRegistry can be injected 
only once,
  * any new tests should make sure that they flush the type registry at the end 
of the test.
@@ -206,6 +207,39 @@ public class AtlasBusinessMetadataDefStoreV2Test {
         }
     }
 
+    @Test
+    public void 
deleteBusinessMetadataDefWithMixedIndexableAttributesWithoutForceShouldFail() 
throws AtlasBaseException {
+        createBusinessMetadataTypesWithMixedIndexability(businessMetadataName);
+
+        AtlasBusinessMetadataDef businessMetadataDef = 
findBusinessMetadataDef(businessMetadataName);
+        assertNotNull(businessMetadataDef);
+
+        typesDefs = new AtlasTypesDef(Collections.emptyList(), 
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), 
Collections.emptyList(), Collections.singletonList(businessMetadataDef));
+
+        try {
+            typeDefStore.deleteTypesDef(typesDefs, false);
+            fail("Deletion without force should fail when BM has non-indexable 
attributes");
+        } catch (AtlasBaseException e) {
+            assertEquals(e.getAtlasErrorCode(), 
AtlasErrorCode.NON_INDEXABLE_BM_DELETE_NOT_ALLOWED);
+        }
+    }
+
+    @Test
+    public void 
deleteBusinessMetadataDefWithMixedIndexableAttributesWithForceShouldSucceed() 
throws AtlasBaseException {
+        createBusinessMetadataTypesWithMixedIndexability(businessMetadataName);
+
+        AtlasBusinessMetadataDef businessMetadataDef = 
findBusinessMetadataDef(businessMetadataName);
+        assertNotNull(businessMetadataDef);
+
+        typesDefs = new AtlasTypesDef(Collections.emptyList(), 
Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), 
Collections.emptyList(), Collections.singletonList(businessMetadataDef));
+
+        typeDefStore.deleteTypesDef(typesDefs, true);
+
+        for (AtlasBusinessMetadataDef def : 
typeRegistry.getAllBusinessMetadataDefs()) {
+            assertNotEquals(def.getName(), businessMetadataName);
+        }
+    }
+
     @Test
     public void updateBusinessMetadataDefs() throws AtlasBaseException {
         createBusinessMetadataTypes(businessMetadataName);
@@ -494,6 +528,29 @@ public class AtlasBusinessMetadataDefStoreV2Test {
         return businessMetadataDef1;
     }
 
+    private void createBusinessMetadataTypesWithMixedIndexability(String 
businessMetadataName) throws AtlasBaseException {
+        List<AtlasBusinessMetadataDef> businessMetadataDefs = new 
ArrayList<>(typesDefs.getBusinessMetadataDefs());
+
+        
businessMetadataDefs.add(createBusinessMetadataDefWithMixedIndexability(businessMetadataName));
+
+        typesDefs.setBusinessMetadataDefs(businessMetadataDefs);
+
+        AtlasTypesDef createdTypesDef = typeDefStore.createTypesDef(typesDefs);
+
+        assertEquals(createdTypesDef.getBusinessMetadataDefs(), 
businessMetadataDefs, "Data integrity issue while persisting");
+    }
+
+    private AtlasBusinessMetadataDef 
createBusinessMetadataDefWithMixedIndexability(String businessMetadataName) {
+        AtlasBusinessMetadataDef businessMetadataDef = new 
AtlasBusinessMetadataDef(businessMetadataName, "test_mixed_indexability", null);
+
+        addBusinessAttribute(businessMetadataDef, "mixed_indexable_attr", 
Collections.singleton("hive_table"), "string", 
AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, true);
+        addBusinessAttribute(businessMetadataDef, "mixed_non_indexable_attr", 
Collections.singleton("hive_table"), "string", 
AtlasStructDef.AtlasAttributeDef.Cardinality.SINGLE, false);
+
+        TestUtilsV2.populateSystemAttributes(businessMetadataDef);
+
+        return businessMetadataDef;
+    }
+
     private AtlasBusinessMetadataDef createBusinessMetadataDef2(String 
businessMetadataName) {
         AtlasBusinessMetadataDef businessMetadataDef1 = new 
AtlasBusinessMetadataDef(businessMetadataName, "test_description", null);
 
@@ -525,6 +582,28 @@ public class AtlasBusinessMetadataDefStoreV2Test {
             attributeDef.setOption(ATTR_MAX_STRING_LENGTH, "20");
         }
 
+        attributeDef.setIsOptional(true);
+        attributeDef.setValuesMinCount(0);
+        attributeDef.setValuesMaxCount(1);
+        attributeDef.setIsUnique(false);
+        attributeDef.setDisplayName(name);
+        attributeDef.setIsIndexable(true);
+
+        businessMetadataDef.addAttribute(attributeDef);
+    }
+
+    private void addBusinessAttribute(AtlasBusinessMetadataDef 
businessMetadataDef, String name, Set<String> applicableEntityTypes, String 
typeName,
+                                      
AtlasStructDef.AtlasAttributeDef.Cardinality cardinality, boolean isIndexable) {
+        AtlasStructDef.AtlasAttributeDef attributeDef = new 
AtlasStructDef.AtlasAttributeDef(name, typeName);
+
+        attributeDef.setCardinality(cardinality);
+        attributeDef.setOption(ATTR_OPTION_APPLICABLE_ENTITY_TYPES, 
AtlasType.toJson(applicableEntityTypes));
+
+        if (typeName.contains(AtlasBaseTypeDef.ATLAS_TYPE_STRING)) {
+            attributeDef.setOption(ATTR_MAX_STRING_LENGTH, "20");
+        }
+
+        attributeDef.setIsIndexable(isIndexable);
         attributeDef.setIsOptional(true);
         attributeDef.setValuesMinCount(0);
         attributeDef.setValuesMaxCount(1);
diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java 
b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
index e42b7627b..9a2b679a0 100644
--- a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
+++ b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java
@@ -43,12 +43,14 @@ import javax.inject.Singleton;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 
@@ -399,25 +401,35 @@ public class TypesREST {
     }
 
     /**
-     * Bulk delete API for all types
-     * @param typesDef A composite object that captures all types to be deleted
+     * Bulk delete API for all types.
+     *
+     * @param typesDef A composite object that captures all types to be 
deleted.
+     * @param forceDelete Supported only for BusinessMetadata typedefs:
+     *                    If true, bypasses pre-delete validation checks and 
forcefully removes the type definition.
+     *                    For BusinessMetadata only:
+     *                    - If isIndexable=true and force=false: performs 
normal graph scan validation
+     *                    - If isIndexable=false and force=false: blocks 
deletion (requires force=true)
+     *                    - If force=true: skips all validation and deletes 
the type
+     *                    Defaults to false for backward compatibility.
      * @throws AtlasBaseException
      * @HTTP 204 On successful deletion of the requested type definitions
-     * @HTTP 400 On validation failure for any type definitions
+     * @HTTP 400 On validation failure for any type definitions or when 
attempting to delete
+     *           non-indexable BusinessMetadata without force-delete parameter
      */
     @DELETE
     @Path("/typedefs")
     @Experimental
     @Timed
-    public void deleteAtlasTypeDefs(final AtlasTypesDef typesDef) throws 
AtlasBaseException {
+    public void deleteAtlasTypeDefs(final AtlasTypesDef typesDef,
+                                    @QueryParam("force") 
@DefaultValue("false") final boolean forceDelete) throws AtlasBaseException {
         AtlasPerfTracer perf = null;
 
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
-                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"TypesREST.deleteAtlasTypeDefs(" + AtlasTypeUtil.toDebugString(typesDef) + ")");
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"TypesREST.deleteAtlasTypeDefs(" + AtlasTypeUtil.toDebugString(typesDef) + ", 
force=" + forceDelete + ")");
             }
 
-            typeDefStore.deleteTypesDef(typesDef);
+            typeDefStore.deleteTypesDef(typesDef, forceDelete);
         } finally {
             AtlasPerfTracer.log(perf);
         }
@@ -426,22 +438,31 @@ public class TypesREST {
     /**
      * Delete API for type identified by its name.
      * @param typeName Name of the type to be deleted.
+     * @param forceDelete Supported only for BusinessMetadata typedefs:
+     *                    If true, bypasses pre-delete validation checks and 
forcefully removes the type definition.
+     *                    For BusinessMetadata only:
+     *                    - If isIndexable=true and force=false: performs 
normal graph scan validation
+     *                    - If isIndexable=false and force=false: blocks 
deletion (requires force=true)
+     *                    - If force=true: skips all validation and deletes 
the type
+     *                    Defaults to false for backward compatibility.
      * @throws AtlasBaseException
      * @HTTP 204 On successful deletion of the requested type definitions
-     * @HTTP 400 On validation failure for any type definitions
+     * @HTTP 400 On validation failure for any type definitions or when 
attempting to delete
+     *           non-indexable BusinessMetadata without force-delete parameter
      */
     @DELETE
     @Path("/typedef/name/{typeName}")
     @Timed
-    public void deleteAtlasTypeByName(@PathParam("typeName") final String 
typeName) throws AtlasBaseException {
+    public void deleteAtlasTypeByName(@PathParam("typeName") final String 
typeName,
+                                      @QueryParam("force") 
@DefaultValue("false") final boolean forceDelete) throws AtlasBaseException {
         AtlasPerfTracer perf = null;
 
         try {
             if (AtlasPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
-                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"TypesREST.deleteAtlasTypeByName(" + typeName + ")");
+                perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, 
"TypesREST.deleteAtlasTypeByName(" + typeName + ", force=" + forceDelete + ")");
             }
 
-            typeDefStore.deleteTypeByName(typeName);
+            typeDefStore.deleteTypeByName(typeName, forceDelete);
         } finally {
             AtlasPerfTracer.log(perf);
         }
diff --git 
a/webapp/src/test/java/org/apache/atlas/web/adapters/TypeDefsRESTTest.java 
b/webapp/src/test/java/org/apache/atlas/web/adapters/TypeDefsRESTTest.java
index 0c74dc1d3..3ef8ed65a 100644
--- a/webapp/src/test/java/org/apache/atlas/web/adapters/TypeDefsRESTTest.java
+++ b/webapp/src/test/java/org/apache/atlas/web/adapters/TypeDefsRESTTest.java
@@ -108,7 +108,7 @@ public class TypeDefsRESTTest {
         AtlasErrorCode errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeByName(bmWithAllTypes);
+            typesREST.deleteAtlasTypeByName(bmWithAllTypes, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -124,7 +124,7 @@ public class TypeDefsRESTTest {
         errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeByName(bmWithAllTypes);
+            typesREST.deleteAtlasTypeByName(bmWithAllTypes, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -135,7 +135,7 @@ public class TypeDefsRESTTest {
 
         entityREST.removeBusinessAttributes(dbEntity.getGuid(), 
bmWithAllTypes, objectMap);
 
-        typesREST.deleteAtlasTypeByName(bmWithAllTypes);
+        typesREST.deleteAtlasTypeByName(bmWithAllTypes, false);
     }
 
     @Test
@@ -155,7 +155,7 @@ public class TypeDefsRESTTest {
         AtlasErrorCode errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeByName(bmWithSuperType);
+            typesREST.deleteAtlasTypeByName(bmWithSuperType, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -171,7 +171,7 @@ public class TypeDefsRESTTest {
         errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeByName(bmWithSuperType);
+            typesREST.deleteAtlasTypeByName(bmWithSuperType, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -182,7 +182,7 @@ public class TypeDefsRESTTest {
 
         entityREST.removeBusinessAttributes(dbEntity.getGuid(), 
bmWithSuperType, objectMap);
 
-        typesREST.deleteAtlasTypeByName(bmWithSuperType);
+        typesREST.deleteAtlasTypeByName(bmWithSuperType, false);
     }
 
     @Test
@@ -209,7 +209,7 @@ public class TypeDefsRESTTest {
         AtlasErrorCode errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeDefs(atlasTypesDef);
+            typesREST.deleteAtlasTypeDefs(atlasTypesDef, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -227,7 +227,7 @@ public class TypeDefsRESTTest {
         errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeDefs(atlasTypesDef);
+            typesREST.deleteAtlasTypeDefs(atlasTypesDef, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -243,7 +243,7 @@ public class TypeDefsRESTTest {
         errorCode = null;
 
         try {
-            typesREST.deleteAtlasTypeDefs(atlasTypesDef);
+            typesREST.deleteAtlasTypeDefs(atlasTypesDef, false);
         } catch (AtlasBaseException e) {
             errorCode = e.getAtlasErrorCode();
         }
@@ -254,7 +254,7 @@ public class TypeDefsRESTTest {
 
         entityREST.removeBusinessAttributes(dbEntity.getGuid(), 
bmWithAllTypesMV, objectMap);
 
-        typesREST.deleteAtlasTypeDefs(atlasTypesDef);
+        typesREST.deleteAtlasTypeDefs(atlasTypesDef, false);
     }
 
     private void createTestEntity() throws AtlasBaseException {
diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java 
b/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java
index 257a4b5ec..bd44ab077 100644
--- a/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java
+++ b/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java
@@ -532,7 +532,7 @@ public class TypesRESTTest {
     @Test
     public void testDeleteAtlasTypeDefs_Success() throws AtlasBaseException {
         // Setup
-        doNothing().when(typeDefStore).deleteTypesDef(mockTypesDef);
+        doNothing().when(typeDefStore).deleteTypesDef(mockTypesDef, false);
 
         try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class);
                 MockedStatic<AtlasTypeUtil> mockedAtlasTypeUtil = 
mockStatic(AtlasTypeUtil.class)) {
@@ -540,17 +540,17 @@ public class TypesRESTTest {
             mockedAtlasTypeUtil.when(() -> 
AtlasTypeUtil.toDebugString(mockTypesDef)).thenReturn("debug_string");
 
             // Execute
-            typesREST.deleteAtlasTypeDefs(mockTypesDef);
+            typesREST.deleteAtlasTypeDefs(mockTypesDef, false);
 
             // Verify
-            verify(typeDefStore).deleteTypesDef(mockTypesDef);
+            verify(typeDefStore).deleteTypesDef(mockTypesDef, false);
         }
     }
 
     @Test
     public void testDeleteAtlasTypeDefs_WithPerfTracerEnabled() throws 
AtlasBaseException {
         // Setup
-        doNothing().when(typeDefStore).deleteTypesDef(mockTypesDef);
+        doNothing().when(typeDefStore).deleteTypesDef(mockTypesDef, false);
 
         try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class);
                 MockedStatic<AtlasTypeUtil> mockedAtlasTypeUtil = 
mockStatic(AtlasTypeUtil.class)) {
@@ -559,10 +559,10 @@ public class TypesRESTTest {
             mockedAtlasTypeUtil.when(() -> 
AtlasTypeUtil.toDebugString(mockTypesDef)).thenReturn("debug_string");
 
             // Execute
-            typesREST.deleteAtlasTypeDefs(mockTypesDef);
+            typesREST.deleteAtlasTypeDefs(mockTypesDef, false);
 
             // Verify
-            verify(typeDefStore).deleteTypesDef(mockTypesDef);
+            verify(typeDefStore).deleteTypesDef(mockTypesDef, false);
             mockedPerfTracer.verify(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any()));
             mockedPerfTracer.verify(() -> AtlasPerfTracer.getPerfTracer(any(), 
anyString()));
         }
@@ -572,7 +572,7 @@ public class TypesRESTTest {
     public void testDeleteAtlasTypeDefs_WithException() throws 
AtlasBaseException {
         // Setup
         AtlasBaseException exception = new AtlasBaseException("Delete failed");
-        doThrow(exception).when(typeDefStore).deleteTypesDef(mockTypesDef);
+        doThrow(exception).when(typeDefStore).deleteTypesDef(mockTypesDef, 
false);
 
         try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class);
                 MockedStatic<AtlasTypeUtil> mockedAtlasTypeUtil = 
mockStatic(AtlasTypeUtil.class)) {
@@ -581,11 +581,29 @@ public class TypesRESTTest {
 
             // Execute & Verify
             AtlasBaseException thrownException = 
expectThrows(AtlasBaseException.class, () -> {
-                typesREST.deleteAtlasTypeDefs(mockTypesDef);
+                typesREST.deleteAtlasTypeDefs(mockTypesDef, false);
             });
 
             assertEquals(thrownException.getMessage(), "Delete failed");
-            verify(typeDefStore).deleteTypesDef(mockTypesDef);
+            verify(typeDefStore).deleteTypesDef(mockTypesDef, false);
+        }
+    }
+
+    @Test
+    public void testDeleteAtlasTypeDefs_WithForceDelete() throws 
AtlasBaseException {
+        // Setup
+        doNothing().when(typeDefStore).deleteTypesDef(mockTypesDef, true);
+
+        try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class);
+                MockedStatic<AtlasTypeUtil> mockedAtlasTypeUtil = 
mockStatic(AtlasTypeUtil.class)) {
+            mockedPerfTracer.when(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(false);
+            mockedAtlasTypeUtil.when(() -> 
AtlasTypeUtil.toDebugString(mockTypesDef)).thenReturn("debug_string");
+
+            // Execute
+            typesREST.deleteAtlasTypeDefs(mockTypesDef, true);
+
+            // Verify
+            verify(typeDefStore).deleteTypesDef(mockTypesDef, true);
         }
     }
 
@@ -593,16 +611,16 @@ public class TypesRESTTest {
     public void testDeleteAtlasTypeByName_Success() throws AtlasBaseException {
         // Setup
         String typeName = "test_type";
-        doNothing().when(typeDefStore).deleteTypeByName(typeName);
+        doNothing().when(typeDefStore).deleteTypeByName(typeName, false);
 
         try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class)) {
             mockedPerfTracer.when(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(false);
 
             // Execute
-            typesREST.deleteAtlasTypeByName(typeName);
+            typesREST.deleteAtlasTypeByName(typeName, false);
 
             // Verify
-            verify(typeDefStore).deleteTypeByName(typeName);
+            verify(typeDefStore).deleteTypeByName(typeName, false);
         }
     }
 
@@ -610,17 +628,17 @@ public class TypesRESTTest {
     public void testDeleteAtlasTypeByName_WithPerfTracerEnabled() throws 
AtlasBaseException {
         // Setup
         String typeName = "test_type_perf";
-        doNothing().when(typeDefStore).deleteTypeByName(typeName);
+        doNothing().when(typeDefStore).deleteTypeByName(typeName, false);
 
         try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class)) {
             mockedPerfTracer.when(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(true);
             mockedPerfTracer.when(() -> AtlasPerfTracer.getPerfTracer(any(), 
anyString())).thenReturn(null);
 
             // Execute
-            typesREST.deleteAtlasTypeByName(typeName);
+            typesREST.deleteAtlasTypeByName(typeName, false);
 
             // Verify
-            verify(typeDefStore).deleteTypeByName(typeName);
+            verify(typeDefStore).deleteTypeByName(typeName, false);
             mockedPerfTracer.verify(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any()));
             mockedPerfTracer.verify(() -> AtlasPerfTracer.getPerfTracer(any(), 
anyString()));
         }
@@ -631,18 +649,35 @@ public class TypesRESTTest {
         // Setup
         String typeName = "test_type_error";
         AtlasBaseException exception = new AtlasBaseException("Delete by name 
failed");
-        doThrow(exception).when(typeDefStore).deleteTypeByName(typeName);
+        doThrow(exception).when(typeDefStore).deleteTypeByName(typeName, 
false);
 
         try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class)) {
             mockedPerfTracer.when(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(false);
 
             // Execute & Verify
             AtlasBaseException thrownException = 
expectThrows(AtlasBaseException.class, () -> {
-                typesREST.deleteAtlasTypeByName(typeName);
+                typesREST.deleteAtlasTypeByName(typeName, false);
             });
 
             assertEquals(thrownException.getMessage(), "Delete by name 
failed");
-            verify(typeDefStore).deleteTypeByName(typeName);
+            verify(typeDefStore).deleteTypeByName(typeName, false);
+        }
+    }
+
+    @Test
+    public void testDeleteAtlasTypeByName_WithForceDelete() throws 
AtlasBaseException {
+        // Setup
+        String typeName = "test_type_force";
+        doNothing().when(typeDefStore).deleteTypeByName(typeName, true);
+
+        try (MockedStatic<AtlasPerfTracer> mockedPerfTracer = 
mockStatic(AtlasPerfTracer.class)) {
+            mockedPerfTracer.when(() -> 
AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(false);
+
+            // Execute with force=true
+            typesREST.deleteAtlasTypeByName(typeName, true);
+
+            // Verify
+            verify(typeDefStore).deleteTypeByName(typeName, true);
         }
     }
 

Reply via email to