Neo4j深度学习

Neo4j的简介

Neo4j是用Java实现的开源NoSQL图数据库。从2003年开始开发,2007年正式发布第一版,其源码托管于GitHtb。Neo4j作为图数据库中的代表产品,已经在众多的行业项目中进行了应用,如:网络管理、软件分析、组织和项目管理、社交项目等方面。

Neo4j的安装(docker安装)

通过docker拉起镜像

docker pull neo4j:4.4.5

创建/data/neo4j文件夹,用于配置文件夹的挂载

kmdir /data/neo4j

启动docker容器(需要开启对应的映射端口,这里需要开放的端口就是 7474,7687)

docker run \
-d \
--restart=always \
--name neo4j \
-p 7474:7474 \
-p 7687:7687 \
-v neo4j:/data \
neo4j:4.4.5

neo4j控制面板的地址: http://localhost:7474/browser/

初始的账号为:neo4j,密码为:neo4j。

如果出现下面情况,说明neo4j设置了只应许本地连接。

需要进入容器中修改 neo4f.conf文件

进入neo4j容器

docker exec -it 对应的容器Id /bin/bash

进入/conf文件夹中

cd /conf

在容器中默认是没有安装vim的,所以我需要手动安装

apt-get update
apt-get install -y vim

修改neo4j.conf中的配置

将Bolt connetor的配置进行修改

dbms.connector.bolt.enabled=true
#dbms.connector.bolt.tls_level=DISABLED
dbms.connector.bolt.listen_address=对应Ip:7687 

Neo4j的使用

Neo4j的数据结构

  • 节点
    • 存储实体数据,在上图中,演员、电影都是节点。
    • 可以理解为关系型数据库中的表。
  • 关系
    • 存储节点之间的关系。
    • 关系只能有一个类型,必须有开始节点和结束节点以及指向。
    • 关系可以自我循环引用,但是两头永远不能为空。
  • 属性
    • 节点和关系都可以有属性,它是由键值对组成的。
    • 节点的属性可以理解为关系型数据库中的字段。
    • 关系中的属性进一步的明确了关系。
  • 标签
    • 标签是对节点的分类,这样使得构建Neo4j数据模型更加简单。
    • 在上面的电影案例中,Movie、Person就是标签。

Cypher使用

1.创建数据

//查询所有数据
MATCH (n) RETURN n
//强制删除所有节点和关系,慎用!
MATCH (n) DETACH DELETE n
CREATE (n {name: $value}) RETURN n   //创建节点,该节点具备name属性,n为该节点的变量,创建完成后返回该节点
CREATE (n:$Tag {name: $value}) //创建节点,指定标签
CREATE (n)-[r:KNOWS]->(m)  //创建n指向m的关系,并且指定关系类型为:KNOWS
                 
//示例
CREATE (n {name:'迪士尼营业部'})
CREATE (n:AGENCY {name:'航头营业部'})
//创建浦东新区转运中心、上海转运中心节点,并且创建关系为:IN_LINE,创建完成后返回节点和关系
//TLT -> Two Level Transport(二级转运中心)
//OLT -> One Level Transport(一级转运中心)
CREATE (n:TLT {name:'浦东新区转运中心'}) -[r:IN_LINE]-> (m:OLT {name:'上海转运中心'}) RETURN n,r,m
//关系也是可以反向,并且可以为关系中指定属性
CREATE (n:TLT {name:'浦东新区转运中心'}) <-[r:OUT_LINE]- (m:OLT {name:'上海转运中心'}) RETURN n,r,m

2.查询数据

查询数据的格式为下:

[MATCH WHERE]  //条件查询
[WITH [ORDER BY] [SKIP] [LIMIT]] //查询的结果以管道的形式传递给下面的语句,聚合查询必须使用WITH
RETURN [ORDER BY] [SKIP] [LIMIT] //返回、排序、跳过、返回个数

插入数据进行测试

