mirror of
https://github.com/tencentmusic/supersonic.git
synced 2025-12-11 12:07:42 +00:00
(improvement)(chat) remove group by dimension and add FunctionAliasReplaceVisitor in dsl (#77)
* (improvement)(chat) remove group by dimension in join case * (improvement)(chat) add FunctionAliasReplaceVisitor in dsl ---------
This commit is contained in:
@@ -10,13 +10,15 @@ public class FieldReplaceVisitor extends ExpressionVisitorAdapter {
|
|||||||
|
|
||||||
ParseVisitorHelper parseVisitorHelper = new ParseVisitorHelper();
|
ParseVisitorHelper parseVisitorHelper = new ParseVisitorHelper();
|
||||||
private Map<String, String> fieldToBizName;
|
private Map<String, String> fieldToBizName;
|
||||||
|
private boolean exactReplace;
|
||||||
|
|
||||||
public FieldReplaceVisitor(Map<String, String> fieldToBizName) {
|
public FieldReplaceVisitor(Map<String, String> fieldToBizName, boolean exactReplace) {
|
||||||
this.fieldToBizName = fieldToBizName;
|
this.fieldToBizName = fieldToBizName;
|
||||||
|
this.exactReplace = exactReplace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Column column) {
|
public void visit(Column column) {
|
||||||
parseVisitorHelper.replaceColumn(column, fieldToBizName);
|
parseVisitorHelper.replaceColumn(column, fieldToBizName, exactReplace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.tencent.supersonic.common.util.jsqlparser;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import net.sf.jsqlparser.expression.Function;
|
||||||
|
import net.sf.jsqlparser.statement.select.SelectExpressionItem;
|
||||||
|
import net.sf.jsqlparser.statement.select.SelectItemVisitorAdapter;
|
||||||
|
|
||||||
|
public class FunctionAliasReplaceVisitor extends SelectItemVisitorAdapter {
|
||||||
|
|
||||||
|
private Map<String, String> aliasToActualExpression = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(SelectExpressionItem selectExpressionItem) {
|
||||||
|
if (selectExpressionItem.getExpression() instanceof Function) {
|
||||||
|
Function function = (Function) selectExpressionItem.getExpression();
|
||||||
|
if (Objects.nonNull(selectExpressionItem.getAlias())) {
|
||||||
|
aliasToActualExpression.put(selectExpressionItem.getAlias().getName(), function.toString());
|
||||||
|
selectExpressionItem.setAlias(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getAliasToActualExpression() {
|
||||||
|
return aliasToActualExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,10 +18,11 @@ public class GroupByReplaceVisitor implements GroupByVisitor {
|
|||||||
|
|
||||||
ParseVisitorHelper parseVisitorHelper = new ParseVisitorHelper();
|
ParseVisitorHelper parseVisitorHelper = new ParseVisitorHelper();
|
||||||
private Map<String, String> fieldToBizName;
|
private Map<String, String> fieldToBizName;
|
||||||
|
private boolean exactReplace;
|
||||||
|
|
||||||
|
public GroupByReplaceVisitor(Map<String, String> fieldToBizName, boolean exactReplace) {
|
||||||
public GroupByReplaceVisitor(Map<String, String> fieldToBizName) {
|
|
||||||
this.fieldToBizName = fieldToBizName;
|
this.fieldToBizName = fieldToBizName;
|
||||||
|
this.exactReplace = exactReplace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void visit(GroupByElement groupByElement) {
|
public void visit(GroupByElement groupByElement) {
|
||||||
@@ -32,7 +33,8 @@ public class GroupByReplaceVisitor implements GroupByVisitor {
|
|||||||
for (int i = 0; i < groupByExpressions.size(); i++) {
|
for (int i = 0; i < groupByExpressions.size(); i++) {
|
||||||
Expression expression = groupByExpressions.get(i);
|
Expression expression = groupByExpressions.get(i);
|
||||||
|
|
||||||
String replaceColumn = parseVisitorHelper.getReplaceColumn(expression.toString(), fieldToBizName);
|
String replaceColumn = parseVisitorHelper.getReplaceColumn(expression.toString(), fieldToBizName,
|
||||||
|
exactReplace);
|
||||||
if (StringUtils.isNotEmpty(replaceColumn)) {
|
if (StringUtils.isNotEmpty(replaceColumn)) {
|
||||||
if (expression instanceof Column) {
|
if (expression instanceof Column) {
|
||||||
groupByExpressions.set(i, new Column(replaceColumn));
|
groupByExpressions.set(i, new Column(replaceColumn));
|
||||||
|
|||||||
@@ -12,23 +12,25 @@ public class OrderByReplaceVisitor extends OrderByVisitorAdapter {
|
|||||||
|
|
||||||
ParseVisitorHelper parseVisitorHelper = new ParseVisitorHelper();
|
ParseVisitorHelper parseVisitorHelper = new ParseVisitorHelper();
|
||||||
private Map<String, String> fieldToBizName;
|
private Map<String, String> fieldToBizName;
|
||||||
|
private boolean exactReplace;
|
||||||
|
|
||||||
public OrderByReplaceVisitor(Map<String, String> fieldToBizName) {
|
public OrderByReplaceVisitor(Map<String, String> fieldToBizName, boolean exactReplace) {
|
||||||
this.fieldToBizName = fieldToBizName;
|
this.fieldToBizName = fieldToBizName;
|
||||||
|
this.exactReplace = exactReplace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(OrderByElement orderBy) {
|
public void visit(OrderByElement orderBy) {
|
||||||
Expression expression = orderBy.getExpression();
|
Expression expression = orderBy.getExpression();
|
||||||
if (expression instanceof Column) {
|
if (expression instanceof Column) {
|
||||||
parseVisitorHelper.replaceColumn((Column) expression, fieldToBizName);
|
parseVisitorHelper.replaceColumn((Column) expression, fieldToBizName, exactReplace);
|
||||||
}
|
}
|
||||||
if (expression instanceof Function) {
|
if (expression instanceof Function) {
|
||||||
Function function = (Function) expression;
|
Function function = (Function) expression;
|
||||||
List<Expression> expressions = function.getParameters().getExpressions();
|
List<Expression> expressions = function.getParameters().getExpressions();
|
||||||
for (Expression column : expressions) {
|
for (Expression column : expressions) {
|
||||||
if (column instanceof Column) {
|
if (column instanceof Column) {
|
||||||
parseVisitorHelper.replaceColumn((Column) column, fieldToBizName);
|
parseVisitorHelper.replaceColumn((Column) column, fieldToBizName, exactReplace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,27 +11,32 @@ import org.apache.commons.lang3.StringUtils;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class ParseVisitorHelper {
|
public class ParseVisitorHelper {
|
||||||
|
|
||||||
public void replaceColumn(Column column, Map<String, String> fieldToBizName) {
|
public void replaceColumn(Column column, Map<String, String> fieldToBizName, boolean exactReplace) {
|
||||||
String columnName = column.getColumnName();
|
String columnName = column.getColumnName();
|
||||||
column.setColumnName(getReplaceColumn(columnName, fieldToBizName));
|
String replaceColumn = getReplaceColumn(columnName, fieldToBizName, exactReplace);
|
||||||
|
if (StringUtils.isNotBlank(replaceColumn)) {
|
||||||
|
column.setColumnName(replaceColumn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getReplaceColumn(String columnName, Map<String, String> fieldToBizName) {
|
public String getReplaceColumn(String columnName, Map<String, String> fieldToBizName, boolean exactReplace) {
|
||||||
String fieldBizName = fieldToBizName.get(columnName);
|
String fieldBizName = fieldToBizName.get(columnName);
|
||||||
if (StringUtils.isNotEmpty(fieldBizName)) {
|
if (StringUtils.isNotBlank(fieldBizName)) {
|
||||||
return fieldBizName;
|
return fieldBizName;
|
||||||
} else {
|
}
|
||||||
Optional<Entry<String, String>> first = fieldToBizName.entrySet().stream().sorted((k1, k2) -> {
|
if (exactReplace) {
|
||||||
String k1FieldNameDb = k1.getKey();
|
return null;
|
||||||
String k2FieldNameDb = k2.getKey();
|
}
|
||||||
Double k1Similarity = getSimilarity(columnName, k1FieldNameDb);
|
Optional<Entry<String, String>> first = fieldToBizName.entrySet().stream().sorted((k1, k2) -> {
|
||||||
Double k2Similarity = getSimilarity(columnName, k2FieldNameDb);
|
String k1FieldNameDb = k1.getKey();
|
||||||
return k2Similarity.compareTo(k1Similarity);
|
String k2FieldNameDb = k2.getKey();
|
||||||
}).collect(Collectors.toList()).stream().findFirst();
|
Double k1Similarity = getSimilarity(columnName, k1FieldNameDb);
|
||||||
|
Double k2Similarity = getSimilarity(columnName, k2FieldNameDb);
|
||||||
|
return k2Similarity.compareTo(k1Similarity);
|
||||||
|
}).collect(Collectors.toList()).stream().findFirst();
|
||||||
|
|
||||||
if (first.isPresent()) {
|
if (first.isPresent()) {
|
||||||
return first.get().getValue();
|
return first.get().getValue();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return columnName;
|
return columnName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,11 @@ public class SqlParserUpdateHelper {
|
|||||||
return selectStatement.toString();
|
return selectStatement.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String replaceFields(String sql, Map<String, String> fieldToBizName) {
|
public static String replaceFields(String sql, Map<String, String> fieldToBizName) {
|
||||||
|
return replaceFields(sql, fieldToBizName, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String replaceFields(String sql, Map<String, String> fieldToBizName, boolean exactReplace) {
|
||||||
Select selectStatement = SqlParserSelectHelper.getSelect(sql);
|
Select selectStatement = SqlParserSelectHelper.getSelect(sql);
|
||||||
SelectBody selectBody = selectStatement.getSelectBody();
|
SelectBody selectBody = selectStatement.getSelectBody();
|
||||||
if (!(selectBody instanceof PlainSelect)) {
|
if (!(selectBody instanceof PlainSelect)) {
|
||||||
@@ -68,7 +71,7 @@ public class SqlParserUpdateHelper {
|
|||||||
PlainSelect plainSelect = (PlainSelect) selectBody;
|
PlainSelect plainSelect = (PlainSelect) selectBody;
|
||||||
//1. replace where fields
|
//1. replace where fields
|
||||||
Expression where = plainSelect.getWhere();
|
Expression where = plainSelect.getWhere();
|
||||||
FieldReplaceVisitor visitor = new FieldReplaceVisitor(fieldToBizName);
|
FieldReplaceVisitor visitor = new FieldReplaceVisitor(fieldToBizName, exactReplace);
|
||||||
if (Objects.nonNull(where)) {
|
if (Objects.nonNull(where)) {
|
||||||
where.accept(visitor);
|
where.accept(visitor);
|
||||||
}
|
}
|
||||||
@@ -82,14 +85,14 @@ public class SqlParserUpdateHelper {
|
|||||||
List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
|
List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
|
||||||
if (!CollectionUtils.isEmpty(orderByElements)) {
|
if (!CollectionUtils.isEmpty(orderByElements)) {
|
||||||
for (OrderByElement orderByElement : orderByElements) {
|
for (OrderByElement orderByElement : orderByElements) {
|
||||||
orderByElement.accept(new OrderByReplaceVisitor(fieldToBizName));
|
orderByElement.accept(new OrderByReplaceVisitor(fieldToBizName, exactReplace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//4. replace group by fields
|
//4. replace group by fields
|
||||||
GroupByElement groupByElement = plainSelect.getGroupBy();
|
GroupByElement groupByElement = plainSelect.getGroupBy();
|
||||||
if (Objects.nonNull(groupByElement)) {
|
if (Objects.nonNull(groupByElement)) {
|
||||||
groupByElement.accept(new GroupByReplaceVisitor(fieldToBizName));
|
groupByElement.accept(new GroupByReplaceVisitor(fieldToBizName, exactReplace));
|
||||||
}
|
}
|
||||||
return selectStatement.toString();
|
return selectStatement.toString();
|
||||||
}
|
}
|
||||||
@@ -178,6 +181,24 @@ public class SqlParserUpdateHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String replaceAlias(String sql) {
|
||||||
|
Select selectStatement = SqlParserSelectHelper.getSelect(sql);
|
||||||
|
SelectBody selectBody = selectStatement.getSelectBody();
|
||||||
|
if (!(selectBody instanceof PlainSelect)) {
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
PlainSelect plainSelect = (PlainSelect) selectBody;
|
||||||
|
FunctionAliasReplaceVisitor visitor = new FunctionAliasReplaceVisitor();
|
||||||
|
for (SelectItem selectItem : plainSelect.getSelectItems()) {
|
||||||
|
selectItem.accept(visitor);
|
||||||
|
}
|
||||||
|
Map<String, String> aliasToActualExpression = visitor.getAliasToActualExpression();
|
||||||
|
if (Objects.nonNull(aliasToActualExpression) && !aliasToActualExpression.isEmpty()) {
|
||||||
|
return replaceFields(selectStatement.toString(), aliasToActualExpression, true);
|
||||||
|
}
|
||||||
|
return selectStatement.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public static String addWhere(String sql, String column, Object value) {
|
public static String addWhere(String sql, String column, Object value) {
|
||||||
if (StringUtils.isEmpty(column) || Objects.isNull(value)) {
|
if (StringUtils.isEmpty(column) || Objects.isNull(value)) {
|
||||||
return sql;
|
return sql;
|
||||||
|
|||||||
@@ -217,6 +217,19 @@ class SqlParserUpdateHelperTest {
|
|||||||
replaceSql);
|
replaceSql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void replaceAlias() {
|
||||||
|
String sql = "select 部门, sum(访问次数) as 总访问次数 from 超音数 where "
|
||||||
|
+ "datediff('day', 数据日期, '2023-09-05') <= 3 group by 部门 order by 总访问次数 desc limit 10";
|
||||||
|
String replaceSql = SqlParserUpdateHelper.replaceAlias(sql);
|
||||||
|
System.out.println(replaceSql);
|
||||||
|
Assert.assertEquals(
|
||||||
|
"SELECT 部门, sum(访问次数) FROM 超音数 WHERE "
|
||||||
|
+ "datediff('day', 数据日期, '2023-09-05') <= 3 GROUP BY 部门 ORDER BY sum(访问次数) DESC LIMIT 10",
|
||||||
|
replaceSql);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, String> initParams() {
|
private Map<String, String> initParams() {
|
||||||
Map<String, String> fieldToBizName = new HashMap<>();
|
Map<String, String> fieldToBizName = new HashMap<>();
|
||||||
fieldToBizName.put("部门", "department");
|
fieldToBizName.put("部门", "department");
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ com.tencent.supersonic.auth.api.authentication.adaptor.UserAdaptor=\
|
|||||||
|
|
||||||
com.tencent.supersonic.chat.api.component.SemanticCorrector=\
|
com.tencent.supersonic.chat.api.component.SemanticCorrector=\
|
||||||
com.tencent.supersonic.chat.corrector.DateFieldCorrector, \
|
com.tencent.supersonic.chat.corrector.DateFieldCorrector, \
|
||||||
|
com.tencent.supersonic.chat.corrector.FunctionAliasReplaceVisitor, \
|
||||||
com.tencent.supersonic.chat.corrector.FieldNameCorrector, \
|
com.tencent.supersonic.chat.corrector.FieldNameCorrector, \
|
||||||
com.tencent.supersonic.chat.corrector.FieldCorrector, \
|
com.tencent.supersonic.chat.corrector.FieldCorrector, \
|
||||||
com.tencent.supersonic.chat.corrector.FunctionCorrector, \
|
com.tencent.supersonic.chat.corrector.FunctionCorrector, \
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ com.tencent.supersonic.auth.api.authentication.adaptor.UserAdaptor=\
|
|||||||
|
|
||||||
com.tencent.supersonic.chat.api.component.SemanticCorrector=\
|
com.tencent.supersonic.chat.api.component.SemanticCorrector=\
|
||||||
com.tencent.supersonic.chat.corrector.DateFieldCorrector, \
|
com.tencent.supersonic.chat.corrector.DateFieldCorrector, \
|
||||||
|
com.tencent.supersonic.chat.corrector.FunctionAliasReplaceVisitor, \
|
||||||
com.tencent.supersonic.chat.corrector.FieldNameCorrector, \
|
com.tencent.supersonic.chat.corrector.FieldNameCorrector, \
|
||||||
com.tencent.supersonic.chat.corrector.FieldCorrector, \
|
com.tencent.supersonic.chat.corrector.FieldCorrector, \
|
||||||
com.tencent.supersonic.chat.corrector.FunctionCorrector, \
|
com.tencent.supersonic.chat.corrector.FunctionCorrector, \
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package com.tencent.supersonic.semantic.query.parser.calcite.sql.render;
|
package com.tencent.supersonic.semantic.query.parser.calcite.sql.render;
|
||||||
|
|
||||||
import com.tencent.supersonic.semantic.api.query.request.MetricReq;
|
import com.tencent.supersonic.semantic.api.query.request.MetricReq;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.Renderer;
|
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.AggFunctionNode;
|
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.DataSourceNode;
|
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.FilterNode;
|
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.MetricNode;
|
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.TableView;
|
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Constants;
|
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Constants;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.DataSource;
|
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.DataSource;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Dimension;
|
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Dimension;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Identify;
|
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Identify;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Metric;
|
import com.tencent.supersonic.semantic.query.parser.calcite.dsl.Metric;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.schema.SemanticSchema;
|
import com.tencent.supersonic.semantic.query.parser.calcite.schema.SemanticSchema;
|
||||||
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.Renderer;
|
||||||
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.TableView;
|
||||||
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.AggFunctionNode;
|
||||||
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.DataSourceNode;
|
||||||
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.FilterNode;
|
||||||
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.MetricNode;
|
||||||
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.SemanticNode;
|
import com.tencent.supersonic.semantic.query.parser.calcite.sql.node.SemanticNode;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -118,7 +118,7 @@ public class JoinRender extends Renderer {
|
|||||||
innerView.setTable(left);
|
innerView.setTable(left);
|
||||||
filterView.setTable(SemanticNode.buildAs(Constants.JOIN_TABLE_OUT_PREFIX, innerView.build()));
|
filterView.setTable(SemanticNode.buildAs(Constants.JOIN_TABLE_OUT_PREFIX, innerView.build()));
|
||||||
if (!filterDimension.isEmpty()) {
|
if (!filterDimension.isEmpty()) {
|
||||||
for (String d : filterDimension) {
|
for (String d : getQueryDimension(filterDimension, queryAllDimension, whereFields)) {
|
||||||
if (nonAgg) {
|
if (nonAgg) {
|
||||||
filterView.getMeasure().add(SemanticNode.parse(d, scope));
|
filterView.getMeasure().add(SemanticNode.parse(d, scope));
|
||||||
} else {
|
} else {
|
||||||
@@ -183,6 +183,12 @@ public class JoinRender extends Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Set<String> getQueryDimension(Set<String> filterDimension, Set<String> queryAllDimension,
|
||||||
|
Set<String> whereFields) {
|
||||||
|
return filterDimension.stream().filter(d -> queryAllDimension.contains(d) || whereFields.contains(d)).collect(
|
||||||
|
Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean getMatchMetric(SemanticSchema schema, Set<String> sourceMeasure, String m,
|
private boolean getMatchMetric(SemanticSchema schema, Set<String> sourceMeasure, String m,
|
||||||
List<String> queryMetrics) {
|
List<String> queryMetrics) {
|
||||||
|
|||||||
Reference in New Issue
Block a user