springboot集成neo4j搭建知识图谱后端项目(一)
- 1.概述
- 2.安装neo4j
- 3.项目搭建
- 3.1.引入pom依赖
- 3.2.添加application.yml配置
- 3.3.添加Neo4jConfig.java配置
- 3.4.添加Neo4jService接口
- 3.5.添加Neo4jServiceImpl实现类
- 3.7.调用
- 4.总结
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依赖
<!-- 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配置
spring:# Neo4j数据源neo4j:uri: bolt://192.168.80.196:7687authentication:username: neo4jpassword: 123456# postgresql数据源datasource:driver-class-name: org.postgresql.Driverurl: 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=unspecifiedusername: postgrespassword: postgres
3.3.添加Neo4jConfig.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@Beanpublic Driver createDriver() {return GraphDatabase.driver(uri, AuthTokens.basic(authentication.getUsername(), authentication.getPassword()));}// 嵌套类,用于映射 authentication 配置@Datapublic static class Authentication {private String username;private String password;}}
3.4.添加Neo4jService接口
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实现类
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;@Overridepublic <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);}}@Overridepublic <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);}}@Overridepublic 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);}}@Overridepublic 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;}}@Overridepublic 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 节点标签*/@Overridepublic 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;}}@Overridepublic 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.调用
在其他类注入,然后调用方法即可
@Autowiredprivate Neo4jServiceImpl neo4jService;
4.总结
本文主要是采用拼接cql的方式,来实现对neo4j的一些基础操作。
存在问题:可能会存在cql注入风险,后续需要优化。