[improvement][headless] Fix the null pointer issue when handling execution permissions and retrieving recommended metrics (#1780)

This commit is contained in:
lexluo09
2024-10-10 23:22:48 +08:00
committed by GitHub
parent 484861571f
commit 0fe7f0e1a7
4 changed files with 72 additions and 49 deletions

View File

@@ -43,6 +43,7 @@ import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static com.tencent.supersonic.common.pojo.Constants.DAY_FORMAT; import static com.tencent.supersonic.common.pojo.Constants.DAY_FORMAT;
import static com.tencent.supersonic.common.pojo.Constants.DAY_FORMAT_INT; import static com.tencent.supersonic.common.pojo.Constants.DAY_FORMAT_INT;
@@ -51,7 +52,9 @@ import static com.tencent.supersonic.common.pojo.Constants.MONTH_FORMAT_INT;
import static com.tencent.supersonic.common.pojo.Constants.TIMES_FORMAT; import static com.tencent.supersonic.common.pojo.Constants.TIMES_FORMAT;
import static com.tencent.supersonic.common.pojo.Constants.TIME_FORMAT; import static com.tencent.supersonic.common.pojo.Constants.TIME_FORMAT;
/** Add ratio queries for metric queries. */ /**
* Add ratio queries for metric queries.
*/
@Slf4j @Slf4j
public class MetricRatioProcessor implements ExecuteResultProcessor { public class MetricRatioProcessor implements ExecuteResultProcessor {
@@ -71,10 +74,8 @@ public class MetricRatioProcessor implements ExecuteResultProcessor {
public AggregateInfo getAggregateInfo(User user, SemanticParseInfo semanticParseInfo, public AggregateInfo getAggregateInfo(User user, SemanticParseInfo semanticParseInfo,
QueryResult queryResult) { QueryResult queryResult) {
Set<String> resultMetricNames = getResultMetricNames(queryResult);
Set<String> resultMetricNames = new HashSet<>();
queryResult.getQueryColumns().stream().forEach(
c -> resultMetricNames.addAll(SqlSelectHelper.getColumnFromExpr(c.getNameEn())));
Optional<SchemaElement> ratioMetric = semanticParseInfo.getMetrics().stream() Optional<SchemaElement> ratioMetric = semanticParseInfo.getMetrics().stream()
.filter(m -> resultMetricNames.contains(m.getBizName())).findFirst(); .filter(m -> resultMetricNames.contains(m.getBizName())).findFirst();
@@ -92,17 +93,20 @@ public class MetricRatioProcessor implements ExecuteResultProcessor {
if (!lastDayOp.isPresent()) { if (!lastDayOp.isPresent()) {
return new AggregateInfo(); return new AggregateInfo();
} }
Optional<Map<String, Object>> lastValue = queryResult.getQueryResults().stream() Optional<Map<String, Object>> lastValue = queryResult.getQueryResults().stream()
.filter(r -> r.get(dateField).toString().equals(lastDayOp.get())).findFirst(); .filter(r -> r.get(dateField).toString().equals(lastDayOp.get())).findFirst();
MetricInfo metricInfo = new MetricInfo(); MetricInfo metricInfo = new MetricInfo();
metricInfo.setStatistics(new HashMap<>()); metricInfo.setStatistics(new HashMap<>());
if (lastValue.isPresent()
&& lastValue.get().containsKey(ratioMetric.get().getBizName())) { lastValue.ifPresent(value -> {
DecimalFormat df = new DecimalFormat("#.####"); if (value.containsKey(ratioMetric.get().getBizName())) {
metricInfo.setValue(df.format(lastValue.get().get(ratioMetric.get().getBizName()))); DecimalFormat df = new DecimalFormat("#.####");
} metricInfo.setValue(df.format(value.get(ratioMetric.get().getBizName())));
metricInfo.setDate(lastValue.get().get(dateField).toString()); }
metricInfo.setDate(value.get(dateField).toString());
});
CompletableFuture<MetricInfo> metricInfoRoll = CompletableFuture<MetricInfo> metricInfoRoll =
CompletableFuture.supplyAsync(() -> queryRatio(user, semanticParseInfo, CompletableFuture.supplyAsync(() -> queryRatio(user, semanticParseInfo,
@@ -110,11 +114,14 @@ public class MetricRatioProcessor implements ExecuteResultProcessor {
CompletableFuture<MetricInfo> metricInfoOver = CompletableFuture<MetricInfo> metricInfoOver =
CompletableFuture.supplyAsync(() -> queryRatio(user, semanticParseInfo, CompletableFuture.supplyAsync(() -> queryRatio(user, semanticParseInfo,
ratioMetric.get(), AggOperatorEnum.RATIO_OVER, queryResult)); ratioMetric.get(), AggOperatorEnum.RATIO_OVER, queryResult));
CompletableFuture.allOf(metricInfoRoll, metricInfoOver);
CompletableFuture.allOf(metricInfoRoll, metricInfoOver).join();
metricInfo.setName(metricInfoRoll.get().getName()); metricInfo.setName(metricInfoRoll.get().getName());
metricInfo.setValue(metricInfoRoll.get().getValue()); metricInfo.setValue(metricInfoRoll.get().getValue());
metricInfo.getStatistics().putAll(metricInfoRoll.get().getStatistics()); metricInfo.getStatistics().putAll(metricInfoRoll.get().getStatistics());
metricInfo.getStatistics().putAll(metricInfoOver.get().getStatistics()); metricInfo.getStatistics().putAll(metricInfoOver.get().getStatistics());
aggregateInfo.getMetricInfos().add(metricInfo); aggregateInfo.getMetricInfos().add(metricInfo);
} catch (Exception e) { } catch (Exception e) {
log.error("queryRatio error {}", e); log.error("queryRatio error {}", e);
@@ -122,6 +129,15 @@ public class MetricRatioProcessor implements ExecuteResultProcessor {
return aggregateInfo; return aggregateInfo;
} }
private static Set<String> getResultMetricNames(QueryResult queryResult) {
if (queryResult.getQueryColumns() == null) {
return new HashSet<>();
}
return queryResult.getQueryColumns().stream()
.flatMap(c -> SqlSelectHelper.getColumnFromExpr(c.getNameEn()).stream())
.collect(Collectors.toSet());
}
@SneakyThrows @SneakyThrows
private MetricInfo queryRatio(User user, SemanticParseInfo semanticParseInfo, private MetricInfo queryRatio(User user, SemanticParseInfo semanticParseInfo,
SchemaElement metric, AggOperatorEnum aggOperatorEnum, QueryResult queryResult) { SchemaElement metric, AggOperatorEnum aggOperatorEnum, QueryResult queryResult) {

View File

@@ -43,6 +43,7 @@ import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -180,64 +181,66 @@ public class S2DataPermissionAspect {
private void doRowPermission(QuerySqlReq querySqlReq, private void doRowPermission(QuerySqlReq querySqlReq,
AuthorizedResourceResp authorizedResource) { AuthorizedResourceResp authorizedResource) {
log.debug("start doRowPermission logic"); log.debug("Start doRowPermission logic");
StringJoiner joiner = new StringJoiner(" OR ");
List<String> dimensionFilters = new ArrayList<>();
if (!CollectionUtils.isEmpty(authorizedResource.getFilters())) {
authorizedResource.getFilters().stream()
.forEach(filter -> dimensionFilters.addAll(filter.getExpressions()));
}
if (CollectionUtils.isEmpty(dimensionFilters)) { if (CollectionUtils.isEmpty(authorizedResource.getFilters())) {
log.debug("dimensionFilters is empty"); log.debug("authorizedResource.getFilters() is empty");
return;
}
List<String> dimensionFilters = authorizedResource.getFilters().stream()
.flatMap(filter -> filter.getExpressions().stream()).filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
if (dimensionFilters.isEmpty()) {
log.debug("Dimension filters are empty");
return; return;
} }
dimensionFilters.stream().forEach(filter -> { StringJoiner joiner = new StringJoiner(" OR ");
if (StringUtils.isNotEmpty(filter) && StringUtils.isNotEmpty(filter.trim())) { dimensionFilters.stream().filter(
joiner.add(" ( " + filter + " ) "); filter -> StringUtils.isNotEmpty(filter) && StringUtils.isNotEmpty(filter.trim()))
} .forEach(filter -> joiner.add(" ( " + filter + " ) "));
});
try { try {
Expression expression = CCJSqlParserUtil.parseCondExpression(" ( " + joiner + " ) "); Expression expression = CCJSqlParserUtil.parseCondExpression(" ( " + joiner + " ) ");
if (StringUtils.isNotEmpty(joiner.toString())) { if (StringUtils.isNotEmpty(joiner.toString())) {
String sql = SqlAddHelper.addWhere(querySqlReq.getSql(), expression); String originalSql = querySqlReq.getSql();
log.info("before doRowPermission, queryS2SQLReq:{}", querySqlReq.getSql()); String modifiedSql = SqlAddHelper.addWhere(originalSql, expression);
querySqlReq.setSql(sql); log.info("Before doRowPermission, querySqlReq: {}", originalSql);
log.info("after doRowPermission, queryS2SQLReq:{}", querySqlReq.getSql()); querySqlReq.setSql(modifiedSql);
log.info("After doRowPermission, querySqlReq: {}", modifiedSql);
} }
} catch (JSQLParserException jsqlParserException) { } catch (JSQLParserException e) {
log.info("jsqlParser has an exception:{}", jsqlParserException.toString()); log.error("JSQLParser encountered an exception: {}", e.toString());
} }
} }
private void doRowPermission(QueryStructReq queryStructReq, private void doRowPermission(QueryStructReq queryStructReq,
AuthorizedResourceResp authorizedResource) { AuthorizedResourceResp authorizedResource) {
log.debug("start doRowPermission logic"); log.debug("start doRowPermission logic");
StringJoiner joiner = new StringJoiner(" OR ");
List<String> dimensionFilters = new ArrayList<>();
if (!CollectionUtils.isEmpty(authorizedResource.getFilters())) {
authorizedResource.getFilters().stream()
.forEach(filter -> dimensionFilters.addAll(filter.getExpressions()));
}
if (CollectionUtils.isEmpty(dimensionFilters)) { if (CollectionUtils.isEmpty(authorizedResource.getFilters())) {
log.debug("authorizedResource.getFilters() is empty");
return;
}
List<String> dimensionFilters = authorizedResource.getFilters().stream()
.flatMap(filter -> filter.getExpressions().stream()).filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
if (dimensionFilters.isEmpty()) {
log.debug("dimensionFilters is empty"); log.debug("dimensionFilters is empty");
return; return;
} }
dimensionFilters.stream().forEach(filter -> { StringJoiner joiner = new StringJoiner(" OR ");
if (StringUtils.isNotEmpty(filter) && StringUtils.isNotEmpty(filter.trim())) { dimensionFilters.forEach(filter -> joiner.add(" ( " + filter + " ) "));
joiner.add(" ( " + filter + " ) ");
}
});
if (StringUtils.isNotEmpty(joiner.toString())) { String joinedFilters = joiner.toString();
if (StringUtils.isNotEmpty(joinedFilters)) {
log.info("before doRowPermission, queryStructReq:{}", queryStructReq); log.info("before doRowPermission, queryStructReq:{}", queryStructReq);
Filter filter = new Filter("", FilterOperatorEnum.SQL_PART, joiner.toString()); Filter filter = new Filter("", FilterOperatorEnum.SQL_PART, joinedFilters);
List<Filter> filters = List<Filter> filters = Optional.ofNullable(queryStructReq.getOriginalFilter())
Objects.isNull(queryStructReq.getOriginalFilter()) ? new ArrayList<>() .orElseGet(ArrayList::new);
: queryStructReq.getOriginalFilter();
filters.add(filter); filters.add(filter);
queryStructReq.setDimensionFilters(filters); queryStructReq.setDimensionFilters(filters);
log.info("after doRowPermission, queryStructReq:{}", queryStructReq); log.info("after doRowPermission, queryStructReq:{}", queryStructReq);

View File

@@ -11,6 +11,7 @@ import org.springframework.beans.BeanUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
public class DomainConvert { public class DomainConvert {
@@ -26,6 +27,9 @@ public class DomainConvert {
} }
public static DomainResp convert(DomainDO domainDO, Map<Long, String> domainFullPathMap) { public static DomainResp convert(DomainDO domainDO, Map<Long, String> domainFullPathMap) {
if (Objects.isNull(domainDO)) {
return null;
}
DomainResp domainResp = new DomainResp(); DomainResp domainResp = new DomainResp();
BeanUtils.copyProperties(domainDO, domainResp); BeanUtils.copyProperties(domainDO, domainResp);
domainResp.setFullPath(domainFullPathMap.get(domainDO.getId())); domainResp.setFullPath(domainFullPathMap.get(domainDO.getId()));

View File

@@ -1,2 +1,2 @@
root=. root=.
CustomDictionaryPath=data/dictionary/custom/DimValue_1_1.txt;data/dictionary/custom/DimValue_1_2.txt; CustomDictionaryPath=data/dictionary/custom/dic_value_1_DIMENSION_1.txt