SpringBoot集成图数据库neo4j实现简单的关联图谱

news2025/3/12 19:15:55

社交领域:Facebook, Twitter,Linkedin用它来管理社交关系,实现好友推荐

图数据库neo4j安装:

  1. 下载镜像:docker pull neo4j:3.5.0
  2. 运行容器:docker run -d -p 7474:7474 -p 7687:7687 --name neo4j-3.5.0 neo4j:3.5.0
  3. 停止容器:docker stop neo4j-3.5.0
  4. 启动容器:docker start neo4j-3.5.0
  5. 浏览器 http://localhost:7474/ 访问 neo4j 管理后台,初始账号/密码 neo4j/neo4j,会要求修改初始化密码,我们修改为 neo4j/123456

neo4j中可以使用Cypher查询语言(CQL)进行图形数据库的查询

①、添加节点

CREATE (:) 创建不含有属性节点

节点名称node-name和标签名称lable-name:标签名称相当于关系型数据库中的表名,而节点名称则代指这一条数据
而创建包含属性的节点时,可以在标签名称后面追加一个描绘属性的json字符串

CREATE (索尔:Person)

CREATE (洛基:Person {name:"洛基",title:"诡计之神"})

②、查询节点

MATCH (:) 查询已存在的节点及属性的数据

MATCH命令在后面配合RETURN、DELETE等命令使用,执行具体的返回或删除等操作

MATCH (p:Person) RETURN p

在这里插入图片描述
可以看到上面添加的两个节点,分别是不包含属性的空节点和包含属性的节点,并且所有节点会有一个默认生成的id作为唯一标识

③、删除节点

MATCH (p:Person) WHERE id§=100
DELETE p

在这条删除语句中,额外使用了WHERE过滤条件,它与SQL中的WHERE非常相似,命令中通过节点的id进行了过滤。 删除完成后,再次执行查询操作,可以看到只保留了洛基这一个节点

④、添加关联

再创建一个节点作为关系的两端:CREATE (p:Person {name:“索尔”,title:“雷神”})

创建关系的基本语法如下:

CREATE (:)

  • [:]
    -> (:)

也可以利用已经存在的节点创建关系,下面我们借助MATCH先进行查询,再将结果进行关联,创建两个节点之间的关联关系:

MATCH (m:Person),(n:Person) 
WHERE m.name='索尔' and n.name='洛基' 
CREATE (m)-[r:BROTHER {relation:"无血缘兄弟"}]->(n)
RETURN r

添加完成后,可以通过关系查询符合条件的节点及关系:

MATCH (m:Person)-[re:BROTHER]->(n:Person) 
RETURN m,re,n

在这里插入图片描述
如果节点被添加了关联关系后,单纯删除节点的话会报错,:

Neo.ClientError.Schema.ConstraintValidationFailed
Cannot delete node<85>, because it still has relationships. To delete this node, you must first delete its relationships.

这时,需要在删除节点时同时删除关联关系:

MATCH (m:Person)-[r:BROTHER]->(n:Person) 
DELETE m,r

SpringBoot整合neo4j

一、依赖


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springboot-demo</artifactId>
        <groupId>com.et</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>neo4j</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-neo4j</artifactId>
        </dependency>

        <dependency>
            <groupId>com.hankcs</groupId>
            <artifactId>hanlp</artifactId>
            <version>portable-1.2.4</version>
        </dependency>
        <dependency>
            <groupId>edu.stanford.nlp</groupId>
            <artifactId>stanford-parser</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

二、属性文件和启动类


server:
  port: 8088
spring:
  data:
    neo4j:
      uri: bolt://127.0.0.1:7687
      username: neo4j
      password: 123456

三、文本SPO抽取

借助Git上一个现成的工具类,来进行文本的语义分析和SPO三元组的抽取工作
项目地址:https://github.com/hankcs/MainPartExtracto

//提取主谓宾
public class MainPartExtractor{
	
	private static final Logger LOG = LoggerFactory.getLogger(MainPartExtractor.class);
	
	private static LexicalizedParser lp;//加载模型
	private static GrammaticalStructureFactory gsf;

