跳转到主要内容

热门内容

今日:


总体:


最近浏览:


Chinese, Simplified

category

D2是一种现代的从文本到图表的语言。它的构建考虑到了可扩展性。

…D2有一个内置的API,用于在高级操作AST(比AST节点上的CRUD更智能)。这允许任何人以编程方式构建和编辑图表。目标不是启用特定的用例,而是为无限的工作流启用不可预见的用例。
- https://d2lang.com/tour/future#developer-工具

这篇博客文章将展示一个具体的例子,通过使用D2的语言API来构建一个图表,在SQL语句的每一行之后可视化模式。

我们将把这组语句。。。


CREATE TABLE movie;
ALTER TABLE movie ADD COLUMN id integer;
ALTER TABLE movie ADD COLUMN star integer;
ALTER TABLE movie ADD COLUMN budget integer;
ALTER TABLE movie ADD COLUMN profit integer;
ALTER TABLE movie ADD COLUMN producer integer;
ALTER TABLE movie ADD COLUMN dialogue integer;

CREATE TABLE actor;
ALTER TABLE actor ADD COLUMN id integer;
ALTER TABLE actor ADD COLUMN name text;
ALTER TABLE actor ADD COLUMN native_lang integer;

CREATE TABLE producer;
ALTER TABLE producer ADD COLUMN id integer;
ALTER TABLE producer ADD COLUMN name text;
ALTER TABLE producer ADD COLUMN native_lang integer;

CREATE TABLE language;
ALTER TABLE language ADD COLUMN id integer;
ALTER TABLE language ADD COLUMN name text;

ALTER TABLE movie ADD CONSTRAINT fk_movie_actor FOREIGN KEY (star) REFERENCES actor (id)
ALTER TABLE movie ADD CONSTRAINT fk_movie_producer FOREIGN KEY (producer) REFERENCES producer (id)
ALTER TABLE movie ADD CONSTRAINT fk_movie_language FOREIGN KEY (dialogue) REFERENCES language (id)
ALTER TABLE producer ADD CONSTRAINT fk_producer_language FOREIGN KEY (native_lang) REFERENCES language (id)
ALTER TABLE actor ADD CONSTRAINT fk_actor_language FOREIGN KEY (native_lang) REFERENCES language (id)

注意每个步骤如何与上面的SQL查询相对应,从而创建正在构建的模式的可视化。
步骤1:设置
下面的代码将调用D2库来创建一个具有单个形状的新图表,并呈现SVG文件。我将隐藏导入并跳过错误检查以避免样板文件。您可以在GitHub存储库中找到此演示的可运行代码。

func main() {
    ctx := context.Background()
    // Start with a new, empty graph
    _, graph, _ := d2lib.Compile(ctx, "", nil)

    // Create a shape, "meow"
    graph, _, _ = d2oracle.Create(graph, "meow")

    // Turn the graph into a script (which would just be "meow")
    script := d2format.Format(graph.AST)

    // Initialize a ruler to measure font glyphs
    ruler, _ := textmeasure.NewRuler()

    // Compile the script into a diagram
    diagram, _, _ := d2lib.Compile(context.Background(), script, &d2lib.CompileOptions{
        Layout:  d2dagrelayout.DefaultLayout,
        Ruler:   ruler,
    })

    // Render to SVG
    out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
        Pad: d2svg.DEFAULT_PADDING,
    })

    // Write to disk
    _ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
}

这为我们提供了以下SVG:

 

步骤2:解析SQL语句
下面的代码遍历SQL语句,并为我们提供结构化数据,比如命令调用的命令和表。一堆假设,例如语句必须是一行。不重要/演示要点。

type Query struct {
    Command string
    Table   string
    Column  string
    Type    string

    ForeignTable  string
    ForeignColumn string
}

func parseSQL(plan string) (out []Query) {
    lines := strings.Split(plan, "\n")

    for _, line := range lines {
        if len(strings.TrimSpace(line)) == 0 {
            continue
        }
        out = append(out, parseSQLCommand(strings.Trim(line, ";")))
    }
    return out
}

func parseSQLCommand(command string) Query {
    q := Query{}

    words := strings.Split(command, " ")
    if strings.HasPrefix(command, "CREATE") {
        q.Command = "create_table"
        q.Table = words[2]
    } else if strings.Contains(command, "ADD COLUMN") {
        q.Command = "add_column"
        q.Table = words[2]
        q.Column = words[5]
        q.Type = words[6]
    } else if strings.Contains(command, "ADD CONSTRAINT") {
        q.Command = "add_foreign_key"
        q.Table = words[2]
        q.Column = strings.Trim(strings.Trim(words[8], "("), ")")
        q.ForeignTable = words[10]
        q.ForeignColumn = strings.Trim(strings.Trim(words[11], "("), ")")
    }

    return q
}

