thingsboard-aplcache

Changes

common/data/src/main/java/org/thingsboard/server/common/data/asset/TenantAssetType.java 92(+0 -92)

dao/src/main/java/org/thingsboard/server/dao/model/sql/TenantDeviceTypeEntity.java 26(+0 -26)

dao/src/main/java/org/thingsboard/server/dao/model/TenantDeviceTypeEntity.java 107(+0 -107)

Details

diff --git a/application/src/main/data/upgrade/1.3.0/schema_update.cql b/application/src/main/data/upgrade/1.3.0/schema_update.cql
index 4070614..1e4302d 100644
--- a/application/src/main/data/upgrade/1.3.0/schema_update.cql
+++ b/application/src/main/data/upgrade/1.3.0/schema_update.cql
@@ -69,13 +69,6 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_an
     PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
     WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
 
-CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_types_by_tenant AS
-    SELECT *
-    from thingsboard.device
-    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
-    PRIMARY KEY ( (type, tenant_id), id, customer_id)
-    WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
-
 DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_and_name;
 DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_and_search_text;
 DROP MATERIALIZED VIEW IF EXISTS thingsboard.asset_by_tenant_by_type_and_search_text;
@@ -131,12 +124,12 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and
     PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
     WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
 
-CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_types_by_tenant AS
-    SELECT *
-    from thingsboard.asset
-    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
-    PRIMARY KEY ( (type, tenant_id), id, customer_id)
-    WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
+CREATE TABLE IF NOT EXISTS thingsboard.entity_subtype (
+    tenant_id timeuuid,
+    entity_type text, // (DEVICE, ASSET)
+    type text,
+    PRIMARY KEY (tenant_id, entity_type, type)
+);
 
 CREATE TABLE IF NOT EXISTS thingsboard.alarm (
     id timeuuid,
diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
index dd43e1c..2857874 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java
@@ -20,8 +20,8 @@ import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -246,11 +246,11 @@ public class AssetController extends BaseController {
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/asset/types", method = RequestMethod.GET)
     @ResponseBody
-    public List<TenantAssetType> getAssetTypes() throws ThingsboardException {
+    public List<EntitySubtype> getAssetTypes() throws ThingsboardException {
         try {
             SecurityUser user = getCurrentUser();
             TenantId tenantId = user.getTenantId();
-            ListenableFuture<List<TenantAssetType>> assetTypes = assetService.findAssetTypesByTenantId(tenantId);
+            ListenableFuture<List<EntitySubtype>> assetTypes = assetService.findAssetTypesByTenantId(tenantId);
             return checkNotNull(assetTypes.get());
         } catch (Exception e) {
             throw handleException(e);
diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
index f0cde33..68b29b8 100644
--- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java
@@ -21,7 +21,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.TenantDeviceType;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -283,11 +283,11 @@ public class DeviceController extends BaseController {
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/device/types", method = RequestMethod.GET)
     @ResponseBody
-    public List<TenantDeviceType> getDeviceTypes() throws ThingsboardException {
+    public List<EntitySubtype> getDeviceTypes() throws ThingsboardException {
         try {
             SecurityUser user = getCurrentUser();
             TenantId tenantId = user.getTenantId();
-            ListenableFuture<List<TenantDeviceType>> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId);
+            ListenableFuture<List<EntitySubtype>> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId);
             return checkNotNull(deviceTypes.get());
         } catch (Exception e) {
             throw handleException(e);
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
index 4e769f7..83f1435 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
@@ -64,26 +64,23 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
 
                 log.info("Dumping devices ...");
                 Path devicesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "device",
-                        new String[]{"id", "tenant_id", "customer_id", "name", "search_text", "additional_info"},
+                        new String[]{"id", "tenant_id", "customer_id", "name", "search_text", "additional_info", "type"},
+                        new String[]{"", "", "", "", "", "", "default"},
                         "tb-devices");
-                if (devicesDump != null) {
-                    CassandraDbHelper.appendToEndOfLine(devicesDump, "default");
-                }
                 log.info("Devices dumped.");
 
                 log.info("Dumping assets ...");
                 Path assetsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "asset",
                         new String[]{"id", "tenant_id", "customer_id", "name", "search_text", "additional_info", "type"},
+                        new String[]{"", "", "", "", "", "", "default"},
                         "tb-assets");
                 log.info("Assets dumped.");
 
                 log.info("Dumping relations ...");
                 Path relationsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "relation",
-                        new String[]{"from_id", "from_type", "to_id", "to_type", "relation_type", "additional_info"},
+                        new String[]{"from_id", "from_type", "to_id", "to_type", "relation_type", "additional_info", "relation_type_group"},
+                        new String[]{"", "", "", "", "", "", "COMMON"},
                         "tb-relations");
-                if (relationsDump != null) {
-                    CassandraDbHelper.appendToEndOfLine(relationsDump, "COMMON");
-                }
                 log.info("Relations dumped.");
 
                 log.info("Updating schema ...");
@@ -101,6 +98,23 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
                 }
                 log.info("Devices restored.");
 
+                log.info("Dumping device types ...");
+                Path deviceTypesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "device",
+                        new String[]{"tenant_id", "type"},
+                        new String[]{"", ""},
+                        "tb-device-types");
+                if (deviceTypesDump != null) {
+                    CassandraDbHelper.appendToEndOfLine(deviceTypesDump, "DEVICE");
+                }
+                log.info("Device types dumped.");
+                log.info("Loading device types ...");
+                if (deviceTypesDump != null) {
+                    CassandraDbHelper.loadCf(ks, cluster.getSession(), "entity_subtype",
+                            new String[]{"tenant_id", "type", "entity_type"}, deviceTypesDump);
+                    Files.deleteIfExists(deviceTypesDump);
+                }
+                log.info("Device types loaded.");
+
                 log.info("Restoring assets ...");
                 if (assetsDump != null) {
                     CassandraDbHelper.loadCf(ks, cluster.getSession(), "asset",
@@ -109,6 +123,23 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
                 }
                 log.info("Assets restored.");
 
+                log.info("Dumping asset types ...");
+                Path assetTypesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "asset",
+                        new String[]{"tenant_id", "type"},
+                        new String[]{"", ""},
+                        "tb-asset-types");
+                if (assetTypesDump != null) {
+                    CassandraDbHelper.appendToEndOfLine(assetTypesDump, "ASSET");
+                }
+                log.info("Asset types dumped.");
+                log.info("Loading asset types ...");
+                if (assetTypesDump != null) {
+                    CassandraDbHelper.loadCf(ks, cluster.getSession(), "entity_subtype",
+                            new String[]{"tenant_id", "type", "entity_type"}, assetTypesDump);
+                    Files.deleteIfExists(assetTypesDump);
+                }
+                log.info("Asset types loaded.");
+
                 log.info("Restoring relations ...");
                 if (relationsDump != null) {
                     CassandraDbHelper.loadCf(ks, cluster.getSession(), "relation",
diff --git a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
index 4a92db2..0a411f6 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/cql/CassandraDbHelper.java
@@ -33,7 +33,7 @@ public class CassandraDbHelper {
     private static final CSVFormat CSV_DUMP_FORMAT = CSVFormat.DEFAULT.withNullString("\\N");
 
     public static Path dumpCfIfExists(KeyspaceMetadata ks, Session session, String cfName,
-                                      String[] columns, String dumpPrefix) throws Exception {
+                                      String[] columns, String[] defaultValues, String dumpPrefix) throws Exception {
         if (ks.getTable(cfName) != null) {
             Path dumpFile = Files.createTempFile(dumpPrefix, null);
             Files.deleteIfExists(dumpFile);
@@ -45,7 +45,7 @@ public class CassandraDbHelper {
                 while (iter.hasNext()) {
                     Row row = iter.next();
                     if (row != null) {
-                        dumpRow(row, columns, csvPrinter);
+                        dumpRow(row, columns, defaultValues, csvPrinter);
                     }
                 }
             }
@@ -89,18 +89,25 @@ public class CassandraDbHelper {
     }
 
 
-    private static void dumpRow(Row row, String[] columns, CSVPrinter csvPrinter) throws Exception {
+    private static void dumpRow(Row row, String[] columns, String[] defaultValues, CSVPrinter csvPrinter) throws Exception {
         List<String> record = new ArrayList<>();
-        for (String column : columns) {
-            record.add(getColumnValue(column, row));
+        for (int i=0;i<columns.length;i++) {
+            String column = columns[i];
+            String defaultValue;
+            if (defaultValues != null && i < defaultValues.length) {
+                defaultValue = defaultValues[i];
+            } else {
+                defaultValue = "";
+            }
+            record.add(getColumnValue(column, defaultValue, row));
         }
         csvPrinter.printRecord(record);
     }
 
-    private static String getColumnValue(String column, Row row) {
-        String str = "";
+    private static String getColumnValue(String column, String defaultValue, Row row) {
         int index = row.getColumnDefinitions().getIndexOf(column);
         if (index > -1) {
+            String str;
             DataType type = row.getColumnDefinitions().getType(index);
             try {
                 if (row.isNull(index)) {
@@ -123,8 +130,10 @@ public class CassandraDbHelper {
             } catch (Exception e) {
                 str = "";
             }
+            return str;
+        } else {
+            return defaultValue;
         }
-        return str;
     }
 
     private static String createInsertStatement(String cfName, String[] columns) {
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
index 1350aae..fb2e720 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
@@ -15,30 +15,31 @@
  */
 package org.thingsboard.server.controller;
 
-import static org.hamcrest.Matchers.containsString;
-import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.core.type.TypeReference;
 import org.apache.commons.lang3.RandomStringUtils;
-import org.thingsboard.server.common.data.*;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.Tenant;
+import org.thingsboard.server.common.data.User;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.page.TextPageData;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.common.data.security.Authority;
 import org.thingsboard.server.dao.model.ModelConstants;
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
 
-import com.datastax.driver.core.utils.UUIDs;
-import com.fasterxml.jackson.core.type.TypeReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
 
 public abstract class BaseAssetControllerTest extends AbstractControllerTest {
 
@@ -128,8 +129,8 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
             asset.setType("typeA");
             assets.add(doPost("/api/asset", asset, Asset.class));
         }
-        List<TenantAssetType> assetTypes = doGetTyped("/api/asset/types",
-                new TypeReference<List<TenantAssetType>>(){});
+        List<EntitySubtype> assetTypes = doGetTyped("/api/asset/types",
+                new TypeReference<List<EntitySubtype>>(){});
 
         Assert.assertNotNull(assetTypes);
         Assert.assertEquals(3, assetTypes.size());
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
index 3713fc4..f8da8fc 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
@@ -140,8 +140,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
             device.setType("typeA");
             devices.add(doPost("/api/device", device, Device.class));
         }
-        List<TenantDeviceType> deviceTypes = doGetTyped("/api/device/types",
-                new TypeReference<List<TenantDeviceType>>(){});
+        List<EntitySubtype> deviceTypes = doGetTyped("/api/device/types",
+                new TypeReference<List<EntitySubtype>>(){});
 
         Assert.assertNotNull(deviceTypes);
         Assert.assertEquals(3, deviceTypes.size());
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
index 73211fe..f953335 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java
@@ -16,8 +16,8 @@
 package org.thingsboard.server.dao.asset;
 
 import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.Dao;
 
@@ -112,6 +112,6 @@ public interface AssetDao extends Dao<Asset> {
      *
      * @return the list of tenant asset type objects
      */
-    ListenableFuture<List<TenantAssetType>> findTenantAssetTypesAsync();
+    ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId);
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
index 25baeda..214ef15 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetService.java
@@ -16,8 +16,8 @@
 package org.thingsboard.server.dao.asset;
 
 import com.google.common.util.concurrent.ListenableFuture;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -61,5 +61,5 @@ public interface AssetService {
 
     ListenableFuture<List<Asset>> findAssetsByQuery(AssetSearchQuery query);
 
-    ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId);
+    ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
index 3a2805c..615cdf8 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java
@@ -25,10 +25,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.EntityId;
@@ -217,22 +217,15 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ
     }
 
     @Override
-    public ListenableFuture<List<TenantAssetType>> findAssetTypesByTenantId(TenantId tenantId) {
+    public ListenableFuture<List<EntitySubtype>> findAssetTypesByTenantId(TenantId tenantId) {
         log.trace("Executing findAssetTypesByTenantId, tenantId [{}]", tenantId);
         validateId(tenantId, "Incorrect tenantId " + tenantId);
-        ListenableFuture<List<TenantAssetType>> tenantAssetTypeEntities = assetDao.findTenantAssetTypesAsync();
-        ListenableFuture<List<TenantAssetType>> tenantAssetTypes = Futures.transform(tenantAssetTypeEntities,
-                (Function<List<TenantAssetType>, List<TenantAssetType>>) assetTypeEntities -> {
-                    List<TenantAssetType> assetTypes = new ArrayList<>();
-                    for (TenantAssetType assetType : assetTypeEntities) {
-                        if (assetType.getTenantId().equals(tenantId)) {
-                            assetTypes.add(assetType);
-                        }
-                    }
-                    assetTypes.sort(Comparator.comparing(TenantAssetType::getType));
+        ListenableFuture<List<EntitySubtype>> tenantAssetTypes = assetDao.findTenantAssetTypesAsync(tenantId.getId());
+        return Futures.transform(tenantAssetTypes,
+                (Function<List<EntitySubtype>, List<EntitySubtype>>) assetTypes -> {
+                    assetTypes.sort(Comparator.comparing(EntitySubtype::getType));
                     return assetTypes;
                 });
-        return tenantAssetTypes;
     }
 
     private DataValidator<Asset> assetValidator =
diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
index 339c9ea..d6c6ca4 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/asset/CassandraAssetDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.asset;
 
 import com.datastax.driver.core.ResultSet;
 import com.datastax.driver.core.ResultSetFuture;
+import com.datastax.driver.core.Statement;
 import com.datastax.driver.core.querybuilder.Select;
 import com.datastax.driver.mapping.Result;
 import com.google.common.base.Function;
@@ -24,11 +25,12 @@ import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.DaoUtil;
-import org.thingsboard.server.dao.model.TenantAssetTypeEntity;
+import org.thingsboard.server.dao.model.EntitySubtypeEntity;
 import org.thingsboard.server.dao.model.nosql.AssetEntity;
 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
 import org.thingsboard.server.dao.util.NoSqlDao;
@@ -55,6 +57,16 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
     }
 
     @Override
+    public Asset save(Asset domain) {
+        Asset savedAsset = super.save(domain);
+        EntitySubtype entitySubtype = new EntitySubtype(savedAsset.getTenantId(), EntityType.ASSET, savedAsset.getType());
+        EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
+        Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
+        executeWrite(saveStatement);
+        return savedAsset;
+    }
+
+    @Override
     public List<Asset> findAssetsByTenantId(UUID tenantId, TextPageLink pageLink) {
         log.debug("Try to find assets by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
         List<AssetEntity> assetEntities = findPageWithTextSearch(ASSET_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
@@ -130,36 +142,29 @@ public class CassandraAssetDao extends CassandraAbstractSearchTextDao<AssetEntit
     }
 
     @Override
-    public ListenableFuture<List<TenantAssetType>> findTenantAssetTypesAsync() {
-        Select statement = select().distinct().column(ASSET_TYPE_PROPERTY).column(ASSET_TENANT_ID_PROPERTY).from(ASSET_TYPES_BY_TENANT_VIEW_NAME);
-        statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
-        ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
-        ListenableFuture<List<TenantAssetTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantAssetTypeEntity>>() {
+    public ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId) {
+        Select select = select().from(ENTITY_SUBTYPE_COLUMN_FAMILY_NAME);
+        Select.Where query = select.where();
+        query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
+        query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.ASSET));
+        query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
+        ResultSetFuture resultSetFuture = getSession().executeAsync(query);
+        return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
             @Nullable
             @Override
-            public List<TenantAssetTypeEntity> apply(@Nullable ResultSet resultSet) {
-                Result<TenantAssetTypeEntity> result = cluster.getMapper(TenantAssetTypeEntity.class).map(resultSet);
+            public List<EntitySubtype> apply(@Nullable ResultSet resultSet) {
+                Result<EntitySubtypeEntity> result = cluster.getMapper(EntitySubtypeEntity.class).map(resultSet);
                 if (result != null) {
-                    return result.all();
+                    List<EntitySubtype> entitySubtypes = new ArrayList<>();
+                    result.all().forEach((entitySubtypeEntity) ->
+                            entitySubtypes.add(entitySubtypeEntity.toEntitySubtype())
+                    );
+                    return entitySubtypes;
                 } else {
                     return Collections.emptyList();
                 }
             }
         });
-        return Futures.transform(result, new Function<List<TenantAssetTypeEntity>, List<TenantAssetType>>() {
-            @Nullable
-            @Override
-            public List<TenantAssetType> apply(@Nullable List<TenantAssetTypeEntity> entityList) {
-                List<TenantAssetType> list = Collections.emptyList();
-                if (entityList != null && !entityList.isEmpty()) {
-                    list = new ArrayList<>();
-                    for (TenantAssetTypeEntity object : entityList) {
-                        list.add(object.toTenantAssetType());
-                    }
-                }
-                return list;
-            }
-        });
     }
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
index a0d4fe1..9c3a4bb 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/CassandraDeviceDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.device;
 
 import com.datastax.driver.core.ResultSet;
 import com.datastax.driver.core.ResultSetFuture;
+import com.datastax.driver.core.Statement;
 import com.datastax.driver.core.querybuilder.Select;
 import com.datastax.driver.mapping.Result;
 import com.google.common.base.Function;
@@ -25,10 +26,11 @@ import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.TenantDeviceType;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.DaoUtil;
-import org.thingsboard.server.dao.model.TenantDeviceTypeEntity;
+import org.thingsboard.server.dao.model.EntitySubtypeEntity;
 import org.thingsboard.server.dao.model.nosql.DeviceEntity;
 import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
 import org.thingsboard.server.dao.util.NoSqlDao;
@@ -55,6 +57,16 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
     }
 
     @Override
+    public Device save(Device domain) {
+        Device savedDevice = super.save(domain);
+        EntitySubtype entitySubtype = new EntitySubtype(savedDevice.getTenantId(), EntityType.DEVICE, savedDevice.getType());
+        EntitySubtypeEntity entitySubtypeEntity = new EntitySubtypeEntity(entitySubtype);
+        Statement saveStatement = cluster.getMapper(EntitySubtypeEntity.class).saveQuery(entitySubtypeEntity);
+        executeWrite(saveStatement);
+        return savedDevice;
+    }
+
+    @Override
     public List<Device> findDevicesByTenantId(UUID tenantId, TextPageLink pageLink) {
         log.debug("Try to find devices by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
         List<DeviceEntity> deviceEntities = findPageWithTextSearch(DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
@@ -130,36 +142,29 @@ public class CassandraDeviceDao extends CassandraAbstractSearchTextDao<DeviceEnt
     }
 
     @Override
-    public ListenableFuture<List<TenantDeviceType>> findTenantDeviceTypesAsync() {
-        Select statement = select().distinct().column(DEVICE_TYPE_PROPERTY).column(DEVICE_TENANT_ID_PROPERTY).from(DEVICE_TYPES_BY_TENANT_VIEW_NAME);
-        statement.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
-        ResultSetFuture resultSetFuture = getSession().executeAsync(statement);
-        ListenableFuture<List<TenantDeviceTypeEntity>> result = Futures.transform(resultSetFuture, new Function<ResultSet, List<TenantDeviceTypeEntity>>() {
+    public ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId) {
+        Select select = select().from(ENTITY_SUBTYPE_COLUMN_FAMILY_NAME);
+        Select.Where query = select.where();
+        query.and(eq(ENTITY_SUBTYPE_TENANT_ID_PROPERTY, tenantId));
+        query.and(eq(ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY, EntityType.DEVICE));
+        query.setConsistencyLevel(cluster.getDefaultReadConsistencyLevel());
+        ResultSetFuture resultSetFuture = getSession().executeAsync(query);
+        return Futures.transform(resultSetFuture, new Function<ResultSet, List<EntitySubtype>>() {
             @Nullable
             @Override
-            public List<TenantDeviceTypeEntity> apply(@Nullable ResultSet resultSet) {
-                Result<TenantDeviceTypeEntity> result = cluster.getMapper(TenantDeviceTypeEntity.class).map(resultSet);
+            public List<EntitySubtype> apply(@Nullable ResultSet resultSet) {
+                Result<EntitySubtypeEntity> result = cluster.getMapper(EntitySubtypeEntity.class).map(resultSet);
                 if (result != null) {
-                    return result.all();
+                    List<EntitySubtype> entitySubtypes = new ArrayList<>();
+                    result.all().forEach((entitySubtypeEntity) ->
+                        entitySubtypes.add(entitySubtypeEntity.toEntitySubtype())
+                    );
+                    return entitySubtypes;
                 } else {
                     return Collections.emptyList();
                 }
             }
         });
-        return Futures.transform(result, new Function<List<TenantDeviceTypeEntity>, List<TenantDeviceType>>() {
-            @Nullable
-            @Override
-            public List<TenantDeviceType> apply(@Nullable List<TenantDeviceTypeEntity> entityList) {
-                List<TenantDeviceType> list = Collections.emptyList();
-                if (entityList != null && !entityList.isEmpty()) {
-                    list = new ArrayList<>();
-                    for (TenantDeviceTypeEntity object : entityList) {
-                        list.add(object.toTenantDeviceType());
-                    }
-                }
-                return list;
-            }
-        });
     }
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
index a6d785e..c0c1744 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
@@ -17,7 +17,7 @@ package org.thingsboard.server.dao.device;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.TenantDeviceType;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.Dao;
 
@@ -113,5 +113,5 @@ public interface DeviceDao extends Dao<Device> {
      *
      * @return the list of tenant device type objects
      */
-    ListenableFuture<List<TenantDeviceType>> findTenantDeviceTypesAsync();
+    ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
index 3e7b6af..cd56fef 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
@@ -17,7 +17,7 @@ package org.thingsboard.server.dao.device;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.TenantDeviceType;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -61,6 +61,6 @@ public interface DeviceService {
 
     ListenableFuture<List<Device>> findDevicesByQuery(DeviceSearchQuery query);
 
-    ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId);
+    ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId);
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
index 7535967..e1056f8 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
@@ -237,22 +237,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
     }
 
     @Override
-    public ListenableFuture<List<TenantDeviceType>> findDeviceTypesByTenantId(TenantId tenantId) {
+    public ListenableFuture<List<EntitySubtype>> findDeviceTypesByTenantId(TenantId tenantId) {
         log.trace("Executing findDeviceTypesByTenantId, tenantId [{}]", tenantId);
         validateId(tenantId, "Incorrect tenantId " + tenantId);
-        ListenableFuture<List<TenantDeviceType>> tenantDeviceTypeEntities = deviceDao.findTenantDeviceTypesAsync();
-        ListenableFuture<List<TenantDeviceType>> tenantDeviceTypes = Futures.transform(tenantDeviceTypeEntities,
-            (Function<List<TenantDeviceType>, List<TenantDeviceType>>) deviceTypeEntities -> {
-                List<TenantDeviceType> deviceTypes = new ArrayList<>();
-                for (TenantDeviceType deviceType : deviceTypeEntities) {
-                    if (deviceType.getTenantId().equals(tenantId)) {
-                        deviceTypes.add(deviceType);
-                    }
-                }
-                deviceTypes.sort(Comparator.comparing(TenantDeviceType::getType));
+        ListenableFuture<List<EntitySubtype>> tenantDeviceTypes = deviceDao.findTenantDeviceTypesAsync(tenantId.getId());
+        return Futures.transform(tenantDeviceTypes,
+            (Function<List<EntitySubtype>, List<EntitySubtype>>) deviceTypes -> {
+                deviceTypes.sort(Comparator.comparing(EntitySubtype::getType));
                 return deviceTypes;
-            });
-        return tenantDeviceTypes;
+        });
     }
 
     private DataValidator<Device> deviceValidator =
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index 1bf88be..194a41c 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
@@ -151,6 +151,14 @@ public class ModelConstants {
     public static final String ASSET_TYPES_BY_TENANT_VIEW_NAME = "asset_types_by_tenant";
 
     /**
+     * Cassandra entity_subtype constants.
+     */
+    public static final String ENTITY_SUBTYPE_COLUMN_FAMILY_NAME = "entity_subtype";
+    public static final String ENTITY_SUBTYPE_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
+    public static final String ENTITY_SUBTYPE_ENTITY_TYPE_PROPERTY = "entity_type";
+    public static final String ENTITY_SUBTYPE_TYPE_PROPERTY = "type";
+
+    /**
      * Cassandra alarm constants.
      */
     public static final String ALARM_COLUMN_FAMILY_NAME = "alarm";
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java
index 478c6ef..b0636f1 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java
@@ -19,7 +19,8 @@ import org.springframework.data.domain.Pageable;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.data.repository.query.Param;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.dao.model.sql.AssetEntity;
 import org.thingsboard.server.dao.util.SqlDao;
 
@@ -76,6 +77,7 @@ public interface AssetRepository extends CrudRepository<AssetEntity, String> {
                                                          @Param("idOffset") String idOffset,
                                                          Pageable pageable);
 
-    @Query("SELECT NEW org.thingsboard.server.common.data.asset.TenantAssetType(a.type, a.tenantId) FROM AssetEntity a GROUP BY a.tenantId, a.type")
-    List<TenantAssetType> findTenantAssetTypes();
+    @Query("SELECT DISTINCT a.type FROM AssetEntity a WHERE a.tenantId = :tenantId")
+    List<String> findTenantAssetTypes(@Param("tenantId") String tenantId);
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java
index 81ce691..ea256e6 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java
@@ -20,8 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
+import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.DaoUtil;
 import org.thingsboard.server.dao.asset.AssetDao;
@@ -29,10 +31,7 @@ import org.thingsboard.server.dao.model.sql.AssetEntity;
 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
 import org.thingsboard.server.dao.util.SqlDao;
 
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.UUID;
+import java.util.*;
 
 import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID;
 import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUIDs;
@@ -121,7 +120,18 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao<AssetEntity, Asset> im
     }
 
     @Override
-    public ListenableFuture<List<TenantAssetType>> findTenantAssetTypesAsync() {
-        return service.submit(() -> assetRepository.findTenantAssetTypes());
+    public ListenableFuture<List<EntitySubtype>> findTenantAssetTypesAsync(UUID tenantId) {
+        return service.submit(() -> convertTenantAssetTypesToDto(tenantId, assetRepository.findTenantAssetTypes(fromTimeUUID(tenantId))));
+    }
+
+    private List<EntitySubtype> convertTenantAssetTypesToDto(UUID tenantId, List<String> types) {
+        List<EntitySubtype> list = Collections.emptyList();
+        if (types != null && !types.isEmpty()) {
+            list = new ArrayList<>();
+            for (String type : types) {
+                list.add(new EntitySubtype(new TenantId(tenantId), EntityType.ASSET, type));
+            }
+        }
+        return list;
     }
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
index 256f623..bcea4ce 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
@@ -20,7 +20,6 @@ import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.data.repository.query.Param;
 import org.thingsboard.server.dao.model.sql.DeviceEntity;
-import org.thingsboard.server.dao.model.sql.TenantDeviceTypeEntity;
 import org.thingsboard.server.dao.util.SqlDao;
 
 import java.util.List;
@@ -72,8 +71,8 @@ public interface DeviceRepository extends CrudRepository<DeviceEntity, String> {
                                                           @Param("idOffset") String idOffset,
                                                           Pageable pageable);
 
-    @Query("SELECT DISTINCT NEW org.thingsboard.server.dao.model.sql.TenantDeviceTypeEntity(d.tenantId, d.type) FROM DeviceEntity d")
-    List<TenantDeviceTypeEntity> findTenantDeviceTypes();
+    @Query("SELECT DISTINCT d.type FROM DeviceEntity d WHERE d.tenantId = :tenantId")
+    List<String> findTenantDeviceTypes(@Param("tenantId") String tenantId);
 
     DeviceEntity findByTenantIdAndName(String tenantId, String name);
 
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
index 4835c6c..a3f02f8 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
@@ -21,14 +21,14 @@ import org.springframework.data.domain.PageRequest;
 import org.springframework.data.repository.CrudRepository;
 import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.Device;
-import org.thingsboard.server.common.data.TenantDeviceType;
+import org.thingsboard.server.common.data.EntitySubtype;
+import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.UUIDConverter;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageLink;
 import org.thingsboard.server.dao.DaoUtil;
 import org.thingsboard.server.dao.device.DeviceDao;
 import org.thingsboard.server.dao.model.sql.DeviceEntity;
-import org.thingsboard.server.dao.model.sql.TenantDeviceTypeEntity;
 import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
 import org.thingsboard.server.dao.util.SqlDao;
 
@@ -120,16 +120,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
     }
 
     @Override
-    public ListenableFuture<List<TenantDeviceType>> findTenantDeviceTypesAsync() {
-        return service.submit(() -> convertTenantDeviceTypeEntityToDto(deviceRepository.findTenantDeviceTypes()));
+    public ListenableFuture<List<EntitySubtype>> findTenantDeviceTypesAsync(UUID tenantId) {
+        return service.submit(() -> convertTenantDeviceTypesToDto(tenantId, deviceRepository.findTenantDeviceTypes(fromTimeUUID(tenantId))));
     }
 
-    private List<TenantDeviceType> convertTenantDeviceTypeEntityToDto(List<TenantDeviceTypeEntity> entities) {
-        List<TenantDeviceType> list = Collections.emptyList();
-        if (entities != null && !entities.isEmpty()) {
+    private List<EntitySubtype> convertTenantDeviceTypesToDto(UUID tenantId, List<String> types) {
+        List<EntitySubtype> list = Collections.emptyList();
+        if (types != null && !types.isEmpty()) {
             list = new ArrayList<>();
-            for (TenantDeviceTypeEntity entity : entities) {
-                list.add(new TenantDeviceType(entity.getType(), new TenantId(UUIDConverter.fromString(entity.getTenantId()))));
+            for (String type : types) {
+                list.add(new EntitySubtype(new TenantId(tenantId), EntityType.DEVICE, type));
             }
         }
         return list;
diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql
index 71de677..dda8067 100644
--- a/dao/src/main/resources/cassandra/schema.cql
+++ b/dao/src/main/resources/cassandra/schema.cql
@@ -197,13 +197,6 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_by_customer_by_type_an
     PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
     WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
 
-CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.device_types_by_tenant AS
-    SELECT *
-    from thingsboard.device
-    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
-    PRIMARY KEY ( (type, tenant_id), id, customer_id)
-    WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
-
 CREATE TABLE IF NOT EXISTS thingsboard.device_credentials (
 	id timeuuid PRIMARY KEY,
 	device_id timeuuid,
@@ -270,12 +263,12 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_by_customer_by_type_and
     PRIMARY KEY ( customer_id, tenant_id, type, search_text, id )
     WITH CLUSTERING ORDER BY ( tenant_id DESC, type ASC, search_text ASC, id DESC );
 
-CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.asset_types_by_tenant AS
-    SELECT *
-    from thingsboard.asset
-    WHERE tenant_id IS NOT NULL AND customer_id IS NOT NULL AND type IS NOT NULL AND id IS NOT NULL
-    PRIMARY KEY ( (type, tenant_id), id, customer_id)
-    WITH CLUSTERING ORDER BY ( id ASC, customer_id DESC);
+CREATE TABLE IF NOT EXISTS thingsboard.entity_subtype (
+    tenant_id timeuuid,
+    entity_type text, // (DEVICE, ASSET)
+    type text,
+    PRIMARY KEY (tenant_id, entity_type, type)
+);
 
 CREATE TABLE IF NOT EXISTS thingsboard.alarm (
 	id timeuuid,
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java
index 2dbf739..6bcc74a 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAssetServiceTest.java
@@ -22,9 +22,9 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.thingsboard.server.common.data.Customer;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageData;
@@ -181,7 +181,7 @@ public abstract class BaseAssetServiceTest extends AbstractServiceTest {
                 asset.setType("typeA");
                 assets.add(assetService.saveAsset(asset));
             }
-            List<TenantAssetType> assetTypes = assetService.findAssetTypesByTenantId(tenantId).get();
+            List<EntitySubtype> assetTypes = assetService.findAssetTypesByTenantId(tenantId).get();
             Assert.assertNotNull(assetTypes);
             Assert.assertEquals(3, assetTypes.size());
             Assert.assertEquals("typeA", assetTypes.get(0).getType());
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java
index addaf8f..0c35798 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java
@@ -23,8 +23,8 @@ import org.junit.Before;
 import org.junit.Test;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Device;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.TenantDeviceType;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.TextPageData;
@@ -191,7 +191,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest {
                 device.setType("typeA");
                 devices.add(deviceService.saveDevice(device));
             }
-            List<TenantDeviceType> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId).get();
+            List<EntitySubtype> deviceTypes = deviceService.findDeviceTypesByTenantId(tenantId).get();
             Assert.assertNotNull(deviceTypes);
             Assert.assertEquals(3, deviceTypes.size());
             Assert.assertEquals("typeA", deviceTypes.get(0).getType());
diff --git a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java
index 4be102e..062f0e6 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/sql/asset/JpaAssetDaoTest.java
@@ -19,8 +19,8 @@ import com.datastax.driver.core.utils.UUIDs;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.thingsboard.server.common.data.EntitySubtype;
 import org.thingsboard.server.common.data.asset.Asset;
-import org.thingsboard.server.common.data.asset.TenantAssetType;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.CustomerId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -33,12 +33,9 @@ import java.util.List;
 import java.util.Optional;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
-import java.util.stream.Collectors;
 
 import static junit.framework.TestCase.assertFalse;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 
 /**
  * Created by Valerii Sosliuk on 5/21/2017.
@@ -192,11 +189,10 @@ public class JpaAssetDaoTest extends AbstractJpaDaoTest {
         saveAsset(UUIDs.timeBased(), tenantId2, customerId2, "TEST_ASSET_8", "TYPE_1");
         saveAsset(UUIDs.timeBased(), tenantId2, customerId2, "TEST_ASSET_9", "TYPE_1");
 
-        ListenableFuture<List<TenantAssetType>> tenantAssetTypesFuture = assetDao.findTenantAssetTypesAsync();
-        List<TenantAssetType> tenantAssetTypes = tenantAssetTypesFuture.get();
-        assertNotNull(tenantAssetTypes);
-        List<TenantAssetType> tenant1Types = tenantAssetTypes.stream().filter(t -> t.getTenantId().getId().equals(tenantId1)).collect(Collectors.toList());
-        List<TenantAssetType> tenant2Types = tenantAssetTypes.stream().filter(t -> t.getTenantId().getId().equals(tenantId2)).collect(Collectors.toList());
+        List<EntitySubtype> tenant1Types = assetDao.findTenantAssetTypesAsync(tenantId1).get();
+        assertNotNull(tenant1Types);
+        List<EntitySubtype> tenant2Types = assetDao.findTenantAssetTypesAsync(tenantId2).get();
+        assertNotNull(tenant2Types);
 
         assertEquals(3, tenant1Types.size());
         assertTrue(tenant1Types.stream().anyMatch(t -> t.getType().equals("TYPE_1")));