	static{
		//模型
        String models = "models/chineseFactored.ser";
        LOG.info("载入文法模型:" + models);
        lp = LexicalizedParser.loadModel(models);
        //汉语
        TreebankLanguagePack tlp = new ChineseTreebankLanguagePack();
        gsf = tlp.grammaticalStructureFactory();
	}

	//获取句子的主谓宾
	public static MainPart getMainPart(String sentence){
		
		// 去掉不可见字符
        sentence = sentence.replace("\\s+", "");
        // 分词,用空格隔开
        List<Word> wordList = seg(sentence);
        return getMainPart(wordList);
	}

	public static MainPart getMainPart(List<Word> words)
    {
        MainPart mainPart = new MainPart();
        if (words == null || words.size() == 0) return mainPart;
        Tree tree = lp.apply(words);
        LOG.info("句法树:{}", tree.pennString());
        // 根据整个句子的语法类型来采用不同的策略提取主干
        switch (tree.firstChild().label().toString())
        {
            case "NP":
                // 名词短语,认为只有主语,将所有短NP拼起来作为主语即可
                mainPart = getNPPhraseMainPart(tree);
                break;
            default:
                GrammaticalStructure gs = gsf.newGrammaticalStructure(tree);
                Collection<TypedDependency> tdls = gs.typedDependenciesCCprocessed(true);
                LOG.info("依存关系:{}", tdls);
                TreeGraphNode rootNode = getRootNode(tdls);
                if (rootNode == null)
                {
                    return getNPPhraseMainPart(tree);
                }
                LOG.info("中心词语:", rootNode);
                mainPart = new MainPart(rootNode);
                for (TypedDependency td : tdls)
                {
                    // 依存关系的出发节点,依存关系,以及结束节点
                    TreeGraphNode gov = td.gov();
                    GrammaticalRelation reln = td.reln();
                    String shortName = reln.getShortName();
                    TreeGraphNode dep = td.dep();
                    if (gov == rootNode)
                    {
                        switch (shortName)
                        {
                            case "nsubjpass":
                            case "dobj":
                            case "attr":
                                mainPart.object = dep;
                                break;
                            case "nsubj":
                            case "top":
                                mainPart.subject = dep;
                                break;
                        }
                    }
                    if (mainPart.object != null && mainPart.subject != null)
                    {
                        break;
                    }
                }
                // 尝试合并主语和谓语中的名词性短语
                combineNN(tdls, mainPart.subject);
                combineNN(tdls, mainPart.object);
                if (!mainPart.isDone()) mainPart.done();
        }

        return mainPart;
    }

	private static MainPart getNPPhraseMainPart(Tree tree)
    {
        MainPart mainPart = new MainPart();
        StringBuilder sbResult = new StringBuilder();
        List<String> phraseList = getPhraseList("NP", tree);
        for (String phrase : phraseList)
        {
            sbResult.append(phrase);
        }
        mainPart.result = sbResult.toString();
        return mainPart;
    }
	
	//从句子中提取最小粒度的短语
	public static List<String> getPhraseList(String type, String sentence)
    {
        return getPhraseList(type, lp.apply(seg(sentence)));
    }

    private static List<String> getPhraseList(String type, Tree tree)
    {
        List<String> phraseList = new LinkedList<String>();
        for (Tree subtree : tree)
        {
            if(subtree.isPrePreTerminal() && subtree.label().value().equals(type))
            {
                StringBuilder sbResult = new StringBuilder();
                for (Tree leaf : subtree.getLeaves())
                {
                    sbResult.append(leaf.value());
                }
                phraseList.add(sbResult.toString());
            }
        }
        return phraseList;
    }

	//合并名词性短语为一个节点
	private static void combineNN(Collection<TypedDependency> tdls, TreeGraphNode target)
    {
        if (target == null) return;
        for (TypedDependency td : tdls)
        {
            // 依存关系的出发节点,依存关系,以及结束节点
            TreeGraphNode gov = td.gov();
            GrammaticalRelation reln = td.reln();
            String shortName = reln.getShortName();
            TreeGraphNode dep = td.dep();
            if (gov == target)
            {
                switch (shortName)
                {
                    case "nn":
                        target.setValue(dep.toString("value") + target.value());
                        return;
                }
            }
        }
    }

