知识图谱概论及neo4j图数据库使用

1. 知识图谱概论

  知识图谱,是结构化的语义知识库,用于迅速描述物理世界中的概念及其相互关系,通过将数据粒度从document级别降到data级别,聚合大量知识,从而实现知识的快速响应和推理。
  当下知识图谱已在工业领域得到了广泛应用,如搜索领域的Google搜索、百度搜索,社交领域的领英经济图谱,企业信息领域的天眼查企业图谱等。
在这里插入图片描述

  如图所示,可以看到,如果两个节点之间存在关系,他们就会被一条无向边连接在一起,那么这个节点,我们就称为实体(Entity),它们之间的这条边,我们就称为关系(Relationship)。
  知识图谱的基本单位,便是“实体(Entity)-关系(Relationship)-实体(Entity)”构成的三元组,这也是知识图谱的核心。

2. 数据类型和存储方式

  知识图谱的原始数据类型一般来说有三类(也是互联网上的三类原始数据):
  结构化数据(Structed Data),如关系数据库
  非结构化数据,如图片、音频、视频
  半结构化数据 如XML、JSON、百科
在这里插入图片描述

  如何存储上面这三类数据类型呢?一般有两种选择,一个是通过RDF(资源描述框架)这样的规范存储格式来进行存储,比较常用的有Jena等。

  还有一种方法,就是使用图数据库来进行存储,常用的有Neo4j等。
在这里插入图片描述

  那你可能会问我了,你不就是一大堆的三元组吗,用关系数据库来存储不也一样嘛。

  是的,用关系数据库来存储,尤其是存储简单的知识图谱,从技术上来说是完全没问题的。

  但需要注意的是,一旦知识图谱变复杂,图数据库在关联查询的效率上会比传统的关系数据存储方式有显著的提高。当我们涉及到2,3度的关联查询,基于知识图谱的查询效率会高出几千倍甚至几百万倍。

  除此之外,基于图的存储在设计上会非常灵活,一般只需要局部的改动即可。
  因此如果你的数据量较大,还是建议直接用图数据库来进行存储的。

3. neo4j windows社区版安装

  1. 前置配置jdk环境(此处省略)
  2. 下载neo4j 社区版注意与jdk对应的版本,目前3.5.X支持jdk1.8

下载地址https://neo4j.com/release-notes/database/
在这里插入图片描述

  1. 解压下载压缩包,并配置环境变量

变量名:NEO4J_HOME
变量值:。。。。路径
在这里插入图片描述

编辑path环境变量:路径\bin
在这里插入图片描述

  1. win+r打开cmd,输入neo4j console 即可开启neo4j服务

 之后在浏览器中登录neo4j,地址:http://localhost:7474/
 初始的用户名和密码都是neo4j,第一次登录会让改密码

 neo4j console 是在控制台打印日志,如需使用neo4j start后台开启,需要安装相关服务,
 输入命令neo4j install-service 即可。

 常见命令:如开始服务、停止服务、重启服务、安装、卸载、更新等。
 Usage: neo4j { console | start | stop | restart | status | install-service | uninstall-service | update-service } < -Verbose >

4. 基本命令和使用方法

 Neo4j服务器具有一个集成的浏览器,在启动neo4j服务之后,可以使用neo4j集成的浏览器管理图数据库。
在一个运行neo4j服务器主机上访问 “http://localhost:7474/”,显示以下的界面:
在这里插入图片描述

默认的host是bolt://localhost:7687,默认的用户是neo4j,默认的密码是:neo4j,第一次成功connect到Neo4j服务器之后,需要重置密码。
在这里插入图片描述

  1. 创建节点
CREATE (n:person { name: 'jzy', comment: '大帅' }) return n;
CREATE (n:person { name: 'lyf', comment: '小帅' }) return n;
CREATE (n:person { name: 'dw', comment: '中帅' }) return n;

如下创建了三个个person节点,并带有相关属性comment
在这里插入图片描述

  1. 创建关系
MATCH (m:person{name:"jzy"}) 
,(n:person{name:"lyf"})
CREATE (m)<-[r:`父亲`]-(n) 
RETURN m,r,n

