new relation path query
This commit is contained in:
parent
2c06aa475f
commit
909497703a
@ -26,6 +26,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
|
import org.thingsboard.server.common.data.cf.configuration.Argument;
|
||||||
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
|
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
|
||||||
|
import org.thingsboard.server.common.data.cf.configuration.RelationPathQueryDynamicSourceConfiguration;
|
||||||
import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration;
|
import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
@ -178,6 +179,11 @@ public abstract class AbstractCalculatedFieldProcessingService {
|
|||||||
yield Futures.transform(relationService.findByQuery(tenantId, configuration.toEntityRelationsQuery(entityId)),
|
yield Futures.transform(relationService.findByQuery(tenantId, configuration.toEntityRelationsQuery(entityId)),
|
||||||
configuration::resolveEntityIds, calculatedFieldCallbackExecutor);
|
configuration::resolveEntityIds, calculatedFieldCallbackExecutor);
|
||||||
}
|
}
|
||||||
|
case RELATION_PATH_QUERY -> {
|
||||||
|
var configuration = (RelationPathQueryDynamicSourceConfiguration) refDynamicSourceConfiguration;
|
||||||
|
yield Futures.transform(relationService.findByRelationPathQueryAsync(tenantId, configuration.toRelationPathQuery(entityId)),
|
||||||
|
configuration::resolveEntityIds, calculatedFieldCallbackExecutor);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ 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.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
|
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntityRelationPathQuery;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
|
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
|
||||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||||
@ -83,6 +84,8 @@ public interface RelationService {
|
|||||||
|
|
||||||
List<EntityRelation> findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit);
|
List<EntityRelation> findRuleNodeToRuleChainRelations(TenantId tenantId, RuleChainType ruleChainType, int limit);
|
||||||
|
|
||||||
|
ListenableFuture<List<EntityRelation>> findByRelationPathQueryAsync(TenantId tenantId, EntityRelationPathQuery relationPathQuery);
|
||||||
|
|
||||||
// TODO: This method may be useful for some validations in the future
|
// TODO: This method may be useful for some validations in the future
|
||||||
// ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
|
// ListenableFuture<Boolean> checkRecursiveRelation(EntityId from, EntityId to);
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,6 @@ package org.thingsboard.server.common.data.cf.configuration;
|
|||||||
|
|
||||||
public enum CFArgumentDynamicSourceType {
|
public enum CFArgumentDynamicSourceType {
|
||||||
|
|
||||||
RELATION_QUERY
|
RELATION_QUERY, RELATION_PATH_QUERY
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|||||||
property = "type"
|
property = "type"
|
||||||
)
|
)
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
@JsonSubTypes.Type(value = RelationQueryDynamicSourceConfiguration.class, name = "RELATION_QUERY")
|
@JsonSubTypes.Type(value = RelationQueryDynamicSourceConfiguration.class, name = "RELATION_QUERY"),
|
||||||
|
@JsonSubTypes.Type(value = RelationPathQueryDynamicSourceConfiguration.class, name = "RELATION_PATH_QUERY")
|
||||||
})
|
})
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public interface CfArgumentDynamicSourceConfiguration {
|
public interface CfArgumentDynamicSourceConfiguration {
|
||||||
|
|||||||
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2025 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.common.data.cf.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntityRelationPathQuery;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||||
|
import org.thingsboard.server.common.data.relation.RelationPathLevel;
|
||||||
|
import org.thingsboard.server.common.data.util.CollectionsUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RelationPathQueryDynamicSourceConfiguration implements CfArgumentDynamicSourceConfiguration, RelationQueryBased {
|
||||||
|
|
||||||
|
private List<RelationPathLevel> levels;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CFArgumentDynamicSourceType getType() {
|
||||||
|
return CFArgumentDynamicSourceType.RELATION_PATH_QUERY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() {
|
||||||
|
if (CollectionsUtil.isEmpty(levels)) {
|
||||||
|
throw new IllegalArgumentException("At least one relation level must be specified!");
|
||||||
|
}
|
||||||
|
levels.forEach(RelationPathLevel::validate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<EntityId> resolveEntityIds(List<EntityRelation> relations) {
|
||||||
|
EntitySearchDirection lastLevelDirection = getLastLevel().direction();
|
||||||
|
return switch (lastLevelDirection) {
|
||||||
|
case FROM -> relations.stream().map(EntityRelation::getTo).toList();
|
||||||
|
case TO -> relations.stream().map(EntityRelation::getFrom).toList();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@JsonIgnore
|
||||||
|
public int getMaxLevel() {
|
||||||
|
return levels != null ? levels.size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityRelationPathQuery toRelationPathQuery(EntityId entityId) {
|
||||||
|
return new EntityRelationPathQuery(entityId, levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RelationPathLevel getLastLevel() {
|
||||||
|
if (CollectionsUtil.isEmpty(levels)) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
return levels.get(levels.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2025 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.common.data.cf.configuration;
|
||||||
|
|
||||||
|
public interface RelationQueryBased {
|
||||||
|
|
||||||
|
int getMaxLevel();
|
||||||
|
|
||||||
|
default void validateMaxRelationLevel(String argumentName, int maxAllowedRelationLevel) {
|
||||||
|
if (getMaxLevel() > maxAllowedRelationLevel) {
|
||||||
|
throw new IllegalArgumentException("Max relation level is greater than configured " +
|
||||||
|
"maximum allowed relation level in tenant profile: " + maxAllowedRelationLevel + " for argument: " + argumentName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -29,7 +29,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class RelationQueryDynamicSourceConfiguration implements CfArgumentDynamicSourceConfiguration {
|
public class RelationQueryDynamicSourceConfiguration implements CfArgumentDynamicSourceConfiguration, RelationQueryBased {
|
||||||
|
|
||||||
private int maxLevel;
|
private int maxLevel;
|
||||||
private boolean fetchLastLevelOnly;
|
private boolean fetchLastLevelOnly;
|
||||||
@ -59,13 +59,6 @@ public class RelationQueryDynamicSourceConfiguration implements CfArgumentDynami
|
|||||||
return maxLevel == 1;
|
return maxLevel == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateMaxRelationLevel(String argumentName, int maxAllowedRelationLevel) {
|
|
||||||
if (maxLevel > maxAllowedRelationLevel) {
|
|
||||||
throw new IllegalArgumentException("Max relation level is greater than configured " +
|
|
||||||
"maximum allowed relation level in tenant profile: " + maxAllowedRelationLevel + " for argument: " + argumentName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityRelationsQuery toEntityRelationsQuery(EntityId rootEntityId) {
|
public EntityRelationsQuery toEntityRelationsQuery(EntityId rootEntityId) {
|
||||||
if (isSimpleRelation()) {
|
if (isSimpleRelation()) {
|
||||||
throw new IllegalArgumentException("Entity relations query can't be created for a simple relation!");
|
throw new IllegalArgumentException("Entity relations query can't be created for a simple relation!");
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2025 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.common.data.relation;
|
||||||
|
|
||||||
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public record EntityRelationPathQuery(EntityId rootEntityId, List<RelationPathLevel> levels) {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2025 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.common.data.relation;
|
||||||
|
|
||||||
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
|
|
||||||
|
public record RelationPathLevel(EntitySearchDirection direction, String relationType) {
|
||||||
|
|
||||||
|
public void validate() {
|
||||||
|
if (direction == null) {
|
||||||
|
throw new IllegalArgumentException("Direction must be specified!");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(relationType)) {
|
||||||
|
throw new IllegalArgumentException("Relation type must be specified!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -32,17 +32,21 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.transaction.event.TransactionalEventListener;
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||||
import org.thingsboard.server.cache.TbTransactionalCache;
|
import org.thingsboard.server.cache.TbTransactionalCache;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.audit.ActionType;
|
import org.thingsboard.server.common.data.audit.ActionType;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
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.id.UUIDBased;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
|
import org.thingsboard.server.common.data.relation.EntityRelationInfo;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntityRelationPathQuery;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
|
import org.thingsboard.server.common.data.relation.EntityRelationsQuery;
|
||||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||||
import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter;
|
import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter;
|
||||||
|
import org.thingsboard.server.common.data.relation.RelationPathLevel;
|
||||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||||
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
|
import org.thingsboard.server.common.data.relation.RelationsSearchParameters;
|
||||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||||
@ -495,6 +499,23 @@ public class BaseRelationService implements RelationService {
|
|||||||
return relationDao.findRuleNodeToRuleChainRelations(ruleChainType, limit);
|
return relationDao.findRuleNodeToRuleChainRelations(ruleChainType, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<List<EntityRelation>> findByRelationPathQueryAsync(TenantId tenantId, EntityRelationPathQuery relationPathQuery) {
|
||||||
|
log.trace("Executing findByRelationPathQuery, tenantId [{}], relationPathQuery {}", tenantId, relationPathQuery);
|
||||||
|
validateId(tenantId, id -> "Invalid tenant id: " + id);
|
||||||
|
validate(relationPathQuery);
|
||||||
|
return executor.submit(() -> relationDao.findByRelationPathQuery(tenantId, relationPathQuery));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validate(EntityRelationPathQuery relationPathQuery) {
|
||||||
|
validateId((UUIDBased) relationPathQuery.rootEntityId(), id -> "Invalid root entity id: " + id);
|
||||||
|
List<RelationPathLevel> levels = relationPathQuery.levels();
|
||||||
|
if (CollectionUtils.isEmpty(levels)) {
|
||||||
|
throw new DataValidationException("Relation path levels should be specified!");
|
||||||
|
}
|
||||||
|
levels.forEach(RelationPathLevel::validate);
|
||||||
|
}
|
||||||
|
|
||||||
protected void validate(EntityRelation relation) {
|
protected void validate(EntityRelation relation) {
|
||||||
if (relation == null) {
|
if (relation == null) {
|
||||||
throw new DataValidationException("Relation type should be specified!");
|
throw new DataValidationException("Relation type should be specified!");
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
import org.thingsboard.server.common.data.id.EntityId;
|
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.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntityRelationPathQuery;
|
||||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||||
|
|
||||||
@ -71,4 +72,6 @@ public interface RelationDao {
|
|||||||
|
|
||||||
List<EntityRelation> findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit);
|
List<EntityRelation> findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit);
|
||||||
|
|
||||||
|
List<EntityRelation> findByRelationPathQuery(TenantId tenantId, EntityRelationPathQuery relationPathQuery);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.server.common.data.cf.CalculatedField;
|
import org.thingsboard.server.common.data.cf.CalculatedField;
|
||||||
import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration;
|
import org.thingsboard.server.common.data.cf.configuration.ArgumentsBasedCalculatedFieldConfiguration;
|
||||||
import org.thingsboard.server.common.data.cf.configuration.RelationQueryDynamicSourceConfiguration;
|
import org.thingsboard.server.common.data.cf.configuration.RelationQueryBased;
|
||||||
import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration;
|
import org.thingsboard.server.common.data.cf.configuration.ScheduledUpdateSupportedCalculatedFieldConfiguration;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
||||||
@ -98,10 +98,10 @@ public class CalculatedFieldDataValidator extends DataValidator<CalculatedField>
|
|||||||
if (!(calculatedField.getConfiguration() instanceof ArgumentsBasedCalculatedFieldConfiguration argumentsBasedCfg)) {
|
if (!(calculatedField.getConfiguration() instanceof ArgumentsBasedCalculatedFieldConfiguration argumentsBasedCfg)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, RelationQueryDynamicSourceConfiguration> relationQueryBasedArguments = argumentsBasedCfg.getArguments().entrySet()
|
Map<String, RelationQueryBased> relationQueryBasedArguments = argumentsBasedCfg.getArguments().entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(entry -> entry.getValue().hasDynamicSource())
|
.filter(entry -> entry.getValue().hasDynamicSource())
|
||||||
.collect(Collectors.toMap(Map.Entry::getKey, entry -> (RelationQueryDynamicSourceConfiguration) entry.getValue().getRefDynamicSourceConfiguration()));
|
.collect(Collectors.toMap(Map.Entry::getKey, entry -> (RelationQueryBased) entry.getValue().getRefDynamicSourceConfiguration()));
|
||||||
if (relationQueryBasedArguments.isEmpty()) {
|
if (relationQueryBasedArguments.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,9 @@ import org.thingsboard.server.common.data.id.EntityId;
|
|||||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.relation.EntityRelation;
|
import org.thingsboard.server.common.data.relation.EntityRelation;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntityRelationPathQuery;
|
||||||
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
|
||||||
|
import org.thingsboard.server.common.data.relation.RelationPathLevel;
|
||||||
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
|
||||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||||
import org.thingsboard.server.dao.DaoUtil;
|
import org.thingsboard.server.dao.DaoUtil;
|
||||||
@ -43,6 +46,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_ID_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_ID_PROPERTY;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_TYPE_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_FROM_TYPE_PROPERTY;
|
||||||
|
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TABLE_NAME;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_ID_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_ID_PROPERTY;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_TYPE_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TO_TYPE_PROPERTY;
|
||||||
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_GROUP_PROPERTY;
|
import static org.thingsboard.server.dao.model.ModelConstants.RELATION_TYPE_GROUP_PROPERTY;
|
||||||
@ -293,4 +297,99 @@ public class JpaRelationDao extends JpaAbstractDaoListeningExecutorService imple
|
|||||||
public List<EntityRelation> findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit) {
|
public List<EntityRelation> findRuleNodeToRuleChainRelations(RuleChainType ruleChainType, int limit) {
|
||||||
return DaoUtil.convertDataList(relationRepository.findRuleNodeToRuleChainRelations(ruleChainType, PageRequest.of(0, limit)));
|
return DaoUtil.convertDataList(relationRepository.findRuleNodeToRuleChainRelations(ruleChainType, PageRequest.of(0, limit)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<EntityRelation> findByRelationPathQuery(TenantId tenantId, EntityRelationPathQuery query) {
|
||||||
|
List<RelationPathLevel> levels = query.levels();
|
||||||
|
if (levels == null || levels.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
String sql = buildRelationPathSql(query);
|
||||||
|
Object[] params = buildRelationPathParams(query);
|
||||||
|
|
||||||
|
log.info("[{}] relation path query: {}", tenantId, sql);
|
||||||
|
|
||||||
|
return jdbcTemplate.queryForList(sql, params).stream()
|
||||||
|
.map(row -> {
|
||||||
|
var entityRelation = new EntityRelation();
|
||||||
|
var fromId = (UUID) row.get(RELATION_FROM_ID_PROPERTY);
|
||||||
|
var fromType = (String) row.get(RELATION_FROM_TYPE_PROPERTY);
|
||||||
|
var toId = (UUID) row.get(RELATION_TO_ID_PROPERTY);
|
||||||
|
var toType = (String) row.get(RELATION_TO_TYPE_PROPERTY);
|
||||||
|
var grp = (String) row.get(RELATION_TYPE_GROUP_PROPERTY);
|
||||||
|
var type = (String) row.get(RELATION_TYPE_PROPERTY);
|
||||||
|
var version = (Long) row.get(VERSION_COLUMN);
|
||||||
|
|
||||||
|
entityRelation.setFrom(EntityIdFactory.getByTypeAndUuid(fromType, fromId));
|
||||||
|
entityRelation.setTo(EntityIdFactory.getByTypeAndUuid(toType, toId));
|
||||||
|
entityRelation.setType(type);
|
||||||
|
entityRelation.setTypeGroup(RelationTypeGroup.valueOf(grp));
|
||||||
|
entityRelation.setVersion(version);
|
||||||
|
return entityRelation;
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object[] buildRelationPathParams(EntityRelationPathQuery query) {
|
||||||
|
final List<Object> params = new ArrayList<>();
|
||||||
|
// seed
|
||||||
|
params.add(query.rootEntityId().getId());
|
||||||
|
params.add(query.rootEntityId().getEntityType().name());
|
||||||
|
|
||||||
|
// levels
|
||||||
|
for (var lvl : query.levels()) {
|
||||||
|
params.add(lvl.relationType());
|
||||||
|
}
|
||||||
|
return params.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildRelationPathSql(EntityRelationPathQuery query) {
|
||||||
|
List<RelationPathLevel> levels = query.levels();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.append("WITH seed AS (\n")
|
||||||
|
.append(" SELECT ?::uuid AS id, ?::varchar AS type\n")
|
||||||
|
.append(")");
|
||||||
|
|
||||||
|
String prev = "seed";
|
||||||
|
for (int i = 0; i < levels.size() - 1; i++) {
|
||||||
|
RelationPathLevel lvl = levels.get(i);
|
||||||
|
boolean down = lvl.direction() == EntitySearchDirection.FROM;
|
||||||
|
|
||||||
|
String cur = "lvl" + (i + 1);
|
||||||
|
String joinCond = down
|
||||||
|
? "r.from_id = p.id AND r.from_type = p.type"
|
||||||
|
: "r.to_id = p.id AND r.to_type = p.type";
|
||||||
|
String selectNext = down
|
||||||
|
? "r.to_id AS id, r.to_type AS type"
|
||||||
|
: "r.from_id AS id, r.from_type AS type";
|
||||||
|
|
||||||
|
sb.append(",\n").append(cur).append(" AS (\n")
|
||||||
|
.append(" SELECT ").append(selectNext).append("\n")
|
||||||
|
.append(" FROM ").append(RELATION_TABLE_NAME).append(" r\n")
|
||||||
|
.append(" JOIN ").append(prev).append(" p ON ").append(joinCond).append("\n")
|
||||||
|
.append(" WHERE r.relation_type_group = '").append(RelationTypeGroup.COMMON).append("'\n")
|
||||||
|
.append(" AND r.relation_type = ?\n")
|
||||||
|
.append(")");
|
||||||
|
prev = cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
RelationPathLevel last = levels.get(levels.size() - 1);
|
||||||
|
boolean lastDown = last.direction() == EntitySearchDirection.FROM;
|
||||||
|
String prevForLast = (levels.size() == 1) ? "seed" : prev;
|
||||||
|
String lastJoin = lastDown
|
||||||
|
? "r.from_id = p.id AND r.from_type = p.type"
|
||||||
|
: "r.to_id = p.id AND r.to_type = p.type";
|
||||||
|
|
||||||
|
sb.append("\n")
|
||||||
|
.append("SELECT r.from_id, r.from_type, r.to_id, r.to_type,\n")
|
||||||
|
.append(" r.relation_type_group, r.relation_type, r.version\n")
|
||||||
|
.append("FROM ").append(RELATION_TABLE_NAME).append(" r\n")
|
||||||
|
.append("JOIN ").append(prevForLast).append(" p ON ").append(lastJoin).append("\n")
|
||||||
|
.append("WHERE r.relation_type_group = '").append(RelationTypeGroup.COMMON).append("'\n")
|
||||||
|
.append(" AND r.relation_type = ?");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user