    private static TreeGraphNode getRootNode(Collection<TypedDependency> tdls)
    {
        for (TypedDependency td : tdls)
        {
            if (td.reln() == GrammaticalRelation.ROOT)
            {
                return td.dep();
            }
        }

        return null;
    }

	//分词
	private static List<Word> seg(String sentence)
    {
        //分词
        LOG.info("正在对短句进行分词:" + sentence);
        List<Word> wordList = new LinkedList<>();
        List<Term> terms = HanLP.segment(sentence);
        StringBuffer sbLogInfo = new StringBuffer();
        for (Term term : terms)
        {
            Word word = new Word(term.word);
            wordList.add(word);
            sbLogInfo.append(word);
            sbLogInfo.append(' ');
        }
        LOG.info("分词结果为:" + sbLogInfo);
        return wordList;
    }

    public static MainPart getMainPart(String sentence, String delimiter)
    {
        List<Word> wordList = new LinkedList<>();
        for (String word : sentence.split(delimiter))
        {
            wordList.add(new Word(word));
        }
        return getMainPart(wordList);
    }

	public static void main(String[] args)
    {
       /* String[] testCaseArray = {
                "我一直很喜欢你",
                "你被我喜欢",
                "美丽又善良的你被卑微的我深深的喜欢着……",
                "只有自信的程序员才能把握未来",
                "主干识别可以提高检索系统的智能",
                "这个项目的作者是hankcs",
                "hankcs是一个无门无派的浪人",
                "搜索hankcs可以找到我的博客",
                "静安区体育局2013年部门决算情况说明",
                "这类算法在有限的一段时间内终止",
        };
        for (String testCase : testCaseArray)
        {
            MainPart mp = MainPartExtractor.getMainPart(testCase);
            System.out.printf("%s\t%s\n", testCase, mp);

        }*/
        mpTest();
    }
    public static void mpTest(){
        String[] testCaseArray = {
                "我一直很喜欢你",
                "你被我喜欢",
                "美丽又善良的你被卑微的我深深的喜欢着……",
                "小米公司主要生产智能手机",
                "他送给了我一份礼物",
                "这类算法在有限的一段时间内终止",
                "如果大海能够带走我的哀愁",
                "天青色等烟雨,而我在等你",
                "我昨天看见了一个非常可爱的小孩"
        };
        for (String testCase : testCaseArray) {
            MainPart mp = MainPartExtractor.getMainPart(testCase);
            System.out.printf("%s   %s   %s \n",
                    GraphUtil.getNodeValue(mp.getSubject()),
                    GraphUtil.getNodeValue(mp.getPredicate()),
                    GraphUtil.getNodeValue(mp.getObject()));
        }
    }
}

四、动态构建知识图谱

新建一个NodeServiceImpl,其中实现两个关键方法parseAndBind和addNode 首先是根据句子中抽取的主语或宾语在neo4j中创建节点的方法,这里根据节点的name判断是否为已存在的节点,如果存在则直接返回,不存在则添加:

@Service
@AllArgsConstructor
public class NodeServiceImpl implements NodeService {
    private final NodeRepository nodeRepository;
    private final RelationRepository relationRepository;


    @Override
    public Node save(Node node) {
        Node save = nodeRepository.save(node);
        return save;
    }
    @Override
    public void bind(String name1, String name2, String relationName) {
        Node start = nodeRepository.findByName(name1);
        Node end = nodeRepository.findByName(name2);

        Relation relation =new Relation();
        relation.setStartNode(start);
        relation.setEndNode(end);
        relation.setRelation(relationName);

        relationRepository.save(relation);
    }
    private Node addNode(TreeGraphNode treeGraphNode){

        String nodeName = GraphUtil.getNodeValue(treeGraphNode);

        Node existNode = nodeRepository.findByName(nodeName);
        if (Objects.nonNull(existNode))
            return existNode;

        Node node =new Node();
        node.setName(nodeName);
        return nodeRepository.save(node);
    }
    @Override
    public List<Relation> parseAndBind(String sentence) {
        MainPart mp = MainPartExtractor.getMainPart(sentence);

        TreeGraphNode subject = mp.getSubject();    //主语
        TreeGraphNode predicate = mp.getPredicate();//谓语
        TreeGraphNode object = mp.getObject();      //宾语

        if (Objects.isNull(subject) || Objects.isNull(object))
            return null;

        Node startNode = addNode(subject);
        Node endNode = addNode(object);
        String relationName = GraphUtil.getNodeValue(predicate);//关系词

        List<Relation> oldRelation = relationRepository
                .findRelation(startNode, endNode,relationName);
        if (!oldRelation.isEmpty())
            return oldRelation;

        Relation botRelation=new Relation();
        botRelation.setStartNode(startNode);
        botRelation.setEndNode(endNode);
        botRelation.setRelation(relationName);
        Relation relation = relationRepository.save(botRelation);

        return Arrays.asList(relation);
    }

}

