图数据库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、引入依赖

<!--        neo4j的包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-neo4j</artifactId>
        </dependency>

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

进行自定义查询:

KeywordSampleCypher snippet
AfterfindByLaunchDateAfter(Date date)n.launchDate > date
BeforefindByLaunchDateBefore(Date date)n.launchDate < date
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)
InfindByNameIn(Iterable names)n.name IN names
BetweenfindByScoreBetween(double min, double max) findByScoreBetween(Range range)n.score >= min AND n.score <= max Depending on the Range definition n.score >= min AND n.score <= max or n.score > min AND n.score < max
StartingWithfindByNameStartingWith(String nameStart)n.name STARTS WITH nameStart
EndingWithfindByNameEndingWith(String nameEnd)n.name ENDS WITH nameEnd
ExistsfindByNameExists()EXISTS(n.name)
TruefindByActivatedIsTrue()n.activated = true
FalsefindByActivatedIsFalse()NOT(n.activated = true)
IsfindByNameIs(String name)n.name = name
NotNullfindByNameNotNull()NOT(n.name IS NULL)
NullfindByNameNull()n.name IS NULL
GreaterThanfindByScoreGreaterThan(double score)n.score > score
GreaterThanEqualfindByScoreGreaterThanEqual(double score)n.score >= score
LessThanfindByScoreLessThan(double score)n.score < score
LessThanEqualfindByScoreLessThanEqual(double score)n.score <= score
LikefindByNameLike(String name)n.name =~ name
NotLikefindByNameNotLike(String name)NOT(n.name =~ name)
NearfindByLocationNear(Distance distance, Point point)distance( point(n),point({latitude:lat, longitude:lon}) ) < distance
RegexfindByNameRegex(String regex)n.name =~ regex
AndfindByNameAndDescription(String name, String description)n.name = name AND n.description = description
OrfindByNameOrDescription(String name, String description)n.name = name OR n.description = description (Cannot be used to OR nested properties)

在这里插入图片描述

package com.tianju.mapper;

import com.tianju.entity.AgencyEntity;
import org.mapstruct.Mapper;
import org.springframework.data.neo4j.repository.Neo4jRepository;

/**
 * 网点的mapper,比如菜鸟驿站
 */
@Mapper
public interface AgencyMapper extends Neo4jRepository<AgencyEntity,Long> {
    /**
     * 根据bid 查询
     * @param bid 业务id
     * @return 网点数据
     */
    AgencyEntity findByBid(Long bid);


    /**
     * 根据bid删除
     *
     * @param bid 业务id
     * @return 删除的数据条数
     */
    Long deleteByBid(Long bid);
}

在这里插入图片描述

复杂查询

在这里插入图片描述

最短路径查询

//查询两个网点之间最短路径,查询深度最大为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<TransportLineNodeDTO> 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<String, Object> 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<String, Object> map = relationship.asMap();
                        Double cost = MapUtil.get(map, "cost", Double.class);
                        transportLineNodeDTO.setCost(cost + transportLineNodeDTO.getCost());
                    });

                    System.out.println("transportLineNodeDTO: "+transportLineNodeDTO);
                    return transportLineNodeDTO;
                });
        Optional<TransportLineNodeDTO> 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<TransportLineNodeDTO> 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<String, Object> 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<String, Object> 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进行复杂的查询;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

防止重复提交请求

前景提要&#xff1a; ts 简易封装 axios&#xff0c;统一 API 实现在 config 中配置开关拦截器 axios 实现请求 loading 效果 用一个数组保存当前请求的 url&#xff0c;此时还未响应。如果再次发起同样请求&#xff0c;比对 url 发现已经存在数组中&#xff0c;则拦截请求&a…

【PyQt学习篇 · ⑨】:QWidget -控件交互

文章目录 是否可用是否显示/隐藏是否编辑是否为活跃窗口关闭综合案例信息提示状态提示工具提示“这是什么”提示 焦点控制单个控件角度父控件角度 是否可用 setEnabled(bool)&#xff1a;该函数用于设置QWidget控件的可用性&#xff0c;参数bool为True表示该控件为可用状态&…

Spring底层原理(六)

Spring底层原理(六) 本章内容 介绍AOP的实现方式、JDK代理的模拟实现与源码 AOP的实现方式 使用代理模式 jdk动态代理cglib动态代理 使用aspectj的编译器&#xff0c;该编译器会直接对字节码进行修改&#xff0c;可以实现静态方法增强 使用javaagent,在jvm option中指定-…

FlinkCDC系列:通过skipped.operations参数选择性处理新增、更新、删除数据

在flinkCDC源数据配置&#xff0c;通过debezium.skipped.operations参数控制&#xff0c;配置需要过滤的 oplog 操作。操作包括 c 表示插入&#xff0c;u 表示更新&#xff0c;d 表示删除。默认情况下&#xff0c;不跳过任何操作&#xff0c;以逗号分隔。配置多个操作&#xff…

概念解析 | 神经网络中的位置编码(Positional Encoding)

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:Positional Encoding 神经网络中的位置编码(Positional Encoding) A Gentle Introduction to Positional Encoding in Transformer Models, Part 1 1.背景介绍 在自然语言处理任…

