You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by xx...@apache.org on 2023/01/12 09:28:05 UTC

[kylin] 09/17: KYLIN-5395 sync column comment to dimension name and measure comment (#29499)

This is an automated email from the ASF dual-hosted git repository.

xxyu pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git

commit ae22f91de94d4e399f4498e51876e6ca9cd28712
Author: fanfanAlice <41...@users.noreply.github.com>
AuthorDate: Sun Nov 20 11:44:47 2022 +0800

    KYLIN-5395 sync column comment to dimension name and measure comment (#29499)
    
    * sync column comment to dimension name and measure comment
    * bug fix for special char replace
    
    Co-authored-by: fanfanAlice <18...@163.com>
---
 .../apache/kylin/metadata/model/ColumnDesc.java    |   8 +
 .../org/apache/kylin/metadata/model/TblColRef.java |   4 +-
 .../rest/controller/open/OpenModelController.java  |  17 ++
 .../controller/open/OpenModelControllerTest.java   |  16 ++
 .../response/SynchronizedCommentsResponse.java     | 220 +++++++++++++++++++++
 .../apache/kylin/rest/service/ModelService.java    |   8 +-
 .../kylin/rest/service/ModelServiceTest.java       |  95 +++++++++
 7 files changed, 362 insertions(+), 6 deletions(-)

diff --git a/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/ColumnDesc.java b/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/ColumnDesc.java
index a5968b6cf5..e179e2bb63 100644
--- a/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/ColumnDesc.java
+++ b/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/ColumnDesc.java
@@ -268,6 +268,14 @@ public class ColumnDesc implements Serializable {
         return desc;
     }
 
+    public String getCanonicalName() {
+        String tableName = null;
+        if (table != null) {
+            tableName = table.getIdentity();
+        }
+        return tableName + "." + getName();
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
diff --git a/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java b/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
index d4764ff2e2..13022d135d 100644
--- a/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
+++ b/src/core-metadata/src/main/java/org/apache/kylin/metadata/model/TblColRef.java
@@ -203,7 +203,7 @@ public class TblColRef implements Serializable {
         this.table = table;
         this.column = column;
     }
-    
+
     private String wrapIdentity(String wrap) {
         return wrap + getTableAlias() + wrap + "." + wrap + getName() + wrap;
     }
@@ -281,7 +281,7 @@ public class TblColRef implements Serializable {
     }
 
     public String getCanonicalName() {
-        return getTable() + "." + getName();
+        return column.getCanonicalName();
     }
 
     public String getDatatype() {
diff --git a/src/metadata-server/src/main/java/io/kyligence/kap/rest/controller/open/OpenModelController.java b/src/metadata-server/src/main/java/io/kyligence/kap/rest/controller/open/OpenModelController.java
index d9642ade0c..23c16ef30a 100644
--- a/src/metadata-server/src/main/java/io/kyligence/kap/rest/controller/open/OpenModelController.java
+++ b/src/metadata-server/src/main/java/io/kyligence/kap/rest/controller/open/OpenModelController.java
@@ -68,6 +68,7 @@ import org.apache.kylin.rest.response.IndexResponse;
 import org.apache.kylin.rest.response.NModelDescResponse;
 import org.apache.kylin.rest.response.OpenGetIndexResponse;
 import org.apache.kylin.rest.response.OpenGetIndexResponse.IndexDetail;
+import org.apache.kylin.rest.response.SynchronizedCommentsResponse;
 import org.apache.kylin.rest.service.FusionIndexService;
 import org.apache.kylin.rest.service.FusionModelService;
 import org.apache.kylin.rest.service.ModelService;
@@ -111,6 +112,8 @@ public class OpenModelController extends NBasicController {
             .collect(Collectors.toSet());
     public static final String MODEL_ID = "modelId";
 
+    public static final String FACT_TABLE = "fact_table";
+
     @Autowired
     private NModelController modelController;
 
@@ -493,4 +496,18 @@ public class OpenModelController extends NBasicController {
             throw new KylinException(FAILED_UPDATE_MODEL, root);
         }
     }
+
+    @ApiOperation(value = "comments synchronization", tags = { "AI" })
+    @PostMapping(value = "/comments_synchronization")
+    @ResponseBody
+    public EnvelopeResponse<SynchronizedCommentsResponse> commentsSynchronization(
+            @RequestBody ModelRequest modelRequest) {
+        modelRequest.setProject(checkProjectName(modelRequest.getProject()));
+        checkRequiredArg(ALIAS, modelRequest.getRawAlias());
+        checkRequiredArg(FACT_TABLE, modelRequest.getRootFactTableName());
+        SynchronizedCommentsResponse synchronizedCommentsResponse = new SynchronizedCommentsResponse();
+        synchronizedCommentsResponse.syncComment(modelRequest);
+        modelService.checkBeforeModelSave(synchronizedCommentsResponse.getModelRequest());
+        return new EnvelopeResponse<>(KylinException.CODE_SUCCESS, synchronizedCommentsResponse, "");
+    }
 }
diff --git a/src/metadata-server/src/test/java/io/kyligence/kap/rest/controller/open/OpenModelControllerTest.java b/src/metadata-server/src/test/java/io/kyligence/kap/rest/controller/open/OpenModelControllerTest.java
index e36ccf48de..ccc7864448 100644
--- a/src/metadata-server/src/test/java/io/kyligence/kap/rest/controller/open/OpenModelControllerTest.java
+++ b/src/metadata-server/src/test/java/io/kyligence/kap/rest/controller/open/OpenModelControllerTest.java
@@ -641,4 +641,20 @@ public class OpenModelControllerTest extends NLocalFileMetadataTestCase {
                 .andExpect(MockMvcResultMatchers.status().isOk());
         Mockito.verify(openModelController).updateSemantic(Mockito.any(OpenModelRequest.class));
     }
+
+    @Test
+    public void testCommentsSynchronization() throws Exception {
+        String project = "default";
+        String modelName = "model1";
+        ModelRequest modelRequest = new ModelRequest();
+        modelRequest.setProject(project);
+        modelRequest.setAlias(modelName);
+        modelRequest.setRootFactTableName("test_fact_table");
+
+        mockMvc.perform(MockMvcRequestBuilders.post("/api/models/comments_synchronization")
+                .contentType(MediaType.APPLICATION_JSON).content(JsonUtil.writeValueAsString(modelRequest))
+                .accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_V4_PUBLIC_JSON)))
+                .andExpect(MockMvcResultMatchers.status().isOk());
+        Mockito.verify(openModelController).commentsSynchronization(modelRequest);
+    }
 }