测试:启动java应用,输入以下地址

http://127.0.0.1:8088/parse?sentence=海拉又被称为死亡女神
http://127.0.0.1:8088/parse?sentence= 死亡女神捏碎了雷神之锤
http://127.0.0.1:8088/parse?sentence=雷神之锤属于索尔

在图数据库neo4j里面查询:

MATCH (p:Person) RETURN p

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1495387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

进程间通信之信号灯 || 网络协议UDP/TCP || 三次握手四次挥手

在线程通信中由于数据段等内存空间的共用性&#xff0c;导致同时访问时资源竞争的问题&#xff0c;在线程中我们使用信号量的申请和释放&#xff0c;在防止资源竞争的产生。在进程间的通信中&#xff0c;有信号灯的概念。搭配共享内存实现进程同步。 有名信号量: 1.创建 …

数字创新的风口:创业者如何在Web3时代抢占先机

随着区块链技术的不断发展&#xff0c;Web3正成为数字创新的新风口&#xff0c;为创业者们带来了前所未有的机遇和挑战。本文将从另一个角度探讨Web3对创业者的影响&#xff0c;并提出创业者在Web3时代抢占先机的策略和方法。 1. Web3重新定义了商业模式 Web3不仅仅是一种技术…

安全测试报告-模板内容

1. 概述 为检验XXXX平台 系统的安全性&#xff0c;于 XXXX年 XX 月 XX 日至 XXXX年 XX 月 XX日对目标系统进行了安全测试。在此期间测试人员将使用各 种非破坏性质的攻击手段&#xff0c;对目标系统做深入的探测分析&#xff0c;进而挖掘系统中的安 全漏洞和风险隐患。研发团队…

论文翻译:一种基于强化学习的车辆队列控制策略,用于减少交通振荡中的能量消耗

A Reinforcement Learning-Based Vehicle Platoon Control Strategy for Reducing Energy Consumption in Traffic Oscillations 一种基于强化学习的车辆队列控制策略&#xff0c;用于减少交通振荡中的能量消耗 文章目录 A Reinforcement Learning-Based Vehicle Platoon Cont…

【Selenium】UI自动化|元素定位常见问题

1、报错NoSuchElementException——定位不到元素 分析的可能原因&#xff1a; 页面还没有加载出来&#xff0c;就对页面上的元素进行的操作 元素在iframe中&#xff0c;先要理解下frame的实质&#xff0c;frame中实际上是嵌入了另一个页面&#xff0c;而webdriver每次只能在一…

数仓之【MaxCompute】

一、简介 Maxcompute&#xff1a;云原生大数据计算服务是一种快速、完全托管的TB/PB级数据仓库解决方案。Maxcompute向用户提供了完善的数据导入方案以及多种经典的分布式计算模型&#xff0c;能够更快速的解决用户海量数据计算问题&#xff0c;有效降低企业成本&#xff0c;并…

单细胞联合BulkRNA分析思路|加个MR锦上添花,增强验证~

今天给大家分享一篇IF7.3的单细胞MR的文章&#xff0c;2023年12月发表在Frontiers in Immunology&#xff1a;An integrative analysis of single-cell and bulk transcriptome and bidirectional mendelian randomization analysis identified C1Q as a novel stimulated risk…

vue3学习(续篇)

vue3学习(续篇) 默认有vue3基础并有一定python编程经验。 chrome浏览器安装vue.js devtools拓展。 文章目录 vue3学习(续篇)1. element-ui使用2. axios 网络请求1. 简介2. 操作 3. flask-cors解决跨域问题1. 简介2. 操作 4. 前端路由 vue-router1. 简单使用2. 配置路径别名和…