如下将1创建的两个节点匹配了关系,注意语句create的方向关系,即lyf的父亲是jzy
在这里插入图片描述

同样,我们也可以反向创建,jzy的儿子是lyf这样的关系

MATCH (m:person{name:"jzy"}) 
,(n:person{name:"lyf"})
CREATE (m)-[r:`儿子`]->(n) 
RETURN m,r,n

在这里插入图片描述

  1. 同时创建节点和匹配关系
MATCH (m:person{name:"jzy"}) 
CREATE (n:person{name:"cty",comment:"小逗"})
CREATE (m)-[r:`小弟`]->(n) 
RETURN m,r,n

执行命令,如下所示,jzy是已创建好的节点,cty是新建的节点,同时新建节点,并匹配‘小弟’的关系

在这里插入图片描述

  1. 查看所有节点
MATCH (n:person) RETURN n 

image.png

  1. 删除关系

删除创建的‘小弟’关系

MATCH(n:person)-[`小弟`]-(m:person) delete r

如下执行成功,并查看成功删除jzy与cty之间的小弟关系

在这里插入图片描述

  1. 重命名标签

当前创建标签名为’person’
重命名改标签执行如下命令

MATCH (n:person) 
REMOVE n:person 
SET n:cityos_staff

如下所示,person标签名已变更为cityos_staff
在这里插入图片描述

  1. 删除没有关系的节点

经过上面的操作,我们当前cityos_staff的实体节点存在两个为创建关系,不指定该节点删除方法如下

MATCH (n:cityos_staff)
WHERE size((n)--())=0
DELETE (n)

如图所示,cty和dw两个不存在关系的实体节点已被删除
在这里插入图片描述

  1. 修改具有属性值的节点
match (n:cityos_staff) where n.name='jzy' set n.comment='特别帅'

此时jzy的comment属性已发生变更
在这里插入图片描述

  1. 删除节点
MATCH (n:cityos_staff) delete n
  1. 删除存在关系节点

删除关系和存在关系的节点
当我们直接删除有关系的节点时会报错,需要先删除关系再删除节点
在这里插入图片描述

可以分步骤,按5删除关系,或执行如下语句

match (n:cityos_staff)-[r]-() delete n,r
  1. 删除海量数据节点

并发执行,需要安装apoc插件(下面再讲)