diff --git a/src/modeling-service/src/main/java/org/apache/kylin/rest/response/SynchronizedCommentsResponse.java b/src/modeling-service/src/main/java/org/apache/kylin/rest/response/SynchronizedCommentsResponse.java
new file mode 100644
index 0000000000..3ec10cc795
--- /dev/null
+++ b/src/modeling-service/src/main/java/org/apache/kylin/rest/response/SynchronizedCommentsResponse.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.apache.kylin.rest.response;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.kylin.common.KylinConfig;
+import org.apache.kylin.metadata.model.ColumnDesc;
+import org.apache.kylin.metadata.model.FunctionDesc;
+import org.apache.kylin.metadata.model.JoinTableDesc;
+import org.apache.kylin.metadata.model.NDataModel;
+import org.apache.kylin.metadata.model.NTableMetadataManager;
+import org.apache.kylin.metadata.model.TableDesc;
+import org.apache.kylin.rest.request.ModelRequest;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+@Data
+public class SynchronizedCommentsResponse {
+
+    @JsonProperty("model_request")
+    private ModelRequest modelRequest;
+
+    @JsonProperty("conflict_info")
+    private ConflictInfo conflictInfo;
+
+    @Data
+    @AllArgsConstructor
+    public static class ConflictInfo implements Serializable {
+        @JsonProperty("cols_with_same_comment")
+        private List<String> colsWithSameComment;
+        @JsonProperty("dims_origin_from_same_col")
+        private List<String> dimsOriginFromSameCol;
+    }
+
+    public static final Pattern SPECIAL_CHAR_PTN = Pattern.compile("((?![\\u4E00-\\u9FA5a-zA-Z0-9 _\\-()%?()]+).)*");
+
+    public void syncComment(ModelRequest modelRequest) {
+        this.modelRequest = modelRequest;
+        Map<String, ColumnDesc> columnDescMap = getColumnDescMap();
+        syncDimensionNames(columnDescMap, modelRequest.getSimplifiedDimensions());
+        syncMeasureComments(columnDescMap, modelRequest.getSimplifiedMeasures());
+    }
+
+    private void syncDimensionNames(Map<String, ColumnDesc> columnDescMap,
+            List<NDataModel.NamedColumn> simplifiedDimensions) {
+        List<String> colsWithSameComment = Lists.newArrayList();
+        List<String> dimsOriginFromSameCol = Lists.newArrayList();
+        simplifiedDimensions.forEach(namedColumn -> {
+            String aliasDotColumn = StringUtils.upperCase(namedColumn.getAliasDotColumn());
+            ColumnDesc columnDesc = columnDescMap.get(aliasDotColumn);
+            String name = StringUtils.upperCase(namedColumn.getColTableName());
+            if (columnDesc == null) {
+                return;
+            }
+            String canonicalName = StringUtils.upperCase(columnDesc.getCanonicalName());
+            List<ColumnDesc> columnDescList = simplifiedDimensions.stream()
+                    .map(simpleDimension -> columnDescMap
+                            .get(StringUtils.upperCase(simpleDimension.getAliasDotColumn())))
+                    .filter(Objects::nonNull)
+                    .filter(column -> StringUtils.upperCase(column.getCanonicalName()).equals(canonicalName))
+                    .collect(Collectors.toList());
+            long dimsOriginFromSameColCount = columnDescList.size();
+            long colsWithSameCommentCount = simplifiedDimensions.stream()
+                    .map(simpleDimension -> columnDescMap
+                            .get(StringUtils.upperCase(simpleDimension.getAliasDotColumn())))
+                    .filter(Objects::nonNull)
+                    .filter(column -> StringUtils.isNotBlank(column.getComment())
+                            && StringUtils.isNotBlank(columnDesc.getComment())
+                            && StringUtils.upperCase(column.getComment())
+                                    .equals(StringUtils.upperCase(columnDesc.getComment())))
+                    .count();
+            if (dimsOriginFromSameColCount > 1) {
+                dimsOriginFromSameCol.add(name);
+            }
+            if (colsWithSameCommentCount > 1) {
+                colsWithSameComment.add(name);
+            }
+            if (dimsOriginFromSameColCount == 1 && colsWithSameCommentCount == 1
+                    && StringUtils.isNotBlank(columnDesc.getComment())) {
+                name = columnDesc.getComment();
+            }
+            namedColumn.setName(name);
+        });
+        this.modelRequest.setSimplifiedDimensions(simplifiedDimensions);
+        this.conflictInfo = new ConflictInfo(colsWithSameComment, dimsOriginFromSameCol);
+    }
+
+    private void syncMeasureComments(Map<String, ColumnDesc> columnDescMap,
+            List<SimplifiedMeasure> simplifiedMeasures) {
+        simplifiedMeasures.forEach(simplifiedMeasure -> {
+            List<ParameterResponse> parameterResponses = simplifiedMeasure.getParameterValue().stream().filter(
+                    parameterResponse -> !FunctionDesc.PARAMETER_TYPE_CONSTANT.equals(parameterResponse.getType()))
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(parameterResponses)) {
+                return;
+            }
+            String aliasDotColumn = parameterResponses.get(0).getValue();
+            ColumnDesc columnDesc = columnDescMap.get(aliasDotColumn);
+            if (columnDesc != null && StringUtils.isNotBlank(columnDesc.getComment())) {
+                simplifiedMeasure.setComment(columnDesc.getComment());
+            }
+        });
+        modelRequest.setSimplifiedMeasures(simplifiedMeasures);
+    }
+
+    public Map<String, ColumnDesc> getColumnDescMap() {
+        Map<String, ColumnDesc> columnDescMap = Maps.newHashMap();
+        String project = modelRequest.getProject();
+        String factTableName = modelRequest.getRootFactTableName();
+        Set<String> computedColumnNames = modelRequest.getComputedColumnNames().stream().map(StringUtils::upperCase)
+                .collect(Collectors.toSet());
+        List<JoinTableDesc> joinTables = modelRequest.getJoinTables();
+        List<NDataModel.NamedColumn> simplifiedDimensions = modelRequest.getSimplifiedDimensions();
+        simplifiedDimensions.forEach(namedColumn -> putColumnDesc(project, computedColumnNames, factTableName,
+                joinTables, columnDescMap, namedColumn.getAliasDotColumn()));
+        List<SimplifiedMeasure> simplifiedMeasures = modelRequest.getSimplifiedMeasures();
+        simplifiedMeasures.forEach(simplifiedMeasure -> {
+            List<ParameterResponse> parameterResponses = simplifiedMeasure.getParameterValue().stream().filter(
+                    parameterResponse -> !FunctionDesc.PARAMETER_TYPE_CONSTANT.equals(parameterResponse.getType()))
+                    .collect(Collectors.toList());
+            if (CollectionUtils.isEmpty(parameterResponses)) {
+                return;
+            }
+            parameterResponses.forEach(parameterResponse -> putColumnDesc(project, computedColumnNames, factTableName,
+                    joinTables, columnDescMap, parameterResponse.getValue()));
+        });
+        return columnDescMap;
+    }
+
+    private void putColumnDesc(String project, Set<String> computedColumnNames, String factTableName,
+            List<JoinTableDesc> joinTables, Map<String, ColumnDesc> columnDescMap, String aliasDotColumn) {
+        if (aliasDotColumn == null) {
+            return;
+        }
+        ColumnDesc columnDesc = getColumnDescByAliasDotColumn(project, StringUtils.upperCase(aliasDotColumn),
+                computedColumnNames, StringUtils.upperCase(factTableName), joinTables);
+        if (columnDesc != null) {
+            columnDescMap.put(aliasDotColumn, columnDesc);
+        }
+    }
+
+    private ColumnDesc getColumnDescByAliasDotColumn(String project, String aliasDotColumn,
+            Set<String> computedColumnNames, String factTableName, List<JoinTableDesc> joinTables) {
+        String[] aliasDotColumnSplit = aliasDotColumn.split("\\.");
+        if (aliasDotColumnSplit.length != 2) {
+            return null;
+        }
+        String tableName = aliasDotColumnSplit[0];
+        String columnName = aliasDotColumnSplit[1];
+        if (computedColumnNames.contains(aliasDotColumn) || computedColumnNames.contains(columnName)) {
+            return null;
+        }
+        String factTableAlias = factTableName;
+        if (factTableName.contains(".") && factTableName.split("\\.").length == 2) {
+            factTableAlias = factTableName.split("\\.")[1];
+        }
+        NTableMetadataManager mgr = NTableMetadataManager.getInstance(KylinConfig.getInstanceFromEnv(), project);
+        TableDesc tableDesc;
+        if (tableName.equals(factTableAlias)) {
+            tableDesc = mgr.getTableDesc(factTableName);
+        } else {
+            Optional<JoinTableDesc> joinTable = joinTables.stream()
+                    .filter(joinTableDesc -> StringUtils.upperCase(joinTableDesc.getAlias()).equals(tableName))
+                    .findFirst();
+            if (joinTable.isPresent()) {
+                tableDesc = mgr.getTableDesc(joinTable.get().getTable());
+            } else {
+                return null;
+            }
+        }
+        Optional<ColumnDesc> column = Arrays.stream(tableDesc.getColumns())
+                .filter(columnDesc -> StringUtils.upperCase(columnDesc.getName()).equals(columnName)).findFirst();
+        if (column.isPresent()) {
+            ColumnDesc columnDesc = column.get();
+            String comment = columnDesc.getComment();
+            if (comment != null) {
+                columnDesc.setComment(replaceInvalidCharacters(comment.trim()));
+            }
+            return columnDesc;
+        }
+        return null;
+    }
+
+    private String replaceInvalidCharacters(String name) {
+        Matcher matcher = SPECIAL_CHAR_PTN.matcher(name);
+        return matcher.replaceAll("");
+    }
+}
diff --git a/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java b/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
index ab859e89b0..77fd3c6c5f 100644
--- a/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
+++ b/src/modeling-service/src/main/java/org/apache/kylin/rest/service/ModelService.java
@@ -298,9 +298,9 @@ public class ModelService extends AbstractModelService implements TableModelSupp
     private static final String LAST_MODIFY = "last_modify";
     public static final String REC_COUNT = "recommendations_count";
 
