使用neo4j建立知识图谱,优化进阶与实战踩坑。

neo4j 简介与优化

架构与存储

社区版:单节点,可存储200亿数据

企业版:支持集群

数据存储:Neo4j如何对大量数据(千万节点及以上)进行初始化

简介与优化:知识图谱和 Neo4j 浅析

语法与java 调用:Neo4j的使用与Java调用实例 neo4j 基本语法笔记

时间序问题

给关系增加时间属性,相同实体每次更新时间即可

neo4j 时间格式:neo4j的日期时间格式转换函数

timestamp(): 存入的是毫秒数,可以通过 return timestamp()来查看

时间范围参考案例:cypher 的时间日期范围操作

旧数据删除问题

给每个实体增加创建时间和更新时间,给每个关系增加创建时间和更新时间

这样删除的时候可以根据清理策略先删除关系,再将相应的实体删除

联系的方向问题

参考:Neo4j数据建模优化:双向关系

关系是存在方向的,例如父子关系。但是对于朋友这种关系来说是互相的。

对于 neo4j,查询的时候可以不用指明关系方向,因此朋友关系只需要建立一个关系边即可。

使用案例

基础语法

查看所有/删除所有

1
2
match (n) return n
match (n) detach delete n

创建一个关联实体和关系

参考:neo4j如何为关系添加时间

1
2
3
match (ip1:IP) where ip1.value="192.168.0.1"
create (d1:DEVICE{value:"deviceId1", source:"android_app"}),
(ip1)-[:ASSOCIATE{latest:timestamp()}]->(d1)

使用时间进行关联

查找 Ip1 并找出关联时间范围在

1
2
3
4
match (ip1:IP) where ip1.value="192.168.0.1"
with ip1
match (ip1)-[r:ASSOCIATE]-(d1:DEVICE) where timestamp() - r.latest <= 5*1000*1000
return ip1,d1

查询关联数量

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
2
3
4
5
6
7
match (ip1:IP) where ip1.value="192.168.0.1"
optional match (ip1)-[:ASSOCIATE]-(d1:DEVICE)
create (card1:CARDNO{value:"cardNo1"}),(smd1:SMDEVICE{value:"smDeviceId1"}),
(ip1)-[:ASSOCIATE{latest:timestamp()}]->(card1),
(ip1)-[:ASSOCIATE{latest:timestamp()}]->(smd1),
(d1)-[:ASSOCIATE{latest:timestamp()}]->(card1),
(d1)-[:ASSOCIATE{latest:timestamp()}]->(smd1)

查询多个关系实体

查找 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
2
3
4
5
6
7
// 第一个 merge 更新 ip 的 updateTime,第二个 merge 无效(节点已存在),最后把关系的属性 latest 更新到最新
MERGE (ip:IP{value:"192.168.0.1"}) ON MATCH SET ip.updateTime=localdatetime('2020-04-23T17:38:00'), ip.type="黑名单"
MERGE (d1:DEVICE{value:"deviceId1"}) ON CREATE SET d1.updateTime=localdatetime('2020-04-23T17:47:00')
with ip, d1
MATCH (ip)-[aso:ASSOCIATE]-(d1)
SET aso.latest = timestamp()
return ip,d1

添加约束索引

1
2
create constraint on (ip:IP) ASSERT ip.value IS UNIQUE
create index on :IP(updateTime)

时间相关

官网时间格式:各种时间格式

1
RETURN time('13:42:19')+ duration({ days: 1, hours: 12 }) AS theTime
  1. 给某个时间加减时间与另外一个时间作对比

    1
    2
    with localdatetime('2020-04-23T16:40:00') as dt1, localdatetime('2020-04-23T16:00:00')  as dt2
    return dt1+duration('PT-41M') > dt2

实战案例

新增或更新节点和与之关联的其他节点、关系

1
2
3
4
5
6
7
8
merge (cd:CARD_NO{value:'cardNoTest11'})
on create set cd.createTime='2020-04-27T13:00:00'
on match set cd.updateTime='2020-04-27T13:20:00'
with cd
merge (cd)-[r:LINK]-(d:DEVICE{value:'device2'})
on create set d.createTime='2020-04-27T13:10:00', r.createTime='2020-04-27T13:10:00'
set r.updateTime='2020-04-27T13:20:00', d.updateTime='2020-04-27T13:20:00'
return cd,r,d

