一 Neo4j 简介
Neo4j是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中。Neo4j也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。程序员工作在一个面向对象的、灵活的网络结构下而不是严格、静态的表中——但是他们可以享受到具备完全的事务特性、企业级的数据库的所有好处。
1.1 图形数据结构
在一个图中包含两种基本的数据类型:Nodes(节点) 和 Relationships(关系)。Nodes 和 Relationships 包含key/value形式的属性。Nodes通过Relationships所定义的关系相连起来,形成关系型网络结构。
1.2 安装Neo4j
1.2.1 下载安装包
liunx环境Neo4j下载地址:https://neo4j.com/download/other-releases/#releases(社区版免费)
注意:下载各自系统所需版本tar即可,我是Centos系统,故下载的是liunx版本,4.X后需要JDK11版本,3.X需要JDK8
1.2.2 上传下载的tar包,进行解压
tar -zxvf neo4j-community-3.4.5-unix.tar.gz
1.2.3 修改配置文件
在安装目录下找到conf目录下的neo4j.conf文件
vim neo4j.conf
修改相应配置如下:
1.2.4 常用命令
进入bin目录执行命令
启动命令:./neo4j start
停止命令:./neo4j stop
查看图数据库状态:./neo4j status
1.2.5 客户端访问
在浏览器访问图数据库所在的机器上的7474端口(第一次访问账号neo4j,密码neo4j,会提示修改初始密码)
二 Spring Boot2 集成 Neo4j
Spring Boo2版本为:2.0.6
2.1 pom.xml依赖
<!-- neo4j-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<!-- neo4j driver驱动-->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>1.7.5</version>
</dependency>
完整pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.modules</groupId>
<artifactId>spring-boot2-neo4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot2-neo4j</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
</properties>
<dependencies>
<!-- spring boot2 核心-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- spring boot2 web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- devtools-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- neo4j-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<!-- neo4j driver驱动-->
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver</artifactId>
<version>1.7.5</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.58</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- swagger2-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- swagger2-UI-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--防止进入swagger页面报类型转换错误,排除2.9.2中的引用,手动增加1.5.21版本-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
</dependencies>
<!-- 依赖版本管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- 引入系统范围内的依赖 -->
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
<!--添加配置跳过测试-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2 application.properties配置
server.port=8088
server.servlet.context-path=/neo4j
# 启用优雅关机
server.shutdown=graceful
# 缓冲10秒
spring.lifecycle.timeout-per-shutdown-phase=10s
# neo4j配置
spring.data.neo4j.uri= bolt://172.16.21.201:7687
spring.data.neo4j.username=neo4j
spring.data.neo4j.password=111111
#日志配置
logging.level.com.modules.project.dao=debug
# 设置时间
spring.jackson.time-zone=GMT+8
# 全局格式化日期
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
2.3 Neo4j 配置类
package com.modules.common.config;
import org.neo4j.driver.v1.AuthTokens;
import org.neo4j.driver.v1.Driver;
import org.neo4j.driver.v1.GraphDatabase;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* neo4j图数据库配置
*
* @author li'chao
*/
@Configuration
public class Neo4jConfig {
@Value("${spring.data.neo4j.uri}")
private String url;
@Value("${spring.data.neo4j.username}")
private String username;
@Value("${spring.data.neo4j.password}")
private String password;
/**
* neo4j图数据库驱动模式
*
* @return
*/
@Bean
public Driver neo4jDriver() {
return GraphDatabase.driver(url, AuthTokens.basic(username, password));
}
}
2.4 Neo4jUtil工具类
package com.modules.common.utils;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.v1.*;
import org.neo4j.driver.v1.types.Node;
import org.neo4j.driver.v1.types.Path;
import org.neo4j.driver.v1.types.Relationship;
import org.neo4j.driver.v1.util.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;
@Slf4j
@Component
public class Neo4jUtil {
@Autowired
private Driver neo4jDriver;
public boolean isNeo4jOpen() {
try (Session session = neo4jDriver.session()) {
log.info("连接成功:" + session.isOpen());
return session.isOpen();
} catch (Exception e) {
log.error("连接异常:" + e.getMessage());
return false;
}
}
public StatementResult excuteCypherSql(String cypherSql) {
StatementResult result = null;
try (Session session = neo4jDriver.session()) {
log.info("cypher语句:" + cypherSql);
result = session.run(cypherSql);
session.close();
} catch (Exception e) {
throw e;
}
return result;
}
public HashMap<String, Object> getEntityMap(String cypherSql) {
HashMap<String, Object> rss = new HashMap<String, Object>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
List<Record> records = result.list();
for (Record recordItem : records) {
for (Value value : recordItem.values()) {
if (value.type().name().equals("NODE")) {// 结果里面只要类型为节点的值
Node noe4jNode = value.asNode();
Map<String, Object> map = noe4jNode.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (rss.containsKey(key)) {
String oldValue = rss.get(key).toString();
String newValue = oldValue + "," + entry.getValue();
rss.replace(key, newValue);
} else {
rss.put(key, entry.getValue());
}
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return rss;
}
public List<HashMap<String, Object>> getGraphNode(String cypherSql) {
List<HashMap<String, Object>> ents = new ArrayList<HashMap<String, Object>>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
List<Record> records = result.list();
for (Record recordItem : records) {
List<Pair<String, Value>> f = recordItem.fields();
for (Pair<String, Value> pair : f) {
HashMap<String, Object> rss = new HashMap<String, Object>();
String typeName = pair.value().type().name();
if (typeName.equals("NODE")) {
Node noe4jNode = pair.value().asNode();
String uuid = String.valueOf(noe4jNode.id());
Map<String, Object> map = noe4jNode.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", uuid);
ents.add(rss);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ents;
}
public List<HashMap<String, Object>> getGraphRelationShip(String cypherSql) {
List<HashMap<String, Object>> ents = new ArrayList<HashMap<String, Object>>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
List<Record> records = result.list();
for (Record recordItem : records) {
List<Pair<String, Value>> f = recordItem.fields();
for (Pair<String, Value> pair : f) {
HashMap<String, Object> rss = new HashMap<String, Object>();
String typeName = pair.value().type().name();
if (typeName.equals("RELATIONSHIP")) {
Relationship rship = pair.value().asRelationship();
String uuid = String.valueOf(rship.id());
String sourceid = String.valueOf(rship.startNodeId());
String targetid = String.valueOf(rship.endNodeId());
Map<String, Object> map = rship.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", uuid);
rss.put("sourceid", sourceid);
rss.put("targetid", targetid);
ents.add(rss);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ents;
}
public List<HashMap<String, Object>> getGraphItem(String cypherSql) {
List<HashMap<String, Object>> ents = new ArrayList<HashMap<String, Object>>();
List<String> nodeids = new ArrayList<String>();
List<String> shipids = new ArrayList<String>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
List<Record> records = result.list();
for (Record recordItem : records) {
List<Pair<String, Value>> f = recordItem.fields();
HashMap<String, Object> rss = new HashMap<String, Object>();
for (Pair<String, Value> pair : f) {
String typeName = pair.value().type().name();
if (typeName.equals("NODE")) {
Node noe4jNode = pair.value().asNode();
String uuid = String.valueOf(noe4jNode.id());
if(!nodeids.contains(uuid)) {
Map<String, Object> map = noe4jNode.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", uuid);
}
}else if (typeName.equals("RELATIONSHIP")) {
Relationship rship = pair.value().asRelationship();
String uuid = String.valueOf(rship.id());
if (!shipids.contains(uuid)) {
String sourceid = String.valueOf(rship.startNodeId());
String targetid = String.valueOf(rship.endNodeId());
Map<String, Object> map = rship.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", uuid);
rss.put("sourceid", sourceid);
rss.put("targetid", targetid);
}
}else {
rss.put(pair.key(),pair.value().toString());
}
}
ents.add(rss);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ents;
}
/*
* 获取值类型的结果,如count,uuid
* @return 1 2 3 等数字类型
*/
public long getGraphValue(String cypherSql) {
long val=0;
try {
StatementResult cypherResult = excuteCypherSql(cypherSql);
if (cypherResult.hasNext()) {
Record record = cypherResult.next();
for (Value value : record.values()) {
val = value.asLong();
}
}
} catch (Exception e) {
e.printStackTrace();
}
return val;
}
public HashMap<String, Object> getGraphNodeAndShip(String cypherSql) {
HashMap<String, Object> mo = new HashMap<String, Object>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
List<Record> records = result.list();
List<HashMap<String, Object>> ents = new ArrayList<HashMap<String, Object>>();
List<HashMap<String, Object>> ships = new ArrayList<HashMap<String, Object>>();
List<String> uuids = new ArrayList<String>();
List<String> shipids = new ArrayList<String>();
for (Record recordItem : records) {
List<Pair<String, Value>> f = recordItem.fields();
for (Pair<String, Value> pair : f) {
HashMap<String, Object> rships = new HashMap<String, Object>();
HashMap<String, Object> rss = new HashMap<String, Object>();
String typeName = pair.value().type().name();
if (typeName.equals("NULL")) {
continue;
} else if (typeName.equals("NODE")) {
Node noe4jNode = pair.value().asNode();
Map<String, Object> map = noe4jNode.asMap();
String uuid = String.valueOf(noe4jNode.id());
if (!uuids.contains(uuid)) {
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", uuid);
uuids.add(uuid);
}
if (rss != null && !rss.isEmpty()) {
ents.add(rss);
}
} else if (typeName.equals("RELATIONSHIP")) {
Relationship rship = pair.value().asRelationship();
String uuid = String.valueOf(rship.id());
if (!shipids.contains(uuid)) {
String sourceid = String.valueOf(rship.startNodeId());
String targetid = String.valueOf(rship.endNodeId());
Map<String, Object> map = rship.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rships.put(key, entry.getValue());
}
rships.put("uuid", uuid);
rships.put("sourceid", sourceid);
rships.put("targetid", targetid);
shipids.add(uuid);
if (rships != null && !rships.isEmpty()) {
ships.add(rships);
}
}
} else if (typeName.equals("PATH")) {
Path path = pair.value().asPath();
Map<String, Object> startNodemap = path.start().asMap();
String startNodeuuid = String.valueOf(path.start().id());
if (!uuids.contains(startNodeuuid)) {
rss=new HashMap<String, Object>();
for (Entry<String, Object> entry : startNodemap.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", startNodeuuid);
uuids.add(startNodeuuid);
if (rss != null && !rss.isEmpty()) {
ents.add(rss);
}
}
Map<String, Object> endNodemap = path.end().asMap();
String endNodeuuid = String.valueOf(path.end().id());
if (!uuids.contains(endNodeuuid)) {
rss=new HashMap<String, Object>();
for (Entry<String, Object> entry : endNodemap.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", endNodeuuid);
uuids.add(endNodeuuid);
if (rss != null && !rss.isEmpty()) {
ents.add(rss);
}
}
Iterator<Node> allNodes = path.nodes().iterator();
while (allNodes.hasNext()) {
Node next = allNodes.next();
String uuid = String.valueOf(next.id());
if (!uuids.contains(uuid)) {
rss=new HashMap<String, Object>();
Map<String, Object> map = next.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
rss.put("uuid", uuid);
uuids.add(uuid);
if (rss != null && !rss.isEmpty()) {
ents.add(rss);
}
}
}
Iterator<Relationship> reships = path.relationships().iterator();
while (reships.hasNext()) {
Relationship next = reships.next();
String uuid = String.valueOf(next.id());
if (!shipids.contains(uuid)) {
rships=new HashMap<String, Object>();
String sourceid = String.valueOf(next.startNodeId());
String targetid = String.valueOf(next.endNodeId());
Map<String, Object> map = next.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rships.put(key, entry.getValue());
}
rships.put("uuid", uuid);
rships.put("sourceid", sourceid);
rships.put("targetid", targetid);
shipids.add(uuid);
if (rships != null && !rships.isEmpty()) {
ships.add(rships);
}
}
}
} else if (typeName.contains("LIST")) {
Iterable<Value> val=pair.value().values();
Value next = val.iterator().next();
String type=next.type().name();
if (type.equals("RELATIONSHIP")) {
Relationship rship = next.asRelationship();
String uuid = String.valueOf(rship.id());
if (!shipids.contains(uuid)) {
String sourceid = String.valueOf(rship.startNodeId());
String targetid = String.valueOf(rship.endNodeId());
Map<String, Object> map = rship.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rships.put(key, entry.getValue());
}
rships.put("uuid", uuid);
rships.put("sourceid", sourceid);
rships.put("targetid", targetid);
shipids.add(uuid);
if (rships != null && !rships.isEmpty()) {
ships.add(rships);
}
}
}
} else if (typeName.contains("MAP")) {
rss.put(pair.key(), pair.value().asMap());
} else {
rss.put(pair.key(), pair.value().toString());
if (rss != null && !rss.isEmpty()) {
ents.add(rss);
}
}
}
}
mo.put("node", ents);
mo.put("relationship", ships);
}
} catch (Exception e) {
throw new RuntimeException("执行Cypher查询异常");
}
return mo;
}
/**
* 匹配所有类型的节点,可以是节点,关系,数值,路径
* @param cypherSql
* @return
*/
public List<HashMap<String, Object>> getEntityList(String cypherSql) {
List<HashMap<String, Object>> ents = new ArrayList<HashMap<String, Object>>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
List<Record> records = result.list();
for (Record recordItem : records) {
HashMap<String, Object> rss = new HashMap<String, Object>();
List<Pair<String, Value>> f = recordItem.fields();
for (Pair<String, Value> pair : f) {
String typeName = pair.value().type().name();
if (typeName.equals("NULL")) {
continue;
} else if (typeName.equals("NODE")) {
Node noe4jNode = pair.value().asNode();
Map<String, Object> map = noe4jNode.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
} else if (typeName.equals("RELATIONSHIP")) {
Relationship rship = pair.value().asRelationship();
Map<String, Object> map = rship.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
rss.put(key, entry.getValue());
}
} else if (typeName.equals("PATH")) {
} else if (typeName.contains("LIST")) {
rss.put(pair.key(), pair.value().asList());
} else if (typeName.contains("MAP")) {
rss.put(pair.key(), pair.value().asMap());
} else {
rss.put(pair.key(), pair.value().toString());
}
}
ents.add(rss);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return ents;
}
public <T> List<T> getEntityItemList(String cypherSql, Class<T> type) {
List<HashMap<String, Object>> ents = getGraphNode(cypherSql);
List<T> model = hashMapToObject(ents, type);
return model;
}
public <T> T getEntityItem(String cypherSql, Class<T> type) {
HashMap<String, Object> rss = new HashMap<String, Object>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
Record record = result.next();
for (Value value : record.values()) {
if (value.type().name().equals("NODE")) {// 结果里面只要类型为节点的值
Node noe4jNode = value.asNode();
Map<String, Object> map = noe4jNode.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (rss.containsKey(key)) {
String oldValue = rss.get(key).toString();
String newValue = oldValue + "," + entry.getValue();
rss.replace(key, newValue);
} else {
rss.put(key, entry.getValue());
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
T model = hashMapToObjectItem(rss, type);
return model;
}
public HashMap<String, Object> getEntity(String cypherSql) {
HashMap<String, Object> rss = new HashMap<String, Object>();
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
Record record = result.next();
for (Value value : record.values()) {
String t = value.type().name();
if (value.type().name().equals("NODE")) {// 结果里面只要类型为节点的值
Node noe4jNode = value.asNode();
Map<String, Object> map = noe4jNode.asMap();
for (Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (rss.containsKey(key)) {
String oldValue = rss.get(key).toString();
String newValue = oldValue + "," + entry.getValue();
rss.replace(key, newValue);
} else {
rss.put(key, entry.getValue());
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return rss;
}
public Integer executeScalar(String cypherSql) {
Integer count = 0;
try {
StatementResult result = excuteCypherSql(cypherSql);
if (result.hasNext()) {
Record record = result.next();
for (Value value : record.values()) {
String t = value.type().name();
if (t.equals("INTEGER")) {
count = Integer.valueOf(value.toString());
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return count;
}
public HashMap<String, Object> getRelevantEntity(String cypherSql) {
HashMap<String, Object> rss = new HashMap<String, Object>();
try {
StatementResult resultNode = excuteCypherSql(cypherSql);
if (resultNode.hasNext()) {
List<Record> records = resultNode.list();
for (Record recordItem : records) {
Map<String, Object> r = recordItem.asMap();
String key = r.get("key").toString();
if (rss.containsKey(key)) {
String oldValue = rss.get(key).toString();
String newValue = oldValue + "," + r.get("value");
rss.replace(key, newValue);
} else {
rss.put(key, r.get("value"));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return rss;
}
public String getFilterPropertiesJson(String jsonStr) {
String propertiesString = jsonStr.replaceAll("\"(\\w+)\"(\\s*:\\s*)", "$1$2"); // 去掉key的引号
return propertiesString;
}
public <T>String getkeyvalCyphersql(T obj) {
Map<String, Object> map = new HashMap<String, Object>();
List<String> sqlList=new ArrayList<String>();
// 得到类对象
Class userCla = obj.getClass();
/* 得到类中的所有属性集合 */
Field[] fs = userCla.getDeclaredFields();
for (int i = 0; i < fs.length; i++) {
Field f = fs[i];
Class type = f.getType();
f.setAccessible(true); // 设置些属性是可以访问的
Object val = new Object();
try {
val = f.get(obj);
if(val==null) {
val="";
}
String sql="";
String key=f.getName();
log.info("key:"+key+"type:"+type);
if ( val instanceof Integer ){
// 得到此属性的值
map.put(key, val);// 设置键值
sql="n."+key+"="+val;
}
else if ( val instanceof String[] ){
//如果为true则强转成String数组
String [] arr = ( String[] ) val ;
String v="";
for ( int j = 0 ; j < arr.length ; j++ ){
arr[j]="'"+ arr[j]+"'";
}
v=String.join(",", arr);
sql="n."+key+"=["+val+"]";
}
else if (val instanceof List){
//如果为true则强转成String数组
List<String> arr = ( ArrayList<String> ) val ;
List<String> aa=new ArrayList<String>();
String v="";
for (String s : arr) {
s="'"+ s+"'";
aa.add(s);
}
v=String.join(",", aa);
sql="n."+key+"=["+v+"]";
}
else {
// 得到此属性的值
map.put(key, val);// 设置键值
sql="n."+key+"='"+val+"'";
}
sqlList.add(sql);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
String finasql=String.join(",",sqlList);
log.info("单个对象的所有键值==反射==" + map.toString());
return finasql;
}
public <T> List<T> hashMapToObject(List<HashMap<String, Object>> maps, Class<T> type) {
try {
List<T> list = new ArrayList<T>();
for (HashMap<String, Object> r : maps) {
T t = type.newInstance();
Iterator iter = r.entrySet().iterator();// 该方法获取列名.获取一系列字段名称.例如name,age...
while (iter.hasNext()) {
Entry entry = (Entry) iter.next();// 把hashmap转成Iterator再迭代到entry
String key = entry.getKey().toString(); // 从iterator遍历获取key
Object value = entry.getValue(); // 从hashmap遍历获取value
if("serialVersionUID".toLowerCase().equals(key.toLowerCase()))continue;
Field field = type.getDeclaredField(key);// 获取field对象
if (field != null) {
field.setAccessible(true);
if (field.getType() == int.class || field.getType() == Integer.class) {
if (value==null||StringUtils.isBlank(value.toString())) {
field.set(t, 0);// 设置值
} else {
field.set(t, Integer.parseInt(value.toString()));// 设置值
}
}
else if (field.getType() == long.class||field.getType() == Long.class ) {
if (value==null||StringUtils.isBlank(value.toString())) {
field.set(t, 0);// 设置值
} else {
field.set(t, Long.parseLong(value.toString()));// 设置值
}
}
else {
field.set(t, value);// 设置值
}
}
}
list.add(t);
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public <T> T hashMapToObjectItem(HashMap<String, Object> map, Class<T> type) {
try {
T t = type.newInstance();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Entry entry = (Entry) iter.next();// 把hashmap转成Iterator再迭代到entry
String key = entry.getKey().toString(); // 从iterator遍历获取key
Object value = entry.getValue(); // 从hashmap遍历获取value
if("serialVersionUID".toLowerCase().equals(key.toLowerCase()))continue;
Field field = type.getDeclaredField(key);// 获取field对象
if (field != null) {
field.setAccessible(true);
if (field.getType() == int.class || field.getType() == Integer.class) {
if (value==null||StringUtils.isBlank(value.toString())) {
field.set(t, 0);// 设置值
} else {
field.set(t, Integer.parseInt(value.toString()));// 设置值
}
}
else if (field.getType() == long.class||field.getType() == Long.class ) {
if (value==null||StringUtils.isBlank(value.toString())) {
field.set(t, 0);// 设置值
} else {
field.set(t, Long.parseLong(value.toString()));// 设置值
}
}
else {
field.set(t, value);// 设置值
}
}
}
return t;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public List<String> getNodesLabelsEntityList(String cypherSql) {
List<String> rss = new ArrayList<>();
try {
StatementResult resultNode = excuteCypherSql(cypherSql);
if (resultNode.hasNext()) {
List<Record> records = resultNode.list();
for (Record recordItem : records) {
Map<String, Object> r = recordItem.asMap();
rss.add(r.get("label").toString());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return rss;
}
public List<String> getRelationshipTypeEntityList(String cypherSql) {
List<String> rss = new ArrayList<>();
try {
StatementResult resultNode = excuteCypherSql(cypherSql);
if (resultNode.hasNext()) {
List<Record> records = resultNode.list();
for (Record recordItem : records) {
Map<String, Object> r = recordItem.asMap();
rss.add(r.get("relationshipType").toString());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return rss;
}
}
2.5 实现图库数据查询功能
2.5.1 controller
package com.modules.project.controller;
import com.modules.common.utils.Neo4jUtil;
import com.modules.common.web.BaseController;
import com.modules.common.web.Result;
import com.modules.project.entity.GraphQuery;
import com.modules.project.service.GraphService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
/**
* neo4j图数据管理
*
* @author li'chao
*/
@Api(tags = "neo4j图数据管理")
@Slf4j
@RestController
@RequestMapping("/graph")
public class GraphController extends BaseController {
@Autowired
private Neo4jUtil neo4jUtil;
@Autowired
private GraphService graphService;
@ApiOperation(value = "执行Cypher查询", notes = "执行Cypher查询")
@GetMapping(value = "/getCypherResult")
public Result getCypherResult(String cypher) {
try {
HashMap<String, Object> graphData = neo4jUtil.getGraphNodeAndShip(cypher);
return success(graphData);
} catch (Exception e) {
log.error("执行Cypher查询异常:" + e.getMessage());
return error("执行Cypher查询异常");
}
}
@ApiOperation(value = "查询图节点和关系", notes = "查询图节点和关系")
@GetMapping(value = "/getDomainGraph")
public Result getDomainGraph(@Validated GraphQuery query) {
try {
HashMap<String, Object> graphData = graphService.getDomainGraph(query);
return success(graphData);
} catch (Exception e) {
log.error("查询图节点和关系异常:" + e.getMessage());
return error("查询图节点和关系异常");
}
}
@ApiOperation(value = "获取节点列表", notes = "获取节点列表")
@GetMapping(value = "/getDomainNodes")
public Result getDomainNodes(String domain, Integer pageIndex, Integer pageSize) {
try {
HashMap<String, Object> graphData = graphService.getDomainNodes(domain, pageIndex,pageSize);
return success(graphData);
} catch (Exception e) {
log.error("获取节点列表异常:" + e.getMessage());
return error("获取节点列表异常");
}
}
@ApiOperation(value = "查询所有节点标签", notes = "查询所有的节点标签")
@GetMapping(value = "/getNodesLabels")
public Result getNodesLabels() {
try {
List<String> graphData = graphService.getNodesLabels();
return success(graphData);
} catch (Exception e) {
log.error("查询所有节点标签异常:" + e.getMessage());
return error("查询所有节点标签异常");
}
}
@ApiOperation(value = "查询所有关系类型", notes = "查询所有关系类型")
@GetMapping(value = "/getRelationshipType")
public Result getRelationshipType() {
try {
List<String> graphData = graphService.getRelationshipType();
return success(graphData);
} catch (Exception e) {
log.error("查询所有关系类型异常:" + e.getMessage());
return error("查询所有关系类型异常");
}
}
}
2.5.2 service
package com.modules.project.service;
import com.modules.common.utils.StringUtils;
import com.modules.project.entity.GraphQuery;
import com.modules.project.repository.GraphRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
/**
* neo4j图库服务层
*
* @author li'chao
*
*/
@Service
public class GraphService {
@Autowired
private GraphRepository graphRepository;
/**
* 查询图节点和关系
*
* @param query
* @return
*/
public HashMap<String, Object> getDomainGraph(GraphQuery query) {
if(StringUtils.isNotEmpty(query.getDomain())){
return graphRepository.getDomainGraph(query);
}else{
return graphRepository.getNodeNameGraph(query);
}
}
/**
* 获取节点列表
* @param domain
* @param pageIndex
* @param pageSize
* @return
*/
public HashMap<String, Object> getDomainNodes(String domain, Integer pageIndex, Integer pageSize){
return graphRepository.getDomainNodes(domain, pageIndex, pageSize);
}
/**
* 查询所有节点标签
* @return
*/
public List<String> getNodesLabels(){
return graphRepository.getNodesLabels();
}
/**
* 查询所有关系类型
* @return
*/
public List<String> getRelationshipType(){
return graphRepository.getRelationshipType();
}
}
2.5.3 repository
@Override
public List<String> getNodesLabels() {
String cypherSql = " CALL db.labels(); ";
return neo4jUtil.getNodesLabelsEntityList(cypherSql);
}
@Override
public List<String> getRelationshipType() {
String cypherSql = " CALL db.relationshipTypes(); ";
return neo4jUtil.getRelationshipTypeEntityList(cypherSql);
}
@Override
public List<String> getNodesSchema() {
String cypherSql = " CALL db.schema(); ";
return neo4jUtil.getRelationshipTypeEntityList(cypherSql);
}
/**
* 查询图谱节点和关系
*
* @param query
* @return node relationship
*/
public HashMap<String, Object> getDomainGraph(GraphQuery query) {
HashMap<String, Object> nr = new HashMap<String, Object>();
try {
String domain = query.getDomain();
// MATCH (n:`症状`) -[r]-(m:症状) where r.name='治疗' or r.name='危险因素' return n,m
if (!StringUtils.isBlank(domain)) {
String cqr = "";
List<String> lis = new ArrayList<String>();
if (query.getRelation() != null && query.getRelation().length > 0) {
for (String r : query.getRelation()) {
String it = String.format("r.name='%s'", r);
lis.add(it);
}
cqr = String.join(" or ", lis);
}
String cqWhere = "";
if (!StringUtils.isBlank(query.getNodename()) || !StringUtils.isBlank(cqr)) {
if (!StringUtils.isBlank(query.getNodename())) {
if (query.getMatchtype() == 1) {
cqWhere = String.format("where n.name ='%s' ", query.getNodename());
} else {
cqWhere = String.format("where n.name contains('%s')", query.getNodename());
}
}
String nodeOnly = cqWhere;
if (!StringUtils.isBlank(cqr)) {
if (StringUtils.isBlank(cqWhere)) {
cqWhere = String.format(" where ( %s )", cqr);
} else {
cqWhere += String.format(" and ( %s )", cqr);
}
}
// 下边的查询查不到单个没有关系的节点,考虑要不要左箭头
String nodeSql = String.format("MATCH (n:`%s`) <-[r]->(m) %s return * limit %s", domain, cqWhere,
query.getPageSize());
HashMap<String, Object> graphNode = neo4jUtil.getGraphNodeAndShip(nodeSql);
Object node = graphNode.get("node");
// 没有关系显示则显示节点
if (node != null) {
nr.put("node", graphNode.get("node"));
nr.put("relationship", graphNode.get("relationship"));
} else {
String nodecql = String.format("MATCH (n:`%s`) %s RETURN distinct(n) limit %s", domain,
nodeOnly, query.getPageSize());
List<HashMap<String, Object>> nodeItem = neo4jUtil.getGraphNode(nodecql);
nr.put("node", nodeItem);
nr.put("relationship", new ArrayList<HashMap<String, Object>>());
}
} else {
String nodeSql = String.format("MATCH (n:`%s`) %s RETURN distinct(n) limit %s", domain, cqWhere,
query.getPageSize());
List<HashMap<String, Object>> graphNode = neo4jUtil.getGraphNode(nodeSql);
nr.put("node", graphNode);
String domainSql = String.format("MATCH (n:`%s`)<-[r]-> (m) %s RETURN distinct(r) limit %s", domain,
cqWhere, query.getPageSize());// m是否加领域
List<HashMap<String, Object>> graphRelation = neo4jUtil.getGraphRelationShip(domainSql);
nr.put("relationship", graphRelation);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return nr;
}
/**
* 查询图谱节点和关系(条件无节点标签)
*
* @param query
* @return node relationship
*/
public HashMap<String, Object> getNodeNameGraph(GraphQuery query) {
HashMap<String, Object> nr = new HashMap<String, Object>();
try {
// MATCH (n) -[r]-(m:症状) where r.name='治疗' or r.name='危险因素' return n,m
String cqr = "";
List<String> lis = new ArrayList<String>();
if (query.getRelation() != null && query.getRelation().length > 0) {
for (String r : query.getRelation()) {
String it = String.format("r.name='%s'", r);
lis.add(it);
}
cqr = String.join(" or ", lis);
}
String cqWhere = "";
if (!StringUtils.isBlank(query.getNodename()) || !StringUtils.isBlank(cqr)) {
if (!StringUtils.isBlank(query.getNodename())) {
if (query.getMatchtype() == 1) {
cqWhere = String.format("where n.name ='%s' ", query.getNodename());
} else {
cqWhere = String.format("where n.name contains('%s')", query.getNodename());
}
}
String nodeOnly = cqWhere;
if (!StringUtils.isBlank(cqr)) {
if (StringUtils.isBlank(cqWhere)) {
cqWhere = String.format(" where ( %s )", cqr);
} else {
cqWhere += String.format(" and ( %s )", cqr);
}
}
// 下边的查询查不到单个没有关系的节点,考虑要不要左箭头
String nodeSql = String.format("MATCH (n) <-[r]->(m) %s return * limit %s", cqWhere,
query.getPageSize());
HashMap<String, Object> graphNode = neo4jUtil.getGraphNodeAndShip(nodeSql);
Object node = graphNode.get("node");
// 没有关系显示则显示节点
if (node != null) {
nr.put("node", graphNode.get("node"));
nr.put("relationship", graphNode.get("relationship"));
} else {
String nodecql = String.format("MATCH (n) %s RETURN distinct(n) limit %s",
nodeOnly, query.getPageSize());
List<HashMap<String, Object>> nodeItem = neo4jUtil.getGraphNode(nodecql);
nr.put("node", nodeItem);
nr.put("relationship", new ArrayList<HashMap<String, Object>>());
}
} else {
String nodeSql = String.format("MATCH (n) %s RETURN distinct(n) limit %s", cqWhere,
query.getPageSize());
List<HashMap<String, Object>> graphNode = neo4jUtil.getGraphNode(nodeSql);
nr.put("node", graphNode);
String domainSql = String.format("MATCH (n)<-[r]-> (m) %s RETURN distinct(r) limit %s",
cqWhere, query.getPageSize());// m是否加领域
List<HashMap<String, Object>> graphRelation = neo4jUtil.getGraphRelationShip(domainSql);
nr.put("relationship", graphRelation);
}
} catch (Exception e) {
e.printStackTrace();
}
return nr;
}
2.5.4 swagger界面
三 加载CSV文件,添加图库数据
3.1 准备CSV文件
3.2 上传CSV文件至import目录下
3.3 加载CSV文件
删除所有节点
MATCH (n)
OPTIONAL MATCH (n)-[r]-()
DELETE n,r
加载train CSV
LOAD CSV WITH HEADERS FROM 'file:///train.csv' as line
CREATE (:train {train_id:line.train_id,train_type:line.train_type,name:line.train_name,create_time:line.create_time})
加载task CSV
LOAD CSV WITH HEADERS FROM 'file:///task.csv' as line
CREATE (:task {task_id:line.task_id,train_id:line.train_id,name:line.task_name,create_time:line.create_time})
创建train和task关系
LOAD CSV WITH HEADERS FROM "file:///tt.csv" AS line
MATCH(a:train{train_id:line.train_id}),(b:task{task_id:line.task_id})
MERGE(a) -[r:关联]->(b)
创建索引
CREATE INDEX ON :train(train_id)
CREATE INDEX ON :task(task_id)
3.4 图库数据
四 前端界面
前端实现技术:
cytoscape.js 官方地址:https://js.cytoscape.org/
d3.js 官方地址:D3 by Observable | The JavaScript library for bespoke data visualization
vis.js 官方地址:vis.js
普通查询
高级查询
五 源码
码云源码地址:spring-boot2-neo4j: spring-boot2-neo4j是精简的图库项目,基于Spring Boot2和Neo4j实现。