表建模可视化

通常表的血缘关系、ER图、异构表建模都属于差不多类型,相较于关系模型,此类更于复杂,本次展开讲一讲异构表建模的实现及过程。

发现身边事儿、聊点周奇遇,我是沈二,期待奇遇的互联网灵魂~、一起聊天吹水,探索新的可能~wx:breathingss,入圈吧!

效果

先说说思路,需求是能够表现出不同数据库连接之间的关联汇聚,类似于一个ETL的汇聚过程,但能尽量在可视化部分表现出相关的关联;

  1. 表类似于组的概念,列类似于连接锚点的概念,此处的思路会有一些区别

  2. 有关于结构化存储的json存储,涉及的内容包含表信息源信息选择列信息连接信息 等,处理起来比较繁琐,因此有了转sql的处理

  3. Spark SQL 作为处理端,通过源和sql的汇聚计算处理,形成新的物理表存储

组件解析

前端

以vue2作为基础进行开发,相关涉及到自定义HTML节点需要引用@antv/x6-vue-shape

@antv/x6

经过该节点的自定义,实现了列的显示,因原本x6 markup的语法针对一些如选择、文本超长缩略显示等问题,最终选用灵活可控的这种方式进行,紧接着遇到一些问题。

1. 锚点的设置及显示交互问题

因为用的html表格,而与绘制的锚点需要进行重叠,因此对高度及相关的显示都要准确,

  • table-layout: fixed; 表格和列的宽度是由 tablecol 元素的宽度或第一行单元格的宽度来设置的。后续行中的单元格不会影响列的宽度。
  • 高度的问题,需要注意的是,如果没有内部子标签,高度经常会得不到理想高度,通过增加子标签的形式,对子标签设置高度进行解决
js 复制代码
  .data-table {
  width: 300px;
  background: #fff;
  table-layout: fixed;
  border-collapse: collapse;
  word-break: break-all;
  padding: 10px;

  }
  tr {
    td {
      white-space: nowrap;
      text-overflow: ellipsis;
      overflow: hidden;
      //  max-width: 100px;
      margin: 0;
      padding: 0;
      section {
        box-sizing: border-box;
       border-bottom: 2px dashed #eee;
        height: 24px;
        line-height:24px;
        overflow: hidden;
        white-space: nowrap;
      text-overflow: ellipsis;
      }
    }
  }

2. 附加操作问题

本来想着可能如果涉及LEFT JOIN ,RIGHT JOIN 会导致顺序及参照问题,需要设置一个表作为主参照,另外就是全选和反选的全局操作,因此增加了此项,最开始想放在点击或者其他地方,后面就索性放在了显眼的位置。

3.锚点交互的问题

一种是如此类进行以⚪的形式一行数据两个锚点,但这种感觉绘制的有点儿多 主要采用的是长方形的锚点,左右突出,设置层级的方式,保证了交互及显示的形式,通过样式融入。

waterline-sql-builder

用以将关系数据解析成sql语法表达式

js 复制代码
var SQLBuilder = require('waterline-sql-builder');
var compile = SQLBuilder({ dialect: 'postgres' }).generate;

// Compile a statement to obtain a SQL template string and an array of bindings.
var report = compile({
  select: ['id'],
  where: {
    firstName: 'Test',
    lastName: 'User'
  },
  from: 'users'
});

console.log(report);
//=>
//{
//  sql: 'select "id" from "users" where "firstName" = $1 and "lastName" = $2',
//  bindings: ['Test', 'User']
//}

转换方法

将x6产生的关系构建成sql的解析结构,从而获取sql表达式

js 复制代码
//获取sql模板字符串
    getSqlString() {
      const { cells } = this.graph.toJSON();
      if (cells.length <= 0) {
        return { sql: "", tables: [] };
      }
      var tables = [];
      const [firstNode, ...nodes] = cells.filter((it) => it.ports);
      const edgs = cells.filter((it) => !it.ports);
      var columns = [];
      var mapTables = new Map();
      var mapSource=new Map();
      [firstNode, ...nodes].map((it, index) => {
        const { items } = it.ports;
        tables.push(it.data);
        const reName = `T${(index+1)}`;
        mapTables.set(it.data.tableName, reName);
        // mapSource.set(it.data.tableName, reName);
        const column = items
          .filter((iv) => iv.data.checked)
          .map((iv) => `${reName}.${iv.data.columnName}`);
        // .map(iv=>`${it.data.tableName}.${iv.data.columnName}`)

        columns = columns.concat(column);
      });
      const getTplTb = (tableName) =>
        `${tableName} ${mapTables.get(tableName)} `;

      var sqlBuildSql = {
        select: columns,
        from: getTplTb(firstNode.data.tableName),
        join: [],
      };
      // [firstNode,...nodes].sort((a,b)=>{
      const pdt = [firstNode, ...nodes];
      for (let index = 0; index < pdt.length - 1; index++) {
        const a = pdt[index];
        const b = pdt[index + 1];
        const { data } = b;
        // a~b之间存在关系, 关联因为以第一个节点为依据,所以用第二个节点作为主体;
        var item = {};
        const links = edgs
          .filter(
            (it) =>
              (it.source.cell == a.data.tableId &&
                it.target.cell == b.data.tableId) ||
              (it.source.cell == b.data.tableId &&
                it.target.cell == a.data.tableId)
          )
          .map((it) => {
            //it.source.cell==a.data.tableId
            const func = (firstNode, type) => {
              let columnItemA = firstNode.ports.items.find(
                (iv) => iv.id == it[type].port
              );
              if (columnItemA) {
                // item[firstNode.data.tableName]=columnItemA.data.columnName;
                const name = mapTables.get(firstNode.data.tableName);
                item[name] = columnItemA.data.columnName;
              }
            };
            func(a, "source");
            func(a, "target");
            func(b, "source");
            func(b, "target");
          });
        //port
        if (!Object.keys(item).length > 0) {
          return;
        }
        var joinStr = {
          //  from: data.tableName,
          from: getTplTb(data.tableName),
          on: [item],
        };

        sqlBuildSql.join.push(joinStr);
      }
      //解析返回
      var sqlStr = SQL.generate(sqlBuildSql);
      //补充表源信息
      sqlStr.tables = tables;
      sqlStr.sql=sqlStr.sql.replaceAll('`','');
      return sqlStr;
    },

服务端

处理的本质其实在于有点儿类似于labda表达式那种,不同源的数据需要获取,再根据sql语法解析关联获取到实际的数据内容,然后再建表入库操作。

相关推荐
聪明的墨菲特i3 分钟前
Django前后端分离基本流程
后端·python·django·web3
熊的猫12 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
速盾cdn19 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
hlsd#1 小时前
go mod 依赖管理
开发语言·后端·golang
四喜花露水1 小时前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
陈大爷(有低保)1 小时前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、1 小时前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头1 小时前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
前端Hardy1 小时前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端