新建关系

1
2
3
4
match (varFrom:CARD_NO{value:'cardNoTest11'}), (varTo:DEVICE{value:'deviceTest13'})
merge (varFrom)-[rel:LINK]-(varTo)
on create set rel.createTime='relCreateTime'
set varFrom.updateTime='varFromUpdateTime', rel.updateTime='relUpdateTime', varTo.updateTime='varToUpdateTime'

建立或更新节点与关系

1
2
3
4
5
6
7
merge (varFrom:CARD_NO{value:'cardNoTest11'})
on create set varFrom.createTime='2020-05-02T22:10', varFrom.type = 'CARD_NO'
merge (varTo:DEVICE{value:'dd2'})
on create set varTo.createTime='2020-05-02T22:10', varTo.type = 'DEVICE'
merge (varFrom)-[rel:LINK]-(varTo)
on create set rel.createTime='2020-05-02T22:10'
set varFrom.updateTime='2020-05-02T22:10', rel.updateTime='2020-05-02T22:10', varTo.updateTime='2020-05-02T22:10'

删除小于等于某个时间的关系和主节点

1
2
match (cd:CARD_NO{value:'cardNo1'})-[r:LINK]-(n) where localdatetime(r.updateTime) <= localdatetime('2020-04-28T16:58:10')
delete cd,r

对匹配到的节点遍历计算关联节点数

1
2
3
4
5
6
MATCH p=(cd:DEVICE)-[r:LINK]-(:CARD_NO)
WHERE localdatetime(r.updateTime) >= localdatetime('2020-04-30T00:00')
UNWIND NODES(p) as x
WITH COUNT(x) as cc, x, cd
WHERE cc > 1
RETURN cd

查询更新时间大于某个时间的指定关联特征数量大于等于2的所有节点

1
2
3
4
5
6
7
8
MATCH (cd:DEVICE)-[:LINK]-(:CARD_NO)
WHERE localdatetime(cd.updateTime) > localdatetime('2020-04-30T15:32')
WITH cd
MATCH pp=(cd)-[r1:LINK]-(:CARD_NO)
UNWIND NODES(pp) as x
WITH COUNT(x) as cc, x, cd
WHERE cc >= 2
RETURN cd

按时间排序跳过 3000 个数据,返回 limit 为 100 的剩余数据

1
2
3
4
5
6
7
MATCH (c:CARD_NO)-[:LINK]-(d:DEVICE)
WHERE c.value = '3175778'
WITH d
ORDER BY d.updateTime desc
SKIP 3000
LIMIT 100
RETURN d

springboot 集成

参考案例

  1. spring官方demo:github (墙裂建议参考,解决多个属性之间复杂关联以及关系属性的问题)
  2. 电影演员demo:github(如果关系没有属性,参考这个就行)
  3. 社交好友推荐项目github
  4. 电影知识问答项目:github
  5. 复杂查询、事务:csdn (使用原生的 session 进行查询)

踩坑

  1. springboot 的 neo4j 组件自带 bolt 协议,如果想使用 http 或 embed 需要增加相应的依赖,项目使用的 springboot 版本为 2.1.4.RELEASE

  2. 实体 bean 注入失败,不要用 lombok 的 @Data 注解,用 @Getter

  3. 父类存入库中以后也生成了节点,父类改为 abstract

  4. 复杂的实体关系参考案例1,可为关系添加属性,多个节点交叉关联等

  5. 实体属性为 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)做比较

  6. 时间存到库里的格式严格按照 yyyy-MM-ddTHH:mm:ss,不然会报错

    1
    java.time.format.DateTimeParseException: Text '2020-1-5T17' could not be parsed at index 5
  7. 提示找不到注解定义的类匹配,异常信息如下

    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

    https://github.com/spring-projects/spring-boot/issues/6709

    https://github.com/spring-projects/spring-boot/issues/7157

  8. 警告信息,提示

    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 提示框、左右键点击事件等,各种形式的图,入手容易。

参考:https://g6.antv.vision/zh/

 Comments