Neo4j-SpringBoot简单操作

  • Neo4j
  • Neo4j安装
  • 数据导入Neo4j
  • SpringBoot 整合neo4j
  • Neo4j 字符串转列表
  • 持续学习


Neo4j

Neo4j是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎,但是它将结构化数据存储在网络(从数学角度叫做图)上而不是表中。Neo4j也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。程序员工作在一个面向对象的、灵活的网络结构下而不是严格、静态的表中——但是他们可以享受到具备完全的事务特性、企业级的数据库的所有好处。
Neo4j图数据库遵循属性图模型来存储和管理其数据。属性图由点(节点)、边(关系)和属性三者组成。可以为点设置不同标签,边也可以分为很多种类型。点和边可以有多个属性,属性以kv键值对的方式表示。边是必需有方向的,neo4j边只能是单向的,边的方向不影响查询的效率。
Neo4j是一个无架构数据库。在开始添加数据之前,你并不需要定义表和关系。一个节点可以具有你喜欢的任何属性,任何节点都可以与其他任何节点建立关系。Neo4j数据库中的数据模型隐含在它存储的数据中,而不是明确地将数据模型定义为数据库本身的一个部分。它是对你想要存入数据库的数据的一个描述,而不是数据库的一系列方法来限制将要存储的内容。

Neo4j安装

Neo4j官方下载,小提示:

  1. Neo4j需要合适版本的jdk支持(neo4j 4以后的版本需求jdk11以上,亲测jdk17不行);
  2. neo4j启动直接用neo4j.bat console即可,无需下载本地服务;
  3. 按照官方建议下载jdk11和neo4j 4版本可能更佳(笔者是用的jdk8和neo4j 3.53);
  4. 详细安装步骤网上很多,这里就不赘述了;

数据导入Neo4j

导入方式:

CREATE语句

LOAD CSV语句

Batch Inserter

Batch Import

Neo4j-import

适用场景

1 ~ 1w nodes

1w ~ 10 w nodes

千万以上 nodes

千万以上 nodes

千万以上 nodes

速度

很慢 (1000 nodes/s)

一般 (5000 nodes/s)

非常快 (数万 nodes/s)

非常快 (数万 nodes/s)

非常快 (数万 nodes/s)

优点

使用方便,可实时插入

使用方便,可以加载本地/远程CSV;可实时插入

速度相比于前两个,有数量级的提升

基于Batch Inserter,可以直接运行编译好的jar包;可以在已存在的数据库中导入数据

官方出品,比Batch Import占用更少的资源

缺点

速度慢

需要将数据转换成CSV

需要转成CSV;只能在JAVA中使用;且插入时必须停止neo4j

需要转成CSV;必须停止neo4j

需要转成CSV;必须停止neo4j;只能生成新的数据库,而不能在已存在的数据库中插入数据。

导入数据:

neo4j-admin import --database demo.db --nodes:Company test_5.csv

spring data neo4j配置 springboot整合neo4j_数据库


修改neo4j配置文件:

spring data neo4j配置 springboot整合neo4j_数据库_02


打开neo4j浏览器可视化界面查看数据:

spring data neo4j配置 springboot整合neo4j_数据库_03

SpringBoot 整合neo4j

  • 小提示:
  • Springboot有jar包内置了neo4j bolt 驱动,且方便调试维护
  • Springboot版本必须和neo4j版本对应,否则依赖jar包会有问题
  • 这里使用的neo4j版本为3.5.34(4.*版本需要11及以后的jdk,但是我的17版本不行)
  • 一定要修改Spirngboot的版本号!
  • version
<parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.3.5.RELEASE</version>
      <relativePath/>
</parent>
  • entity
package com.zzx.neo4jspringboot4.entity;

import lombok.Data;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Property;

import java.io.Serializable;
import java.util.List;

@Data
@NodeEntity(label = "Company")
public class Company implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Property
    private String PID;

    @Property
    private String district;

    @Property
    private String ENTNAME;

    @Property
    private String short_name;

    @Property
    private String DOM;

    @Property
    private List<String> CTBRANCHINFO;

    @Property
    private List<String> INVEST;

    @Property
    private List<String> SHAREHOLDER;

    @Property
    private List<String> PERSONLIST;

    @Property
    private String PERSONKVS;

}
package com.zzx.neo4jspringboot4.entity;