CREATE (北京市转运中心:OLT {bid: 8001, name: "北京市转运中心", address: "北京市转运中心", location : point({latitude:39.904179, longitude:116.407387})})
CREATE (上海市转运中心:OLT {bid: 8002, name: "上海市转运中心", address: "上海市转运中心", location : point({latitude:31.230525, longitude:121.473667})})
CREATE (南京市转运中心:OLT {bid: 8003, name: "南京市转运中心", address: "南京市转运中心", location : point({latitude:32.059344, longitude:118.796624})})
CREATE (太原市转运中心:OLT {bid: 8004, name: "太原市转运中心", address: "太原市转运中心", location : point({latitude:37.870451, longitude:112.549656})})
CREATE (郑州市转运中心:OLT {bid: 8005, name: "郑州市转运中心", address: "郑州市转运中心", location : point({latitude:34.745551, longitude:113.624321})})
CREATE
    (北京市转运中心)-[:IN_LINE {cost:10684.9}]->(上海市转运中心),
    (北京市转运中心)<-[:OUT_LINE {cost:10684.9}]-(上海市转运中心),
    (北京市转运中心)-[:IN_LINE {cost:8993.1}]->(南京市转运中心),
    (北京市转运中心)<-[:OUT_LINE {cost:8993.1}]-(南京市转运中心),
    (南京市转运中心)-[:IN_LINE {cost:2699.4}]->(上海市转运中心),
    (南京市转运中心)<-[:OUT_LINE {cost:2699.4}]-(上海市转运中心),
    (太原市转运中心)-[:IN_LINE {cost:3609.7}]->(郑州市转运中心),
    (太原市转运中心)<-[:OUT_LINE {cost:3609.7}]-(郑州市转运中心),
    (郑州市转运中心)-[:IN_LINE {cost:5659.7}]->(南京市转运中心),
    (郑州市转运中心)<-[:OUT_LINE {cost:5659.7}]-(南京市转运中心)
CREATE (昌平区转运中心:TLT {bid: 90001, name: "昌平区转运中心", address: "昌平区转运中心", location : point({latitude:40.220952, longitude:116.231034})})
CREATE (北京市昌平区新龙城:AGENCY {bid: 100260, name: "北京市昌平区新龙城", address: "龙跃苑四区3号楼底商", phone : "010-53049073,010-53576707", location : point({latitude:40.07544443596149, longitude:116.3470535709328})})
CREATE
    (北京市昌平区新龙城)-[:IN_LINE {cost:189.7}]->(昌平区转运中心),
    (北京市昌平区新龙城)<-[:OUT_LINE {cost:189.7}]-(昌平区转运中心)
CREATE (北京市昌平区定泗路:AGENCY {bid: 100280, name: "北京市昌平区定泗路", address: "北七家镇定泗路苍龙街交叉口", phone : "010-86392987", location : point({latitude:40.11765281246394, longitude:116.37212849638287})})
CREATE
    (北京市昌平区定泗路)-[:IN_LINE {cost:166.2}]->(昌平区转运中心),
    (北京市昌平区定泗路)<-[:OUT_LINE {cost:166.2}]-(昌平区转运中心)
CREATE (海淀区转运中心:TLT {bid: 90002, name: "海淀区转运中心", address: "海淀区转运中心", location : point({latitude:39.959893, longitude:116.2977})})
CREATE (北京市海淀区小营:AGENCY {bid: 100347, name: "北京市海淀区小营", address: "北京市昌平区回龙观街道金燕龙大厦停车场", phone : "010-86483817,010-86483817,010-86483817", location : point({latitude:40.06177798692319, longitude:116.32706587559049})})
CREATE
    (北京市海淀区小营)-[:IN_LINE {cost:116.1}]->(海淀区转运中心),
    (北京市海淀区小营)<-[:OUT_LINE {cost:116.1}]-(海淀区转运中心)
CREATE (北京市海淀区万泉河:AGENCY {bid: 100227, name: "北京市海淀区万泉河", address: "北京市海淀区四季青镇杏石口路47号院", phone : "18521852356", location : point({latitude:39.94882822425318, longitude:116.25707017441161})})
CREATE
    (北京市海淀区万泉河)-[:IN_LINE {cost:36.8}]->(海淀区转运中心),
    (北京市海淀区万泉河)<-[:OUT_LINE {cost:36.8}]-(海淀区转运中心)
