官方文档: Spring Data Neo4j
基于官方文档,基本就能完成基本的接入,Neo4j的版本很乱,如果使用SpringBoot2.+的话,一定要注意Neo4j Server的版本与Driver的匹配关系,否则很容易踩坑。 由于我不是生产使用,直接用官方最新反倒是没遇到此类问题,但网上对于这个问题遇到的很多。
简单接入
写了几个单测,看了下流程,得益于SpringBoot-Data的封装,和操作MongoDB、MySQL等关系型数据库基本无异,使用过程很丝滑。
@Test
public void readAll() {
List<Person> personList = personRepository.findAll();
Assert.isTrue(!personList.isEmpty(), "isEmpty");
}
@Test
public void delById() {
personRepository.findById(71L).ifPresent(person -> {
System.out.println("before delete: " + person.getName());
});
personRepository.deleteById(71L);
Person person = personRepository.findById(71L).orElse(null);
System.out.println("after delete: " + person);
}
@Test
public void save() {
Person person = new Person();
person.setName("人参果树");
personRepository.save(person);
}
findAll
delById
删除前:
删除后:
saveOne
关系的创建
对于节点的基本查询,由于其结构类似于结构性,和结构性数据库查询无异,但是对于N4o4j特殊的关系结构,就有一些不一样了。
@Repository
public interface PersonRepository extends Neo4jRepository<Person, Long> {
Person findByName(String name);
/**
* 创建人物关系
* @param from 源
* @param relation 关系
* @param to 目标
*/
@Query("match (n:person {name: $from}),(m:person {name: $to}) " +
"create (n)-[:西游人物关系{relation:$relation}]->(m)")
void createRelation(String from, String relation, String to);
}
@SpringBootTest
public class PersonRelationShipRepositoryTest {
@Resource
private PersonRepository personRepository;
@Test
public void addRelation() {
Person personYangjian = personRepository.findByName("杨戬");
Person person2 = new Person();
person2.setName("玉鼎真人");
PersonRelationShip relationShip = new PersonRelationShip();
relationShip.setChild(person2);
relationShip.setRelation("师傅");
personYangjian.getPersonRelationShips().add(relationShip);
personRepository.save(personYangjian);
}
@Test
public void addRelation2() {
personRepository.createRelation("玉皇大帝", "妻子", "王母娘娘");
}
}
创建关系第一种方式
创建关系第二种方式
坑
坑1
spring-boot-data 3.+的很多注解都改变了,例如:
在老版本中标注节点使用的是@NodeEntity,新版本被替换为@Node(labels = "person")
在老版本中,关系的表述是在关系对象中通过@RelationshipEntity、@StartNode、@EndNode来描述,在新版这些都没有了。取而代之的是在源对象中补充属性@Relationship(type = "师傅", direction = Relationship.Direction.OUTGOING)来描述关系。
老关系:
@Data
@RelationshipEntity(type = "xxx")
public class PersonRelationShip {
@Id
@GeneratedValue
private Long id;
@StartNode
private Person parent;
@TargetNode
private Person child;
}
新关系:
@Node(labels = "person")
@Data
public class Person {
@Id
@GeneratedValue
private Long id;
@Property
private String name;
@Relationship(type = "师傅", direction = Relationship.Direction.OUTGOING)
private List<PersonRelationShip> personRelationShips;
}
@Data
@RelationshipProperties
public class PersonRelationShip {
@Id
@GeneratedValue
private Long id;
@TargetNode
private Person child;
@Property
private String relation;
}
坑2
spring-boot-data 3.+的@Query中想要获取参数,需要使用 $fieldName,而不是 {0} 的方式,如果自己写的时候一定要注意。比如:
/**
* 创建人物关系
* @param from 源
* @param relation 关系
* @param to 目标
*/
@Query("match (n:person {name: $from}),(m:person {name: $to}) " +
"create (n)-[:西游人物关系{relation:$relation}]->(m)")
void createRelation(String from, String relation, String to);
Read More ~
标签:#
Neo4j
Neo4j常用函数
字符串函数
match (n:Lianhuachi) where n.name = '去种田的向凹凸' return n.name,substring(n.name, 0,3)
AGGREGATION聚合函数
和普通SQL一致的含义
match (n:Lianhuachi) return count(n),max(n.age),min(n.age),avg(n.age),sum(n.age)
关系函数
match (x)-[n:`莲花池人物关系`]->(y) return id(n),n
备份
Neo4j 官方文档: Backup modes
➜ bin ./neo4j-admin database dump neo4j --to-path=/tmp
2024-06-26 15:24:15.414+0000 INFO [o.n.c.d.DumpCommand] Starting dump of database 'neo4j'
Done: 37 files, 2.993MiB processed.
2024-06-26 15:24:19.338+0000 INFO [o.n.c.d.DumpCommand] Dump completed successfully
➜ bin ll /tmp/ |grep "neo4j.dump"
-rw-r--r-- 1 imyzt wheel 305K Jun 26 23:24 neo4j.dump
恢复
Neo4j 官方文档: Restore a database dump
➜ bin ./neo4j-admin database load --from-path=/tmp/ neo4j --overwrite-destination=true
Done: 37 files, 2.993MiB processed.
可以看到,数据恢复了过来。
Read More ~
Neo常用命令 二
UNIQUE约束
避免重复时使用(姓名、身份证不能重复)
旧版本
create constraint on (n:Lianhuachi) assert n.name is unique
新版本
如果再执行上面的语法时报错,说明你使用的是新版本的Neo4j,变更了命令语法。
create constraint uniq_name for (n:Lianhuachi) require n.name is unique
在加了唯一索引后,可以看到,在添加重复数据时会报错:
删除唯一约束
drop constraint uniq_name
可以看到,在删除之后再进行添加,是可以添加进去的。
create (n:Lianhuachi {name: "我是野农", "field": "test"}) return n
match (n:Lianhuachi {name: "我是野农""}) return n
distinct去重
和SQL一致,对内容进行去重
match (n:Lianhuachi) return distinct(n.name)
Read More ~
Neo4j 常用命令 一
WHERE
可以类似于SQL的写法来实现,首先通过match将节点查询出来(n,m),然后通过WHERE将数据
match (n:Lianhuachi),(m:Lianhuachi) where n.name = '去种田的向凹凸' and m.name = "向云朵"
return id(n),n.name,id(m),m.name
match (n:Lianhuachi {name: "去种田的向凹凸"}),(m:Lianhuachi {name: "向云朵"})
return id(n),n.name,id(m),m.name
DELETE-删除节点
删除节点(如果节点存在关系时,是无法删除的)
match (n: Person {name: "xxx"}) delete n
删除关系
match (n: Person {name: "小红"})-[r]->(m: Person {name: "小明"}) delete r
REMOVE-删除属性
首先,在student和person标签创建1一个节点
create (n:student:person {name: "张三", age: 18}) return n
查询节点
MATCH (n:person) where n.name = "张三" RETURN n LIMIT 25
MATCH (n:student) RETURN n LIMIT 25
删除标签中的节点
MATCH (n:person) where n.name = "张三" remove n
删除标签中节点的属性
删除前,age=18 MATCH (n:student) where n.name = "张三" return n
删除后,age无了 MATCH (n:student) where n.name = "张三" remove n.age return n
修改标签中节点的属性
修改后,age回来了 MATCH (n:student) where n.name = "张三" set n.age=18 return n
给榜首增加一个属性 match (n:Lianhuachi {name: "我是野农"}) set n.title = '虾米榜榜首' return n
ORDER BY - 排序
补充测试数据
create (n:student {name: "张三", age: 18}),(m:student {name: "李四", age: 22}) return n,m
排序
match (n:student) return id(n), n.name,n.age order by n.age desc
id(n)
n.name
n.age
120
"李四"
22
119
"张三"
18
NULL - NULL属性
查询name属性不为空的节点
match (n:student) where n.name is not null return n.name,n.label
n.name
n.label
"张三"
null
"李四"
null
查询sex属性为空的节点
match (n:student) where n.sex is null return n.name,n.label
n.name
n.label
"张三"
null
"李四"
null
给张三设置sex属性,注意where在前,set在后,和MySQL等相反
match (n:student) where n.name='张三' set n.sex='男' return n.name,n.sex,n.age
查询结果:match (n:student) return n
IN - 查询多个
查询name等于张三或李四的人:
match (n:student) where n.name in ["张三", "李四"] return n
索引
官方文档
旧版本
创建索引:
create index on :节点 (属性)
删除索引:
drop index on :节点(属性)
新版本(5或更高版本)
创建索引:
缺省索引名称:
CREATE INDEX [index_name(可缺省)] FOR (n:person) ON (n.name)
指定索引名称:
create index index_test for (n:person) on (n.name)
Added 1 index, completed after 992 ms.
查询所有索引:
show indexes
id
name
state
populationPercent
type
entityType
labelsOrTypes
properties
indexProvider
owningConstraint
lastRead
readCount
1
"index_343aff4e"
"ONLINE"
100
"LOOKUP"
"NODE"
null
null
"token-lookup-1.0"
null
"2024-05-18T13:26:33.878000000Z"
571
3
"index_a302cc54"
"ONLINE"
100
"RANGE"
"NODE"
["person"]
["name"]
"range-1.0"
null
null
0
2
"index_f7700477"
"ONLINE"
100
"LOOKUP"
"RELATIONSHIP"
null
null
"token-lookup-1.0"
null
"2024-05-10T00:48:00.395000000Z"
67
删除索引:
drop index index_a302cc54
Removed 1 index, completed after 5 ms.
Read More ~
使用Neo4j建立莲花池人物关系图(核心人物,简版)
最近关注了沅陵的莲花池,整个村子都是钓鱼佬,人均up主,基本都是沾亲带故的,错综复杂,恰逢正在学习Neo4j,可以借此机会将人物关系通过图谱的方式呈现出来。
人物标签创建
首先将人物关系建立出来,利用create来创建效率实在太低了 ,所以决定采用数据导入的方式。
点击查看莲花池人物详情
去种田的向凹凸
小白兔的胡萝卜甜(冉甜)
向云朵
我是野农
画燕儿
向鹿鸣
路人阿丙X
大明星(冉蜜)
向偶然
荷塘星星
向尘俊
向星言
多肉葡萄肉多多
白叔
播音哥
三叔
丹宝
守山人阿亮
导入数据:
load csv from "file:///莲花池人物.csv" as line
create (:Lianhuachi {name: line[0]})
最终效果图:
MATCH (n:Lianhuachi) RETURN n LIMIT 25
人物关系标签创建
莲花池人物关系(简版)
小白兔的胡萝卜甜(冉甜),丈夫,去种田的向凹凸
去种田的向凹凸,妻子,小白兔的胡萝卜甜(冉甜)
小白兔的胡萝卜甜(冉甜),女儿,向云朵
去种田的向凹凸,女儿,向云朵
画燕儿,丈夫,我是野农
我是野农,妻子,画燕儿
画燕儿,儿子,向鹿鸣
我是野农,儿子,向鹿鸣
大明星(冉蜜),丈夫,路人阿丙X
路人阿丙X,妻子,大明星(冉蜜)
大明星(冉蜜),女儿,向偶然
路人阿丙X,女儿,向偶然
多肉葡萄肉多多,丈夫,荷塘星星
荷塘星星,妻子,多肉葡萄肉多多
荷塘星星,儿子,向尘俊
多肉葡萄肉多多,儿子,向尘俊
荷塘星星,女儿,向星言
多肉葡萄肉多多,女儿,向星言
向星言,亲哥,向尘俊
向尘俊,亲妹,向星言
荷塘星星,父亲,三叔
三叔,儿子,荷塘星星
荷塘星星,表哥,守山人阿亮
守山人阿亮,表弟,荷塘星星
我是野农,亲弟,路人阿丙X
路人阿丙X,亲哥,我是野农
我是野农,钓友,去种田的向凹凸
去种田的向凹凸,钓友,我是野农
去种田的向凹凸,同村,荷塘星星
我是野农,钓友,荷塘星星
荷塘星星,钓友,我是野农
去种田的向凹凸,同村,荷塘星星
导入数据:
load csv from "file:///莲花池人物关系.csv" as line
create (n:LianhuachiRelation {from: line[0], relation: line[1], to: line[2]})
return n.from, n.relation, n.to
最终效果:
MATCH (n:LianhuachiRelation) RETURN n.from, n.relation, n.to
借助人物关系标签,创建人物关系
match (f:Lianhuachi),(r:LianhuachiRelation),(t:Lianhuachi) where f.name=r.from and t.name=r.to
create (f)-[rr:莲花池人物关系 {relation: r.relation}]->(t)
return f.name, rr.relation, t.name
最终效果:
MATCH p=()-[r:莲花池人物关系]->() RETURN p LIMIT 25
查询人物关系
match (n:Lianhuachi {name: "向云朵"}),(m:LianhuachiRelation) where m.from='向云朵'
return n.name, m.relation, m.to
在现有的基础上,补充人物关系
match (n:Lianhuachi{name:"大明星(冉蜜)"}),(m:Lianhuachi{name:"小白兔的胡萝卜甜(冉甜)"})
create (n)-[r:`莲花池人物关系`{relation:"亲姐"}]->(m)
match (n:Lianhuachi{name:"大明星(冉蜜)"}),(m:Lianhuachi{name:"小白兔的胡萝卜甜(冉甜)"})
create (m)-[r:`莲花池人物关系`{relation:"亲妹"}]->(n)
删除重建人物关系
MATCH (n:Lianhuachi {name:"去种田的向凹凸"})-[r:`莲花池人物关系` {relation: "钓友"}]->(m:Lianhuachi {name:"我是野农"})
delete r
MATCH (n:Lianhuachi {name:"我是野农"})-[r:`莲花池人物关系` {relation: "钓友"}]->(m:Lianhuachi {name:"去种田的向凹凸"})
delete r
MATCH (n:Lianhuachi {name:"去种田的向凹凸"}),(m:Lianhuachi {name:"我是野农"})
create (n)-[r2:`莲花池人物关系` {relation: "表哥"}]->(m)
create (m)-[r3:`莲花池人物关系` {relation: "表弟"}]->(n)
return r2.relation,r3.relation
Read More ~
Neo4j 使用
图数据模型:与关系型数据库使用表格存储数据不同,图数据库通过节点(Node)和关系(Relationship)来表示数据和它们之间的联系。
节点:代表实体,如人、地点、物品等。
关系:定义节点之间的连接,可以有方向和属性。
属性:节点和关系的附加信息,如人的姓名、年龄等。
CREATE (person1:Person {name: 'Alice', age: 30})
CREATE (person2:Person {name: 'Bob', age: 25})
CREATE (person1)-[:KNOWS]->(person2)
person1/person2:节点
Person:标签
name/age:属性
KNOWS:关系
导入数据
通过GPT,生成《西游记》的人物关系图和人物名单,案例数据如下:
将文件放置于Neo4j的Home/import目录下,然后执行导入命令:
load csv from "file:///西游记.csv" as line
create (:xiyouRelation {from:line[1], relation:line[3],to:line[0]})
load csv from "file:///人物.csv" as line
create (:person {name:line[0]})
执行match (person) return person,查看数据:
创建人物和关系
创建人物和关系
创建人物
create (:student {name: '小明'}),(:student {name : '小红'}),(:student {name: '小李'})
创建人物关系,并且返回人物关系
match (n:student {name: '小明'}),(m:student {name: '小红'})
create (n)-[r:同学]->(m) return n.name,type(r),m.name
显示人物关系
MATCH p=()-[r:`同学`]->() RETURN p LIMIT 25
删除标签
match (n:LianhuachiPerson)
detach delete n
删除关系
命令:
match (n)-[r:西游人物关系]-(s) delete r
结果:
Deleted 18 relationships, completed after 6 ms.
Read More ~