LLM 应用的新兴架构

原文地址&#xff1a;emerging-architectures-for-llm-applications 大语言模型&#xff08;LLM&#xff09;为软件构建提供了一种强大的新方法。由于这种技术相对较新&#xff0c;且其运作方式与传统计算资源大相径庭&#xff0c;如何有效利用它们并不是显而易见的。 在这篇…

腾讯云4核8G服务器轻量和CVM可用来干什么?

腾讯云4核8G服务器适合做什么&#xff1f;搭建网站博客、企业官网、小程序、小游戏后端服务器、电商应用、云盘和图床等均可以&#xff0c;腾讯云4核8G服务器可以选择轻量应用服务器4核8G12M或云服务器CVM&#xff0c;轻量服务器和标准型CVM服务器性能是差不多的&#xff0c;轻…

Python报错ModuleNotFoundError: No module named ‘numpy‘

原因&#xff1a;缺少“numpy” 进入python安装路径&#xff0c;script路径内 在路径下启动终端 01.更新numpy python -m pip install --upgrade pip 02.安装 pip install numpy 03.运行python python 04.导入包 from numpy import * 问题已解决。

Python之Web开发初学者教程—初级教程成果下载

到目前为止&#xff0c;初级教程已经完成。 按照一步步的教程搭建虚拟机中的环境&#xff0c;包括内容如下&#xff1a; √ Ubuntu操作系统搭建 √ Python 配置 √ Redis 安装配置 √ Pycharm 安装配置 √ Python库中包括 √ Pip安装 √ Pyinstaller安装 √ Redis安装 √ Jieb…

vue3 setup函数与setup语法糖之间的区别

普通setup函数构建的组件 import {ref} from vueexport default {setup(){const countref(0)const handleUpdate()>{count.value}return{count,handleUpdate}}} </script>使用setup语法糖构建的组件 <script setup>import {ref} from vueconst countref(0)con…

英福康INFICON真空计VGC012-103-401使用说明

英福康INFICON真空计VGC012-103-401使用说明

stl的基本知识学习

1.vector&#xff1a; 2.set&#xff1a; 3.map&#xff1a; 4.栈&#xff1a; 5.队列&#xff1a; 6. unordered_map与unordered_set: 7. 位运算&#xff1a; 8.cctype&#xff1a; 导图&#xff1a;

linux安装ngnix完整步骤(支持centos/银河麒麟操作系统)

linux安装ngnix&#xff08;支持centos/银河麒麟操作系统&#xff09; 本次操作系统安装ngnix采用离线或在线安装方式&#xff0c;离线就是不联网环境&#xff0c;在线则是联网环境&#xff1b;支持centos7或centos8或国产操作系统&#xff08;银河麒麟高级服务器操作系统&…

MySQl基础入门③

上一遍内容 接下来我们都使用navicat软件来操作数据了。 1.新建数据库 先创建我门自己的一个数据库 鼠标右键点击bendi那个绿色海豚的图标&#xff0c;然后选择新建数据库。 数据库名按自己喜好的填&#xff0c;不要写中文&#xff0c; 在 MySQL 8.0 中&#xff0c;最优的字…

kafka消费端消息去重方案

背景 我们在日常工作中&#xff0c;消费kafka消息是一个最常见的操作&#xff0c;不过由于kafka队列中经常包含重复的消息&#xff0c;并且消息量巨大&#xff0c;所以我们消费端总是需要先把消息进行去重后在消费&#xff0c;以减少消费端的压力&#xff0c;那么日常中我们一…

HTML静态网页成品作业(HTML+CSS)——安徽宣笔设计制作(5个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有6个页面。 &#x1f3f7;️想要…

FPGA开发之libero元件实例化详细步骤

FPGA开发之libero模块实例化详细步骤 第一步&#xff0c;假设已经建立了两个文件&#xff0c;现在需要将这两个文件连接在一起&#xff0c;如下图所示&#xff1a; 第二步&#xff0c;建立一个SD顶层文件&#xff0c;操作如下&#xff1a; 得到结果如下&#xff1a; 点击OK得…