使用neo4j建立知识图谱,优化进阶与实战踩坑。
neo4j 简介与优化
架构与存储
社区版:单节点,可存储200亿数据
企业版:支持集群
数据存储:Neo4j如何对大量数据(千万节点及以上)进行初始化
简介与优化:知识图谱和 Neo4j 浅析
语法与java 调用:Neo4j的使用与Java调用实例 neo4j 基本语法笔记
时间序问题
给关系增加时间属性,相同实体每次更新时间即可
neo4j 时间格式:neo4j的日期时间格式转换函数
timestamp(): 存入的是毫秒数,可以通过 return timestamp()
来查看
时间范围参考案例:cypher 的时间日期范围操作
旧数据删除问题
给每个实体增加创建时间和更新时间,给每个关系增加创建时间和更新时间
这样删除的时候可以根据清理策略先删除关系,再将相应的实体删除
联系的方向问题
关系是存在方向的,例如父子关系。但是对于朋友这种关系来说是互相的。
对于 neo4j,查询的时候可以不用指明关系方向,因此朋友关系只需要建立一个关系边即可。
使用案例
基础语法
查看所有/删除所有
1 | match (n) return n |
创建一个关联实体和关系
1 | match (ip1:IP) where ip1.value="192.168.0.1" |
使用时间进行关联
查找 Ip1 并找出关联时间范围在
1 | match (ip1:IP) where ip1.value="192.168.0.1" |
查询关联数量
1 | match (d1:DEVICE) where d1.value = "deviceId1" optional match (d1)-[:ASSOCIATE]-(:IP) return count(d1.value) |
创建多个关联实体和关系
查找已创建的 ip 节点和 device 节点,新建 cardNo1 节点和 smDeviceId1 节点,并新建 ip1-cardNO1,ip1-smDeviceId1,deviceId1-cardNo1,deviceId1-smDeviceId1 四个关系
1 | match (ip1:IP) where ip1.value="192.168.0.1" |
查询多个关系实体
查找 ip1 并找出与 ip1关联的 deviceIds 以及与 deviceIds 关联的 cardNo
1 | match (ip1:IP)-[:ASSOCIATE]-(d1:DEVICE)-[:ASSOCIATE]-(c1:CARDNO) WHERE ip1.value="192.168.0.1" return ip1,d1,c1 |
更新实体属性
优势:merge 语法,已存在不变,变化部分合并
参考:merge 官方case
1 | // 第一个 merge 更新 ip 的 updateTime,第二个 merge 无效(节点已存在),最后把关系的属性 latest 更新到最新 |
添加约束索引
1 | create constraint on (ip:IP) ASSERT ip.value IS UNIQUE |
时间相关
官网时间格式:各种时间格式
1 | RETURN time('13:42:19')+ duration({ days: 1, hours: 12 }) AS theTime |
给某个时间加减时间与另外一个时间作对比
1
2with localdatetime('2020-04-23T16:40:00') as dt1, localdatetime('2020-04-23T16:00:00') as dt2
return dt1+duration('PT-41M') > dt2
实战案例
新增或更新节点和与之关联的其他节点、关系
1 | merge (cd:CARD_NO{value:'cardNoTest11'}) |
新建关系
1 | match (varFrom:CARD_NO{value:'cardNoTest11'}), (varTo:DEVICE{value:'deviceTest13'}) |
建立或更新节点与关系
1 | merge (varFrom:CARD_NO{value:'cardNoTest11'}) |
删除小于等于某个时间的关系和主节点
1 | match (cd:CARD_NO{value:'cardNo1'})-[r:LINK]-(n) where localdatetime(r.updateTime) <= localdatetime('2020-04-28T16:58:10') |
对匹配到的节点遍历计算关联节点数
1 | MATCH p=(cd:DEVICE)-[r:LINK]-(:CARD_NO) |
查询更新时间大于某个时间的指定关联特征数量大于等于2的所有节点
1 | MATCH (cd:DEVICE)-[:LINK]-(:CARD_NO) |
按时间排序跳过 3000 个数据,返回 limit 为 100 的剩余数据
1 | MATCH (c:CARD_NO)-[:LINK]-(d:DEVICE) |
springboot 集成
参考案例
- spring官方demo:github (墙裂建议参考,解决多个属性之间复杂关联以及关系属性的问题)
- 电影演员demo:github(如果关系没有属性,参考这个就行)
- 社交好友推荐项目:github
- 电影知识问答项目:github
- 复杂查询、事务:csdn (使用原生的 session 进行查询)
踩坑
springboot 的 neo4j 组件自带 bolt 协议,如果想使用 http 或 embed 需要增加相应的依赖,项目使用的 springboot 版本为 2.1.4.RELEASE
实体 bean 注入失败,不要用 lombok 的 @Data 注解,用 @Getter
父类存入库中以后也生成了节点,父类改为 abstract
复杂的实体关系参考案例1,可为关系添加属性,多个节点交叉关联等
实体属性为 localDateTime 类型,保存到 neo4j 时被转成了 string
createTime:2020-04-27T17:40
,而不是createTime:"2020-04-27T17:40"
,在查询时则自动转换 string 为 localDateTime,不然 neo4j 会把 localDateTime 做类型转换,在value = getPropertyConverter().toEntityAttribute(value);
这一步只接收 String 报错。在做时间对比时用localdatetime('2020-04-28T18:00') > localdatetime(createTime)
做比较时间存到库里的格式严格按照 yyyy-MM-ddTHH:mm:ss,不然会报错
1
java.time.format.DateTimeParseException: Text '2020-1-5T17' could not be parsed at index 5
提示找不到注解定义的类匹配,异常信息如下
1
Class class com.elong.hotel.analyzer.neo4j.entity.CardNoNode is not a valid entity class. Please check the entity mapping.
问题解决参考:https://blog.csdn.net/KEY0323/article/details/101082242
警告信息,提示
1
Owning ClassInfo is null for property: private java.time.LocalDateTime com.elong.hotel.analyzer.neo4j.entity.BaseNode.createTime
https://blog.csdn.net/Blanchedingding/article/details/90265881
可视化
参考:https://blog.csdn.net/qq_34414916/article/details/81168275
刚开始使用 d3.js,但是入手困难。最终选用的蚂蚁的 antv,文档全面、产品化好,用户只需要关心业务即可。支持 tooltip 提示框、左右键点击事件等,各种形式的图,入手容易。