import lombok.Data;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Property;

import java.io.Serializable;

@Data
@NodeEntity(label = "Person")
public class Person implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @Property
    private String name;

}
package com.zzx.neo4jspringboot4.entity;


import lombok.Data;
import org.neo4j.ogm.annotation.*;

import java.io.Serializable;

@Data
@RelationshipEntity(type = "PersonRelation")
public class PersonRelation implements Serializable {

    @Id
    @GeneratedValue
    private Long id;

    @StartNode
    private Person parent;

    @EndNode
    private  Company child;

    @Property
    private  String relation;
}
package com.zzx.neo4jspringboot4.entity;

import lombok.Data;

import org.neo4j.ogm.annotation.*;
import org.springframework.stereotype.Component;

import java.util.List;


@Data
@RelationshipEntity(type = "同名高管")
public class Relation {

    @Id
    @GeneratedValue
    private Long id;

    @StartNode
    private Company parent;

    @EndNode
    private  Company child;

    @Property
    private List<String > PERSONS;
}
  • dao
package com.zzx.neo4jspringboot4.dao;

import com.zzx.neo4jspringboot4.entity.Company;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Repository;


@Repository
public interface CompanyRepository extends Neo4jRepository<Company,Long> {

    @Query("match(n:Company{PID:{PID}}) return n.ENTNAME")
    String existsPID(String PID);

    @Query("MATCH (n:Company) WITH n AS n,(CASE WHEN apoc.meta.type(n.CTBRANCHINFO)=\"STRING\" and n.CTBRANCHINFO is not null THEN apoc.convert.fromJsonList(n.CTBRANCHINFO) ELSE n.CTBRANCHINFO END) AS CTBRANCHINFO,(CASE WHEN apoc.meta.type(n.INVEST)=\"STRING\" and n.INVEST is not null THEN apoc.convert.fromJsonList(n.INVEST) ELSE n.INVEST END) AS INVEST,(CASE WHEN apoc.meta.type(n.SHAREHOLDER)=\"STRING\" and n.SHAREHOLDER is not null THEN apoc.convert.fromJsonList(n.SHAREHOLDER) ELSE n.SHAREHOLDER END) AS SHAREHOLDER,(CASE WHEN apoc.meta.type(n.PERSONLIST)=\"STRING\" and n.PERSONLIST is not null THEN apoc.convert.fromJsonList(n.PERSONLIST) ELSE n.PERSONLIST END) AS PERSONLIST SET n.CTBRANCHINFO =CTBRANCHINFO,n.INVEST=INVEST,n.PERSONLIST=PERSONLIST,n.SHAREHOLDER=SHAREHOLDER")
    void toArray();

  
    @Query("MATCH (c:Company),(m:Company) WHERE c.short_name=m.short_name and c.PID<>m.PID and NOT (m)-[:相同简称]->(c) CREATE (c) -[r:相同简称{type:0,rel:0.7}]->(m)")
    void creatRelation_short_name();

  
    @Query("MATCH (c:Company),(m:Company) WHERE c.DOM=m.DOM and c.PID<>m.PID and NOT (m)-[:相同工商注册地址]->(c) CREATE (c) -[r:相同工商注册地址{type:0,rel:0.8}]->(m)")
    void creatRelation_DOM();

 
    @Query("MATCH (c:Company),(m:Company) WHERE c.ENTNAME IN m.CTBRANCHINFO and c.PID<>m.PID CREATE (c) -[r:分支机构{type:0,rel:1}]->(m)")
    void creatRelation_CTBRANCHINFO();

 
    @Query("MATCH (c:Company),(m:Company) WHERE c.ENTNAME IN m.INVEST and c.PID<>m.PID CREATE (m) -[r:对外投资{type:0,rel:1}]->(c)")
    void creatRelation_INVEST();

   
    @Query("MATCH (c:Company),(m:Company) WHERE c.ENTNAME IN m.SHAREHOLDER and c.PID<>m.PID CREATE (c) -[r:股东{type:0,rel:1}]->(m)")
    void creatRelation_SHAREHOLDER();


 
    @Query("MATCH (c:Company),(m:Company) WHERE c.PERSONLIST is not null and m.PERSONLIST is not null and c.PID<>m.PID and length(c.PERSONLIST)>0 and length(m.PERSONLIST)>0 WITH c AS c,m AS m, (FILTER( x in c.PERSONLIST WHERE x in m.PERSONLIST and x<>\"NULL\")) AS list WHERE length(list)>0 and NOT (m)-[:同名高管]->(c) WITH c AS c,m AS m, (CASE WHEN (CASE WHEN (c)-[]-(m) THEN 1 ELSE 0 END)+length(list)+(CASE WHEN c.district = m.district THEN 1 ELSE 0 END)>1 THEN 1 ELSE 2 END) AS type,list AS list CREATE (c)-[r:同名高管]->(m) SET r.type =type,r.PERSONS=list")
    void creatRelation_PERSONS();

  
    @Query("MATCH (c:Company)-[r]->(m:Company) WHERE r.type<>2 and NOT (m)-[:直接关系]-(c) MERGE (c) -[re:直接关系]->(m)")
    void creatRelation_DIRECT();