步骤3:调用d2oracle
d2oracle是D2中的API包。它可以用于创建和删除对象和连接、设置属性以及将对象移动到不同的容器。例如,给定这个D2脚本:

netflix: {
 movie
}
hulu

let's watch -> netflix.movie
你可以调用d2oracle。Move(graph,“netflix.movie”,“hulu.moire”)将电影对象从一个容器移动到另一个容器,生成的脚本将是:

netflix
hulu: {
 movie
}

let's watch -> hulu.movie

回到我们的演示,然后可以将每条SQL语句转换为d2oracle调用。

func (q Query) transformGraph(g *d2graph.Graph) *d2graph.Graph {
    switch q.Command {
    case "create_table":
        // Create an object with the ID set to the table name
        newG, newKey, _ := d2oracle.Create(g, q.Table)
        // Set the shape of the newly created object to be D2 shape type "sql_table"
        shape := "sql_table"
        newG, _ = d2oracle.Set(g, fmt.Sprintf("%s.shape", newKey), nil, &shape)
        return newG
    case "add_column":
        newG, _ := d2oracle.Set(g, fmt.Sprintf("%s.%s", q.Table, q.Column), nil, &q.Type)
        return newG
    case "add_foreign_key":
        newG, _, _ := d2oracle.Create(g, fmt.Sprintf("%s.%s -> %s.%s", q.Table, q.Column, q.ForeignTable, q.ForeignColumn))
        return newG
    }
    return nil
}

第4步:把所有的东西放在一起
读取.sql文件。
将每个原始字符串语句转换为结构化命令(使用步骤2中的函数)。
将每个命令传递到步骤3的函数中,以编程方式编辑图表。
在每行之后渲染一个SVG文件。

ctx := context.Background()
_, graph, _ := d2lib.Compile(ctx, "", nil)
ruler, _ := textmeasure.NewRuler()

// 1----
f, _ := ioutil.ReadFile(filepath.Join("plan.sql"))

// 2----
queries := parseSQL(string(f))

for i, q := range queries {
 // 3----
 graph = q.transformGraph(graph)

 script := d2format.Format(graph.AST)

 diagram, _, _ := d2lib.Compile(context.Background(), script, &d2lib.CompileOptions{
   Layout: d2dagrelayout.DefaultLayout,
   Ruler:  ruler,
 })

 // 4----
 out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
   Pad: d2svg.DEFAULT_PADDING,
 })

 _ = ioutil.WriteFile(filepath.Join("svgs", fmt.Sprintf("step%d.svg", i)), out, 0600)
}

有文本支持的图表
如果您使用图形库来完成此操作,那么生成的SVG就会僵化。但是,使用D2,对于它生成的任何图表,您都可以获得支持它的文本。在我们的程序末尾插入这一行。。。

_ = ioutil.WriteFile("out.d2", []byte(d2format.Format(graph.AST)), 0600)

…为您获取这个D2脚本:


movie: {
 id: integer
 star: integer
 budget: integer
 profit: integer
 producer: integer
 dialogue: integer
}
movie.shape: sql_table
actor: {
 id: integer
 name: text
 native_lang: integer
}
actor.shape: sql_table
producer: {
 id: integer
 name: text
 native_lang: integer
}
producer.shape: sql_table
language: {
 id: integer
 name: text
}
language.shape: sql_table
movie.star -> actor.id
movie.producer -> producer.id
movie.dialogue -> language.id
producer.native_lang -> language.id
actor.native_lang -> language.id

想为受众自定义此图表吗?给你的产品经理或设计师花呢,然后在其他地方重复使用?根据你的喜好设计风格,还是通过互动来丰富?没有必要修改分支的源代码。将支持文本添加到图表中意味着您可以以简单、可访问的格式进行存储、版本控制和进一步修改。也许你的同事会拿下图表并做一些笔记:

在开源的两个月里,可扩展性和可组合性是D2流行的关键。它使我们能够快速集成技术,如用于Latex的MathJax,或用于上面手绘外观的RoughJS。它还使社区能够轻松地在D2之上进行构建。重要的是,它为用户提供了可选性,比如交换布局引擎的能力,或者主题的选择(或者自己制作!)。

本文地址
最后修改
星期日, 五月 26, 2024 - 21:25

Tags

Article