CALL apoc.periodic.commit("
MATCH (n:old_person) WITH n LIMIT $limit detach DELETE n RETURN count(*)",{limit: 10000}) 
YIELD updates, executions, runtime, batches       
RETURN updates, executions, runtime, batches;
  1. 安装apoc插件

根据neo4j版本选择对应apoc组件下载https://github.com/neo4j-contrib/neo4j-apoc-procedures
在这里插入图片描述

将下载好后的jar包放到安装plugins目录下,并上传一个mysql连接jar包,用于数据库连接
在这里插入图片描述

并配置conf目录下,neo4j.conf文件

dbms.security.procedures.unrestricted=apoc.*

在这里插入图片描述

重启neo4j,如下命令执行则显示安装成功

在这里插入图片描述

  1. 此外可通过节点的属性值来创建节点关系,如下所示
-- 老人入住机构
match (m:yl_yljg_admission) match(n:yljg)
WHERE m.fk_yl_yljg=n.pk_yl_yljg
and m.fk_yl_yljg <> ''
and n.pk_yl_yljg <> ''
create (m)-[r:住院机构]->(n)
return m,n

-- 床位所在的机构 
match(m:yl_yljg_bed) match(n:yljg)
WHERE m.fk_yl_yljg=n.pk_yl_yljg
and m.fk_yl_yljg <> ''
and n.pk_yl_yljg <> ''
merge (m)<-[r:机构拥有床位]-(n)
return r
  1. 创建节点关系create和merge区别
    如上13所示,创建节点使用了create和merge两张创建方式
    区别在于create允许关系重复,merge不允许重复,视情况使用即可

5. 使用load csv方法导入实体和关系

  1. 相关配置,需要配置conf目录下,neo4j.conf文件

将dbms.directories.import=import注释放开,配置完成并重启neo4j
在这里插入图片描述

  1. 准备实体数据和关系数据,并将数据转换为csv,utf-8格式

将准备好的数据放置到neo4j安装目录,import目录下
在这里插入图片描述

实体关系如下
在这里插入图片描述

老人实体如下
在这里插入图片描述

其他实体略。。。

  1. 导入实体
-- 导入床位实体
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/床位实体.csv" AS row
CREATE (:zly_demo_bed {bed_floor: row.`床位实体`});

  -- WITH HEADERS 表示导入存在表头的csv
  -- USING PERIODIC COMMIT 默认每次提交1000 行,小数据量可以不适用,修改每次提交数据在后面加提交次数即可,如USING PERIODIC COMMIT 200 表示每次提交200行


-- 导入村社实体
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/村社实体.csv" AS row
CREATE (:zly_demo_village {village_name: row.`村社实体`});

-- 导入机构实体
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/机构实体.csv" AS row
CREATE (:zly_demo_institution {insti_name: row.`机构实体`});

-- 导入家庭类型实体
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/家庭类型实体.csv" AS row
CREATE (:zly_demo_diff_type {diff_type: row.`家庭类型实体`});

-- 导入老人实体
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/老人实体.csv" AS row
CREATE (:zly_demo_old_person {old_name: row.`姓名`});

-- 导入身体能力实体
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/身体能力实体.csv" AS row
CREATE (:zly_demo_assess_level{assess_result: row.`身体能力情况`});

-- 导入性别实体
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/性别实体.csv" AS row
CREATE (:zly_demo_sex{sex_value: row.`性别实体`});
  1. 建立索引

在数据量大或关系复杂的情况下可以快速建立关系,避免OOM

CREATE INDEX ON :zly_demo_bed(bed_floor);
CREATE INDEX ON :zly_demo_village(village_name);
CREATE INDEX ON :zly_demo_institution(insti_name);
CREATE INDEX ON :zly_demo_diff_type(diff_type);
CREATE INDEX ON :zly_demo_old_person(old_name);
CREATE INDEX ON :zly_demo_assess_level(assess_result);
CREATE INDEX ON :zly_demo_sex(sex_value);
  1. 导入实体关系

因为关系在一个文件中,存在多种类型关系,之前我们的关系创建都是create-[r]静态写死的关系,现在可以使用apoc动态读取关系并创建

-- 导入老人-老人关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_old_person {old_name: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;


-- 导入老人-机构关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_institution {insti_name: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;


-- 导入老人-性别关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_sex {sex_value: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;


-- 导入老人-村社关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_village {village_name: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;


-- 导入老人-床位关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_bed {bed_floor: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;


-- 导入老人-家庭类型关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_diff_type {diff_type: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;


-- 导入老人-身体能力关系
LOAD CSV WITH HEADERS FROM "file:///识图谱演示/demo实体关系.csv" AS row
MATCH (m:zly_demo_old_person {old_name: row.`姓名`})
MATCH (n:zly_demo_assess_level {assess_result: row.`实体`})
CALL apoc.merge.relationship(m,row.`关系`,{},{},n) yield rel return *;

如下则是创建成功的demo示例
在这里插入图片描述

6. 使用apoc jdbc方法导入实体和关系

neo4j大多数处理的都是csv格式的文件,使用apoc jdbc方法可以直接连接对应数据库,读取并存入实体和关系

(1)导入实体

由于数据量都是万级别,使用如下方式,并发执行

– 踩坑,相关属性被neo4j转换成了integer类型,导致节点关系数据无法导入,no changes,no records,如果发现无法匹配上,对相关类型进行转换即可

– --附create 和 merge关系创建的不同,create 允许节点重复,merge若节点重复,但merge效率会明显低一些

1. 机构实体
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","yljg")',
	'CREATE (n:yljg {pk_yl_yljg: row.pk_yl_yljg, ycredit_code: row.ycredit_code, ylegal_per: row.ylegal_per,ylegal_per_phone: row.ylegal_per_phone,yname:row.yname})', 
	{ batchSize:500, parallel:true,iterateList:true}
)


2. 床位实体
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","yl_yljg_bed")',
	'CREATE (n:bed {pk_yl_yljg_bed: row.pk_yl_yljg_bed, bed_name: row.bed_name})', 
	{ batchSize:5000, parallel:true,iterateList:true}
)

3. 老人实体
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","in_old")',
	'CREATE (n:old_person {pk_yl_yljg_admission: row.pk_yl_yljg_admission, yadmission_date: row.yadmission_date,yage:row.yage,ybirthday:row.ybirthday,yidcard:row.yidcard,yname:row.yname,ynational:row.ynational,ysex:row.ysex})', 
	{ batchSize:2000, parallel:true,iterateList:true}
)


4. 从业人员实体
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","staff")',
	'CREATE (n:staff {pk_yl_yljg_staff: row.pk_yl_yljg_staff, yname: row.yname,yidcard:row.yidcard,ysex:row.ysex,yage:row.yage,yeducation:row.yeducation,yhave_cert_skill:row.yhave_cert_skill,yhave_cert_skill_level:row.yhave_cert_skill_level,yhave_cert_pro:row.yhave_cert_pro,yhave_cert_pro_level:row.yhave_cert_pro_level})', 
	{ batchSize:5000, parallel:true,iterateList:true}
)

(2)创建索引

create index on :old_person(pk_yl_yljg_admission)
create index on :yljg(pk_yl_yljg)
create index on :bed(pk_yl_yljg_bed)
create index on :staff(pk_yl_yljg_staff)

(3)关系导入

-- 关系导入

1. 老人入住机构

-- 创建索引
create index on :old_person(pk_yl_yljg_admission)
create index on :yljg(pk_yl_yljg)

--不能并行,否则会有一部分数据创建失败
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","select pk_yl_yljg_admission,fk_yl_yljg from in_old")',
	'match(m:old_person{pk_yl_yljg_admission:row.pk_yl_yljg_admission}),(n:yljg{pk_yl_yljg:row.fk_yl_yljg}) create (m)-[r:入住机构]->(n)', 
	{ batchSize:2000,parallel:false, iterateList:true,retries:20}
)

2. 机构拥有床位
--创建索引
create index on :bed(pk_yl_yljg_bed)

CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","select pk_yl_yljg_bed,fk_yl_yljg from yl_yljg_bed")',
	'match(m:bed{pk_yl_yljg_bed:row.pk_yl_yljg_bed}),(n:yljg{pk_yl_yljg:row.fk_yl_yljg}) create (m)<-[r:拥有床位]-(n)', 
	{ batchSize:5000,parallel:false, iterateList:true,retries:20}
)


3. 老人绑定床位
--创建索引
--已创建
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","select pk_yl_yljg_bed,fk_yl_yljg_lr from yl_yljg_bed where fk_yl_yljg_lr is not null")',
	'match(m:bed{pk_yl_yljg_bed:row.pk_yl_yljg_bed}),(n:old_person{pk_yl_yljg_admission:row.fk_yl_yljg_lr}) create (m)<-[r:老人绑定]-(n)', 
	{ batchSize:5000,parallel:false, iterateList:true,retries:20}
)


4. 机构从业护理员
--创建索引
create index on :staff(pk_yl_yljg_staff)
CALL apoc.periodic.iterate(
	'CALL apoc.load.jdbc("jdbc:mysql://localhost/irs?user=root&password=123456&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC","select pk_yl_yljg_staff,fk_yl_yljg from staff")',
	'match(m:staff{pk_yl_yljg_staff:row.pk_yl_yljg_staff}),(n:yljg{pk_yl_yljg:row.fk_yl_yljg}) create (m)-[r:从业于]->(n)', 
	{ batchSize:5000,parallel:false, iterateList:true,retries:20}
)

搭建完整图关系体系如下,web展示只能展示部分,若需要查询具体的关系可以用语句查询

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值