From 2dd2cae91b912dc1eab59d002bafe9d5fe7b437d Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 27 Jul 2020 18:15:05 +0300 Subject: [PATCH] Bug Fix for DeviceSearch and Entity queries --- .../query/DefaultEntityQueryRepository.java | 81 ++++++++++++------- .../server/dao/sql/query/QueryContext.java | 7 +- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index a5d7d570fd..e45af4b15c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -333,8 +333,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { if (pageLink.getPageSize() > 0) { dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); } -// log.error("QUERY: {}", dataQuery); -// Arrays.asList(ctx.getParameterNames()).forEach(param -> log.error("QUERY PARAM: {}->{}", param, ctx.getValue(param))); + log.error("QUERY: {}", dataQuery); + Arrays.asList(ctx.getParameterNames()).forEach(param -> log.error("QUERY PARAM: {}->{}", param, ctx.getValue(param))); List> rows = jdbcTemplate.queryForList(dataQuery, ctx); return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); }); @@ -432,7 +432,12 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { String lvlFilter = getLvlFilter(entityFilter.getMaxLevel()); String selectFields = "SELECT tenant_id, customer_id, id, created_time, type, name, label FROM " + entityType.name() + " WHERE id in ( SELECT entity_id"; String from = getQueryTemplate(entityFilter.getDirection()); - String whereFilter = " WHERE re.relation_type = :where_relation_type AND re.to_type = :where_entity_type"; + String whereFilter = " WHERE"; + if (!StringUtils.isEmpty(entityFilter.getRelationType())) { + ctx.addStringParameter("where_relation_type", entityFilter.getRelationType()); + whereFilter += " re.relation_type = :where_relation_type AND"; + } + whereFilter += " re.to_type = :where_entity_type"; from = String.format(from, lvlFilter, whereFilter); String query = "( " + selectFields + from + ")"; @@ -443,7 +448,6 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { query += " )"; ctx.addUuidParameter("relation_root_id", rootId.getId()); ctx.addStringParameter("relation_root_type", rootId.getEntityType().name()); - ctx.addStringParameter("where_relation_type", entityFilter.getRelationType()); ctx.addStringParameter("where_entity_type", entityType.name()); return query; } @@ -465,36 +469,27 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { StringBuilder whereFilter; if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) { - whereFilter = new StringBuilder(" WHERE "); + whereFilter = new StringBuilder(); boolean first = true; boolean single = entityFilter.getFilters().size() == 1; int entityTypeFilterIdx = 0; for (EntityTypeFilter etf : entityFilter.getFilters()) { - if (first) { - first = false; - } else { - whereFilter.append(" AND "); + String etfCondition = buildEtfCondition(ctx, etf, entityFilter.getDirection(), entityTypeFilterIdx++); + if (!etfCondition.isEmpty()) { + if (first) { + whereFilter.append(" WHERE "); + first = false; + } else { + whereFilter.append(" AND "); + } + if (!single) { + whereFilter.append(" ("); + } + whereFilter.append(etfCondition); + if (!single) { + whereFilter.append(" )"); + } } - String relationType = etf.getRelationType(); - if (!single) { - whereFilter.append(" ("); - } - List whereEntityTypes = etf.getEntityTypes().stream().map(EntityType::name).collect(Collectors.toList()); - whereFilter - .append(" re.relation_type = :where_relation_type").append(entityTypeFilterIdx); - if (!whereEntityTypes.isEmpty()) { - whereFilter.append(" and re.") - .append(entityFilter.getDirection().equals(EntitySearchDirection.FROM) ? "to" : "from") - .append("_type in (:where_entity_types").append(entityTypeFilterIdx).append(")"); - } - if (!single) { - whereFilter.append(" )"); - } - ctx.addStringParameter("where_relation_type" + entityTypeFilterIdx, relationType); - if (!whereEntityTypes.isEmpty()) { - ctx.addStringListParameter("where_entity_types" + entityTypeFilterIdx, whereEntityTypes); - } - entityTypeFilterIdx++; } } else { whereFilter = new StringBuilder(); @@ -503,6 +498,34 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { return "( " + selectFields + from + ")"; } + private String buildEtfCondition(QueryContext ctx, EntityTypeFilter etf, EntitySearchDirection direction, int entityTypeFilterIdx) { + StringBuilder whereFilter = new StringBuilder(); + String relationType = etf.getRelationType(); + List entityTypes = etf.getEntityTypes(); + List whereEntityTypes; + if (entityTypes == null || entityTypes.isEmpty()) { + whereEntityTypes = Collections.emptyList(); + } else { + whereEntityTypes = etf.getEntityTypes().stream().map(EntityType::name).collect(Collectors.toList()); + } + boolean hasRelationType = !StringUtils.isEmpty(relationType); + if (hasRelationType) { + ctx.addStringParameter("where_relation_type" + entityTypeFilterIdx, relationType); + whereFilter + .append("re.relation_type = :where_relation_type").append(entityTypeFilterIdx); + } + if (!whereEntityTypes.isEmpty()) { + if (hasRelationType) { + whereFilter.append(" and "); + } + whereFilter.append("re.") + .append(direction.equals(EntitySearchDirection.FROM) ? "to" : "from") + .append("_type in (:where_entity_types").append(entityTypeFilterIdx).append(")"); + ctx.addStringListParameter("where_entity_types" + entityTypeFilterIdx, whereEntityTypes); + } + return whereFilter.toString(); + } + private String getLvlFilter(int maxLevel) { return maxLevel > 0 ? ("and lvl <= " + (maxLevel - 1)) : ""; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/QueryContext.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/QueryContext.java index c0ce89e2fa..ec862e56d6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/QueryContext.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/QueryContext.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.sql.query; +import lombok.extern.slf4j.Slf4j; import org.hibernate.type.PostgresUUIDType; import org.springframework.jdbc.core.namedparam.SqlParameterSource; import org.thingsboard.server.common.data.EntityType; @@ -27,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +@Slf4j public class QueryContext implements SqlParameterSource { private static final PostgresUUIDType UUID_TYPE = new PostgresUUIDType(); @@ -43,9 +45,12 @@ public class QueryContext implements SqlParameterSource { void addParameter(String name, Object value, int type, String typeName) { Parameter newParam = new Parameter(value, type, typeName); Parameter oldParam = params.put(name, newParam); - if (oldParam != null && !oldParam.value.equals(newParam.value)) { + if (oldParam != null && oldParam.value != null && !oldParam.value.equals(newParam.value)) { throw new RuntimeException("Parameter with name: " + name + " was already registered!"); } + if(value == null){ + log.warn("[{}][{}][{}] Trying to set null value", getTenantId(), getCustomerId(), name); + } } public void append(String s) {