    @Query("MATCH (c:Company)-[r:同名高管{type:2}]->(m:Company) WHERE (c)-[:直接关系*2..6]-(m) SET r.type = 1")
    void changeType();

}
package com.zzx.neo4jspringboot4.dao;

import com.zzx.neo4jspringboot4.entity.PersonRelation;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Repository;


@Repository
public interface PersonRelationRepository extends Neo4jRepository<PersonRelation,Long> {
}
package com.zzx.neo4jspringboot4.dao;

import com.zzx.neo4jspringboot4.entity.Person;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface PersonRepository extends Neo4jRepository<Person,Long> {

    @Query("MATCH (n:Person{name:{name}}) RETURN count(n)>0")
    Boolean findByName(@Param("name") String name);
}
package com.zzx.neo4jspringboot4.dao;

import com.zzx.neo4jspringboot4.entity.Relation;
import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;

@Repository
public interface RelationRepository extends Neo4jRepository<Relation,Long> {

    @Query("MATCH (c:Company)-[r:同名高管{type:1}]-(m:Company) RETURN r,startNode(r),endNode(r)")
    ArrayList<Relation> getRelationType1();

    @Query("MATCH (c:Company)-[r:直接关系]-(m:Company) DELETE r")
    void deleteRelation_DIRECT();

    @Query("MATCH (c:Company)-[r:直接关系]-(m:Company) DELETE r")
    void  deleteType1();
}
  • Test
package com.zzx.neo4jspringboot4;

import com.zzx.neo4jspringboot4.dao.CompanyRepository;
import com.zzx.neo4jspringboot4.dao.PersonRelationRepository;
import com.zzx.neo4jspringboot4.dao.PersonRepository;
import com.zzx.neo4jspringboot4.dao.RelationRepository;
import com.zzx.neo4jspringboot4.entity.Company;
import com.zzx.neo4jspringboot4.entity.Person;
import com.zzx.neo4jspringboot4.entity.PersonRelation;
import com.zzx.neo4jspringboot4.entity.Relation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.alibaba.fastjson.JSONObject;



@SpringBootTest
public class DataTest {

    @Autowired
    CompanyRepository companyRepository;

    @Autowired
    RelationRepository relationRepository;

    @Autowired
    PersonRepository personRepository;

    @Autowired
    PersonRelationRepository personRelationRepository;

    @Test
    public void fun(){
        System.out.println("neo4j ,启动!!!");
        int start = 0;
        int end = 10;
        for(int i =start;i<=end;i++){
            long startTime = new Date().getTime();
            cypherWith(i);
            long time = new Date().getTime()-startTime;
            System.out.println("===================已完成第--"+i+"--步,耗时:"+time+"毫秒===================");
        }
    }