CREATE
    (昌平区转运中心)-[:IN_LINE {cost:383.3}]->(北京市转运中心),
    (昌平区转运中心)<-[:OUT_LINE {cost:383.3}]-(北京市转运中心),
    (海淀区转运中心)-[:IN_LINE {cost:112.3}]->(北京市转运中心),
    (海淀区转运中心)<-[:OUT_LINE {cost:112.3}]-(北京市转运中心)
CREATE (浦东新区转运中心:TLT {bid: 90003, name: "浦东新区转运中心", address: "浦东新区转运中心", location : point({latitude:31.221461, longitude:121.544346})})
CREATE (上海市浦东新区南汇:AGENCY {bid: 210057, name: "上海市浦东新区南汇", address: "园春路8号", phone : "18821179169", location : point({latitude:31.035240152911637, longitude:121.73459966751048})})
CREATE
    (上海市浦东新区南汇)-[:IN_LINE {cost:275.4}]->(浦东新区转运中心),
    (上海市浦东新区南汇)<-[:OUT_LINE {cost:275.4}]-(浦东新区转运中心)
CREATE (上海市浦东新区周浦:AGENCY {bid: 210127, name: "上海市浦东新区周浦", address: "川周公路3278-8号", phone : "021-68060322", location : point({latitude:31.132409729356993, longitude:121.59815370294322})})
CREATE
    (上海市浦东新区周浦)-[:IN_LINE {cost:111.6}]->(浦东新区转运中心),
    (上海市浦东新区周浦)<-[:OUT_LINE {cost:111.6}]-(浦东新区转运中心)
CREATE (奉贤区转运中心:TLT {bid: 90004, name: "奉贤区转运中心", address: "奉贤区转运中心", location : point({latitude:30.918406, longitude:121.473945})})
CREATE (上海市奉贤区东部:AGENCY {bid: 210017, name: "上海市奉贤区东部", address: "上上海市奉贤区洪庙镇洪兰路351", phone : "021-57171717", location : point({latitude:30.917752751719863, longitude:121.67587819184698})})
CREATE
    (上海市奉贤区东部)-[:IN_LINE {cost:192.9}]->(奉贤区转运中心),
    (上海市奉贤区东部)<-[:OUT_LINE {cost:192.9}]-(奉贤区转运中心)
CREATE (上海市奉贤区青村:AGENCY {bid: 210442, name: "上海市奉贤区青村", address: "姚家村1127号", phone : "021-57566663,021-57566606", location : point({latitude:30.916946897994983, longitude:121.57954144207972})})
CREATE
    (上海市奉贤区青村)-[:IN_LINE {cost:100.9}]->(奉贤区转运中心),
    (上海市奉贤区青村)<-[:OUT_LINE {cost:100.9}]-(奉贤区转运中心)
CREATE
    (浦东新区转运中心)-[:IN_LINE {cost:68.0}]->(上海市转运中心),
    (浦东新区转运中心)<-[:OUT_LINE {cost:68.0}]-(上海市转运中心),
    (奉贤区转运中心)-[:IN_LINE {cost:347.4}]->(上海市转运中心),
    (奉贤区转运中心)<-[:OUT_LINE {cost:347.4}]-(上海市转运中心)
CREATE (玄武区转运中心:TLT {bid: 90004, name: "玄武区转运中心" , location : point({latitude:32.048644, longitude:118.797779})})
CREATE (江苏省南京市玄武区紫金墨香苑:AGENCY {bid: 25073, name: "江苏省南京市玄武区紫金墨香苑", address: "栖霞区燕尧路100号", phone : "025-58765331,025-83241955,025-83241881", location : point({latitude:32.117016089520305, longitude:118.86319310255513})})
CREATE
    (江苏省南京市玄武区紫金墨香苑)-[:IN_LINE {cost:98.0}]->(玄武区转运中心),
    (江苏省南京市玄武区紫金墨香苑)<-[:OUT_LINE {cost:98.0}]-(玄武区转运中心)
