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

社交领域: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:7687username: neo4jpassword: 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;@Overridepublic Node save(Node node) {Node save = nodeRepository.save(node);return save;}@Overridepublic 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);}@Overridepublic 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.mzph.cn/news/724658.shtml

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

相关文章

Android开发真等于废人,历经30天

前言 回顾一下自己这段时间的经历&#xff0c;三月份的时候&#xff0c;疫情原因公司通知了裁员&#xff0c;我匆匆忙忙地出去面了几家&#xff0c;但最终都没有拿到offer&#xff0c;我感觉今年的寒冬有点冷。到五月份&#xff0c;公司开始第二波裁员&#xff0c;我决定主动拿…

超简单Windows-kafka安装配置

参考大佬文章&#xff1a; Kafka&#xff08;Windows&#xff09;安装配置启动&#xff08;常见错误扫雷&#xff09;教程_kafka在windows上的安装、运行-CSDN博客Kafka&#xff08;Windows&#xff09;安装配置启动&#xff08;常见错误扫雷&#xff09;教程_kafka在windows上…

基于ERNIR3.0文本分类的开发实践

参考&#xff1a;基于ERNIR3.0文本分类&#xff1a;(KUAKE-QIC)意图识别多分类(单标签) - 飞桨AI Studio星河社区 (baidu.com) https://zhuanlan.zhihu.com/p/574666812?utm_id0 遇到的问题&#xff1a;如下 采用paddleNLP下文本分类实例进行分类训练后发现 生成的模型分类不…

嵌入式学习-FreeRTOS-Day1

一、重点 1、VCC和GND VCC&#xff1a; 1、电路中为电源&#xff0c;供应电压 2、3.3v-5v 3、数字信号中用1表示GND&#xff1a; 1、表示地线 2、一般为0v 3、数字信号中用0表示2、电容和电阻 电容 存储电荷 存储能量&#xff1a; 电容器可以在其两个导体板&#xff08;极…

C++之智能指针

为什么会有智能指针 前面我们知道使用异常可能会导致部分资源没有被正常释放, 因为异常抛出之后会直接跳转到捕获异常的地方从而跳过了一些很重要的的代码, 比如说下面的情况&#xff1a; int div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument(&q…

第三天 Kubernetes进阶实践

第三天 Kubernetes进阶实践 本章介绍Kubernetes的进阶内容&#xff0c;包含Kubernetes集群调度、CNI插件、认证授权安全体系、分布式存储的对接、Helm的使用等&#xff0c;让学员可以更加深入的学习Kubernetes的核心内容。 ETCD数据的访问 kube-scheduler调度策略实践 预选与…

centos7安装maven离线安装

1、从官方网站下载maven文件包 官方下载网站&#xff1a;https://maven.apache.org/download.cgi 2、创建文件夹解压文件 将下载好的安装包&#xff0c;放到创建的目录下&#xff0c;并解压 a、创建/app/maven文件 mkdir /app/mavenb、解压文件 tar -zxvf apache-maven-…

重磅:2024广州国际酒店工程照明展览会

2024广州国际酒店工程照明展览会 Guangzhou international hotel engineering lighting exhibition 2024 时间&#xff1a;2024年12月19-21日 地点&#xff1a;广州.中国进出口商品交易会展馆 承办单位&#xff1a;广州佛兴英耀展览服务有限公司 上海昶文展览服务有限公司…

【Java面试/24春招】技术面试题的准备

Spring MVC的原理 Mybatis的多级缓存机制 线程池的大小和工作原理 上述问题&#xff0c;我们称为静态的问题&#xff0c;具有标准的答案&#xff0c;而且这个答案不会变化&#xff01; 如果没有Spring&#xff0c;会怎么样&#xff1f;IOC这个思想是解决什么问题&#xff1f…

【牛客】VL65 状态机与时钟分频