    private void cypherWith(int i){
        switch (i){
            case 0:
                companyRepository.toArray();
                break;
            case 1:
                companyRepository.creatRelation_short_name();
                break;
            case 2:
                companyRepository.creatRelation_DOM();
                break;
            case 3:
                companyRepository.creatRelation_CTBRANCHINFO();
                break;
            case 4:
                companyRepository.creatRelation_INVEST();
                break;
            case 5:
                companyRepository.creatRelation_SHAREHOLDER();
                break;
            case 6:
                companyRepository.creatRelation_PERSONS();
                break;
            case 7:
                companyRepository.creatRelation_DIRECT();
                break;
            case 8:
                companyRepository.changeType();
                break;
            case 9:
                relationRepository.deleteRelation_DIRECT();
            case 10:
                ArrayList<Relation> relationTypes = relationRepository.getRelationType1();
                for (Relation relation : relationTypes){
                    createNode(relation);
                }
                break;
            default:
                System.out.println("=======错误:"+i);
                break;
        }
    }

    private void createNode(Relation relation){
        Company parent = relation.getParent();
        Company child = relation.getChild();
        List<String> persons = relation.getPERSONS();
        JSONObject jsonParent = new JSONObject();
        JSONObject jsonChild = new JSONObject();
        try{
            jsonParent = JSONObject.parseObject(parent.getPERSONKVS());
        }catch (Exception e){
            e.printStackTrace();
        }
        try{
            jsonChild = JSONObject.parseObject(child.getPERSONKVS());
        }catch (Exception e){
            e.printStackTrace();
        }
        if (jsonParent.size()<1 || jsonChild.size()<1 || persons.size()<1){
            return;
        }
        for (String person : persons){

            Person p = new Person();
            p.setName(person);
//            if (personRepository.findByName(person)){
//                continue;
//            }
            personRepository.save(p);
            System.out.println("创建人物节点成功:"+p.getName());

            try{
                PersonRelation personRelation = new PersonRelation();
                personRelation.setParent(p);
                personRelation.setChild(parent);
                personRelation.setRelation(jsonParent.get(person).toString());
                personRelationRepository.save(personRelation);
                System.out.println("创建人物公司关系成功:"+p.getName()+"=="+personRelation.getRelation()+"==>"+parent.getENTNAME());
            }catch (Exception e){
                e.printStackTrace();
            }
            try{
                PersonRelation personRelation = new PersonRelation();
                personRelation.setParent(p);
                personRelation.setChild(child);
                personRelation.setRelation(jsonChild.get(person).toString());
                personRelationRepository.save(personRelation);
                System.out.println("创建人物公司关系成功:"+p.getName()+"=="+personRelation.getRelation()+"==>"+child.getENTNAME());
            }catch (Exception e){
                e.printStackTrac

Neo4j 字符串转列表

遗憾的是原生的Cypher貌似没有直接修改属性由字符串转列表的方法,我们使用neo4j的扩展库

APOC用户手册 3.4.0.1

spring data neo4j配置 springboot整合neo4j_spring boot_04

  • apoc 字符串转列表操作
MATCH (n:Company) WITH n AS n,(CASE WHEN apoc.meta.type(n.CTBRANCHINFO)="STRING" and n.CTBRANCHINFO is not null THEN apoc.convert.fromJsonList(n.CTBRANCHINFO) ELSE n.CTBRANCHINFO END) AS CTBRANCHINFO,(CASE WHEN apoc.meta.type(n.INVEST)="STRING" and n.INVEST is not null THEN apoc.convert.fromJsonList(n.INVEST) ELSE n.INVEST END) AS INVEST,(CASE WHEN apoc.meta.type(n.SHAREHOLDER)="STRING" and n.SHAREHOLDER is not null THEN apoc.convert.fromJsonList(n.SHAREHOLDER) ELSE n.SHAREHOLDER END) AS SHAREHOLDER,(CASE WHEN apoc.meta.type(n.PERSONLIST)="STRING" and n.PERSONLIST is not null THEN apoc.convert.fromJsonList(n.PERSONLIST) ELSE n.PERSONLIST END) AS PERSONLIST SET n.CTBRANCHINFO =CTBRANCHINFO,n.INVEST=INVEST,n.PERSONLIST=PERSONLIST,n.SHAREHOLDER=SHAREHOLDER