-    public static final String VALID_NAME_FOR_DIMENSION = "^[\\u4E00-\\u9FA5a-zA-Z0-9 _\\-()%?()]+$";
+    public static final Pattern VALID_NAME_FOR_DIMENSION = Pattern.compile("^[\\u4E00-\\u9FA5a-zA-Z0-9 _\\-()%?()]+$");
 
-    public static final String VALID_NAME_FOR_MEASURE = "^[\\u4E00-\\u9FA5a-zA-Z0-9 _\\-()%?().]+$";
+    public static final Pattern VALID_NAME_FOR_MEASURE = Pattern.compile("^[\\u4E00-\\u9FA5a-zA-Z0-9 _\\-()%?().]+$");
 
     private static final List<String> MODEL_CONFIG_BLOCK_LIST = Lists.newArrayList("kylin.index.rule-scheduler-data");
 
@@ -2149,7 +2149,7 @@ public class ModelService extends AbstractModelService implements TableModelSupp
             dimension.setName(StringUtils.trim(dimension.getName()));
             // check if the dimension name is valid
             if (StringUtils.length(dimension.getName()) > maxModelDimensionMeasureNameLength
-                    || !Pattern.compile(VALID_NAME_FOR_DIMENSION).matcher(dimension.getName()).matches())
+                    || !VALID_NAME_FOR_DIMENSION.matcher(dimension.getName()).matches())
                 throw new KylinException(INVALID_NAME,
                         String.format(Locale.ROOT, MsgPicker.getMsg().getInvalidDimensionName(), dimension.getName(),
                                 maxModelDimensionMeasureNameLength));
