软件工程应用与实践——知识图谱树形结构获取
Posted 叶卡捷琳堡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了软件工程应用与实践——知识图谱树形结构获取相关的知识,希望对你有一定的参考价值。
2021SC@SDUSC
一、知识图谱的结构
老年健康知识图谱系统的知识结构是一棵4层的树,每个叶子节点对应一个知识。
以老年综合征为例,具体的图形如下(该图仅包含部分细节)
该系统实现的功能是,获取整棵树的所有节点,并返回到前端,用户点击对应的节点后,查看该知识的详细信息(包含视频,图片,文字,音频)
二、前端代码
2.1 对axios请求的封装
设置baseURL及超时时间
通过设置baseURL,可以简化每次发请求时输入的路径,只需要写后面的路径,前面的路径直接省略,避免请求路径重复的问题。设置超时时间为5秒,若网速过慢发送不成功则会报错
import axios from "axios";
//创建默认实例
const instance = axios.create({
baseURL:"http://localhost:8989",
timeout:5000
})
//暴露instance实例
export default instance
设置请求拦截器,响应拦截器,并添加加载效果
当前端向后端请求数据,数据尚未返回时,由于被请求拦截器拦截,会出现加载效果,当返回数据时,经过响应拦截器,终止加载效果。加载效果的具体动画使用element ui自带的loading组件实现
let loadingInstance = null;
//请求拦截器
instance.interceptors.request.use(config=>{
//展示loading效果
loadingInstance = Loading.service({fullscreen:true,background:'rgba(0,0,0,0.6)',text:'拼命加载中'});
return config;
})
//响应拦截器
instance.interceptors.response.use(response=>{
//隐藏loading效果
loadingInstance.close()
return response;
})
设置导航守卫
当用户未登录时,部分页面无法访问,若用户直接访问链接,则需跳转到登录界面,不能直接进入受到保护的界面,本项目使用vue的beforeEach方法实现导航守卫
子路由中配置页面的权限,使用meta中的requireAuth进行配置,如果为true,则说明需要登录权限才可访问
{
path: '/personInfo',
name: 'PersonInfo',
component: PersonInfo,
meta:{
requireAuth:true,
title:'个人信息'
}
},
设置路由拦截方法
所有请求都会经过这个方法,如果需要权限且用户未登录,则跳转至登录界面,如果有权限,则使用next方法放行该请求
//路由拦截
router.beforeEach((to,from,next)=>{
//设置标题
if (to.meta.title) {
document.title = to.meta.title
}
// 判断该路由是否需要登录权限
if (to.meta.requireAuth) {
// 通过sessionStorage检验当前的token是否存在
if (sessionStorage.getItem("token") == 'login') {
next();
}
else {
next({
path: '/login',
// 将跳转的路由path作为参数,登录成功后跳转到该路由
query: {redirect: to.fullPath}
})
}
}
else {
next();
}
})
2.2 树形控件代码及其分析
前端使用element ui的树形控件,展示知识图谱
树形控件及其方法
使用element ui的el-tree标签,该系统定义了 @node-click="getCheckedNodes"方法,实现点击“叶子”后,向后端发送请求,后端返回具体的数据,并呈现在表格中
<el-tree
class="filter-tree"
:data="treeData"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
@node-click="getCheckedNodes"
ref="tree">
</el-tree>
对应的数据和方法
treeData用于保存后端返回的树形数据
data(){
return{
//树形控件数据
filterText: '',
treeData: [{
label: '一级',
children: [{
label: '二级 1-1',
children: [{
label: '三级 1-1-1'
}, {
label: '三级 1-1-2'
}]
}]
}],
defaultProps: {
children: 'children',
label: 'label'
},
//表格数据
tableData: []
}
},
getCheckedNodes(data,node,element)方法在用户点击具体的叶子时触发,根据node.level获取当前知识所在的层数,由于第三层和第四层都有相应的数据(有部分第三层的节点没有第四层)。同时,第三层的节点有属于自己的数据,与第四层的数据不同,因此需要分别获取。
<script>
methods:{
//获取当前选中的节点并呈现数据
getCheckedNodes(data,node,element){
//如果是第3层节点
if(node.level == 3){
//获取知识名称
let knowledgeName = data.label;
//向后端发送axios请求,获取数据
instance.get("knowledge/third/" + knowledgeName).then(res=>{
this.tableData = res.data;
})
}
//如果是第4层节点
else if(node.level == 4){
//获取知识名称
let knowledgeName = data.label;
//向后端发送axios请求,获取数据
instance.get("knowledge/fourth/" + knowledgeName).then(res=>{
this.tableData = res.data;
})
}
}
}
}
</script>
vue的created方法执行时,data数据已经初始化完成,此时从后端获取树形知识图谱的数据(即整棵树)
created() {
//从后端获取对应的数据
instance.get("treeDate/nodes").then(res=>{
this.treeData = res.data
})
}
三、后端代码
后端代码分为controller层,service层,dao层
controller层负责定义与前端交互的接口,service层负责定义执行具体的事务,dao层负责操作数据库
controller层调用service层,service层调用dao层代码
3.1 树形结构对应的实体类
为了返回对应的树形结构到前端,后端需要定义对应的实体类存储对应的树形结构,在定义实体类的时候,本项目使用lombok注解,避免了大量的set,get方法重复书写。
叶子节点使用List结构存储
package com.sdu.nurse.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class TreeData
{
//节点名
private String label;
//节点对应的子节点
private List<TreeData> children;
}
3.2 填充知识树的过程
在本项目中,知识树共有四层,本项目使用了逐层获取,逐层填充的方式从数据库中获取对应的数据,并填充知识树
dao层接口
本项目使用mybatis-plus,继承BaseMapper,可以对单表查询进行快速操作
package com.sdu.nurse.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sdu.nurse.entity.TreeData;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface TreeDataDao extends BaseMapper<TreeData>
{
//获取所有一级名称
List<TreeData> getAllFirstNodes();
//根据一级名称获取所有二级名称
List<TreeData> getAllSecondNodes(@Param("firstNodeName") String firstNodeName);
//根据二级名称获取所有三级名称
List<TreeData> getAllThirdNodes(@Param("secondNodeName") String secondNodeName);
//根据三级名称获取所有四级名称
List<TreeData> getALlFourthNodes(@Param("thirdNodeName") String thirdNodeName);
}
service层代码
使用@Autowired注解,利用spring工厂自动注入的特性将TreeDataDao注入,不需要程序员手动注入。在service层中通过getAllTreeNodes方法逐层获取叶子节点的数据,最终将List<TreeData>填充完成,返回给controller层
package com.sdu.nurse.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sdu.nurse.dao.TreeDataDao;
import com.sdu.nurse.entity.TreeData;
import com.sdu.nurse.service.TreeDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class TreeDateServiceImpl implements TreeDataService
{
@Autowired
private TreeDataDao treeDataDao;
@Override
public List<TreeData> getAllTreeNodes() {
//获取一级节点
List<TreeData> healthTreeNodes = getFirstNodes();
//获取二级节点
healthTreeNodes.forEach(healthTreeNode->{
healthTreeNode.setChildren(getSecondNodes(healthTreeNode.getLabel()));
});
//获取三级节点
healthTreeNodes.forEach(healthTreeNode->{
healthTreeNode.getChildren().forEach(second->{
second.setChildren(getThirdNodes(second.getLabel()));
});
});
//获取四级节点
healthTreeNodes.forEach(healthTreeNode->{
healthTreeNode.getChildren().forEach(second->{
second.getChildren().forEach(third->{
third.setChildren(getFourthNodes(third.getLabel()));
});
});
});
return healthTreeNodes;
}
@Override
public List<TreeData> getFirstNodes() {
return treeDataDao.getAllFirstNodes();
}
@Override
public List<TreeData> getSecondNodes(String firstNodeName) {
return treeDataDao.getAllSecondNodes(firstNodeName);
}
@Override
public List<TreeData> getThirdNodes(String secondNodeName) {
return treeDataDao.getAllThirdNodes(secondNodeName);
}
@Override
public List<TreeData> getFourthNodes(String thirdNodeName) {
return treeDataDao.getALlFourthNodes(thirdNodeName);
}
}
controller层代码
@CrossOrigin注解解决前后端通过AJAX传输数据时跨域的问题,@RestController注解表明这是一个符合restful规范的,前后端交互的接口。@GetMapper注解表明使用get请求获取数据
package com.sdu.nurse.controller;
import com.sdu.nurse.entity.TreeData;
import com.sdu.nurse.service.TreeDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@CrossOrigin
@RestController
@RequestMapping("treeDate")
public class TreeDataController
{
@Autowired
private TreeDataService treeDataService;
@GetMapping("nodes")
public List<TreeData> getAllTreeNodes(){
return treeDataService.getAllTreeNodes();
}
}
3.3 知识树的JSON表示
使用postman工具对后端接口进行测试,返回的知识树的部分结构如下
将此JSON返回到前端,结合element ui的树形控件,即可实现树形数据的呈现与选择
四、总结
由于本项目的主要功能之一就是方便老年人查看关于老年疾病的各种类型和详细信息,因此获取知识图谱的树状数据是该系统很重要的一项功能,经过小组讨论与分工,最终决定由我完成树形结构获取及树形数据具体展示的源代码分析。本项目关于知识图谱的内容还是比较清晰的,除了本篇博客以外,之后还会有一篇补充博客对获取具体叶子节点对应的具体知识数据进行分析。
以上是关于软件工程应用与实践——知识图谱树形结构获取的主要内容,如果未能解决你的问题,请参考以下文章