---
title: Christin开发记录(1)
date: 2021-05-21 20:46:07
tags:
  - 毕业设计
author: harumonia
categories:
  - 源流清泉
  - Python
noThumbInfoStyle: default
outdatedNotice: "no"
thumbChoice: default
thumbStyle: default
hidden: false
email: zxjlm233@gmail.com
---

## 关于 Christin

`Christin` 是笔者本科毕业设计的伴生项目, 笔者本科毕业设计的课题叫做 **基于多元异构数据的中医药知识图谱构建及应用** , 作为其实践产物的平台,我将其命名为 **Christin** , 选名自我所以喜爱的 ARPG 游戏系列 _<伊苏>_ 的主角 _亚特鲁-克里斯汀( Adol Christin )_ 以及我第二喜欢的小说家 _Dame Agatha Mary Clarissa Christie_. 当然, 其对外的平台名称叫做 **中医药知识图谱构建平台**.

本系列将主要介绍这个项目开发的 **思路** / **完善过程** 与 **技术栈** . (截止到毕设答辩前夕)

<!-- more -->

## 平台设计思路

平台的开发流程采用 _敏捷开发_ 的软件开发方法.即:

1. 需求分析
2. 设计
3. 编码
4. 测试
5. CI/CD/部署 

### 需求分析

不妨先来一个头脑风暴, 想一想整个过程的流程是什么样, 在这个过程中会遇到哪些问题.

平台面临两个来自"用户"的需求: 提取多源异构数据;构建知识图谱. 此外, 还存在一个来自平台的隐含的需求, 数据管理.

对于"提取多源异构数据" 这个问题, 我们可以进一步拆分为两个需求, **数据提取** 和 **数据归一化** .

- 如何从 _结构化_/ _半结构化_ / _非结构化_ 的数据中提取出所需要的数据?
- 如何将提取出的数据进行归一化, 使其符合后续数据使用的标准?

而"构建知识图谱"这个需求则是要求我们能上一步归一化之后的数据中 _自动化_ 地构建出一个知识图谱. 这个知识图谱需要结合我们已有的数据, 比如用户的数据中存在"板蓝根" 这味中药(仅仅是一个名称), 我们需要将 "板蓝根" 的中药特性/主治功效/组成蛋白质/基因结构等内容放入知识图谱中.

这里又是个问题, 这条数据是从关系型数据库中抽取对应的数据构建知识图谱, 还是从一个现成的图数据库中抽取出对应的实体和关系?

而后, 构建完图数据库后, 如何将图数据进行展示? 构建的数据库与它的源数据如何串联起来?

最后, 关于数据管理, 这个倒是简单, 与传统的数据管理一样即可. 需要进行管理的数据分为以下类型:

- 平台维护的基础数据, 中药/方剂/蛋白质/基因等
- 用户数据
- 项目以及项目分析数据

~~最后的最后, 登录注册, 这个功能太常见了, 所以不做讨论~~

以上问题的涉及到项目如何架构, 技术如何应用, 是整个项目的前驱, 也是整个项目的核心. 需求分析阶段, 我们需要的就是对上面这些问题进行思考并提出对应的可行的解决方案.

Q1: 如何从 _结构化_/ _半结构化_ / _非结构化_ 的数据中提取出所需要的数据?
A1: 这一点在 _<知识图谱方法、实践与应用>_ 一书中有很好的解释, 结构化数据/ 半结构化数据考虑 **映射规则** , 而对于非结构化数据, 则需要构建一个命名实体识别模型.

Q2: 如何将提取出的数据进行归一化, 使其符合后续数据使用的标准?
A2: 归根结底, 我们需要从多元异构数据中提取出实体关系信息, 归一化的要求就是最后取得格式一致的实体\关系信息, 但是在后续的开发过程中, 这一点看似理所当然的要求却实际上遇到了很多困难, 以至于最后不得不将 结构化/ 半结构化 与 非结构化 割裂开来分别处理.

Q3: 用户的这些分析数据应该以何种方式进行管理?
A3: 用户每次的分析作为一个分析记录, 生成一个分析项目, 其中包含了 _源数据_ / _分析结果_ / _项目描述信息_ 等内容.

Q4: 从关系型数据库中抽取数据 or 预构建图数据库并中抽取数据
A4: 预构建一个图数据所需要考虑的问题很多, 两个数据库的同步方案 / 性能代价等, 而直接从关系型数据库中抽取数据, 则需要进行一次实体和关系的链接处理. 但是实际的考虑并不止于此, 实体属性本质上是文档型数据而非关系型数据, 使用关系型数据库存储不利于实体属性的扩展. 比如我要给某个中药加上备注, 而关系型数据库中没有备注这个字段, 这种临时性的需求, 如果是分数据库的构建方案, 解决起来会非常简.
所以, 最后决定使用 **预构建图数据库的方案**, 而关系型数据库则负责约束图数据库的关键字段.