CREATE (江苏省南京市玄武区长江路:AGENCY {bid: 25023, name: "江苏省南京市玄武区长江路", address: "观音阁10号", phone : "18521133265,18695799166", location : point({latitude:32.04803554410631, longitude:118.79190455263355})})
CREATE
    (江苏省南京市玄武区长江路)-[:IN_LINE {cost:5.6}]->(玄武区转运中心),
    (江苏省南京市玄武区长江路)<-[:OUT_LINE {cost:5.6}]-(玄武区转运中心)
CREATE
    (玄武区转运中心)-[:IN_LINE {cost:12.0}]->(南京市转运中心),
    (玄武区转运中心)<-[:OUT_LINE {cost:12.0}]-(南京市转运中心)
CREATE (小店区转运中心:TLT {bid: 90005, name: "小店区转运中心" , location : point({latitude:37.736865, longitude:112.565524})})
CREATE (山西省太原市青龙:AGENCY {bid: 351068, name: "山西省太原市青龙", address: "长治路33号经典家园停车场内13号商铺", phone : "0351-2025888", location : point({latitude:37.83589608758359, longitude:112.56059258109424})})
CREATE
    (山西省太原市青龙)-[:IN_LINE {cost:110.3}]->(小店区转运中心),
    (山西省太原市青龙)<-[:OUT_LINE {cost:110.3}]-(小店区转运中心)
CREATE (山西省太原市长风街:AGENCY {bid: 351045, name: "山西省太原市长风街", address: "平阳路104号省农机公司院内", phone : "18636100730", location : point({latitude:37.809964384001226, longitude:112.55299317699505})})
CREATE
    (山西省太原市长风街)-[:IN_LINE {cost:82.1}]->(小店区转运中心),
    (山西省太原市长风街)<-[:OUT_LINE {cost:82.1}]-(小店区转运中心)
CREATE
    (小店区转运中心)-[:IN_LINE {cost:149.4}]->(太原市转运中心),
    (小店区转运中心)<-[:OUT_LINE {cost:149.4}]-(太原市转运中心)
CREATE (中原区转运中心:TLT {bid: 90006, name: "中原区转运中心" , location : point({latitude:34.74828, longitude:113.612966})})
CREATE (河南省郑州市郑上路:AGENCY {bid: 371067, name: "河南省郑州市郑上路", address: "中原西路西四环西北角", phone : "0371-55116757,0371-68014786", location : point({latitude:34.74753024533005, longitude:113.57428550005442})})
CREATE
    (河南省郑州市郑上路)-[:IN_LINE {cost:35.4}]->(中原区转运中心),
    (河南省郑州市郑上路)<-[:OUT_LINE {cost:35.4}]-(中原区转运中心)
CREATE (河南省郑州市颍河路:AGENCY {bid: 371086, name: "河南省郑州市颍河路", address: "航海西路与西三环交叉口向南300米路西中贸商务", phone : "19139415556", location : point({latitude:34.71593280680163, longitude:113.60398506929064})})
CREATE
    (河南省郑州市颍河路)-[:IN_LINE {cost:36.9}]->(中原区转运中心),
    (河南省郑州市颍河路)<-[:OUT_LINE {cost:36.9}]-(中原区转运中心)
CREATE
    (中原区转运中心)-[:IN_LINE {cost:11.5}]->(郑州市转运中心),
    (中原区转运中心)<-[:OUT_LINE {cost:11.5}]-(郑州市转运中心)

基础查询

MATCH (n) RETURN n  //查询所有的数据,数据量大是勿用
MATCH (n:AGENCY) RETURN n  //查询所有的网点(AGENCY)
MATCH (n:OLT {name: "北京市转运中心"}) -- (m) RETURN n,m //查询所有与"北京市转运中心"有关系的节点
MATCH (n:OLT {name:"北京市转运中心"}) --> (m:OLT) RETURN n,m //查询所有"北京市转运中心"关联的一级转运中心
MATCH (n:OLT {name:"北京市转运中心"}) -[r:IN_LINE]- (m) RETURN n,r,m //可以指定关系标签查询
MATCH p = (n:OLT {name:"北京市转运中心"}) --> (m:OLT) RETURN p //将查询赋值与变量
//通过 type()函数查询关系类型
MATCH (n:OLT {name:"北京市转运中心"}) -[r]-> (m:OLT {name:"南京市转运中心"}) RETURN type(r)

