@ -21,6 +21,7 @@ import org.springframework.stereotype.Repository;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.EntityType ;
import org.thingsboard.server.common.data.UUIDConverter ;
import org.thingsboard.server.common.data.UUIDConverter ;
import org.thingsboard.server.common.data.id.CustomerId ;
import org.thingsboard.server.common.data.id.CustomerId ;
import org.thingsboard.server.common.data.id.EntityId ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.id.TenantId ;
import org.thingsboard.server.common.data.page.PageData ;
import org.thingsboard.server.common.data.page.PageData ;
import org.thingsboard.server.common.data.query.AssetTypeFilter ;
import org.thingsboard.server.common.data.query.AssetTypeFilter ;
@ -31,17 +32,19 @@ import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery ;
import org.thingsboard.server.common.data.query.EntityDataQuery ;
import org.thingsboard.server.common.data.query.EntityDataSortOrder ;
import org.thingsboard.server.common.data.query.EntityDataSortOrder ;
import org.thingsboard.server.common.data.query.EntityFilter ;
import org.thingsboard.server.common.data.query.EntityFilter ;
import org.thingsboard.server.common.data.query.EntityKey Type ;
import org.thingsboard.server.common.data.query.EntityFilter Type ;
import org.thingsboard.server.common.data.query.EntityListFilter ;
import org.thingsboard.server.common.data.query.EntityListFilter ;
import org.thingsboard.server.common.data.query.EntityNameFilter ;
import org.thingsboard.server.common.data.query.EntityNameFilter ;
import org.thingsboard.server.common.data.query.EntityViewTypeFilter ;
import org.thingsboard.server.common.data.query.EntityViewTypeFilter ;
import org.thingsboard.server.common.data.query.RelationsQueryFilter ;
import org.thingsboard.server.common.data.query.SingleEntityFilter ;
import org.thingsboard.server.common.data.query.SingleEntityFilter ;
import org.thingsboard.server.common.data.relation.EntitySearchDirection ;
import org.thingsboard.server.common.data.relation.EntityTypeFilter ;
import org.thingsboard.server.dao.util.SqlDao ;
import org.thingsboard.server.dao.util.SqlDao ;
import javax.persistence.EntityManager ;
import javax.persistence.EntityManager ;
import javax.persistence.PersistenceContext ;
import javax.persistence.PersistenceContext ;
import java.math.BigInteger ;
import java.math.BigInteger ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.HashMap ;
import java.util.HashMap ;
import java.util.List ;
import java.util.List ;
@ -56,6 +59,7 @@ import java.util.stream.Collectors;
public class DefaultEntityQueryRepository implements EntityQueryRepository {
public class DefaultEntityQueryRepository implements EntityQueryRepository {
private static final Map < EntityType , String > entityTableMap = new HashMap < > ( ) ;
private static final Map < EntityType , String > entityTableMap = new HashMap < > ( ) ;
static {
static {
entityTableMap . put ( EntityType . ASSET , "asset" ) ;
entityTableMap . put ( EntityType . ASSET , "asset" ) ;
entityTableMap . put ( EntityType . DEVICE , "device" ) ;
entityTableMap . put ( EntityType . DEVICE , "device" ) ;
@ -73,9 +77,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
public long countEntitiesByQuery ( TenantId tenantId , CustomerId customerId , EntityCountQuery query ) {
public long countEntitiesByQuery ( TenantId tenantId , CustomerId customerId , EntityCountQuery query ) {
EntityType entityType = resolveEntityType ( query . getEntityFilter ( ) ) ;
EntityType entityType = resolveEntityType ( query . getEntityFilter ( ) ) ;
String countQuery = String . format ( "select count(e.id) from %s e where %s" ,
String countQuery = String . format ( "select count(e.id) from %s e where %s" ,
entityTableMap . get ( entityType ) , this . buildEntityWhere ( tenantId , customerId , query . getEntityFilter ( ) ,
getEntityTableQuery ( query . getEntityFilter ( ) , entityType ) , this . buildEntityWhere ( tenantId , customerId , query . getEntityFilter ( ) ,
Collections . emptyList ( ) , entityType ) ) ;
Collections . emptyList ( ) , entityType ) ) ;
return ( ( BigInteger ) entityManager . createNativeQuery ( countQuery )
return ( ( BigInteger ) entityManager . createNativeQuery ( countQuery )
. getSingleResult ( ) ) . longValue ( ) ;
. getSingleResult ( ) ) . longValue ( ) ;
}
}
@ -122,12 +126,12 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
String fromClause = String . format ( "from (select %s from (select %s from %s e where %s) entities %s %s) result" ,
String fromClause = String . format ( "from (select %s from (select %s from %s e where %s) entities %s %s) result" ,
topSelection ,
topSelection ,
entityFieldsSelection ,
entityFieldsSelection ,
entityTableMap . get ( entityType ) ,
getEntityTableQuery ( query . getEntityFilter ( ) , entityType ) ,
entityWhereClause ,
entityWhereClause ,
latestJoins ,
latestJoins ,
whereClause ) ;
whereClause ) ;
int totalElements = ( ( BigInteger ) entityManager . createNativeQuery ( String . format ( "select count(*) %s" , fromClause ) )
int totalElements = ( ( BigInteger ) entityManager . createNativeQuery ( String . format ( "select count(*) %s" , fromClause ) )
. getSingleResult ( ) ) . intValue ( ) ;
. getSingleResult ( ) ) . intValue ( ) ;
String dataQuery = String . format ( "select * %s" , fromClause ) ;
String dataQuery = String . format ( "select * %s" , fromClause ) ;
@ -154,26 +158,34 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
}
}
private String buildEntityWhere ( TenantId tenantId ,
private String buildEntityWhere ( TenantId tenantId ,
CustomerId customerId ,
CustomerId customerId ,
EntityFilter entityFilter ,
EntityFilter entityFilter ,
List < EntityKeyMapping > entityFieldsFilters ,
List < EntityKeyMapping > entityFieldsFilters ,
EntityType entityType ) {
EntityType entityType ) {
String permissionQuery = this . buildPermissionQuery ( tenantId , customerId , entityType ) ;
String permissionQuery = this . buildPermissionQuery ( entityFilter , tenantId , customerId , entityType ) ;
String entityFilterQuery = this . buildEntityFilterQuery ( entityFilter ) ;
String entityFilterQuery = this . buildEntityFilterQuery ( entityFilter ) ;
String result = permissionQuery ;
if ( ! entityFilterQuery . isEmpty ( ) ) {
result + = " and " + entityFilterQuery ;
}
if ( ! entityFieldsFilters . isEmpty ( ) ) {
if ( ! entityFieldsFilters . isEmpty ( ) ) {
String entityFieldsQuery = EntityKeyMapping . buildQuery ( entityFieldsFilters ) ;
result + = " and " + entityFieldsFilters ;
return String . join ( " and " , permissionQuery , entityFilterQuery , entityFieldsQuery ) ;
} else {
return String . join ( " and " , permissionQuery , entityFilterQuery ) ;
}
}
return result ;
}
}
private String buildPermissionQuery ( TenantId tenantId , CustomerId customerId , EntityType entityType ) {
private String buildPermissionQuery ( EntityFilter entityFilter , TenantId tenantId , CustomerId customerId , EntityType entityType ) {
String permissionQuery = String . format ( "e.tenant_id='%s'" , UUIDConverter . fromTimeUUID ( tenantId . getId ( ) ) ) ;
if ( entityFilter . getType ( ) . equals ( EntityFilterType . RELATIONS_QUERY ) ) {
if ( entityType ! = EntityType . TENANT & & entityType ! = EntityType . CUSTOMER ) {
return String . format ( "e.tenant_id='%s' and e.customer_id='%s'" , UUIDConverter . fromTimeUUID ( tenantId . getId ( ) ) , UUIDConverter . fromTimeUUID ( customerId . getId ( ) ) ) ;
permissionQuery = String . format ( "%s and e.customer_id='%s'" , permissionQuery , UUIDConverter . fromTimeUUID ( customerId . getId ( ) ) ) ;
} else {
if ( entityType = = EntityType . TENANT ) {
return String . format ( "e.id='%s'" , UUIDConverter . fromTimeUUID ( tenantId . getId ( ) ) ) ;
} else if ( entityType = = EntityType . CUSTOMER ) {
return String . format ( "e.tenant_id='%s' and e.id='%s'" , UUIDConverter . fromTimeUUID ( tenantId . getId ( ) ) , UUIDConverter . fromTimeUUID ( customerId . getId ( ) ) ) ;
} else {
return String . format ( "e.tenant_id='%s' and e.customer_id='%s'" , UUIDConverter . fromTimeUUID ( tenantId . getId ( ) ) , UUIDConverter . fromTimeUUID ( customerId . getId ( ) ) ) ;
}
}
}
return permissionQuery ;
}
}
private String buildEntityFilterQuery ( EntityFilter entityFilter ) {
private String buildEntityFilterQuery ( EntityFilter entityFilter ) {
@ -188,12 +200,129 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
case DEVICE_TYPE :
case DEVICE_TYPE :
case ENTITY_VIEW_TYPE :
case ENTITY_VIEW_TYPE :
return this . typeQuery ( entityFilter ) ;
return this . typeQuery ( entityFilter ) ;
case RELATIONS_QUERY :
return "" ;
default :
default :
throw new RuntimeException ( "Not implemented!" ) ;
throw new RuntimeException ( "Not implemented!" ) ;
}
}
}
}
private String buildWhere ( List < EntityKeyMapping > selectionMapping , List < EntityKeyMapping > latestFiltersMapping , String searchText ) {
private String getEntityTableQuery ( EntityFilter entityFilter , EntityType entityType ) {
switch ( entityFilter . getType ( ) ) {
case RELATIONS_QUERY :
return relationQuery ( ( RelationsQueryFilter ) entityFilter ) ;
default :
return entityTableMap . get ( entityType ) ;
}
}
private String relationQuery ( RelationsQueryFilter entityFilter ) {
EntityId rootId = entityFilter . getRootEntity ( ) ;
String lvlFilter = entityFilter . getMaxLevel ( ) > 0 ? ( "and lvl <= " + ( entityFilter . getMaxLevel ( ) - 1 ) ) : "" ;
//TODO: refactoring
String selectFields = " select CASE\n" +
" WHEN entity.entity_type = 'DEVICE'\n" +
" THEN (select tenant_id from device where id = entity_id)\n" +
" WHEN entity.entity_type = 'ASSET'\n" +
" THEN (select tenant_id from asset where id = entity_id)\n" +
" WHEN entity.entity_type = 'CUSTOMER'\n" +
" THEN (select tenant_id from customer where id = entity_id)\n" +
" WHEN entity.entity_type = 'TENANT'\n" +
" THEN entity_id\n" +
" END as tenant_id,\n" +
" CASE\n" +
" WHEN entity.entity_type = 'DEVICE'\n" +
" THEN (select customer_id from device where id = entity_id)\n" +
" WHEN entity.entity_type = 'ASSET'\n" +
" THEN (select customer_id from asset where id = entity_id)\n" +
" WHEN entity.entity_type = 'CUSTOMER'\n" +
" THEN entity_id\n" +
" WHEN entity.entity_type = 'TENANT'\n" +
" THEN '1b21dd2138140008080808080808080'\n" +
" END as customer_id,\n" +
" entity.entity_id as id,\n" +
" CASE\n" +
" WHEN entity.entity_type = 'DEVICE'\n" +
" THEN (select type from device where id = entity_id)\n" +
" WHEN entity.entity_type = 'ASSET' THEN (select type from asset where id = entity_id)\n" +
" ELSE entity.entity_type\n" +
" END as type,\n" +
" CASE\n" +
" WHEN entity.entity_type = 'DEVICE'\n" +
" THEN (select name from device where id = entity_id)\n" +
" WHEN entity.entity_type = 'ASSET' THEN (select name from asset where id = entity_id)\n" +
" WHEN entity.entity_type = 'CUSTOMER'\n" +
" THEN (select title from customer where id = entity_id)\n" +
" WHEN entity.entity_type = 'TENANT'\n" +
" THEN (select title from tenant where id = entity_id)\n" +
" ELSE entity.entity_type\n" +
" END as name,\n" +
" CASE\n" +
" WHEN entity.entity_type = 'DEVICE'\n" +
" THEN (select label from device where id = entity_id)\n" +
" WHEN entity.entity_type = 'ASSET' THEN (select label from asset where id = entity_id)\n" +
" WHEN entity.entity_type = 'CUSTOMER'\n" +
" THEN (select title from customer where id = entity_id)\n" +
" WHEN entity.entity_type = 'TENANT'\n" +
" THEN (select title from tenant where id = entity_id)\n" +
" ELSE entity.entity_type\n" +
" END as label,\n" +
" entity.entity_type as entity_type" ;
String fromTemplate = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" +
" SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" +
" FROM relation" +
" WHERE $in_id = '%s' and $in_type = '%s' and relation_type_group = 'COMMON'" +
" UNION ALL" +
" SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" +
" FROM relation r" +
" INNER JOIN related_entities re ON" +
" r.$in_id = re.$out_id and r.$in_type = re.$out_type and" +
" relation_type_group = 'COMMON' %s)" +
" SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" +
" from related_entities re" +
" %s ) entity" ;
String from ;
if ( entityFilter . getDirection ( ) . equals ( EntitySearchDirection . FROM ) ) {
from = fromTemplate . replace ( "$in" , "from" ) . replace ( "$out" , "to" ) ;
} else {
from = fromTemplate . replace ( "$in" , "to" ) . replace ( "$out" , "from" ) ;
}
StringBuilder whereFilter ;
if ( entityFilter . getFilters ( ) ! = null & & ! entityFilter . getFilters ( ) . isEmpty ( ) ) {
whereFilter = new StringBuilder ( " WHERE " ) ;
boolean first = true ;
boolean single = entityFilter . getFilters ( ) . size ( ) = = 1 ;
for ( EntityTypeFilter etf : entityFilter . getFilters ( ) ) {
if ( first ) {
first = false ;
} else {
whereFilter . append ( " AND " ) ;
}
String relationType = etf . getRelationType ( ) ;
String types = etf . getEntityTypes ( ) . stream ( ) . map ( type - > "'" + type + "'" ) . collect ( Collectors . joining ( ", " ) ) ;
if ( ! single ) {
whereFilter . append ( " (" ) ;
}
whereFilter . append ( " re.relation_type = '" ) . append ( relationType ) . append ( "' and re." )
. append ( entityFilter . getDirection ( ) . equals ( EntitySearchDirection . FROM ) ? "to" : "from" )
. append ( "_type in (" ) . append ( types ) . append ( ")" ) ;
if ( ! single ) {
whereFilter . append ( " )" ) ;
}
}
} else {
whereFilter = new StringBuilder ( ) ;
}
from = String . format ( from , UUIDConverter . fromTimeUUID ( rootId . getId ( ) ) , rootId . getEntityType ( ) . name ( ) , lvlFilter , whereFilter ) ;
return "( " + selectFields + from + ")" ;
}
private String buildWhere
( List < EntityKeyMapping > selectionMapping , List < EntityKeyMapping > latestFiltersMapping , String searchText ) {
String latestFilters = EntityKeyMapping . buildQuery ( latestFiltersMapping ) ;
String latestFilters = EntityKeyMapping . buildQuery ( latestFiltersMapping ) ;
String textSearchQuery = this . buildTextSearchQuery ( selectionMapping , searchText ) ;
String textSearchQuery = this . buildTextSearchQuery ( selectionMapping , searchText ) ;
String query ;
String query ;
@ -241,16 +370,16 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
String name ;
String name ;
switch ( filter . getType ( ) ) {
switch ( filter . getType ( ) ) {
case ASSET_TYPE :
case ASSET_TYPE :
type = ( ( AssetTypeFilter ) filter ) . getAssetType ( ) ;
type = ( ( AssetTypeFilter ) filter ) . getAssetType ( ) ;
name = ( ( AssetTypeFilter ) filter ) . getAssetNameFilter ( ) ;
name = ( ( AssetTypeFilter ) filter ) . getAssetNameFilter ( ) ;
break ;
break ;
case DEVICE_TYPE :
case DEVICE_TYPE :
type = ( ( DeviceTypeFilter ) filter ) . getDeviceType ( ) ;
type = ( ( DeviceTypeFilter ) filter ) . getDeviceType ( ) ;
name = ( ( DeviceTypeFilter ) filter ) . getDeviceNameFilter ( ) ;
name = ( ( DeviceTypeFilter ) filter ) . getDeviceNameFilter ( ) ;
break ;
break ;
case ENTITY_VIEW_TYPE :
case ENTITY_VIEW_TYPE :
type = ( ( EntityViewTypeFilter ) filter ) . getEntityViewType ( ) ;
type = ( ( EntityViewTypeFilter ) filter ) . getEntityViewType ( ) ;
name = ( ( EntityViewTypeFilter ) filter ) . getEntityViewNameFilter ( ) ;
name = ( ( EntityViewTypeFilter ) filter ) . getEntityViewNameFilter ( ) ;
break ;
break ;
default :
default :
throw new RuntimeException ( "Not supported!" ) ;
throw new RuntimeException ( "Not supported!" ) ;
@ -261,11 +390,11 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
private EntityType resolveEntityType ( EntityFilter entityFilter ) {
private EntityType resolveEntityType ( EntityFilter entityFilter ) {
switch ( entityFilter . getType ( ) ) {
switch ( entityFilter . getType ( ) ) {
case SINGLE_ENTITY :
case SINGLE_ENTITY :
return ( ( SingleEntityFilter ) entityFilter ) . getSingleEntity ( ) . getEntityType ( ) ;
return ( ( SingleEntityFilter ) entityFilter ) . getSingleEntity ( ) . getEntityType ( ) ;
case ENTITY_LIST :
case ENTITY_LIST :
return ( ( EntityListFilter ) entityFilter ) . getEntityType ( ) ;
return ( ( EntityListFilter ) entityFilter ) . getEntityType ( ) ;
case ENTITY_NAME :
case ENTITY_NAME :
return ( ( EntityNameFilter ) entityFilter ) . getEntityType ( ) ;
return ( ( EntityNameFilter ) entityFilter ) . getEntityType ( ) ;
case ASSET_TYPE :
case ASSET_TYPE :
case ASSET_SEARCH_QUERY :
case ASSET_SEARCH_QUERY :
return EntityType . ASSET ;
return EntityType . ASSET ;
@ -275,8 +404,10 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
case ENTITY_VIEW_TYPE :
case ENTITY_VIEW_TYPE :
case ENTITY_VIEW_SEARCH_QUERY :
case ENTITY_VIEW_SEARCH_QUERY :
return EntityType . ENTITY_VIEW ;
return EntityType . ENTITY_VIEW ;
default :
case RELATIONS_QUERY :
throw new RuntimeException ( "Not implemented!" ) ;
return ( ( RelationsQueryFilter ) entityFilter ) . getRootEntity ( ) . getEntityType ( ) ;
default :
throw new RuntimeException ( "Not implemented!" ) ;
}
}
}
}
}
}