最近参加了一个小比赛,做的一个自动化工具,并使用neo4j出图谱,最后啥奖也没得到 🐶 菜鸡本菜
早就听说过neo4j图数据库,一直没用过,这次正好有个机会学习一下
初探neo4j
因为本地没有服务器,就打算使用docker运行一个
m2的电脑安装docker
brew install --cask docker
安装好之后去docker的客户端下载客户端
拉取neo4j
docker pull neo4j
查看docker 拉取结果
docker images
打开docker客户端之后
在终端运行命令
docker run --publish=7474:7474 --publish=7687:7687 -e 'NEO4J_AUTH=账号/密码' neo4j
账号密码需要修改成自己的~_~
运行之后访问
http://localhost:7474/
即成功在本地搭建好了neo4j 就 可以使用啦
neo4j+springboot2使用
我这里的springboot版本是2.7.4
(neo4j不同版本差异还是很大的,需要注意⚠️,现在我用的这个版本是比较新的,很多注解已经替换不再使用,例如@RelationshipEntity等)
首先需要添加maven依赖
<!-- neo4j 驱动 这个需要自己手动添加一下 -->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
</dependency>
<!-- neo4j 操作实体注解需要 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
添加驱动的配置类
public class ExampleCommandLineRunner implements CommandLineRunner {
private final Driver driver;
private final ConfigurableApplicationContext applicationContext;
public final Session session;
@Bean
Session session(){
return session;
}
@Override
public void run(String... args) throws Exception {
}
public ExampleCommandLineRunner(Driver driver,ConfigurableApplicationContext applicationContext){
this.driver = driver;
this.applicationContext = applicationContext;
this.session = driver.session();
}
}
配置文件里需要进行配置
spring.neo4j.uir = bolt://localhost:7687
spring.neo4j.authentication.username = 账号
spring.neo4j.authentication.password = 密码
spring.data.neo4j.database = 库名
创建一个节点
@Node("Person")
@Data
@AllArgsConstructor
@Accessors(chain = true)
public class Person {
@Id
@GeneratedValue
private Long id;
private String name;
}
使用Repository类似JPA操作数据库
@Repository
public interface PersonRepository extends Neo4jRepository<Person,Long> {
}
@Service
public class PersonServiceImpl implements PersonService {
@Autowired
private PersonRepository personRepository;
@Override
public Person save(Person person) {
return personRepository.save(person);
}
}
添加访问层
@RestController
public class PersonController {
@Autowired
private PersonService personService;
@PutMapping("/create")
public Person create(@RequestBody Person person){
return personService.save(person);
}
}
请求结果
创建图谱
上面过程即可创建单个节点,接下来就需要创建节点之间的关系
因为关系比较多,所以这里使用的是通过读取csv的方式来创建图谱
先创建csv文件
5.csv
name
小红
小绿
小蓝
小乘
小佳
小鱼
小炎
6.csv
name
木木
飞飞
耿耿
小雪
花花
辰辰
文文
格林
婷婷
green
叶寒
汉威
萌萌
7.csv
node1,node2,label
小红,木木,朋友
小红,飞飞,朋友
小红,耿耿,朋友
小红,小雪,朋友
小红,花花,朋友
小红,辰辰,朋友
小红,小佳,朋友
小绿,木木,亲戚
小绿,格林,亲戚
小绿,叶寒,朋友
小绿,婷婷,朋友
小绿,文文,亲戚
小绿,小雪,朋友
小蓝,木木,朋友
小蓝,辰辰,朋友
小蓝,文文,朋友
小蓝,green,朋友
小蓝,格林,朋友
小乘,飞飞,朋友
小乘,花花,朋友
小乘,文文,朋友
小乘,婷婷,朋友
佳佳,飞飞,朋友
佳佳,木木,朋友
佳佳,辰辰,朋友
佳佳,萌萌,朋友
佳佳,叶寒,朋友
小鱼,小雪,亲戚
小鱼,green,朋友
小鱼,婷婷,朋友
小鱼,汉威,朋友
小鱼,萌萌,朋友
小炎,小雪,仇人
小炎,耿耿,朋友
小炎,花花,朋友
小炎,辰辰,家人
小炎,萌萌,朋友
小炎,小绿,朋友
萌萌,小鱼,朋友
汉威,小绿,朋友
小佳,萌萌,朋友
小绿,green,朋友
小佳,小鱼,朋友
叶寒,文文,朋友
这里有一个比较重要的地方,需要先创建节点,然后再创建关系
实现逻辑的地方
解析csv需要的依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
</dependency>
<dependency>
<groupId>com.opencsv</groupId>
<artifactId>opencsv</artifactId>
<version>3.3</version>
</dependency>
具体逻辑实现,使用的是session.run(sql)
去执行sql语句
@Autowired
private Driver driver;
@Override
public void importData() {
// 先清空所有
Session session = driver.session();
session.run("match (n) detach delete n");
// 顺序最好不要乱 先创建节点 再出创建关系 这里应该是使用单例来写的 比赛时间短 就没有进行优化 直接传入session作为参数 🐶
createNode(path+"5.csv",session);
createNode(path+"6.csv",session);
createNode(path+"7.csv",session);
}
public void createNode(String filePath,Session session) {
String sql = "";
try (Reader reader = Files.newBufferedReader(Paths.get(filePath));
CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT.withFirstRecordAsHeader().withIgnoreHeaderCase().withTrim());) {
for (CSVRecord record : csvParser) {
if("src/main/resources/csv/5.csv".equals(filePath)){ // 2
sql = String.format(" CREATE (:Node {title:\"%s\"})", record.get("name"));
}
if("src/main/resources/csv/6.csv".equals(filePath)){ // 3
// 注意这里的节点是Field
sql = String.format(" CREATE (:Field {field:\"%s\"})", record.get("name"));
}
if("src/main/resources/csv/7.csv".equals(filePath)){ // 1
if(record.get("label").equals("朋友")){ //<- belong
// 这里是创建关系 在创建关系的前提要先创建节点
// 下面这样会创建出来很多孤岛
sql = String.format("CREATE (b:Node {title:\"%s\"}) - [r:belong] -> (a:Node {title:\"%s\"}) ",record.get("node1"),record.get("node2"));
sql = String.format(" match(a:Node),(b:Field) where a.title=\"%s\" and b.field=\"%s\" create (b)-[r:朋友]->(a)",record.get("node1"),record.get("node2"));
}
if(record.get("label").equals("亲戚")){ // next
sql = String.format("match(a:Node),(b:Node) where a.title=\"%s\" and b.title=\"%s\" create (a)-[r:亲戚]->(b)",record.get("node1"),record.get("node2"));
}
if(record.get("label").equals("仇人")){ // equal
sql = String.format(" match(a:Field),(b:Field) where a.field=\"%s\" and b.field=\"%s\" create (b)-[r:仇人]->(a)",record.get("node1"),record.get("node2"));
}
}
// 都放这里执行
System.out.println("sql:"+sql);
session.run(sql);
}
} catch (Exception e) {
e.printStackTrace();
}
}
实体类添加 这里使用的是@Node
注解
@Data
@NoArgsConstructor
@Accessors(chain = true)
@Node("Field")
public class FieldEntity {
@Id
@GeneratedValue
private Long id; // 1 2
private String field;
public FieldEntity(String fields){
this.field = fields;
}
public FieldEntity(Long id,String fields){
this.id = id;
this.field = fields;
}
}
@Node("Node")
@Data
@AllArgsConstructor
@Accessors(chain = true)
public class NodeEntity {
@Id
@GeneratedValue
private Long id; // 1 2
private String title;
public NodeEntity(){}
public NodeEntity(String title){
this.title = title;
}
}
最后的效果图
总结:
neo4j不同版本之间差异还是挺大的,最新的neo4j的文档还是比较少的,试了很多次最后终于出了简单的图谱 😢