关系深度查询

//查询【北京市转运中心】关系中深度为1~2层关系的节点
MATCH (n:OLT {name:"北京市转运中心"}) -[*1..2]->(m) RETURN *
//也可以这样
MATCH (n:OLT {name:"北京市转运中心"}) -[*..2]->(m) RETURN *
//也可以通过变量的方式查询
MATCH path = (n:OLT {name:"北京市转运中心"}) -[*..2]->(m)
RETURN path
//查询关系,relationships()获取结果中的关系,WITH向后传递数据
MATCH path = (n:OLT {name:"北京市转运中心"}) -[*..2]->(m)
WITH n,m, relationships(path) AS r
RETURN r
//查询两个网点之间所有的路线,最大深度为6,可以查询到2条路线
MATCH path = (n:AGENCY) -[*..6]->(m:AGENCY)
WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"
RETURN path


重点:查询两地的最最短距离
//查询两个网点之间最短路径,查询深度最大为10
MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))
WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"
RETURN path


//查询两个网点之间所有的路线中成本最低的路线,最大深度为10(如果成本相同,转运节点最少)
MATCH path = (n:AGENCY) -[*..10]->(m:AGENCY)
WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"
UNWIND relationships(path) AS r //unwind将列表变成单独的一行一行的形式,就类是进行循环
WITH sum(r.cost) AS cost, path
RETURN path ORDER BY cost ASC, LENGTH(path) ASC LIMIT 1
//UNWIND是将列表数据展开操作
//sum()是聚合统计函数,类似还有:avg()、max()、min()等

分页查询

//分页查询网点,按照bid正序排序,每页查询2条数据,第一页
MATCH (n:AGENCY) 
RETURN n ORDER BY n.bid ASC SKIP 0 LIMIT 2
//第二页
MATCH (n:AGENCY)  skip就是跳过的个数,这里的效果就是查询第二页
RETURN n ORDER BY n.bid ASC SKIP 2 LIMIT 2
//......

3.更新数据

// 更新/设置 属性
MATCH (n:AGENCY {name:"北京市昌平区新龙城"})
SET n.address = "龙跃苑四区3号楼底商101号"
RETURN n
//通过remove移除属性
MATCH (n:AGENCY {name:"北京市昌平区新龙城"}) REMOVE n.address RETURN n
//没有address属性的增加属性
MATCH (n:AGENCY) WHERE n.address IS NULL SET n.address = "暂无地址" RETURN n

4.删除数据

//删除节点
MATCH (n:AGENCY {name:"航头营业部"}) DELETE n
//有关系的节点是不能直接删除的
MATCH (n:AGENCY {name:"北京市昌平区新龙城"}) DELETE n
//删除节点和关系,强制删除
MATCH (n:AGENCY {name:"北京市昌平区新龙城"}) DETACH DELETE n
//删除所有节点和关系,慎用!
MATCH (n) DETACH DELETE n

5.索引

// 更新/设置 属性
MATCH (n:AGENCY {name:"北京市昌平区新龙城"})
SET n.address = "龙跃苑四区3号楼底商101号"
RETURN n
//通过remove移除属性
MATCH (n:AGENCY {name:"北京市昌平区新龙城"}) REMOVE n.address RETURN n
//没有address属性的增加属性
MATCH (n:AGENCY) WHERE n.address IS NULL SET n.address = "暂无地址" RETURN n

SDN(Spring Data Neo4k)使用

导入依赖

<!--SDN依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-neo4j</artifactId>
        </dependency>

编写application.yml

java 复制代码
server:
  port: 9902
spring:
  application:
    name: sl-express-sdn
  data:
    neo4j:
      database: neo4j
  neo4j:
    authentication:
      username: neo4j
      password: neo4j123
    uri: neo4j://192.168.150.101:7687 #对应的neo4j的地址

使用SDN提供的 Repository

SDN也是遵循了Spring Data JPA规范,同时也提供了Neo4jRepository,该接口中提供了基本的CRUD操作,我们定义Repository需要继承该接口,实现类会自动生成并配置到ioc,后续使用直接从ioc中获取即可。