@@ -2216,7 +2216,7 @@ public class ModelService extends AbstractModelService implements TableModelSupp
         if (!KylinConfig.getInstanceFromEnv().isMeasureNameCheckEnabled()) {
             return true;
         }
-        return Pattern.compile(VALID_NAME_FOR_MEASURE).matcher(measureName).matches();
+        return VALID_NAME_FOR_MEASURE.matcher(measureName).matches();
     }
 
     private boolean isDupMeasure(SimplifiedMeasure measure, SimplifiedMeasure measure1) {
diff --git a/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java b/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
index e55b82cdfe..e8edf45716 100644
--- a/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
+++ b/src/modeling-service/src/test/java/org/apache/kylin/rest/service/ModelServiceTest.java
@@ -71,6 +71,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.TreeMap;
@@ -148,6 +149,7 @@ import org.apache.kylin.metadata.model.SegmentStatusEnum;
 import org.apache.kylin.metadata.model.SegmentStatusEnumToDisplay;
 import org.apache.kylin.metadata.model.Segments;
 import org.apache.kylin.metadata.model.TableDesc;
+import org.apache.kylin.metadata.model.TableRef;
 import org.apache.kylin.metadata.model.TblColRef;
 import org.apache.kylin.metadata.model.VolatileRange;
 import org.apache.kylin.metadata.model.util.ExpandableMeasureUtil;
@@ -185,6 +187,7 @@ import org.apache.kylin.rest.response.SegmentPartitionResponse;
 import org.apache.kylin.rest.response.SimplifiedColumnResponse;
 import org.apache.kylin.rest.response.SimplifiedMeasure;
 import org.apache.kylin.rest.response.SimplifiedTableResponse;
+import org.apache.kylin.rest.response.SynchronizedCommentsResponse;
 import org.apache.kylin.rest.util.AclEvaluate;
 import org.apache.kylin.rest.util.AclPermissionUtil;
 import org.apache.kylin.rest.util.AclUtil;
@@ -5588,4 +5591,96 @@ public class ModelServiceTest extends SourceTestCase {
         measure.setParameterValue(Lists.newArrayList(parameterResponse));
         return measure;
     }
+
+    @Test
+    public void testCreateModelSyncDimensionOrMeasure() {
+        NDataModelManager modelManager = NDataModelManager.getInstance(KylinConfig.getInstanceFromEnv(), "default");
+        ModelRequest modelRequest = createModelRequest(modelManager);
+        SynchronizedCommentsResponse response = new SynchronizedCommentsResponse();
+        response.syncComment(modelRequest);
+        ModelRequest newModelRequest = response.getModelRequest();
+        long measureCount = newModelRequest.getSimplifiedMeasures().stream()
+                .filter(simplifiedMeasure -> simplifiedMeasure.getComment() != null
+                        && simplifiedMeasure.getComment().contains("____"))
+                .count();
+        long dimensionCount = newModelRequest.getSimplifiedDimensions().stream()
+                .filter(namedColumn -> namedColumn.getName().contains("____")).count();
+        Assert.assertEquals(11, measureCount);
+        Assert.assertEquals(10, dimensionCount);
+        SynchronizedCommentsResponse.ConflictInfo conflictInfo = response.getConflictInfo();
+        Assert.assertEquals(2, conflictInfo.getColsWithSameComment().size());
+        Assert.assertEquals(20, conflictInfo.getDimsOriginFromSameCol().size());
+    }
+
+    private ModelRequest createModelRequest(NDataModelManager modelManager) {
+        NDataModel model = modelManager.getDataModelDesc("82fa7671-a935-45f5-8779-85703601f49a");
+        ModelRequest modelRequest = new ModelRequest(model);
+        modelRequest.setProject("default");
+        modelRequest.setAlias("test_model");
+        modelRequest.setRootFactTableName(model.getRootFactTableName());
+        modelRequest.setLastModified(0L);
+        modelRequest.setStart("0");
+        modelRequest.setEnd("100");
+        modelRequest.setUuid(null);
+
+        List<NamedColumn> oriAllNamedColumns = model.getAllNamedColumns();
+
+        List<SimplifiedMeasure> simplified_measures = model.getAllMeasures().stream().map(oldSimplifiedMeasure -> {
+            SimplifiedMeasure simplifiedMeasure = new SimplifiedMeasure();
+            simplifiedMeasure.setName(oldSimplifiedMeasure.getName());
+            simplifiedMeasure.setExpression(oldSimplifiedMeasure.getFunction().getExpression());
+            simplifiedMeasure.setReturnType(oldSimplifiedMeasure.getFunction().getReturnType());
+            List<ParameterResponse> parameterResponses = oldSimplifiedMeasure.getFunction().getParameters().stream()
+                    .map(parameterDesc -> {
+                        String value = parameterDesc.getValue();
+                        String type = parameterDesc.getType();
+                        ParameterResponse response = new ParameterResponse();
+                        response.setType(type);
+                        response.setValue(value);
+                        return response;
+                    }).collect(Collectors.toList());
+            simplifiedMeasure.setParameterValue(parameterResponses);
+            return simplifiedMeasure;
+        }).collect(Collectors.toList());
+
+        modelRequest.setSimplifiedMeasures(simplified_measures);
+        modelRequest.setSimplifiedDimensions(oriAllNamedColumns);
+
+        NTableMetadataManager tableMetadataManager = NTableMetadataManager.getInstance(KylinConfig.getInstanceFromEnv(),
+                "default");
+        TableDesc tableDesc = tableMetadataManager.getTableDesc("DEFAULT.TEST_KYLIN_FACT");
+        int length = tableDesc.getColumns().length;
+        ColumnDesc[] columns = new ColumnDesc[length];
+        for (int i = 0; i < length; i++) {
+            ColumnDesc column = tableDesc.getColumns()[i];
+            column.setComment(
+                    column.getComment() == null ? column.getName() + "____" + i : column.getComment() + "____" + i);
+            columns[i] = column;
+        }
+        String comment = columns[2].getComment();
+        columns[3].setComment(comment);
+        tableDesc.setColumns(columns);
+        tableMetadataManager.updateTableDesc(tableDesc);
+        return modelRequest;
+    }
+
+    @Test
+    public void testGetCanonicalName() {
+        TblColRef colRef = TblColRef.newDynamicColumn("test");
+        Assert.assertEquals("NULL.TEST", colRef.getCanonicalName());
+        TblColRef innerColumn = TblColRef.newInnerColumn("test", TblColRef.InnerDataTypeEnum.AGGREGATION_TYPE);
+        Assert.assertEquals("DEFAULT._KYLIN_TABLE.TEST", innerColumn.getCanonicalName());
+        NDataModelManager modelManager = NDataModelManager.getInstance(KylinConfig.getInstanceFromEnv(), "default");
+        NDataModel model = modelManager.getDataModelDesc("82fa7671-a935-45f5-8779-85703601f49a");
+        List<JoinTableDesc> joinTables = model.getJoinTables();
+        if (joinTables.size() == 0) {
+            return;
+        }
+        TableRef tableRef = joinTables.get(0).getTableRef();
+        Optional<TblColRef> first = tableRef.getColumns().stream().findFirst();
+        if (first.isPresent()) {
+            TblColRef colRef1 = first.get();
+            Assert.assertEquals("DEFAULT.TEST_ORDER.ORDER_ID", colRef1.getCanonicalName());
+        }
+    }
 }