Zygote进程通信为什么用Socket而不是Binder?

Zygote进程是Android系统中的一个特殊进程&#xff0c;它在系统启动时被创建&#xff0c;并负责孵化其他应用进程。它的主要作用是预加载和共享应用进程的资源&#xff0c;以提高应用启动的速度。 在Android系统中&#xff0c;常用的进程通信方式有以下几种&#xff1a; Intent…

ABAP简单的队列设置QRFC

场景&#xff1a;用job的方式在接口里启用job&#xff0c;如果接口调用比较频繁&#xff0c;存在同一时间启动相同job的情况&#xff0c;会导致锁表锁程序这种情况。 查阅job函数&#xff0c;发现在JOB_CLOSE函数里自带了类似队列的参数&#xff0c;但是因为是接口&#xff0c…

文件夹还在,里面文件没了?问题这样解决

文件夹还在但文件无故消失怎么办&#xff1f;文件的消失对于我们来说可能是个令人沮丧且困惑的问题。有时候&#xff0c;我们可能会发现文件夹依然存在&#xff0c;但其中的文件却消失了。在这篇文章中&#xff0c;我们将探讨为什么电脑文件会无故消失的原因&#xff0c;并提供…

这个超实用的门禁技巧,让办公楼安全更简单高效!

门禁监控是现代社会中不可或缺的一部分&#xff0c;用于确保安全和管理进出某个区域的人员。随着科技的不断发展&#xff0c;门禁监控已经远离了传统的机械锁和钥匙&#xff0c;变得更加智能化和高效。 客户案例 企业办公大楼 无锡某大型企业在其办公大楼内部部署了泛地缘科技…

ChatGLM3设置角色和工具调用的解决方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

SpringMVC简单介绍与使用

目录 一、SpringMVC介绍 二、SpringMVC作用 三、SpringMVC核心组件 四、SpringMVC快速体验 一、SpringMVC介绍 Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称&#xff…

Node.js 中解析 HTML 的方法介绍

在 Web 开发中&#xff0c;解析 HTML 是一个常见的任务&#xff0c;特别是当我们需要从网页中提取数据或操作 DOM 时。掌握 Node.js 中解析 HTML 的各种方式&#xff0c;可以大大提高我们提取和处理网页数据的效率。本文将介绍如何在 Node.js 中解析 HTML。 基本概念 HTML 解析…

MySQL第三讲·SQL boy的CRUD操作

你好&#xff0c;我是安然无虞。 文章目录 增删查改&#xff1a;如何操作表中的数据&#xff1f;添加数据插入数据记录插入查询结果 删除数据修改数据查询数据select&#xff5c;where&#xff5c;group by&#xff5c;havingfromorder bylimit 增删查改&#xff1a;如何操作表…

第G7周:Semi-Supervised GAN 理论与实战

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f366; 参考文章&#xff1a;365天深度学习训练营-第G7周&#xff1a;Semi-Supervised GAN 理论与实战&#xff08;训练营内部成员可读&#xff09; &#x1f356; 原作者&#xff1a;K同学啊|接…

【蓝桥杯选拔赛真题44】python小蓝晨跑 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析

目录 python小蓝晨跑 一、题目要求 1、编程实现 2、输入输出 二、算法分析

作为一个初学者,该如何入门大模型?

在生成式 AI 盛行的当下&#xff0c;你是否被这种技术所折服&#xff0c;例如输入一段简简单单的文字&#xff0c;转眼之间&#xff0c;一幅精美的图片&#xff0c;又或者是文笔流畅的文字就展现在你的面前。 相信很多人有这种想法&#xff0c;认为生成式 AI 深不可测&#xf…

系列十五、idea全局配置

一、全局Maven配置 IDEA启动页面>Customize>All settings>Build,Execution,Deployment>Build Tools>Maven 二、全局编码配置 IDEA启动页面>Customize>All settings>Editor>File Encodings 三、全局激活DevTools配置 IDEA启动页面>Customize>A…

ASTM F963-23美国玩具安全新标准发布

新标准发布 2023年10月13日&#xff0c;美国材料与试验协会&#xff08;ASTM&#xff09;发布了新版玩具安全标准ASTM F963-23。 主要更新内容 与ASTM F963-17相比&#xff0c;此次更新包括&#xff1a;单独描述了基材重金属元素的豁免情况&#xff0c;更新了邻苯二甲酸酯的管控…

表格冻结第二行

在网上找了一圈也没找到说明白的。 像这样的表格下面会有很多行&#xff0c;往下翻的时候会忘记第二行显示的什么内容。 需求&#xff1a;将第二行进行冻结 实现&#xff1a; 1&#xff0c;选中第一和第二行&#xff0c;下拉&#xff0c;点击冻结 2&#xff0c;会显示冻结至…

MinIO多容器配置NGINX代理实践(docker-compose版本)

以下nginx配置 分别将本机的9001端口代理到minio1,minio2,minio3,minio4主机的9001端口。用于minio后台 分别将本机的9000端口代理到minio1,minio2,minio3,minio4主机的9000端口。用于minioApi events {worker_connections 1024; }http {upstream minio_console {server min…
最新文章