我们编写Repository时只需要继承Neo4jRepository接口即可,在Neo4jRepository中提供了增删改查等一系列简单的操作方法。

实现Neo4jRepository的格式为下:

java 复制代码
import com.sl.ms.Entitys.AgencyEntity;
import org.springframework.data.neo4j.repository.Neo4jRepository;

/**
 * 网点操作
 */
//Neo4jRepository需要提供: 实体类和Id的类型
public interface AgencyRepository extends Neo4jRepository<AgencyEntity, Long> {

}

Neo4jRepository需要提供: 实体类和Id的类型。

在SDK中只要我们使用符合规范的名命,SDN就可以自动实现对应的方法(有点类似mybatis-plus)。

对应名字规范为下:

例子为下:

java 复制代码
import com.sl.ms.Entitys.AgencyEntity;
import org.springframework.data.neo4j.repository.Neo4jRepository;

/**
 * 网点操作
 */
public interface AgencyRepository extends Neo4jRepository<AgencyEntity, Long> {
    /**
     * 根据bid查询
     *
     * @param bid 业务id
     * @return 网点数据
     */
    AgencyEntity findByBid(Long bid);
    /**
     * 根据bid删除
     *
     * @param bid 业务id
     * @return 删除的数据条数
     */
    Long deleteByBid(Long bid);
}

进行测试

测试代码为下:

java 复制代码
import com.sl.ms.Entitys.AgencyEntity;
import com.sl.ms.Repository.AgencyRepository;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.*;
import javax.annotation.Resource;
import java.util.List;

@SpringBootTest
class AgencyRepositoryTest {
    @Resource
    private AgencyRepository agencyRepository;
    @Test
    public void testSave() {
        AgencyEntity agencyEntity = new AgencyEntity();
        agencyEntity.setAddress("测试数据地址");
        agencyEntity.setBid(9001L);
        agencyEntity.setName("测试节点");
        agencyEntity.setPhone("1388888888888");
        this.agencyRepository.save(agencyEntity);
        System.out.println(agencyEntity);
    }
}

测试结果为下:

复杂查询(自定义cql)

例子(就是获取两地的最短距离)

java 复制代码
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 com.sl.ms.Entitys.AgencyEntity;
import com.sl.ms.Repository.TransportLineRepository;
import com.sl.ms.dto.OrganDTO;
import com.sl.ms.dto.TransportLineNodeDTO;
import com.sl.ms.enums.OrganTypeEnum;
import org.neo4j.driver.internal.InternalPoint2D;
import org.neo4j.driver.types.Path;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;

@Component
public class TransportLineRepositoryImpl implements TransportLineRepository {
    @Resource
    private Neo4jClient neo4jClient;
    /**
     * 根据起止网点  查询转运路线最短的路线信息
     * @param start 开始网点
     * @param end   结束网点
     * @return
     */

