Blog | Phodal - A Growth Engineerhttp://www.phodal.com/blog/2022-02-20T12:06:08.939526+00:00Blog数据处理的抽象:数据与知识处理的思考2022-02-20T12:05:05+00:002022-02-20T12:06:08.939526+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/meta-data-abstract/PS:这是一篇思考 + 笔记的整理。
过去的两个月里,在业余时间里,我一直在设计 Quake(<https: github.com="" phodal="" quake="">),一个我称之为知识管理元框架的工具。Quake 除了是一个知识管理工具,它还包含了我对于数据处理的一系列想法:
* 外部 DSL 描述数据流。在数据领域里,诸如于 Presto 这样的 SQL on Everything 框架就是一个不错的示例。
* 数据源的抽象与类型系统:。对于元数据库来说,Apache Atlas 这样的抽象,也提供了一个非常好的思路。
* 数据与行为抽象的低代码。 类型流 Typeflow 在这方面提供了我在这方面的灵感。
* 端到端的自定义 BI。在 BI 领域里的 Tableau 和 Superset 也是非常不错的一个参考。
回过头来看,如果只从阅读的层面来看,Quake 便是一个关于知识的数据系统,我们也可以把它作为一种知识即服务(KaaS)系统/框架来设计。将它视为知识图谱,便可分为了四层:
* 知识应用。
* 知识服务:规则引擎、知识推理、导入等。
* 知识能力:知识建模、知识融合等。
* 知识数据标准化:数据清洗、数据过滤、知识加工等。
从知识的过程上来说,它便是针对于数据源,对于知识内容进行建模。而建模的核心则是构建一个针对于知识的元数据系统,以及用来描述这些数据的类型系统;随后呢,设计一个针对于元模型的 DSL,用它来对整体的过程进行抽象。针对于结果,我们可以设计一个可视化的组件机制,用于展示不同的内容和数据。最后,因为我们在过程中会不断地产生数据,所以就需要针对于知识相关的事件,实时地计算它们。
## 元模型:元数据与类型系统
几年前,在公司大佬的推荐下,我对 Apache Atlas 做了简单的分析。Apache Atlas 提供了一个开放的元数据管理和治理功能,以构建其数据资产的目录(catalog),对这些资产进行分类和治理。其类型系统中的类型是对如何存储和访问特定类型的元数据对象的定义。简单来说,它提供了一个类型系统用于描述元数据源,统一不同的数据源,以方便我们对其进行管理。维基百科上对于元数据(metadata)的定义是:
> **元数据**(英语:**metadata**),主要是描述资料属性(property)的信息,用来支持如指示存储位置、历史资料、资源查找、文件记录等功能。元数据有六种不同类型,分别是**记叙性元数据**、**结构性元数据**、**管理性元数据**、**参考性元数据**、**统计性元数据**及**法律性元数据**。
在定义了数据的来源之后,进行稍微的编码之后,我们就能针对于这些数据进行处理了。只是呢,为了更有针对性也对这些资料进行处理,我们需要对它们进行归类。接着,去定义如何操作这些类型,这些类型如何互相作用,这便是一个类型系统所要做的。只是呢,基于这种模式,实现的只是一个简单的类型系统。与我们在真正的编程语言里实现的类似系统,有着非常巨大的差异 —— 编程语言的模型,可以作为一种元元模型而存在。
有了元数据来对源数据进行描述,以及一个简单的类型系统之后,我们就相当于构建了一个元模型,即模型的模型。 从定义上来说,这个过程被称之为**元**建模,它的工作包括:分析、构建和开发一套用于给某类指定问题建模的框架、规则、约束、**模型**和理论等。
从结论上来说,我们定义了数据源,如何描述数据,如何对数据进行处理。
## DSL on 元模型
与 Apache Atlas 相比,Presto 的 SQL on Anything 思想则更是我们所需要的。SQL 作为一个对于数据的插入、查询、更新和删除,数据库模式(Schema)创建和修改,以及数据访问控制的语言,它已经被成功地被应用到了一系列的系统中。如果我们将一个个的数据库表视为一个个的模型,那么数据库的模型系统就可以视为一个元模型,SQL 引擎也是则基于这些元模型的计算引擎。基于 SQL 编写的函数,则可以视为一个个的算子。
为了对抽象的模型进行操作和计算,我们需要一个 DSL 来简化这个计算逻辑。毕竟,我们在设计知识领域的元模型时,只针对的是一个领域所特有的属性进行设计。如下是一个 SQL 的示例:
```sql
SELECT ID,Fruit_Name ,Fruit_Color FROM Fruits
```
在设计这样的 SQL 引擎的时候,我们并不需要目标的模型(上面的 Fruits)结构是什么,开发者只需要按照 SQL 的语法编写,引擎在执行之后,便能得到我们所需要的结果。我们所构建的是一个基于“元模型的” DSL,用来对模型中的数据进行处理。
与 SQL 相比,类似于 Apache Camel 提供的 DSL 也是非常好玩的。它是一个基于知名的企业应用模式(Enterprise Integration Patterns)多功能的整合框架。它为 Quake 的 Transflow 提供了基本的思路:
```
from("file:src/data?noop=true")
.choice()
.when(xpath("/person/city = 'London'"))
.to("file:target/messages/uk")
.otherwise()
.to("file:target/messages/others");
```
从某种意义上来说,它也是一种基于元数据的 DSL。
## 数据流与计算
现在,我们已经具备了初步的数据分析功能,能构建出一个完整的知识引擎的分析功能。为了能实时处理数据,我们需要构建一个事件流,来实时处理这些数据。于是乎,结合消息队列、流处理框架和规则引擎就能构建一个简单的实时系统,诸如于 Kafka + Flink + Drools,再结合 Thrift、Avro、Profobuf 等等,就能构建一个市面上常见的大数据架构系统的基础。为了不同的需求,如日志、可视化等,又需要一些其它框架进行配合。
有了框架之后,我们需要思考的是在框架之间如何计算。于是乎,如何提供更细粒度的计算,就变成了一个非常有意思的问题。因此,类似于这样的 Actor 模型就能提供一个非常不错的思路,它定义了一系列系统组件应该如何动作和交互的通用规则。于是乎,就有了基于 Actor 模型与使用 Scala 语言编写的 Akka 框架。
另外一种不错的思路,便是使用更灵活的语言来实现这一类的计算。如 Java 8 以后提供的 Nashorn 虚拟机,可以让开发人员使用 JavaScript 进入编程。
除此,使用 OpenFaas、OpenWhisk、Fisson 这样的 FaaS 框架也是一个友好的选择。
## 可视化与组装
从某种意义上来说,我们在 UI 层面会做两件事情:配置与展示。展示最常见的形式就是 Dashboard,又或者是其它形式。在我们考虑将它们组合在时,BI 是更好的选择,诸如于 Superset、Metabase、Redash 等。
而配置呢,它往往由很多种不同的方式来组合的,有些是通过 UI 配置的形式,有的则是通过流编辑的方式,还有的则是可以通过 DSL 来设计。其实从本质上来看,配置也是某种形式的 DSL,只是它经过更多的抽象。
## 其它
人生苦短,学习创造也是一种乐趣。</https:>Quake Transflow 一个以 DSL 为核心的低代码系统2021-12-13T11:11:41+00:002021-12-13T11:12:51.532509+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/transflow/自我们发布了 Quake 项目之后,我们一直在做各种 Meta 功能。在上次 release 时,可以进行自定义的数据编辑。在这个新的版本里,将会包含一个新的数据可视化特性:Transflow。
## 从类型流(TypeFlow)说起
Transflow 的设计起源需要从我司的大佬 @魔头 提出了类型流的理念,并在那篇《[类型流(TypeFlow)——世俗化的函数式编程和改进的过程式设计](https://zhuanlan.zhihu.com/p/341089716)》中进行了详细的介绍。Typeflow 有几个主要的规则:
1. 从可视化模型上就可以看出:共存在4个待实现的函数,其中两个纯函数,参数校验和返回结果包装;一个副作用函数,保存代办事项;还有一个输入端口,即把这个几个函数编排起来完成业务的程序入口。
2. 每个函数有明确的输入输出类型
3. 函数之间通过匹配的输入输出类型连接起来。
4. 输入输出类型使用业务人员能够理解的业务概念,从而符合DDD的要求。
5. 可视化
简单来说,在有了设计之后,我们可以为功能生成对应的有输入和输出的函数,并可以通过规则将它们匹配起来。原理,就是这么简单。
## Transflow 初识:DSL 生成代码
回到 Quake 的场景里,我们有固定的数据源,即不同的 entry。与此同时我们还有不同的用于展示这些数据的组件。我们所需要做的便是,提供一个从数据流向组件的规则,即 Transflow DSL。
### Transflow 示例
举个例子,当我们想有一个 calendar 来展示所有的 todo 和 blog 时,我们就需要从数据源中取得 todo 和 blog,对数据进行转换然后传输给 calendar 组件。用一句话来表达便是:
```javascript
from('todo','blog').to(<quake-calendar>);
```
这个 Transflow 的 DSL,最简模式下(即没有函数名、不添加数据映射(mapping)和过滤器(filter)、组件定义的情况下),它可以生成以下的 JavaScript 代码:
```javascript
function from_todo_blog_to_quake_calendar(todos, blogs) {
let results = [];
results = results.concat(todos);
results = results.concat(blogs);
return results;
}
const tl_temp_1 = async (context, commands) => {
const el = document.createElement('quake-calendar');
let todos = await Quake.query('todo');
let blogs = await Quake.query('blog');
let data = from_todo_blog_to_quake_calendar(todos, blogs);
el.setAttribute('data', JSON.stringify(data));
return el;
}
Quake.router.addRoutes({path: '/transflow/show_temp_1', action: tl_temp_1 },)
```
代码逻辑上是:
1. 创建一个新的 Quake Calendar 组件(Web Component)
2. 获取 todo、blog 相关的数据
3. 执行对应的数据转换规则
4. 将数据传递给组件
5. 添加可访问的路由
这样一来,只需要跳转到相应的路由即可。如果需要的话,也可以直接生成临时的组件。另外一部分,则是由 Web Components 所构建的组件体系,我们将会在另外一篇文章中展开介绍。
### 多条 Transflow
(PS:虽然尚未进行测试,但是我相信它当前是**不** work 的)
如果有多条 Transflow 规则时:
```javascript
transflow show_calendar {
from('todo','blog').to('record'),
from('record').to(<quake-calendar>);
}
```
就会生成多个 Transflow 函数(部分):
```javascript
function from_todo_blog_to_record(todos, blogs) {
let results = [];
results = results.concat(todos);
results = results.concat(blogs);
return results;
}
function from_record_to_quake_calendar(records) {
let results = [];
results = results.concat(records);
return results;
}
```
Transflow 生成的代码,面临的最大问题是数据量大时的性能问题,但是 Quake 的场景下,不会有这样的问题。
## 目标函数式的 Transflow
既然,我们是对数据流进行操作,那么理想情况下,Transflow 的 DSL 就可以设计为向函数式靠齐。不过,当前,我们还没有理由实现这么复杂的功能,可以在后续展开。
### 一个实现一点点的 map
上述的 `from('todo','blog').to(<quake-calendar>);` 会在转化时生成特定的数据结构。因此,也可以直接从数据结构中读取对应的 Transflow,对它们进行存储:
```yaml
- name: "from_todo_blog_to_quake_calendar_timeline"
from: [ "todo", "blog" ]
to: "<quake-calendar-timeline>"
map:
- entry: "todo"
source: ["title", "content", "start_time", "updated_date"]
target: ["title", "content", "created_date", "updated_date"]
- entry: "blog"
source: ["title", "content", "start_time", "updated_date"]
target: ["title", "content", "created_date", "updated_date"]
```
这里的 `map` 是一个尚未在 DSL 设计的功能,也需要进一步验证是否真的需要。除此,这个 YAML 的设计也是有问题的。
### 还有,一个刚可用的 filter
在 filter 方面,我做了一些简化设计(~~偷懒~~),因为需要的是搜索引擎,可以可以直接使用搜索引擎的 fliter 功能。在评估了多个 filter-parser 的库之后,我发现没有理由在当前做这么复杂的设计。所以,针对于一些特别的过滤条件做了一些特别的处理。
如下是一个过滤时间的表达式:
```javascript
from('todo','blog').to(<quake-calendar>).filter('created_date > 2021.01.01 AND created_date < 2021.12.31')
```
由于搜索引擎并不支持各种各样的时间处理,所以我们可以替换对应的字符器,然后:
```javascript
created_date > 1609459200 AND created_date < 1640908800
```
等时机成熟,再完成整体的 filter 规则设计。
## 下一步:更简单的 Transflow
还在设计中,预期可能会有组件中的编排等。不过,首先我们得需要有足够的 Web Components 组件,才能完成基本的功能开发,并收集这些数据场景。诸如于:
- [ ] Todo 应用
- [ ] Kanban 应用
- [ ] Typeform 编辑器
- [ ] 白板
- [ ] ……
### 双向绑定的中间组件:ComponentFlow
理想的情况下,我们应该在 Transflow 中生成的是一个新的 Web Components 组件,以提供数据到组件的通道。只是呢,当前受限于当前的场景有限,所以提供的是简单的代码生成。等组件库进一步完善之后,便可以尝试引入这个新的设计。
### 面向专业人士的 Transflow
在 Quake 现有的设计里,专业人士可以自由自在的对 Quake 进行定制,所以并不需要高级的 Transflow 存在。
## 其它:体验 Transflow
由于当前精力有限,所以暂时没有开发 Quake 的 GUI 版本(如果你有兴趣,欢迎入坑),所以需要按照 [https://github.com/phodal/quake](https://github.com/phodal/quake) 的安装指南进行。
简单的方式是:从 release 页面下载 `quake` 及 web 部分,然后创建一个新的项目进行体验。
本文使用 Quake 0.2.0 编写。</quake-calendar></quake-calendar-timeline></quake-calendar></quake-calendar></quake-calendar>Quake 一个开源的知识管理元框架2021-12-05T11:56:16+00:002021-12-05T11:56:37.940216+00:00Phodal Huanghttp://www.phodal.com/blog/author/root/http://www.phodal.com/blog/quake/本文使用 Quake Web 应用编写,虽然只有基本的 Command + S 来保存标题 + 内容的功能。这个简单粗糙的页面,让我想起了多年前构建 Phodit 的场景,`it works` 作为开始就足够了。
来, 先上链接 GitHub: <https: github.com="" phodal="" quake="">
# 缘由
半个月前,我在准备一个材料,好不容易从我的博客、Todo、Notes 里找到了一些相关的素材。我使用了不同的工具来管理知识,Microsoft To Do 管理 idea、[Phodit](https://www.phodit.com/) + [Phodal.com](https://www.phodal.com/) 发布文章、Apple Notes 记录笔记等等,知识被分散在各个工具中。不利于我进行洞见,寻找灵感,与此同时,还缺乏书写和记录的方式。
于是,我需要一个新的工具来融合到我的知识体系里,它应该是:
* 开源的。可以自由扩展。
* 分布式 + 本地化的。可以离线使用,方便于出差旅途中使用。
* 版本化的。可以自由查看变更历史。
* 开放的。可以自由与其它工具组合。如 Vim、VSCode 等。
* 易于扩展。可以结合习惯用的工具。诸如于,基于 DSL 的编辑-发布分离的类 Web 模式,用于展示。如 MxGraph、Mermaid、Ledge Framework 等
所以,就有了:<https: github.com="" phodal="" quake="">
# Quake:知识管理元框架
Quake 的目标是构建面向**极客**的知识管理元框架,它可以:
* 自由的文本内容管理。Todo 清单、文章管理、书评、笔记等。
* 构建知识网络体系。定制化 markdown 链接
* 抓住稍纵即逝的灵感。支持快速启动(CLI、TUI)与全局搜索
* 自由的呈现画布。DSL 与自由画板
简单来说,通过 Markdown 来记录数据,Git 来进行版本化,Terminal 来快速编辑,Web + Web Components 提供定制能力。
## Quake 设计理念 1:数据代码化
Quake 延续了 Ledge Framework 中非常成功地思想:文档代码化 + Markdown 图表化 + Git,来提供对于数据的管理。尽管我们没有在 Quake 中引入数据库,但是依旧可以提供如下所功能:
1. 数据迁移。
2. 历史状态。设计一个拥有历史状态的内容是一件麻烦的事情。
3. 数据查询与更新。
4. ……
只是呢,现在的这些功能只能支持基本的开发。对于扩展来说,依旧是有问题的,未来需要提供简化版的 SQL,以提供更好的数据处理。而除了 SQL 之外,另外一种简单的方式,就是提供脚本语言的支持。
## Quake 设计理念 2:自由定制
设计一个能支持不同数据模型的知识管理系统痛苦了,需要大量地前期工作。因此,我们先构建了一个可以自定义数据格式的元数据引擎。让每个人都可以自定义的数据格式,并能为这些数据自定义视图,就能简化大量的工作。
### 自定义数据类型
在 Quake 里,通过 YAML 来定义数据格式,也可以从导出的数据后生成(通过 `quake cmd -i “quake.sync”` ):
```yaml
- type: notes
display: ""
fields:
- title: Title
- description: String
- category: String
- created_date: Date
- updated_date: Date
- author: String
actions: ~
```
生成对应的 markdown 文件,形如:`0001-time-support.md` 即 id + title 的形式,对文件再进行编辑:
```javascript
---
title: time support
author:
content:
created_date: 2021-11-24 19:14:10
updated_date: 2021-11-24 19:14:10
---
ahaha
```
考虑到生态兼容的问题,所以在 Quake 里直接采用了 Jekyll 的 Front Matter 语法来定义数据。我们对于文件的编辑操作,即内容和相关的内容信息,都是直接基于这个 markdown 文件的。
### 自定义显示组件
进行中。
现有的 Web 部分架构是基于 Web Component 构建的,以提供自定义的数据操作能力。如通过下述的代码,可以构建我们的编辑器,并进行对应的交互:
```javascript
const editor = document.createElement('quake-editor');
editor.setAttribute('id', entry.id);
editor.setAttribute('title', entry.title);
editor.setAttribute('value', entry.content);
editor.addEventListener("onSave", function (event) {
update_entry(params.type, params.id, {
title: event.detail.title,
content: event.detail.value.replaceAll("\\\n", "\n")
})
});
return editor
```
对于不同的内容来说,也是类似的,只需要创建好对应的组件,处理相应的结果即可。通过这种方式,构建出常用的各种数据类型,并让所有的开发者都可以自定义。
# 如何使用 Quake
现阶段,Quake 面向的群体主要是极客、软件工程师,又或者是具备一定 IT 基础的软件开发人员。毕竟,我们还没有 GUI,还需要一系列的应用封装工作。不过,GUI 从架构上来说太重了,构建一个基于本地 Web + Terminal 的 MVP 版本反而更加容易,还能验证自由度的可行性。
## 安装 Quake
Quake 的安装在现阶段,还是比较麻烦的,还只能在 CLI 下进行(所以,我们面向开发者,我还有得选吗?):
1. 安装 Quake。
1. 有 Rust 环境的话,可以直接 `cargo install quake`
2. 没有 Rust 环境的话,可以从 [Quake Release](https://github.com/phodal/quake/releases) 页面下载。
2. 安装搜索引擎(可选的)
1. macOS 用户,可以直接 `brew install meilisearch`
2. 其它操作系统的用户,建议访问官方进行:<https: github.com="" meilisearch="">
3. 引入 Web 页面。可以从 [Quake Release](https://github.com/phodal/quake/releases) 页面下载 web.zip,并解压到某个目录。
随后,到相应的文档目录,执行 \`quake init\`,就可以得到一个初始化的环境了。执行 `quake server` ,就可以进入 Web 页面使用了。
## Quake Importer
回到文章的开头,首先我们要解决的是数据迁移的问题。所以,上周末,我的主要工作是在数据迁移上,将不同的数据源转化为 Markdown。如在 [Quake Importer](https://github.com/phodal/quake/tree/master/quake_importer) 中,有下述相关数据源的文档:
1. Django CMS 的相关文章
2. Apple Notes(备忘录)的相关备忘
3. Microsoft To do 的相关待办事项
从我的数据来看,我大概有 888 篇的文章,99 个 Todo,还有 302 篇的备忘。当然了,我还有一部分抓取的资料存储在 Microsoft OneNote 上,这部分在后续需要进一步完善了。
## Quake Cmd
在导出相关数据,便可以通过 `quake cmd -i “quake.sync”` 命令同步生成定义不同内容类型的定义文件。
随后,可以直接创建新的内容,只需要通过 `quake cmd -i “blog.add: Quake 一个知识管理元框架”` 来快速创建新的 `blog` 内容。Quake 优先通过 Terminal 实现了基本的 CRUD 功能,如此一来,我们不需要缓慢地启动笔记工具,才能完成一个快速的想法。我们可以利用大量地再成的基于 Terminal 编辑器,如 Vim,快速完成记录。
在保存之后,我们将更新生成对应的 `csv` 数据索引文件,以面向 Terminal 提供快速的接口能力。
## Quake Server
当我们需要寻找灵感时,便可以通过 `quake server` 启动我们的 Web 服务,在上面搜索、索引知识、管理知识等。基于本地的 Markdown + Meili 搜索引擎,我们能构建最好的本地体验。Quake 的 Web UI 界面是基于一个个的 Web Component 构建的,这就意味着,在我们提供 CRUD API 的基础上,你可以结合我们提供的组件能力,自由地构建你的 Web UI。通过在 Quake 的配置文件 `.quake.yaml` 中修改 `server_location` 参数,就能使用自己开发的页面了。在这时,Quake 只是作为一个 Markdown 的 CRUD API。
最后,因为所有的数据是围绕于 markdown + yaml 的,所以,我们可以结合 Git 进行版本化管理。(PS:这部分功能还没设计)
## 下一步
在完成了 MVP (最小可行产品)版本之后,依旧还有一系列的工作要做:
* Terminal UI。已经有小伙伴工作在上面。
* 定制 Markdown 语法。用于支持诸如于双链、文本图表化、脑图
* 全局 GUI 入口。从全局搜索支持,类似于 Spotlight Search
* Web 应用设计。现在的版本非常粗糙,缺乏各种功能。
* 更好的知识管理。
你可以在 Quake 的 [Story](https://github.com/phodal/quake/tree/master/_fixtures/story) 中看到更多的相关内容。
如果你也有兴趣,欢迎加入我们。</https:></https:></https:>