springboot集成neo4j搭建知识图谱后端项目(一)

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注入风险,后续需要优化。

相关推荐
小蒜学长1 小时前
乡政府管理系统设计与实现(代码+数据库+LW)
数据库·spring boot·后端·学习·旅游
MiniFlyZt1 小时前
excel的导入和下载(poi)
spring boot·spring·excel
做想做的,1 小时前
el-table表格样式设置单元格样式方法 :cell-class-name
前端·javascript·vue.js·spring boot
程序媛学姐2 小时前
SpringBoot缓存抽象:@Cacheable与缓存管理器配置
java·spring boot·缓存
学长论文辅导2 小时前
基于SpringBoot的校园二手交易平台(计算机毕设-JAVA)
java·spring boot·毕业设计·论文·管理系统·校园二手交易平台
尚学教辅学习资料2 小时前
基于SpringBoot+Vue的校园跑腿原生小程序
vue.js·spring boot·小程序·校园跑腿
奋斗的小方2 小时前
Springboot基础篇(5):自定义 MyBatis Starter
spring boot·后端·mybatis
皮皮林5513 小时前
SpringBoot + ResponseBodyEmitter 实时异步流式推送,优雅!
spring boot
MacroZheng3 小时前
Spring 官宣接入 DeepSeek,太香了!
java·spring boot·后端
道友老李5 小时前
【存储中间件】Neo4J图数据库超详细教程(一):相关介绍、特点及优势、数据模型、软件安装
数据库·中间件·neo4j