    //查询的CQL语句为: (最好不用进行拼接,直接用一个完整的字符串)
//    MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY))
//    WHERE n.name = "北京市昌平区定泗路" AND m.name = "上海市浦东新区南汇"
//    RETURN path
    @Override
    public TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) {
        //获取网点数据在Neo4j中的类型,获取@Node中的属性值,这样可以动态获取节点的标签
        //这里的标签有: 一级转运中心,二级转运中心,站点
        String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];
        //构造CQL 查询语句
        String Cql = StrUtil.format("MATCH path = shortestPath((n:{}) -[*..10]->(m:{})) WHERE n.bid=$startId AND m.bid=$endId RETURN path",type, type);
        //执行查询
        Optional<TransportLineNodeDTO> one = this.neo4jClient.query(Cql)
                //       绑定参数, cql中使用$绑定变量
                .bind(start.getBid()).to("startId")
                .bind(end.getBid()).to("endId")
                //       设置响应类型,最终返回的类型,这里我们需要返回
                .fetchAs(TransportLineNodeDTO.class)
                //            mappedBy 设置结果映射
                .mappedBy((typeSystem, record) -> {
                    //record中就是查询的结果(一条最短路线),获取第一个参数,也就是path
                    // 获取0号下标的 Path路径
                    Path path = record.get(0).asPath();
                    // 创建路线对象 TransportLineNodeDTO
                    TransportLineNodeDTO transportLineNodeDTO = new TransportLineNodeDTO();
                    path.nodes().forEach(node -> {
                        // 遍历路径下的所有节点
                        Map<String, Object> nodeMap = node.asMap();
                        // 将每一个节点信息封装成一个OrganDTO
                        // 通过节点得到参数map,将map转为OrganDTO   tips: BeanUtil
                        OrganDTO organDTO = BeanUtil.toBeanIgnoreError(nodeMap, OrganDTO.class);
                        //此时organDTO中的type和经纬度都是null,我们需要为其赋值
                        //标签会存在多个,这里我们就取第一个
                        String title = CollUtil.getFirst(node.labels());
                        // OrganDTO设置类型: 取Node中第一个标签作为类型   tips: OrganTypeEnum
                        organDTO.setType(OrganTypeEnum.valueOf(title).getCode());
                        // OrganDTO设置经纬度: 取出经纬度    经纬度类: InternalPoint2D
                        //从Map中获取经纬度
                        InternalPoint2D location = MapUtil.get(nodeMap, "location", InternalPoint2D.class);
                        organDTO.setLatitude(location.x());//设置纬度,为x
                        organDTO.setLongitude(location.y());//设置经度,为y
                        // 存入到 路线对应的节点集合中
                        transportLineNodeDTO.getNodeList().add(organDTO);
                    });
                    path.relationships().forEach(relationship -> {
                        // 遍历路径下的所有关系
                        // 将每个路线的成本加一起 得到路线总成本 保留2位小数
                        Map<String, Object> relationshipMap = relationship.asMap();
                        //获取成本
                        Double cost = MapUtil.get(relationshipMap, "cost", Double.class);
                        //将成本进行累加
                        transportLineNodeDTO.setCost(transportLineNodeDTO.getCost() + cost);
                    });
                    // 返回路线数据
                    return transportLineNodeDTO;
                }).one();//返回可能会有多个,我们直接取一个即可
        return one.orElse(null); //不会空时返回数据,为空时则返回null
    }
}

进行测试

java 复制代码
import com.sl.ms.Entitys.AgencyEntity;
import com.sl.ms.Repository.TransportLineRepository;
import com.sl.ms.dto.TransportLineNodeDTO;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
@SpringBootTest
class TransportLineRepositoryTest {
    @Resource
    private TransportLineRepository transportLineRepository;
    @Test
    void findShortestPath() {
        AgencyEntity start = AgencyEntity.builder().bid(100280L).build();
        AgencyEntity end = AgencyEntity.builder().bid(210057L).build();
        TransportLineNodeDTO transportLineNodeDTO = this.transportLineRepository.findShortestPath(start, end);
        System.out.println(transportLineNodeDTO);
    }
}

最终成功获取到最短的路径和费用。

相关推荐
engchina2 天前
Neo4j数据库清理指南:如何安全地删除所有节点和索引
数据库·neo4j
麦麦大数据3 天前
vue+django+neo4j航班智能问答知识图谱可视化系统
django·vue·echarts·neo4j·智能问答·ltp·航班
engchina4 天前
docker 启动 neo4j
docker·容器·neo4j
阿梦Anmory6 天前
[neo4j报错]py2neo.errors.ClientError: [Request.Invalid] Not Found解决方案
neo4j
lisacumt7 天前
【neo4j】 neo4j cypher单一语句 optional 可选操作的技巧
neo4j
qa浪涛10 天前
基于知识图谱的教学案例问答系统
人工智能·django·知识图谱·neo4j
qa浪涛11 天前
基于知识图谱的美食推荐系统
人工智能·python·django·知识图谱·neo4j·美食
qa浪涛12 天前
基于neo4j的医疗问诊系统
python·django·知识图谱·neo4j
qa浪涛12 天前
基于neo4j的新冠治疗和新冠患者轨迹的知识图谱问答系统
人工智能·python·django·知识图谱·neo4j·1024程序员节
qa浪涛12 天前
基于neo4j的体育运动员问答问答系统
人工智能·django·知识图谱·neo4j