图数据库Neo4j——SpringBoot使用Neo4j & 简单增删改查 & 复杂查询初步
前言
图形数据库是专门用于存储图形数据的数据库,它使用图形模型来存储数据,并且支持复杂的图形查询。常见的图形数据库有Neo4j、OrientDB等。
Neo4j是用Java实现的开源NoSQL图数据库,本篇博客介绍如何在SpringBoot中使用Neo4j图数据库,如何进行简单的增删改查,以及如何进行复杂的查询。
本篇博客相关代码的git网址如下:
https://gitee.com/pet365/spring-boot-neo4j
关于Neo4j的博客文章如下:
- 图数据库Neo4j——Neo4j简介、数据结构 & Docker版本的部署安装 & Cypher语句的入门
目录
- 前言
- 引出
- springBoot整合
- 1、引入依赖
- 2、配置文件
- 3、实体类定义
- 4、dao继承Neo4jRepository
- 复杂查询
- 最短路径查询
- 最小成本查询
- 总结
引出
1.Neo4j是用Java实现的开源NoSQL图数据库;
2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;
3.使用Neo4jClient进行复杂的查询;
springBoot整合
1、引入依赖
org.springframework.boot spring-boot-starter-data-neo4j
2、配置文件
server: port: 9902 logging: level: org.springframework.data.neo4j: debug spring: application: name: spring-neo4j data: neo4j: database: neo4j neo4j: authentication: username: neo4j password: neo4j123 uri: neo4j://192.168.150.101:7687
3、实体类定义
提取抽象类
不同的节点类,网点、一级转运中心、二级转运中心
4、dao继承Neo4jRepository
进行自定义查询:
Keyword Sample Cypher snippet After findByLaunchDateAfter(Date date) n.launchDate > date Before findByLaunchDateBefore(Date date) n.launchDate Containing (String) findByNameContaining(String namePart) n.name CONTAINS namePart Containing (Collection) findByEmailAddressesContains(Collection addresses) findByEmailAddressesContains(String address) ANY(collectionFields IN [addresses] WHERE collectionFields in n.emailAddresses) ANY(collectionFields IN address WHERE collectionFields in n.emailAddresses) In findByNameIn(Iterable names) n.name IN names Between findByScoreBetween(double min, double max) findByScoreBetween(Range range) n.score >= min AND n.score = min AND n.score min AND n.score StartingWith findByNameStartingWith(String nameStart) n.name STARTS WITH nameStart EndingWith findByNameEndingWith(String nameEnd) n.name ENDS WITH nameEnd Exists findByNameExists() EXISTS(n.name) True findByActivatedIsTrue() n.activated = true False findByActivatedIsFalse() NOT(n.activated = true) Is findByNameIs(String name) n.name = name NotNull findByNameNotNull() NOT(n.name IS NULL) Null findByNameNull() n.name IS NULL GreaterThan findByScoreGreaterThan(double score) n.score > score GreaterThanEqual findByScoreGreaterThanEqual(double score) n.score >= score LessThan findByScoreLessThan(double score) n.score LessThanEqual findByScoreLessThanEqual(double score) n.score /** * 根据bid 查询 * @param bid 业务id * @return 网点数据 */ AgencyEntity findByBid(Long bid); /** * 根据bid删除 * * @param bid 业务id * @return 删除的数据条数 */ Long deleteByBid(Long bid); } pimg src="https://img-blog.csdnimg.cn/401b2fa4a15f454bb3f53379b34f2035.png" //p h3复杂查询/h3 pimg src="https://img-blog.csdnimg.cn/ac2ab1e951b14be08974abe9cd0bf4ef.png" //p h4最短路径查询/h4 pre class="brush:python;toolbar:false"//查询两个网点之间最短路径,查询深度最大为10 MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"RETURN path package com.tianju.mapper.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.db.meta.Column; import com.tianju.dto.OrganDTO; import com.tianju.dto.TransportLineNodeDTO; import com.tianju.entity.AgencyEntity; import com.tianju.enums.OrganTypeEnum; import com.tianju.mapper.TransportLineRepository; import org.neo4j.driver.internal.InternalPoint2D; import org.neo4j.driver.types.Path; import org.neo4j.driver.types.Relationship; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import java.util.Map; import java.util.Optional; @Component public class TransportLineRepositoryImpl implements TransportLineRepository { @Autowired private Neo4jClient neo4jClient; /** * 查询最短路线 * @param start 开始网点 * @param end 结束网点 * @return */ @Override public TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) { // 获取网点数据在Neo4j中的类型 @Node("AGENCY") @Node("OLT") String type = AgencyEntity.class.getAnnotation(Node.class).value()[0]; // 构造Sql语句 $startId // String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))\n" + // "WHERE n.bid = $startId AND m.bid = $endId\n" + // "RETURN path"; String cql = StrUtil.format("MATCH path = shortestPath((n:{}) -[*..10]->(m:{})) " + "WHERE n.bid = $startId AND m.bid = $endId " + "RETURN path",type,type); // 执行自定义查询 Neo4jClient.RecordFetchSpec recordFetchSpec = neo4jClient.query(cql) .bind(start.getBid()).to("startId") // 替换 $startId .bind(end.getBid()).to("endId") // 替换 $endId .fetchAs(TransportLineNodeDTO.class) // 设置响应类型,指定为 TransportLineNodeDTO 类型 .mappedBy((typeSystem, record) -> { // 设置结果集映射 Path path = record.get(0).asPath();// 得到第一条路线 TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO(); path.nodes().forEach(node -> { // 将每个节点信息封装成一个 OrganDto // 获得节点的 键值对 address: 上海市转运中心;bid:8002 Map map = node.asMap(); // {name=北京市昌平区定泗路, // location=Point{srid=4326, x=116.37212849638287, y=40.11765281246394}, // address=北七家镇定泗路苍龙街交叉口, bid=100280, phone=010-86392987} System.out.println("map: "+map); // 把键值对转换成对象 OrganDTO OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class); // organDTO: // OrganDTO(id=100280, name=北京市昌平区定泗路, type=null, phone=010-86392987, // address=北七家镇定泗路苍龙街交叉口, latitude=null, longitude=null) // type,latitude,longitude 没有映射成功 System.out.println("organDTO: "+organDTO); // 获得标签的名称 OLT,TLT, String first = CollUtil.getFirst(node.labels()); // 根据OLT获得枚举类型 OLT(1, "一级转运中心"), OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first); // 再获得枚举类型的 code :1、2、3 organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射 // 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993}) InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x"); organDTO.setLatitude(location.x()); // 设置经纬度映射 organDTO.setLongitude(location.y()); // 经纬度映射 // OrganDTO(id=100280, name=北京市昌平区定泗路, type=3, // phone=010-86392987, address=北七家镇定泗路苍龙街交叉口, // latitude=116.37212849638287, longitude=40.11765281246394) System.out.println("organDTO: "+organDTO); transportLineNodeDTO.getNodeList().add(organDTO); }); System.out.println("transportLineNodeDTO: "+transportLineNodeDTO); path.relationships().forEach(relationship -> { // 路径下面的关系 Map map = relationship.asMap(); Double cost = MapUtil.get(map, "cost", Double.class); transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost()); }); System.out.println("transportLineNodeDTO: "+transportLineNodeDTO); return transportLineNodeDTO; }); Optional one = recordFetchSpec.one(); // Optional,1.8提供的,可以处理null的情况 return one.orElse(null); // 如果为null,就返回null,如果不是null,就返回结果 } }
最小成本查询
package com.tianju.mapper.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.db.meta.Column; import com.tianju.dto.OrganDTO; import com.tianju.dto.TransportLineNodeDTO; import com.tianju.entity.AgencyEntity; import com.tianju.enums.OrganTypeEnum; import com.tianju.mapper.TransportLineRepository; import org.neo4j.driver.internal.InternalPoint2D; import org.neo4j.driver.types.Path; import org.neo4j.driver.types.Relationship; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import java.util.Map; import java.util.Optional; @Component public class TransportLineRepositoryImpl implements TransportLineRepository { @Autowired private Neo4jClient neo4jClient; @Override public TransportLineNodeDTO findCostLeastPath(AgencyEntity start, AgencyEntity end) { String type = AgencyEntity.class.getAnnotation(Node.class).value()[0]; String cqlB = "MATCH path = (n:{}) -[*..10]->(m:{}) " + "WHERE n.bid = $startId AND m.bid = $endId " + "UNWIND relationships(path) AS r " + "WITH sum(r.cost) AS cost, path " + "RETURN path ORDER BY cost ASC, LENGTH(path) ASC LIMIT 1"; String cql = StrUtil.format(cqlB, type,type); Optional one = neo4jClient.query(cql) .bind(start.getBid()).to("startId") .bind(end.getBid()).to("endId") .fetchAs(TransportLineNodeDTO.class) .mappedBy(((typeSystem, record) -> { Path path = record.get(0).asPath(); TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO(); path.nodes().forEach(node -> { Map map = node.asMap(); OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class); // 获得标签的名称 OLT,TLT, String first = CollUtil.getFirst(node.labels()); // 根据OLT获得枚举类型 OLT(1, "一级转运中心"), OrganTypeEnum organTypeEnum = OrganTypeEnum.valueOf(first); // 再获得枚举类型的 code :1、2、3 organDTO.setType(organTypeEnum.getCode()); // 设置类型的映射 // 经纬度 "location": point({srid:4326, x:121.59815370294322, y:31.132409729356993}) InternalPoint2D location = MapUtil.get(map, "location", InternalPoint2D.class); // 经纬度 BeanUtil.getProperty(map.get("location"),"x"); organDTO.setLatitude(location.x()); // 设置经纬度映射 organDTO.setLongitude(location.y()); // 经纬度映射 transportLineNodeDTO.getNodeList().add(organDTO); }); path.relationships().forEach(relationship -> { // 路径下面的关系 Map map = relationship.asMap(); Double cost = MapUtil.get(map, "cost", Double.class); transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost()); }); return transportLineNodeDTO; })).one(); return one.orElse(null); } private void findShortestPathMy(){ String cql = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY)) " + "WHERE n.bid = 210127 AND m.bid = 100260 " + "RETURN path"; // 执行自定义查询 Neo4jClient.UnboundRunnableSpec query = neo4jClient.query(cql); ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); } }
总结
1.Neo4j是用Java实现的开源NoSQL图数据库;
2.SpringBoot使用Neo4j,继承Neo4jRepository进行简单增删改查;
3.使用Neo4jClient进行复杂的查询;
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理!
部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!
图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!