Q5: 构建的数据库与它的源数据如何串联起来?
A5: 接上一条 QA, 我们选择预构建图数据库的方案, 这样就可以使用 _主从数据库_ 的设计思路, 与 neo4j 官网的 _sandbox_ 架构模式. 用户构建的数据库直接从源数据库中抽取数据, 其内容受源数据库和用户数据的共同约束, 其操作权限归用户和管理员共有. 这里对 A3 进行补充, 分析项目还应当包含 _sandbox 图数据库_ 和 _图数据库的配置信息_ .

Q6: 构建完图数据库后, 如何将图数据进行展示?
A6: 图数据库的可视化有两种方案, 其一是经由后端查询, 然后包装数据, 前端只负责数据的渲染; 其二是前端直接链接图数据库进行查询然后渲染, 这样做的好久就是, 简单快速, 降低服务器的性能消耗(这个性能主要是 IO 性能消耗), 弊端也很明显, 这需要将图数据库的配置信息暴露给使用者. 而在我们的架构模式中, 数据库采用主从数据库的设计思路, 用户具有从库的操作权限, 所以这个 "弊端" 实际上并不会影响到系统的安全性.

至此, 一个平台的雏形已经模拟完成, 接下来需要做的就是进行详细的设计工作.

### 设计

对需求分析阶段的得到的需求进行抽象, 划分出平台所具有的功能模块, 设计出平台的总体架构如下图所示.

![总体架构图](https://harumona-blog.oss-cn-beijing.aliyuncs.com/new_articles/%E6%80%BB%E4%BD%93%E6%9E%B6%E6%9E%84%E5%9B%BE.png)

其中, 浅蓝色色块为前端负责功能, 黄色色块为后端负责的功能, 深蓝色色块为数据库功能, 淡红色色块为服务器级负责的功能. 每个功能模块中, 又标注了该模块的主要任务.

详细解释一下吧.

对应 _Q1_, 抽象为 **异构数据录入模块** , 在 **数据交互模块** 中存放了对应的后端实现逻辑.

对应 _Q2_, 设计了 **数据标注模块**, 该模块的任务承接 **异构数据录入**, 为录入数据进行标注与归一化.

对应 _Q3_, 用户的项目管理可以分为基础数据管理和 sandbox 管理, 将该系列的逻辑抽象为 **项目管理模块**.

对应 _Q4_, 该问题的设计方案比较繁琐, 中间也走了不少的弯路, 最后它的设计归属于数据存储模块, 也就是预构建一个主图数据库.

对应 _Q5_, 串联数据最核心的问题是如何保证主图数据库与关系型数据库的数据的 **一致性** , 这里的一致性主要在编码当中进行实现, 一致性设计方案参考了关系型数据库的 _事务_ 的设计思路.

对应 _Q6_, 设计出 **数据可视化模块** , 该模块具备直接连通前端和图数据库的能力.

数据管理的需求则是设计出对应的前后端模块即可.

最后还剩下 **分布式** 和 **集群容器** 模块, 这两个模块是对项目架构的进一步优化, 其本质与 _基础数据管理_ 相类, 属于 _常见划分类型_, 在之后也许会进一步阐释.

而至于数据库的设计, 由于太过于公式化了, 所以这里就不做赘述.

## 编码 && 测试

这二者是可以合并起来讲的, 因为实际上二者都是没什么可以讲的... 顶多是遇到了很多技术方面的问题, 但是这些问题详细铺开却又不是单独一篇文章能够解释清楚的了.

笔者的毕设使用 Flask + React 的前后端分离式设计. 其实在项目的初期, 平台使用的是纯 Flask 的前后端一体化的开发模式, 前端的样式交由 _Bootstrap_ 的 dashlite 样式库, 只是后来随着项目的功能完善, 一体化的开发模式对功能的扩展, 代码的维护等等诸多方面都不太友好, 所以最后选择对项目功能进行进一步的解耦, 然后设计为前后端分离的模式.

测试则是直接使用 pytest 进行单元测试, 而没有再去选择 selenium 这些做更细致的自动化测试. 其中, 单元测试的主要目的就是避免 api 迭代过程中导致的函数或接口失效的问题.

关于这个过程中的一系列技术问题,计划之后在完成一些文章进行系统地阐释.

## CI/CD/部署

本项目在 CI/CD 方面, 使用 **pre-commit** 做代码的质量控制, 使用 **Github Action** 来做线上代码质量的控制, 以及对不同版本的 Python 的支持能力, 使用 **DockerHub** 来对容器的构建进行验证. 并未使用 Jenkins 之类的更专业的持续集成工具.

部署上, 由于采用纯前后端分离的开发形式, 所以前端页面有自己的一套路由, 无法也没有必要与 flask 做兼容, 所以, 使用 nginx 将前后端代理至统一的端口即可.

## 小结

本篇的主要内容是毕设项目的整体设计, 值得一提的是, 这篇文章写于毕设答辩前夕, 站在总结的角度来对项目初期的一些设计和问题进行梳理, 看看整个项目的演进过程与预期的异同, 倒也别是一番滋味~

## 附录

附录中将会链上毕设相关的一些内容.

[Python 自然语言处理应用 - fastnlp 和 spaCy](https://blog.harumonia.moe/fastnlp-and-spacy/)
[Nginx 在纯粹前后端分离设计中的桥梁作用](https://blog.harumonia.moe/christin-develop-2/)
