Commit f9ae43cc authored by wenmo's avatar wenmo

新增字段级血缘分析

parent 51cab779
......@@ -49,7 +49,7 @@ Dinky(原 Dlink):
| | | 新增 作业生命周期管理 | 0.6.0 |
| | | 新增 基于 Explain 的语法校验与逻辑解析 | 0.4.0 |
| | | 新增 JobPlan 图预览 | 0.5.0 |
| | | 新增 基于 StreamGraph 的表级血缘分析 | 0.4.0 |
| | | 新增 基于 StreamGraph 的字段级血缘分析 | 0.6.0 |
| | | 新增 基于上下文元数据自动提示与补全 | 0.4.0 |
| | | 新增 自定义规则的自动提示与补全 | 0.4.0 |
| | | 新增 关键字高亮与代码缩略图 | 0.4.0 |
......@@ -102,7 +102,7 @@ Dinky(原 Dlink):
| | | 新增 作业日志 | dev |
| | | 新增 自动调优 | dev |
| | | 新增 FlinkSQL | 0.6.0 |
| | | 新增 数据地图 | dev |
| | | 新增 数据地图 | 0.6.0 |
| | | 新增 即席查询 | dev |
| | | 新增 历史版本 | dev |
| | | 新增 告警记录 | 0.6.0 |
......
......@@ -98,10 +98,10 @@ public class JobInstanceController {
}
/**
* 获取单的血缘分析
* 获取单任务实例的血缘分析
*/
@GetMapping("/getOneTableColumnCA")
public Result getOneTableColumnCA(@RequestParam Integer id) {
return Result.succeed(jobInstanceService.getOneTableColumnCA(id), "刷新成功");
@GetMapping("/getLineage")
public Result getLineage(@RequestParam Integer id) {
return Result.succeed(jobInstanceService.getLineage(id), "刷新成功");
}
}
......@@ -81,15 +81,11 @@ public class StudioController {
}
/**
* 获取单的血缘分析
* 获取单任务实例的血缘分析
*/
@PostMapping("/getCAByStatement")
public Result getCAByStatement(@RequestBody StudioCADTO studioCADTO) {
switch (studioCADTO.getType()){
case 1:return Result.succeed(studioService.getOneTableColumnCAByStatement(studioCADTO.getStatement()),"执行成功");
case 2:return Result.succeed(studioService.getColumnCAByStatement(studioCADTO.getStatement()),"执行成功");
default:return Result.failed("敬请期待");
}
@PostMapping("/getLineage")
public Result getLineage(@RequestBody StudioCADTO studioCADTO) {
return Result.succeed(studioService.getLineage(studioCADTO), "刷新成功");
}
/**
......
......@@ -2,6 +2,7 @@ package com.dlink.service;
import com.dlink.db.service.ISuperService;
import com.dlink.explainer.ca.TableCANode;
import com.dlink.explainer.lineage.LineageResult;
import com.dlink.model.JobInfoDetail;
import com.dlink.model.JobInstance;
import com.dlink.model.JobInstanceStatus;
......@@ -24,5 +25,5 @@ public interface JobInstanceService extends ISuperService<JobInstance> {
JobInfoDetail getJobInfoDetailInfo(JobInstance jobInstance);
List<TableCANode> getOneTableColumnCA(Integer id);
LineageResult getLineage(Integer id);
}
package com.dlink.service;
import com.dlink.dto.SessionDTO;
import com.dlink.dto.SqlDTO;
import com.dlink.dto.StudioDDLDTO;
import com.dlink.dto.StudioExecuteDTO;
import com.dlink.dto.*;
import com.dlink.explainer.ca.ColumnCANode;
import com.dlink.explainer.ca.TableCANode;
import com.dlink.explainer.lineage.LineageResult;
import com.dlink.job.JobResult;
import com.dlink.result.IResult;
import com.dlink.result.SelectResult;
......@@ -44,11 +42,7 @@ public interface StudioService {
List<SessionInfo> listSession(String createUser);
List<TableCANode> getOneTableCAByStatement(String statement);
List<TableCANode> getOneTableColumnCAByStatement(String statement);
List<ColumnCANode> getColumnCAByStatement(String statement);
LineageResult getLineage(StudioCADTO studioCADTO);
List<JsonNode> listJobs(Integer clusterId);
......
......@@ -6,6 +6,8 @@ import com.dlink.constant.FlinkRestResultConstant;
import com.dlink.db.service.impl.SuperServiceImpl;
import com.dlink.explainer.ca.CABuilder;
import com.dlink.explainer.ca.TableCANode;
import com.dlink.explainer.lineage.LineageBuilder;
import com.dlink.explainer.lineage.LineageResult;
import com.dlink.mapper.JobInstanceMapper;
import com.dlink.model.Cluster;
import com.dlink.model.History;
......@@ -127,8 +129,8 @@ public class JobInstanceServiceImpl extends SuperServiceImpl<JobInstanceMapper,
}
@Override
public List<TableCANode> getOneTableColumnCA(Integer id) {
return CABuilder.getOneTableColumnCAByStatement(getJobInfoDetail(id).getHistory().getStatement());
public LineageResult getLineage(Integer id) {
return LineageBuilder.getLineage(getJobInfoDetail(id).getHistory().getStatement());
}
}
......@@ -8,6 +8,8 @@ import com.dlink.dto.*;
import com.dlink.explainer.ca.CABuilder;
import com.dlink.explainer.ca.ColumnCANode;
import com.dlink.explainer.ca.TableCANode;
import com.dlink.explainer.lineage.LineageBuilder;
import com.dlink.explainer.lineage.LineageResult;
import com.dlink.gateway.GatewayType;
import com.dlink.gateway.model.JobInfo;
import com.dlink.gateway.result.SavePointResult;
......@@ -254,18 +256,9 @@ public class StudioServiceImpl implements StudioService {
}
@Override
public List<TableCANode> getOneTableCAByStatement(String statement) {
return CABuilder.getOneTableCAByStatement(statement);
}
@Override
public List<TableCANode> getOneTableColumnCAByStatement(String statement) {
return CABuilder.getOneTableColumnCAByStatement(statement);
}
@Override
public List<ColumnCANode> getColumnCAByStatement(String statement) {
return CABuilder.getColumnCAByStatement(statement);
public LineageResult getLineage(StudioCADTO studioCADTO) {
addFlinkSQLEnv(studioCADTO);
return LineageBuilder.getLineage(studioCADTO.getStatement());
}
@Override
......
......@@ -12,6 +12,7 @@ import java.util.Set;
* @author wenmo
* @since 2021/6/23 11:03
**/
@Deprecated
public class CABuilder {
public static List<TableCANode> getOneTableCAByStatement(String statement){
......@@ -53,6 +54,7 @@ public class CABuilder {
return tableCANodes;
}
@Deprecated
public static List<ColumnCANode> getColumnCAByStatement(String statement){
List<ColumnCANode> columnCANodes = new ArrayList<>();
FlinkSqlPlus plus = FlinkSqlPlus.build();
......
......@@ -7,6 +7,7 @@ import com.dlink.explainer.trans.OperatorTrans;
import com.dlink.explainer.trans.SinkTrans;
import com.dlink.explainer.trans.SourceTrans;
import com.dlink.explainer.trans.Trans;
import com.dlink.utils.MapParseUtils;
import org.apache.commons.collections.CollectionUtils;
import java.util.ArrayList;
......@@ -23,6 +24,7 @@ import java.util.Set;
* @author wenmo
* @since 2021/6/22
**/
@Deprecated
public class ColumnCAGenerator implements CAGenerator {
private List<Trans> transList;
private Map<Integer, Trans> transMaps;
......@@ -33,7 +35,7 @@ public class ColumnCAGenerator implements CAGenerator {
private Set<NodeRel> columnCASRel;
private ICA sinkTableCA = null;
private String sinkTableName;
private Integer index = 0;
private Integer index = 1;
private List<Integer> sinkColumns;
private List<Integer> sourceColumns;
......@@ -151,15 +153,12 @@ public class ColumnCAGenerator implements CAGenerator {
}
private void searchSelect(TableCA tableCA, ColumnCA columnCA, OperatorTrans trans, String operation, String alias) {
if(Asserts.isEquals(operation,columnCA.getAlias())||operation.contains(" " + columnCA.getAlias() + " ") ||
operation.contains("(" + columnCA.getAlias() + " ") ||
operation.contains(" " + columnCA.getAlias() + ")")) {
if(MapParseUtils.hasField(operation,columnCA.getAlias())) {
boolean isHad = false;
Integer cid = null;
for (int j = 0; j < this.columnCAS.size(); j++) {
ColumnCA columnCA1 = (ColumnCA) this.columnCAS.get(j);
if (columnCA1.getTableCA().getId() == tableCA.getId() &&
columnCA1.getName().equals(operation)) {
if (columnCA1.getTableCA().getId() == tableCA.getId() && columnCA1.getName().equals(alias)) {
isHad = true;
cid = columnCA1.getId();
break;
......@@ -167,10 +166,8 @@ public class ColumnCAGenerator implements CAGenerator {
}
if (!isHad) {
cid = index++;
String columnOperation = operation.replaceAll(" " + columnCA.getAlias() + " "," " + columnCA.getOperation() + " ")
.replaceAll("\\(" + columnCA.getAlias() + " "," " + columnCA.getOperation() + " ")
.replaceAll(" " + columnCA.getAlias() + "\\)"," " + columnCA.getOperation() + " ");
ColumnCA columnCA2 = new ColumnCA(cid, operation, alias, operation, operation,columnOperation, tableCA,trans);
// String columnOperation = MapParseUtils.replaceField(operation,columnCA.getAlias(),columnCA.getOperation());
ColumnCA columnCA2 = new ColumnCA(cid, alias, alias, alias, alias,operation, tableCA,trans);
this.columnCASMaps.put(cid, columnCA2);
this.columnCAS.add(columnCA2);
buildColumnCAFields(tableCA, trans.getParentId(), columnCA2);
......
package com.dlink.explainer.ca;
import com.dlink.explainer.lineage.LineageColumnGenerator;
import lombok.Getter;
import lombok.Setter;
......@@ -13,24 +14,68 @@ import java.util.Set;
* @author wenmo
* @since 2021/6/22
**/
@Getter
@Setter
public class ColumnCAResult {
private String sinkName;
private ICA sinkTableCA;
private List<ICA> columnCAS;
private Map<Integer, ICA> columnCASMaps;
private List<TableCA> tableCAS;
private Map<Integer, ColumnCA> columnCASMaps;
private Set<NodeRel> columnCASRel;
private Set<NodeRel> columnCASRelChain;
private List<Integer> sinkColumns;
private List<Integer> sourColumns;
public ColumnCAResult(ColumnCAGenerator generator) {
this.columnCAS = generator.getColumnCAS();
this.sinkTableCA = generator.getSinkTableCA();
this.sinkName = generator.getSinkTableName();
public ColumnCAResult(LineageColumnGenerator generator) {
this.tableCAS = generator.getTableCAS();
this.columnCASMaps = generator.getColumnCASMaps();
this.columnCASRel = generator.getColumnCASRel();
this.columnCASRelChain = generator.getColumnCASRelChain();
this.sinkColumns = generator.getSinkColumns();
this.sourColumns = generator.getSourceColumns();
}
public List<TableCA> getTableCAS() {
return tableCAS;
}
public void setTableCAS(List<TableCA> tableCAS) {
this.tableCAS = tableCAS;
}
public Map<Integer, ColumnCA> getColumnCASMaps() {
return columnCASMaps;
}
public void setColumnCASMaps(Map<Integer, ColumnCA> columnCASMaps) {
this.columnCASMaps = columnCASMaps;
}
public Set<NodeRel> getColumnCASRel() {
return columnCASRel;
}
public void setColumnCASRel(Set<NodeRel> columnCASRel) {
this.columnCASRel = columnCASRel;
}
public Set<NodeRel> getColumnCASRelChain() {
return columnCASRelChain;
}
public void setColumnCASRelChain(Set<NodeRel> columnCASRelChain) {
this.columnCASRelChain = columnCASRelChain;
}
public List<Integer> getSinkColumns() {
return sinkColumns;
}
public void setSinkColumns(List<Integer> sinkColumns) {
this.sinkColumns = sinkColumns;
}
public List<Integer> getSourColumns() {
return sourColumns;
}
public void setSourColumns(List<Integer> sourColumns) {
this.sourColumns = sourColumns;
}
}
......@@ -30,6 +30,7 @@ public class TableCAGenerator implements CAGenerator {
private Map<Integer, Trans> transMaps;
private Set<Integer> parentIdSet;
private List<ICA> sourceTableCAS = new ArrayList<>();
private List<ICA> sinkTableCAS = new ArrayList<>();
private ICA sinkTableCA = null;
private String sinkTableName;
......@@ -38,8 +39,8 @@ public class TableCAGenerator implements CAGenerator {
this.transMaps = new HashMap<>();
this.parentIdSet = new HashSet<>();
for (int i = 0; i < transList.size(); i++) {
this.transMaps.put(transList.get(i).getId(),transList.get(i));
if(transList.get(i).getParentId()!=null) {
this.transMaps.put(transList.get(i).getId(), transList.get(i));
if (transList.get(i).getParentId() != null) {
parentIdSet.add(transList.get(i).getParentId());
}
}
......@@ -49,35 +50,36 @@ public class TableCAGenerator implements CAGenerator {
return new TableCAGenerator(transList);
}
public TableCAResult getResult(){
public TableCAResult getResult() {
return new TableCAResult(this);
}
@Override
public void translate() {
for(Trans trans : transList){
if(trans instanceof SourceTrans) {
for (Trans trans : transList) {
if (trans instanceof SourceTrans) {
TableCA tableCA = TableCA.build(trans);
List<String> sourceFields = new ArrayList<>();
CollectionUtils.addAll(sourceFields, new Object[tableCA.getFields().size()]);
Collections.copy(sourceFields, tableCA.getFields());
for (int j = 0; j < sourceFields.size(); j++) {
buildTableCAFields(tableCA,tableCA.getParentId(),sourceFields.get(j));
buildTableCAFields(tableCA, tableCA.getParentId(), sourceFields.get(j));
}
this.sourceTableCAS.add(tableCA);
}else if(trans instanceof SinkTrans) {
} else if (trans instanceof SinkTrans) {
TableCA tableCA = TableCA.build(trans);
this.sinkTableCA = tableCA;
this.sinkTableName = tableCA.getName();
this.sinkTableCAS.add(TableCA.build(trans));
}
}
}
public void translateOnlyTable() {
for(Trans trans : transList){
if(trans instanceof SourceTrans) {
for (Trans trans : transList) {
if (trans instanceof SourceTrans) {
this.sourceTableCAS.add(new TableCA((SourceTrans) trans));
}else if(trans instanceof SinkTrans) {
} else if (trans instanceof SinkTrans) {
TableCA tableCA = new TableCA((SinkTrans) trans);
this.sinkTableCA = tableCA;
this.sinkTableName = tableCA.getName();
......@@ -85,26 +87,26 @@ public class TableCAGenerator implements CAGenerator {
}
}
private void buildTableCAFields(TableCA tableCA,Integer id,String field){
if(transMaps.get(id) instanceof OperatorTrans){
private void buildTableCAFields(TableCA tableCA, Integer id, String field) {
if (transMaps.get(id) instanceof OperatorTrans) {
OperatorTrans trans = (OperatorTrans) transMaps.get(id);
searchSelectFields(tableCA, trans.getSelect(),field);
searchWhereFields(tableCA, trans.getWhere(),field);
if(Asserts.isNotNull(trans.getParentId())){
buildTableCAFields(tableCA,trans.getParentId(),field);
searchSelectFields(tableCA, trans.getSelect(), field);
searchWhereFields(tableCA, trans.getWhere(), field);
if (Asserts.isNotNull(trans.getParentId())) {
buildTableCAFields(tableCA, trans.getParentId(), field);
}
}
}
private void searchSelectFields(TableCA tableCA, List<Field> selects, String field){
if(Asserts.isNotNull(selects)){
for (int i = 0; i < selects.size(); i++){
List<String> fields = matchFields( selects.get(i).getFragment(),field);
private void searchSelectFields(TableCA tableCA, List<Field> selects, String field) {
if (Asserts.isNotNull(selects)) {
for (int i = 0; i < selects.size(); i++) {
List<String> fields = matchFields(selects.get(i).getFragment(), field);
/*if(tableCA.getFields().contains(field)){
tableCA.getFields().remove(field);
}*/
for (int j = 0; j < fields.size(); j++) {
if(!tableCA.getUseFields().contains(fields.get(j))){
if (!tableCA.getUseFields().contains(fields.get(j))) {
tableCA.getUseFields().add(fields.get(j));
}
}
......@@ -112,28 +114,30 @@ public class TableCAGenerator implements CAGenerator {
}
}
private void searchWhereFields(TableCA tableCA,String wheres,String field){
if(Asserts.isNotNull(wheres)&&!"[]".equals(wheres)){
List<String> fields = matchFields( wheres,field);
private void searchWhereFields(TableCA tableCA, String wheres, String field) {
if (Asserts.isNotNull(wheres) && !"[]".equals(wheres)) {
List<String> fields = matchFields(wheres, field);
/*if(tableCA.getFields().contains(field)){
tableCA.getFields().remove(field);
}*/
for (int j = 0; j < fields.size(); j++) {
if(!tableCA.getUseFields().contains(fields.get(j))){
if (!tableCA.getUseFields().contains(fields.get(j))) {
tableCA.getUseFields().add(fields.get(j));
}
}
}
}
private List<String> matchFields(String fragement,String field){
private List<String> matchFields(String fragement, String field) {
List<String> fields = new ArrayList<>();
Pattern p = Pattern.compile(field+"\\.(.*?) ");
Matcher m = p.matcher(fragement+" ");
while(m.find()){
fields.add(m.group(0).replaceFirst("\\)","").trim());
String sign = "([^a-zA-Z0-9_]?)";
Pattern p = Pattern.compile(sign + field + sign);
Matcher m = p.matcher(fragement);
while (m.find()) {
fields.add(m.group(1).trim());
fields.add(fragement.trim());
}
if(fragement.equals(field)){
if (fragement.equals(field)) {
fields.add(fragement.trim());
}
return fields;
......
package com.dlink.explainer.lineage;
import com.dlink.explainer.ca.ColumnCAResult;
import com.dlink.explainer.ca.NodeRel;
import com.dlink.explainer.ca.TableCA;
import com.dlink.plus.FlinkSqlPlus;
import java.util.ArrayList;
import java.util.List;
/**
......@@ -16,9 +19,23 @@ public class LineageBuilder {
public static LineageResult getLineage(String statement){
FlinkSqlPlus plus = FlinkSqlPlus.build();
List<ColumnCAResult> columnCAResults = plus.explainSqlColumnCA(statement);
for (int j = 0; j < columnCAResults.size(); j++) {
ColumnCAResult result = columnCAResults.get(j);
List<LineageTable> tables = new ArrayList<>();
List<LineageRelation> relations = new ArrayList<>();
int index = 0;
for (ColumnCAResult item: columnCAResults) {
for(TableCA tableCA: item.getTableCAS()){
tables.add(LineageTable.build(tableCA));
}
for(NodeRel nodeRel: item.getColumnCASRelChain()){
index ++;
relations.add(LineageRelation.build(index+"",
item.getColumnCASMaps().get(nodeRel.getPreId()).getTableId().toString(),
item.getColumnCASMaps().get(nodeRel.getSufId()).getTableId().toString(),
item.getColumnCASMaps().get(nodeRel.getPreId()).getName(),
item.getColumnCASMaps().get(nodeRel.getSufId()).getName()
));
}
}
return null;
return LineageResult.build(tables,relations);
}
}
......@@ -13,6 +13,15 @@ public class LineageColumn {
public LineageColumn() {
}
public LineageColumn(String name, String title) {
this.name = name;
this.title = title;
}
public static LineageColumn build(String name, String title){
return new LineageColumn(name,title);
}
public String getName() {
return name;
}
......
package com.dlink.explainer.lineage;
import com.dlink.assertion.Asserts;
import com.dlink.explainer.ca.ColumnCA;
import com.dlink.explainer.ca.NodeRel;
import com.dlink.explainer.ca.TableCA;
import com.dlink.explainer.trans.Field;
import com.dlink.explainer.trans.OperatorTrans;
import com.dlink.explainer.trans.SinkTrans;
import com.dlink.explainer.trans.SourceTrans;
import com.dlink.explainer.trans.Trans;
import com.dlink.utils.MapParseUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* LineageColumnGenerator
*
* @author wenmo
* @since 2022/3/16 20:20
**/
public class LineageColumnGenerator {
private Map<Integer, Trans> transMaps;
private List<TableCA> tableCAS = new ArrayList<>();
private List<ColumnCA> columnCAS = new ArrayList<>();
private Map<Integer, ColumnCA> columnCASMaps = new HashMap<>();
private Set<NodeRel> columnCASRel = new HashSet<>();
private Set<NodeRel> columnCASRelChain = new HashSet<>();
private int index = 1;
private List<Integer> sinkColumns = new ArrayList<>();
private List<Integer> sourceColumns = new ArrayList<>();
public static LineageColumnGenerator build(List<Trans> transList) {
LineageColumnGenerator generator = new LineageColumnGenerator();
Map<Integer, Trans> map = new HashMap<>();
for (Trans trans : transList) {
map.put(trans.getId(), trans);
}
generator.setTransMaps(map);
return generator;
}
public void translate() {
for (Map.Entry<Integer, Trans> entry : transMaps.entrySet()) {
Trans trans = entry.getValue();
if (trans instanceof SourceTrans) {
TableCA tableCA = new TableCA((SourceTrans) trans);
for (String fieldName : tableCA.getFields()) {
int id = index++;
ColumnCA columnCA = new ColumnCA(id, fieldName, fieldName, fieldName, fieldName, fieldName, tableCA, trans);
columnCASMaps.put(id, columnCA);
columnCAS.add(columnCA);
}
for (ColumnCA columnCA : columnCAS) {
if (columnCA.getTableCA().getId() == tableCA.getId()) {
buildColumnCAFields(tableCA, tableCA.getParentId(), columnCA);
}
}
}
}
for (Map.Entry<Integer, Trans> entry : transMaps.entrySet()) {
Trans trans = entry.getValue();
if (trans instanceof SinkTrans) {
TableCA tableCA = new TableCA((SinkTrans) trans);
matchSinkField(tableCA,trans);
searchColumnCAId(tableCA);
}
}
chainRelation();
}
private void matchSinkField(TableCA tableCA,Trans trans){
for(ColumnCA columnCA: columnCAS){
for(String fieldName: tableCA.getFields()){
if(columnCA.getName().equals(fieldName)){
int cid = index++;
ColumnCA sinkColumnCA = new ColumnCA(cid, fieldName, fieldName, fieldName, fieldName, fieldName, tableCA, trans);
columnCASMaps.put(cid, sinkColumnCA);
columnCASRel.add(new NodeRel(columnCA.getId(), cid));
}
}
}
}
private void buildColumnCAFields(TableCA tableCA, Integer id, ColumnCA columnCA) {
if (transMaps.get(id) instanceof OperatorTrans) {
OperatorTrans trans = (OperatorTrans) transMaps.get(id);
List<Field> selects = trans.getSelect();
if (Asserts.isNotNull(selects)) {
for (int i = 0; i < selects.size(); i++) {
String operation = selects.get(i).getFragment();
String alias = selects.get(i).getAlias();
searchSelect(tableCA, columnCA, trans, operation, alias);
}
}
List<String> fields = trans.getFields();
if (Asserts.isNotNull(fields)) {
for (int i = 0; i < fields.size(); i++) {
String field = fields.get(i);
searchSelect(tableCA, columnCA, trans, field, field);
}
}
if (trans.getParentId() != null) {
buildColumnCAFields(tableCA, trans.getParentId(), columnCA);
}
}
}
private void searchSelect(TableCA tableCA, ColumnCA columnCA, OperatorTrans trans, String operation, String alias) {
if (MapParseUtils.hasField(operation, columnCA.getAlias())) {
boolean isHad = false;
Integer cid = null;
for (Map.Entry<Integer, ColumnCA> item : columnCASMaps.entrySet()) {
ColumnCA columnCA1 = item.getValue();
if (columnCA1.getTableCA().getId() == tableCA.getId() && columnCA1.getName().equals(alias)) {
isHad = true;
cid = columnCA1.getId();
break;
}
}
if(columnCA.getId()==cid){
return;
}
if (!isHad) {
cid = index++;
ColumnCA columnCA2 = new ColumnCA(cid, alias, alias, alias, alias, operation, tableCA, trans);
columnCASMaps.put(cid, columnCA2);
buildColumnCAFields(tableCA, trans.getParentId(), columnCA2);
}
columnCASRel.add(new NodeRel(columnCA.getId(), cid));
}
}
private void searchColumnCAId(TableCA tableCA) {
List<Integer> sufOnly = new ArrayList<>();
for (NodeRel nodeRel : columnCASRel) {
if (!sufOnly.contains(nodeRel.getSufId())) {
sufOnly.add(nodeRel.getSufId());
}
}
for (NodeRel nodeRel : columnCASRel) {
if (sufOnly.contains(nodeRel.getPreId())) {
sufOnly.remove(nodeRel.getPreId());
}
}
List<Integer> preOnly = new ArrayList<>();
for (NodeRel nodeRel : columnCASRel) {
if (!preOnly.contains(nodeRel.getPreId())) {
preOnly.add(nodeRel.getPreId());
}
}
for (NodeRel nodeRel : columnCASRel) {
if (preOnly.contains(nodeRel.getSufId())) {
preOnly.remove(nodeRel.getSufId());
}
}
for (int i = 0; i < sufOnly.size(); i++) {
ColumnCA columnCA = columnCASMaps.get(sufOnly.get(i));
List<String> fields = tableCA.getFields();
for (int j = 0; j < fields.size(); j++) {
if (columnCA.getAlias().equals(fields.get(j))) {
tableCA.getColumnCAIds().add(sufOnly.get(i));
columnCA.setTableId(tableCA.getId());
break;
}
}
}
sinkColumns = sufOnly;
sourceColumns = preOnly;
}
private void chainRelation() {
Set<NodeRel> nodeRelsChain = new HashSet<>();
for (Integer item : sourceColumns) {
nodeRelsChain.add(new NodeRel(item, getNextSuf(item)));
}
columnCASRelChain = nodeRelsChain;
}
private Integer getNextSuf(Integer sufId) {
for (NodeRel nodeRel : columnCASRel) {
if (nodeRel.getPreId() == sufId) {
return getNextSuf(nodeRel.getSufId());
}
}
return sufId;
}
public Map<Integer, Trans> getTransMaps() {
return transMaps;
}
public void setTransMaps(Map<Integer, Trans> transMaps) {
this.transMaps = transMaps;
}
public List<TableCA> getTableCAS() {
return tableCAS;
}
public void setTableCAS(List<TableCA> tableCAS) {
this.tableCAS = tableCAS;
}
public List<ColumnCA> getColumnCAS() {
return columnCAS;
}
public void setColumnCAS(List<ColumnCA> columnCAS) {
this.columnCAS = columnCAS;
}
public Map<Integer, ColumnCA> getColumnCASMaps() {
return columnCASMaps;
}
public void setColumnCASMaps(Map<Integer, ColumnCA> columnCASMaps) {
this.columnCASMaps = columnCASMaps;
}
public Set<NodeRel> getColumnCASRel() {
return columnCASRel;
}
public void setColumnCASRel(Set<NodeRel> columnCASRel) {
this.columnCASRel = columnCASRel;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public List<Integer> getSinkColumns() {
return sinkColumns;
}
public void setSinkColumns(List<Integer> sinkColumns) {
this.sinkColumns = sinkColumns;
}
public List<Integer> getSourceColumns() {
return sourceColumns;
}
public void setSourceColumns(List<Integer> sourceColumns) {
this.sourceColumns = sourceColumns;
}
public Set<NodeRel> getColumnCASRelChain() {
return columnCASRelChain;
}
public void setColumnCASRelChain(Set<NodeRel> columnCASRelChain) {
this.columnCASRelChain = columnCASRelChain;
}
}
......@@ -16,6 +16,17 @@ public class LineageRelation {
public LineageRelation() {
}
public LineageRelation(String id, String srcTableId, String tgtTableId, String srcTableColName, String tgtTableColName) {
this.id = id;
this.srcTableId = srcTableId;
this.tgtTableId = tgtTableId;
this.srcTableColName = srcTableColName;
this.tgtTableColName = tgtTableColName;
}
public static LineageRelation build(String id, String srcTableId, String tgtTableId, String srcTableColName, String tgtTableColName){
return new LineageRelation(id,srcTableId,tgtTableId,srcTableColName,tgtTableColName);
}
public String getId() {
return id;
}
......
......@@ -15,6 +15,15 @@ public class LineageResult {
public LineageResult() {
}
public LineageResult(List<LineageTable> tables, List<LineageRelation> relations) {
this.tables = tables;
this.relations = relations;
}
public static LineageResult build(List<LineageTable> tables, List<LineageRelation> relations){
return new LineageResult(tables,relations);
}
public List<LineageTable> getTables() {
return tables;
}
......
package com.dlink.explainer.lineage;
import com.dlink.explainer.ca.TableCA;
import java.util.ArrayList;
import java.util.List;
/**
......@@ -16,6 +19,18 @@ public class LineageTable {
public LineageTable() {
}
public static LineageTable build(TableCA tableCA){
LineageTable lineageTable = new LineageTable();
lineageTable.setId(tableCA.getId().toString());
lineageTable.setName(tableCA.getName());
List<LineageColumn> columnList = new ArrayList<>();
for(String columnName: tableCA.getFields()){
columnList.add(LineageColumn.build(columnName,columnName));
}
lineageTable.setColumns(columnList);
return lineageTable;
}
public String getId() {
return id;
}
......
package com.dlink.explainer.lineage;
import com.dlink.explainer.ca.TableCA;
import com.dlink.explainer.trans.SinkTrans;
import com.dlink.explainer.trans.SourceTrans;
import com.dlink.explainer.trans.Trans;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* LineageTableGenerator
*
* @author wenmo
* @since 2022/3/16 19:56
**/
public class LineageTableGenerator {
private Map<Integer, Trans> transMaps;
private List<TableCA> tables = new ArrayList<>();
public LineageTableGenerator() {
}
public static LineageTableGenerator build(List<Trans> transList){
LineageTableGenerator generator = new LineageTableGenerator();
Map<Integer, Trans> map = new HashMap<>();
for (Trans trans: transList) {
map.put(trans.getId(), trans);
}
generator.setTransMaps(map);
return generator;
}
public void translate() {
for (Map.Entry<Integer, Trans> entry : transMaps.entrySet()) {
if (entry.getValue() instanceof SourceTrans) {
tables.add(TableCA.build(entry.getValue()));
} else if (entry.getValue() instanceof SinkTrans) {
tables.add(TableCA.build(entry.getValue()));
}
}
}
public Map<Integer, Trans> getTransMaps() {
return transMaps;
}
public void setTransMaps(Map<Integer, Trans> transMaps) {
this.transMaps = transMaps;
}
public List<TableCA> getTables() {
return tables;
}
public void setTables(List<TableCA> tables) {
this.tables = tables;
}
}
......@@ -16,18 +16,23 @@ import java.util.Map;
public class OperatorTrans extends AbstractTrans implements Trans {
private List<Field> select;
private List<String> fields;
private List<String> joinType;
private String where;
private List<String> leftInputSpec;
private List<String> rightInputSpec;
public final static String TRANS_TYPE = "Operator";
private final static String FIELD_SEPARATOR = " AS ";
private final static String FIELD_AS = " AS ";
public List<Field> getSelect() {
return select;
}
public List<String> getFields() {
return fields;
}
public List<String> getJoinType() {
return joinType;
}
......@@ -60,31 +65,32 @@ public class OperatorTrans extends AbstractTrans implements Trans {
name = pact;
Map map = MapParseUtils.parseForSelect(contents);
translateSelect((ArrayList<String>) map.get("select"));
fields = (ArrayList<String>) map.get("fields");
joinType = (ArrayList<String>) map.get("joinType");
where = map.containsKey("where")?map.get("where").toString():null;
where = map.containsKey("where") ? map.get("where").toString() : null;
leftInputSpec = (ArrayList<String>) map.get("leftInputSpec");
rightInputSpec = (ArrayList<String>) map.get("rightInputSpec");
}
private void translateSelect(ArrayList<String> fieldStrs){
if(fieldStrs!=null&&fieldStrs.size()>0) {
private void translateSelect(ArrayList<String> fieldStrs) {
if (fieldStrs != null && fieldStrs.size() > 0) {
select = new ArrayList<>();
for (int i = 0; i < fieldStrs.size(); i++) {
String fieldStr = fieldStrs.get(i);
if(fieldStr.toUpperCase().contains(FIELD_SEPARATOR)){
String [] fieldNames = fieldStr.split(FIELD_SEPARATOR);
if(fieldNames.length==2) {
select.add(new Field(fieldNames[0], fieldNames[1]));
}else if(fieldNames.length==1) {
select.add(new Field(fieldNames[0]));
}else{
String fieldStr = fieldStrs.get(i).trim();
if (fieldStr.toUpperCase().contains(FIELD_AS)) {
String[] fieldNames = fieldStr.split(FIELD_AS);
if (fieldNames.length == 2) {
select.add(new Field(fieldNames[0].trim(), fieldNames[1].trim()));
} else if (fieldNames.length == 1) {
select.add(new Field(fieldNames[0].trim()));
} else {
List<String> fieldNameList = new ArrayList<>();
for (int j = 0; j < fieldNames.length-1; j++) {
for (int j = 0; j < fieldNames.length - 1; j++) {
fieldNameList.add(fieldNames[j]);
}
select.add(new Field(StringUtils.join(fieldNameList,FIELD_SEPARATOR),fieldNames[fieldNames.length-1]));
select.add(new Field(StringUtils.join(fieldNameList, FIELD_AS).trim(), fieldNames[fieldNames.length - 1].trim()));
}
}else{
} else {
select.add(new Field(fieldStr));
}
}
......
......@@ -8,6 +8,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
......@@ -105,6 +107,54 @@ public class MapParseUtils {
return nestIndexList;
}
public static List<String> getSelectList(String inStr) {
List<String> selects = new ArrayList<>();
int startIndex = inStr.indexOf("=[") + 2;
if (inStr == null || inStr.isEmpty()) {
return selects;
}
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < inStr.length(); i++) {
if (inStr.charAt(i) == ',' && stack.size() == 0) {
selects.add(inStr.substring(startIndex, i));
startIndex = i + 1;
}
if (inStr.charAt(i) == '(') {
stack.push(i);
}
if (inStr.charAt(i) == ')') {
stack.pop();
}
}
if (startIndex < inStr.length()) {
selects.add(inStr.substring(startIndex, inStr.length() - 1));
}
return selects;
}
public static boolean hasField(String fragement, String field) {
if(field.startsWith("$")){
field = field.substring(1,field.length());
}
String sign = "([^a-zA-Z0-9_]?)";
Pattern p = Pattern.compile(sign + field + sign);
Matcher m = p.matcher(fragement);
while (m.find()) {
return true;
}
return false;
}
public static String replaceField(String operation, String field, String fragement) {
String newOperation = operation;
String sign = "([^a-zA-Z0-9_]?)";
Pattern p = Pattern.compile(sign + field + sign);
Matcher m = p.matcher(operation);
while (m.find()) {
newOperation = newOperation.substring(0,m.start(1)+1)+ fragement + newOperation.substring(m.end(1)+1,newOperation.length());
}
return newOperation;
}
/**
* 转换map
......@@ -180,27 +230,7 @@ public class MapParseUtils {
*/
public static Map parseForSelect(String inStr) {
Map map = new HashMap();
List<Integer> bracketsList = getBracketsList(inStr);
String mapKey = getMapKey(inStr);
List<String> list = new ArrayList<>();
int size = bracketsList.size();
if (size % 2 != 0) {
// 此处若size部位偶数 则返回空 可能会存在问题
return map;
} else {
int numSize = size / 2;//括号对数
for (int i = 0; i < numSize; i++) {
String msgStr = "";
if (2 * i + 2 >= size) {
msgStr = inStr.substring(bracketsList.get(2 * i), inStr.lastIndexOf("]"));
} else {
msgStr = inStr.substring(bracketsList.get(2 * i), bracketsList.get(2 * i + 2));
msgStr = msgStr.substring(0, msgStr.lastIndexOf(",") > 0 ? msgStr.lastIndexOf(",") : msgStr.length());
}
list.add(msgStr);
}
}
map.put(mapKey, list);
map.put(getMapKey(inStr), getSelectList(inStr));
return map;
}
......
package com.dlink.core;
import com.dlink.explainer.ca.CABuilder;
import com.dlink.explainer.ca.ColumnCANode;
import com.dlink.explainer.ca.TableCANode;
import com.dlink.explainer.lineage.LineageBuilder;
import com.dlink.explainer.lineage.LineageResult;
import org.junit.Test;
import java.util.List;
/**
* LineageTest
*
......
import React from 'react';
import {Tooltip} from 'antd';
import {
FullscreenOutlined,
FullscreenExitOutlined,
VerticalAlignBottomOutlined,
VerticalAlignTopOutlined
} from '@ant-design/icons';
const LineageOps = ({
isExpand,
isFold,
onAction,
tableId,
}) => [
isExpand ?
{
tooltip: '收起血缘',
action: 'shrink',
component: <FullscreenExitOutlined/>
}
:
{
tooltip: '展开血缘',
action: 'expand',
component: <FullscreenOutlined/>
},
isFold ?
{
tooltip: '展开字段',
action: 'fold',
component: <VerticalAlignBottomOutlined/>
}
:
{
tooltip: '收起字段',
action: 'unfold',
component: <VerticalAlignTopOutlined/>
}
].map(op => {
return {
component: (
<Tooltip
title={op.tooltip}
>
<span onClick={() => onAction(op.action, tableId)}>
{
op.component
}
</span>
</Tooltip>
)
}
});
export default LineageOps;
import {LineageTable} from 'react-lineage-dag';
import {useEffect, useState, useRef} from "react";
import LineageOps from "@/components/Lineage/LineageOps";
export const getInit = () => {
return {
tables: [],
relations: []
}
};
const Lineage = (props: any) => {
const {datas} = props;
const cvsRef = useRef(null);
const [data, setData] = useState(getInit());
const [allData, setAllData] = useState(getInit());
const [relayout, setRelayout] = useState(false);
const [focus, setFocus] = useState(false);
const getChildren = (tableId) => {
const children = {
tables: [],
relations: []
};
debugger;
allData.relations.forEach(relation => {
if (relation.srcTableId !== tableId) {
return;
}
children.relations.push(relation)
const tgtTableId = relation.tgtTableId;
if (children.tables.some(table => table.id === tgtTableId)) {
return;
}
const table = allData.tables.find(table => table.id === tgtTableId);
children.tables.push(table);
});
return children;
};
const onAction = (action, tableId) => {
switch (action) {
case 'expand': {
const table = data.tables.find(t => t.id === tableId);
table.isExpand = true;
const children = getChildren(tableId);
children.tables.forEach(table => {
if(data.tables.some(t => t.id === table.id)) {
return;
}
data.tables.push(table);
});
children.relations.forEach(relation => {
if(data.relations.some(r => r.id === relation.id)) {
return;
}
data.relations.push(relation);
});
setData({...data});
break;
}
case 'shrink': {
const table = data.tables.find(t => t.id === tableId);
table.isExpand = false;
const children = getChildren(tableId);
children.tables.forEach(table => {
const index = data.tables.findIndex(t => t.id === table.id);
data.tables.splice(index, 1);
});
children.relations.forEach(relation => {
const index = data.relations.findIndex(r => r.id === relation.id);
data.relations.splice(index, 1);
});
setData({...data});
break;
}
case 'fold': {
data.tables.forEach(table => {
if (table.id !== tableId) {
return;
}
table.isFold = false;
});
data.tables = [...data.tables];
setData({...data});
break;
}
case 'unfold': {
data.tables.forEach(table => {
if (table.id !== tableId) {
return;
}
table.isFold = true;
});
data.tables = [...data.tables];
setData({...data});
break;
}
}
};
const getData = () => {
setData(datas);
let newDatas = {
tables: [...datas.tables],
relations: [...datas.relations]
};
setAllData(newDatas);
};
useEffect(() => {
getData();
}, [datas]);
data.tables.forEach(table => {
table.operators = LineageOps({
isExpand: !!table.isExpand,
isFold: !!table.isFold,
onAction,
tableId: table.id
})
});
return (<LineageTable {...data}
onLoaded={(canvas) => {
cvsRef.current = canvas;
}}
onEachFrame={() => {
if (!cvsRef.current) {
return;
}
if (relayout) {
cvsRef.current.relayout();
setRelayout(false);
}
if (focus) {
cvsRef.current.focusNode(focus);
setFocus(false);
}
}}/>)
};
export default Lineage;
import { Tabs,Empty,Tooltip,Button } from "antd";
import { FlowAnalysisGraph } from '@ant-design/charts';
import { Tabs,Tooltip,Button } from "antd";
import {SearchOutlined} from "@ant-design/icons";
import {StateType} from "@/pages/FlinkSqlStudio/model";
import {connect} from "umi";
import styles from "./index.less";
import { getCAByStatement} from "@/pages/FlinkSqlStudio/service";
import {getLineage} from "@/pages/FlinkSqlStudio/service";
import {useState} from "react";
import Lineage, {getInit} from "@/components/Lineage";
const { TabPane } = Tabs;
const StudioCA = (props:any) => {
const StudioCA = (props: any) => {
const {current} = props;
const [oneTableCAData,setOneTableCAData] = useState<any>(null);
const [oneColumnCAData,setOneColumnCAData] = useState<any>(null);
const [data, setData] = useState(getInit());
const nodeStateStyles = {
hover: {
stroke: '#1890ff',
lineWidth: 2,
},
selected: {
stroke: '#f00',
lineWidth: 3,
},
};
const buildGraphData=(data,graphData)=>{
if(!graphData.nodes){
graphData.nodes = [];
}
if(!graphData.edges){
graphData.edges = [];
}
for(let i in data){
let nodesItem = {
id:data[i].id,
value:{
title:data[i].name,
items: [
{
text: data[i].columns,
},
],
}
}
graphData.nodes.push(nodesItem);
if(data[i].children){
for(let j in data[i].children){
graphData.edges.push({source: data[i].children[j].id,
target: data[i].id,
value: ''});
buildGraphData(data[i].children,graphData);
}
}
}
};
const config = {
data: oneTableCAData,
height:350,
nodeCfg: {
size: [160, 65],
items: {
autoEllipsis: false,
padding: [10],
containerStyle: {
// fill: '#fff',
width:'100px',
},
style: (cfg, group, type) => {
const styles = {
value: {
// fill: '#000',
},
text: {
// fill: '#222',
width:'100px',
},
};
return styles[type];
},
},
nodeStateStyles: {
hover: {
// stroke: '#1890ff',
lineWidth: 2,
},
},
style: {
// fill: '#40a9ff',
// stroke: '#1890ff',
},
},
edgeCfg: {
type: 'polyline',
label: {
style: {
// fill: '#666',
fontSize: 12,
fillOpacity: 1,
},
},
endArrow: {
// fill: '#333',
},
edgeStateStyles: {
hover: {
// stroke: '#1890ff',
lineWidth: 2,
},
},
},
markerCfg: (cfg) => {
const { edges } = oneTableCAData;
return {
position: 'right',
show: edges.find((item) => item.source === cfg.id),
collapsed: !edges.find((item) => item.source === cfg.id),
};
},
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-node'],
/*layout: {
rankdir: 'TB',
ranksepFunc: () => 20,
},*/
};
const columnConfig = {
data:oneColumnCAData,
behaviors: ['drag-canvas', 'zoom-canvas', 'drag-node'],
bodyStyle: {
fill: '#aaa',
},
nodeStateStyles,
onReady: (graph) => {
graph.on('node:mouseenter', (evt) => {
const item = evt.item;
graph.setItemState(item, 'hover', true);
});
graph.on('node:mouseleave', (evt) => {
const item = evt.item;
graph.setItemState(item, 'hover', false);
});
},
edgeStyle: (item, graph) => {
/**
* graph.findById(item.target).getModel()
* item.source: 获取 source 数据
* item.target: 获取 target 数据
*/
// console.log(graph.findById(item.target).getModel());
return {
stroke: '#40a9ff',
// lineWidth: graph.findById(item.target).getModel().columnSize,
lineWidth: 1,
strokeOpacity: 0.5,
};
},
nodeStyle: () => {
return {
stroke: '#40a9ff',
};
},
};
const getOneTableCA=()=>{
const res = getCAByStatement({
const handleLineage=()=>{
const res = getLineage({
statement:current.value,
type: 1,
});
res.then((result)=>{
if(result.code==0){
let graphData = {};
buildGraphData(result.datas,graphData);
setOneTableCAData(graphData);
}else{
setOneTableCAData(null);
}
setData(result.datas);
})
};
const getOneColumnCA=()=>{
const res = getCAByStatement({
statement:current.value,
type: 2,
});
res.then((result)=>{
if(result.code==0){
setOneColumnCAData(buildGraphData(result.datas[0]));
}else{
setOneColumnCAData(null);
}
})
};
const fullTreeData=(node)=>{
if(node){
node.body=node.columns.toString();
for(let i in node.children){
node.children[i] = fullTreeData(node.children[i])
}
return node;
}
return null;
};
return (
<Tabs defaultActiveKey="OneTableCA" size="small" tabPosition="left" >
<TabPane
tab={
<span>
单任务表级血缘
</span>
}
key="OneTableCA"
>
<div>
<div>
<Tooltip title="重新计算血缘">
<Button
type="text"
icon={<SearchOutlined />}
onClick={getOneTableCA}
/>
</Tooltip>
</div>
{oneTableCAData!=null?<FlowAnalysisGraph {...config} />:<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
</div>
</TabPane>
{/*<TabPane
tab={
<span>
单任务字段级血缘
</span>
}
key="OneColumnCA"
>
<div>
<div style={{float: "left"}}>
<Tooltip title="重新计算血缘">
<Button
type="text"
icon={<SearchOutlined />}
onClick={getOneColumnCA}
/>
</Tooltip>
</div>
{oneColumnCAData!=null?<IndentedTreeGraph {...columnConfig} />:<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
</div>
</TabPane>*/}
{/*<TabPane
tab={
<span>
全局表级血缘
</span>
}
key="AllTableCA"
>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
return (<>
<Tabs defaultActiveKey="Lineage" size="small" tabPosition="top" style={{border: "1px solid #f0f0f0"}}
tabBarExtraContent={<Tooltip title="重新计算血缘">
<Button
type="text"
icon={<SearchOutlined />}
onClick={handleLineage}
>
计算血缘
</Button>
</Tooltip>}
>
<TabPane tab={<span>血缘分析</span>} key="Lineage">
<Lineage datas={data}/>
</TabPane>
<TabPane
tab={
<span>
全局字段级血缘
</span>
}
key="AllColumnCA"
>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
</TabPane>*/}
</Tabs>
);
</>)
};
export default connect(({ Studio }: { Studio: StateType }) => ({
......
import {Tabs, Empty} from 'antd';
import CodeShow from "@/components/Common/CodeShow";
import {LineageTable} from 'react-lineage-dag';
import {getLineage} from "@/pages/DevOps/service";
import {useEffect, useState} from "react";
import Lineage, {getInit} from "@/components/Lineage";
const {TabPane} = Tabs;
const DataMap = (props: any) => {
const {job} = props;
const [data, setData] = useState(getInit());
const data = {
tables: [
{
id: '1',
name: 'table-1',
columns: [
{
name: 'id',
title: 'id'
},
{
name: 'age',
title: 'age'
}
]
},
{
id: '2',
name: 'table-2',
columns: [
{
name: 'id',
title: 'id'
},
{
name: 'age',
title: 'age'
}
]
},
{
id: '3',
name: 'table-3',
columns: [
{
name: 'id',
title: 'id'
},
{
name: 'age',
title: 'age'
}
]
}
],
relations: [
{
srcTableId: '1',
tgtTableId: '2',
// srcTableColName: 'id',
// tgtTableColName: 'age'
},
{
srcTableId: '1',
tgtTableId: '3',
// srcTableColName: 'id',
// tgtTableColName: 'age'
}
]
const getData = () => {
const res = getLineage(job.instance?.id);
res.then((result)=>{
result.datas.tables.forEach(table => {
table.isExpand = true;
table.isFold = false;
});
setData(result.datas);
});
};
useEffect(() => {
getData();
}, []);
return (<>
<Tabs defaultActiveKey="OneCA" size="small" tabPosition="top" style={{
<Tabs defaultActiveKey="Lineage" size="small" tabPosition="top" style={{
border: "1px solid #f0f0f0"
}}>
<TabPane tab={<span>血缘分析</span>} key="OneCA">
<LineageTable {...data} onEachFrame={() => { }}/>
<TabPane tab={<span>血缘分析</span>} key="Lineage">
<Lineage datas={data}/>
</TabPane>
</Tabs>
</>)
......
......@@ -11,3 +11,7 @@ export function getJobInfoDetail(id: number) {
export function refreshJobInfoDetail(id: number) {
return getData("api/jobInstance/refreshJobInfoDetail",{id});
}
export function getLineage(id: number) {
return getData("api/jobInstance/getLineage",{id});
}
......@@ -64,8 +64,8 @@ export async function getCatalogueTreeData(params?: StudioParam) {
});
}
export async function getCAByStatement(params: CAParam) {
return request<API.Result>('/api/studio/getCAByStatement', {
export async function getLineage(params: CAParam) {
return request<API.Result>('/api/studio/getLineage', {
method: 'POST',
data: {
...params,
......
......@@ -764,6 +764,12 @@ export default (): React.ReactNode => {
<li>
<Link>修复 kubernetes集群配置相关显示bug</Link>
</li>
<li>
<Link>新增 运维中心血缘分析——字段级</Link>
</li>
<li>
<Link>优化 Studio血缘分析为字段级</Link>
</li>
</ul>
</Paragraph>
</Timeline.Item>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment