springboot集成neo4j搭建知识图谱后端项目(一)
1.概述
getee项目开源地址
:springboot集成neo4j搭建知识图谱后端项目
本文主要实现springboot集成neo4j,实现动态创建neo4j节点以及关系。数据同时存储在postgresql数据库以及neo4j。
2.安装neo4j
使用docker-compose安装neo4j,可以参考这篇文章docker-compose安装neo4j。
3.项目搭建
本项目springboot版本为2.7.18。
3.1.引入pom依赖
xml
<!-- Spring Boot Data Neo4j 依赖,用于集成 Neo4j 数据库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
<version>2.7.18</version>
</dependency>
<!-- Neo4j Driver -->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>4.4.12</version>
</dependency>
3.2.添加application.yml配置
yaml
spring:
# Neo4j数据源
neo4j:
uri: bolt://192.168.80.196:7687
authentication:
username: neo4j
password: 123456
# postgresql数据源
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://${ENV_DB_IP:127.0.0.1}:${ENV_DB_PORT:5432}/${ENV_DB_NAME:test}?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true&stringtype=unspecified
username: postgres
password: postgres
3.3.添加Neo4jConfig.java配置
java
package com.example.graph.config;
import lombok.Data;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* neo4j连接配置
*/
@Configuration
@ConfigurationProperties(prefix = "spring.neo4j")
@Data
public class Neo4jConfig {
private String uri;
private Authentication authentication;
// 创建 Neo4j Driver
@Bean
public Driver createDriver() {
return GraphDatabase.driver(uri, AuthTokens.basic(authentication.getUsername(), authentication.getPassword()));
}
// 嵌套类,用于映射 authentication 配置
@Data
public static class Authentication {
private String username;
private String password;
}
}
3.4.添加Neo4jService接口
java
package com.example.graph.service;
import com.example.graph.entity.Entity;
import com.example.graph.entity.EntityAttribute;
import com.example.graph.vo.NodeRelationVO;
import org.neo4j.driver.Record;
import org.neo4j.driver.types.Node;
import java.util.List;
public interface Neo4jService {
/**
* 新增节点
*
* @param entity
*/
<T> void createNode(Entity entity);
/**
* 修改节点
*
* @param id
* @param nodeLabel
* @param attributeList
*/
<T> void updateNode(String id, String nodeLabel, List<EntityAttribute> attributeList);
/**
* 删除节点
*
* @param id id
* @param nodeLabel 节点类型
*/
void deleteNodeById(String id, String nodeLabel);
Node findNodeById(String nodeLabel, String id);
/**
* 用自定义id属性来创建关系
*
* @param fromNode
* @param toNode
* @param relationship
*/
void createRelationship(Node fromNode, Node toNode, String relationship);
List<NodeRelationVO> selectNodeRelationByPath(String nodeId, String nodeLabel, Integer path);
void deleteAll();
}
3.5.添加Neo4jServiceImpl实现类
java
package com.example.graph.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.BeanUtils;
import com.example.graph.entity.Entity;
import com.example.graph.entity.EntityAttribute;
import com.example.graph.service.Neo4jService;
import com.example.graph.vo.NodeRelationVO;
import com.example.graph.vo.NodeVO;
import com.example.graph.vo.RelationShipVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Slf4j
@Service
@RequiredArgsConstructor
public class Neo4jServiceImpl implements Neo4jService {
private final Driver neo4jDriver;
@Override
public <T> void createNode(Entity entity) {
try (Session session = neo4jDriver.session()) {
StringBuilder cypherQuery = new StringBuilder("CREATE (n:" + entity.getLabel() + " {");
cypherQuery.append("id: \"").append(entity.getId()).append("\"").append(", ");
entity.getAttributeList().stream().forEach(attribute -> {
cypherQuery.append(attribute.getLabel()).append(": \"").append(attribute.getValue()).append("\"").append(", ");
});
cypherQuery.delete(cypherQuery.length() - 2, cypherQuery.length());
cypherQuery.append("})");
session.run(cypherQuery.toString());
log.info("createNode执行cql={}", cypherQuery);
}
}
@Override
public <T> void updateNode(String id, String nodeLabel, List<EntityAttribute> attributeList) {
try (Session session = neo4jDriver.session()) {
StringBuilder cypherQuery = new StringBuilder("MATCH (n:" + nodeLabel + " {id: \"" + id + "\"}) SET ");
attributeList.stream().forEach(attribute -> {
cypherQuery.append("n.").append(attribute.getLabel()).append(" = \"").append(attribute.getValue()).append("\", ");
});
cypherQuery.delete(cypherQuery.length() - 2, cypherQuery.length());
cypherQuery.append(" RETURN n");
session.run(cypherQuery.toString());
log.info("updateNode执行cql={}", cypherQuery);
}
}
@Override
public void deleteNodeById(String id, String nodeLabel) {
try (Session session = neo4jDriver.session()) {
String cql = StrUtil.format("MATCH (n:{}) WHERE n.id = '{}' DETACH DELETE n", nodeLabel, id);
session.run(cql);
log.info("deleteNodeById执行cql={}", cql);
}
}
@Override
public Node findNodeById(String nodeLabel, String id) {
try (Session session = neo4jDriver.session()) {
String cql = StrUtil.format("MATCH (n:{} {id: '{}' }) RETURN n", nodeLabel, id);
Result result = session.run(cql);
while (result.hasNext()) {
Record record = result.next();
Node node = record.get("n").asNode();
return node;
}
return null;
}
}
@Override
public void createRelationship(Node fromNode, Node toNode, String relationship) {
String fromNodeLabel = fromNode.labels().iterator().next();
Map<String, Object> fromNodeMap = fromNode.asMap();
String toNodeLabel = toNode.labels().iterator().next();
Map<String, Object> toNodeMap = toNode.asMap();
try (Session session = neo4jDriver.session()) {
String cypherQuery = "MATCH (a:" + fromNodeLabel + " {id: \"" + fromNodeMap.get("id") + "\"}), " +
"(b:" + toNodeLabel + " {id: \"" + toNodeMap.get("id") + "\"}) " +
"CREATE (a)-[r:" + relationship + "]->(b)";
session.run(cypherQuery);
log.info("createRelationship执行cql={}", cypherQuery);
}
}
/**
* 查询与a节点存在关系、且距离为1的所有节点
* MATCH (a:student {id: '7'})-[r]-(b) RETURN a, r, b;
*
* @param nodeId 节点id
* @param nodeLabel 节点标签
*/
@Override
public List<NodeRelationVO> selectNodeRelationByPath(String nodeId, String nodeLabel, Integer path) {
try (Session session = neo4jDriver.session()) {
String cql = StrUtil.format("MATCH (a:{} {id: '{}'})-[r]-(b) RETURN a, r, b", nodeLabel, nodeId);
if (path > 1) {
cql = StrUtil.format("MATCH (a:{} {id: '{}'})-[r*1..{}]-(b) RETURN a, r, b", nodeLabel, nodeId, path);
}
Result result = session.run(cql);
List<NodeRelationVO> list = new ArrayList<>();
while (result.hasNext()) {
Record record = result.next();
Node nodeA = record.get("a").asNode();
Relationship relationship = record.get("r").asRelationship();
Node nodeB = record.get("b").asNode();
NodeRelationVO nodeRelationVO = new NodeRelationVO();
NodeVO fromNodeVO = new NodeVO();
fromNodeVO.setNodeId(nodeA.id());
fromNodeVO.setNodeLabel(nodeA.labels().iterator().next());
fromNodeVO.setNodeProperties(nodeA.asMap());
RelationShipVO relationShipVO = new RelationShipVO();
relationShipVO.setRelationType(relationship.type());
relationShipVO.setRelationProperties(relationship.asMap());
relationShipVO.setStartNodeId(relationship.startNodeId());
relationShipVO.setEndNodeId(relationship.endNodeId());
NodeVO toNodeVO = new NodeVO();
toNodeVO.setNodeId(nodeB.id());
toNodeVO.setNodeLabel(nodeB.labels().iterator().next());
toNodeVO.setNodeProperties(nodeB.asMap());
nodeRelationVO.setNodeA(fromNodeVO);
nodeRelationVO.setRelationShipVO(relationShipVO);
nodeRelationVO.setNodeB(toNodeVO);
list.add(nodeRelationVO);
}
log.info("selectNodeRelation执行cql={}", cql);
return list;
}
}
@Override
public void deleteAll() {
try (Session session = neo4jDriver.session()) {
String cql = StrUtil.format("MATCH (n) DETACH DELETE n");
session.run(cql);
log.info("deleteAll执行cql={}", cql);
}
}
}
3.7.调用
在其他类注入,然后调用方法即可
java
@Autowired
private Neo4jServiceImpl neo4jService;
4.总结
本文主要是采用拼接cql的方式,来实现对neo4j的一些基础操作。
存在问题:可能会存在cql注入风险,后续需要优化。