描述 题目描述&#xff1a; 使用状态机实现时钟分频&#xff0c;要求对时钟进行四分频&#xff0c;占空比为0.25 信号示意图&#xff1a; clk为时钟 rst为低电平复位 clk_out 信号输出 Ps 本题题解是按照1000的状态转移进行的&#xff0c;不按照此状态进行&#xff0c;编译器…

蓝桥杯练习系统(算法训练)ALGO-985 幸运的店家

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 炫炫开了一家商店&#xff0c;卖的货只有一个&#xff0c;XXX&#xff0c;XXX卖N元钱。有趣的是&#xff0c;世界上只有面值…

剑指offer 二维数组中的查找 C++

目录 前言 一、题目 二、解题思路 1.直接查找 2.二分法 三、输出结果 前言 最近在牛客网刷题&#xff0c;刷到二维数组的查找&#xff0c;在这里记录一下做题过程 一、题目 描述 在一个二维数组中&#xff08;每个一维数组的长度相同&#xff09;&#xff0c;每一行都按照…

微信小程序开发:记一次提审失败的反馈重审

我在第一次提审小程序的时候很明确说了我这个是接入的阿里云的人像动漫化接口&#xff0c;但是还是给我不通过&#xff1a; 说我涉及AI合成&#xff0c;个人是做不了一点AI相关的东西&#xff0c;一点都不行&#xff1a; 我肯定不接受了&#xff0c;反馈说&#xff1a; 还把…

2024.3.6

作业1&#xff1a;使用C语言完成数据库的增删改 #include <myhead.h>//定义添加员工信息函数 int Add_worker(sqlite3 *ppDb) {//准备sql语句printf("请输入要添加的员工信息:\n");//从终端获取员工信息char rbuf[128]"";fgets(rbuf,sizeof(rbuf),s…

ArrayList的扩容机制

ArrayList 的底层操作机制源码分析 ArrayList中维护一个Object类型的数组elementData transient Object[] elementData; //transient表示瞬间 短暂的&#xff0c;表示该属性不会被序列化当创建ArrayList对象时&#xff0c;如果使用的是无参构造器&#xff0c;则初始elementDa…

C#知识点-22(ADO.NET五个对象,SQL漏洞注入攻击)

ADO.NET 概念&#xff1a;ADO.NET就是一组类库&#xff0c;这组类库可以让我们通过程序的方式访问数据库&#xff0c;就像System.IO的类用类操作文件一样&#xff0c;System.Data这组类是用来操作数据库的&#xff08;不光是MSSql Server&#xff09;&#xff0c;它提供了统一…

【Windows 常用工具系列 14 -- windows 网络驱动映射】

文章目录 windows 网络驱动映射 windows 网络驱动映射 映射网络驱动器的意思是将局域网中的某个目录映射成本地驱动器号。 在windows上将服务器目录映射到本地盘&#xff1a; 进入到服务器执行下面命令既可以看到对应的 IP地址&#xff1a; 将对应的IP地址填入上图中。 映…

Synchronized(三:JVM中锁的优化)

简单来说在JVM中的monitorenter和monitorexit字节码依赖于底层的操作系统的Mutex Lock来实现的&#xff0c;但是由于使用Mutex Lock需要将当前线程挂起并从用户态切换到内核态来执行&#xff0c;这种切换的代价是非常昂贵的&#xff1b;然而在现实中大部分情况下&#xff0c;同…

C++ Floyd求最短路 Floyd算法(多源汇最短路)

给定一个 n 个点 m 条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;边权可能为负数。 再给定 k 个询问&#xff0c;每个询问包含两个整数 x 和 y &#xff0c;表示查询从点 x 到点 y 的最短距离&#xff0c;如果路径不存在&#xff0c;则输出 impossible。 数据…

docker部署前后端分离项目

docker部署前后端分离项目 前提&#xff0c;服务器环境是docker环境&#xff0c;如果服务器没有安装docker&#xff0c;可以先安装docker环境。 各个环境安装docker&#xff1a; Ubuntu上安装Docker&#xff1a; ubuntu离线安装docker: CentOS7离线安装Docker&#xff1a; Cen…