关系图谱neo4j部署教程及Springboot集成
前言
最近项目中需要使用关系图谱于是就想到了强大的neo4j是专门用来处理图数据的,所以就给安排上了,同时需要集成Springboot提供接口给前端调用,用来展示数据的关系,并支持可以下钻的方式。
1. neo4j介绍
neo4j是基于java实现的图形数据库,可以存储大量的图信息。可以支持下钻的方式查看数据之间的关系,并可以很好和springboot进行集成,供我们展示数据的关系
2. neo4j 安装
2.1 准备工作
需要安装环境jdk环境,请自行安装
注意neo4j 4.X 版本需要jdk11,neo4j 3.X版本需要jdk8
在官网下载neo4j的安装包官网地址:https://neo4j.com/download-center/
2.2 安装
将下载好的安装包放入linux下面的目录,因为是jdk8所以我下载的是3.5.25的稳定版本
进入conf目录下面,用vi编辑器编辑配置文件neo4j.conf
修改的配置信息如下:
-
修改第22行load csv时l路径,在前面加个#,可从任意路径读取文件
#dbms.directories.import=import -
修改35行和36行,设置JVM初始堆内存和JVM最大堆内存
-
生产环境给的JVM最大堆内存越大越好,但是要小于机器的物理内存
dbms.memory.heap.initial_size=5g
dbms.memory.heap.max_size=10g -
修改46行,可以认为这个是缓存,如果机器配置高,这个越大越好
dbms.memory.pagecache.size=10g -
修改54行,去掉改行的#,可以远程通过ip访问neo4j数据库
dbms.connectors.default_listen_address=0.0.0.0 -
默认 bolt端口是7687,http端口是7474,https关口是7473,不修改下面3项也可以
-
修改71行,去掉#,设置http端口为7687,端口可以自定义,只要不和其他端口冲突就行
#dbms.connector.bolt.listen_address=:7687 -
修改75行,去掉#,设置http端口为7474,端口可以自定义,只要不和其他端口冲突就行
dbms.connector.http.listen_address=:7474 -
修改79行,去掉#,设置http端口为7473,端口可以自定义,只要不和其他端口冲突就行
dbms.connector.https.listen_address=:7473 -
修改227行,去掉#,允许从远程url来load csv
dbms.security.allow_csv_import_from_file_urls=true -
修改246行,允许使用neo4j-shell,类似于mysql 命令行之类的
dbms.shell.enabled=true -
修改235行,去掉#,设置连接neo4j-shell的端口,一般都是localhost或者127.0.0.1,这样安全,其他地址的话,一般使用https就行
dbms.shell.host=127.0.0.1 -
修改250行,去掉#,设置neo4j-shell端口,端口可以自定义,只要不和其他端口冲突就行
dbms.shell.port=1337 -
修改254行,设置neo4j可读可写
dbms.read_only=false
2.3 启动
使用命令 ./bin/neo4j start
启动neo4j
启动成功后浏览器访问 http://服务器ip地址:7474
首次登录账号neo4j,默认密码:neo4j,第一次登录成功后会提示需要修改密码
2.4 停止
使用命令 ./bin/neo4j stop
停止neo4j
2.5 查看状态
使用命令 ./bin/neo4j status
查看neo4j的状态
2.6 导入数据
1)首先把处理好的文件放到到neo4j下面的import目录下面
2)导入前要确认数据实例是否已经存在,比如导入到test
查看neo4j下面的data/databases目录里面是否有test文件夹了,如果有需要手动先删除掉
3) 导入数据
注意如果neo4j在运行,需要先停止掉才能导入数据, 使用命令
./bin/neo4j-admin import --database=test --delimiter="|" --nodes=import/nodes.csv --relationships=import/ed.csv
4) 导入成功后,修改默认的数据库实例为test,再启动neo4j
浏览器访问就可以在neo4j服务上面看到图数据了
3. 与Springboot集成
3.1 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
3.2 配置yml文件
neo4j:
uri: bolt://127.0.0.1:7687
username: neo4j
password: xxxxx
3.3 编写配置类
@Configuration
@EnableNeo4jRepositories(basePackages = "org.xxx.neo4j",
sessionFactoryRef = "neo4jSessionFactory",
transactionManagerRef = "neo4jTransaction")
public class Neo4jConfig {
@Value("${neo4j.uri}")
private String uri;
@Value("${neo4j.username}")
private String userName;
@Value("${neo4j.password}")
private String password;
@Bean(name = "neo4jConfiguration")
public org.neo4j.ogm.config.Configuration neo4jConfiguration() {
org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.Builder().uri(uri).connectionPoolSize(100).credentials(userName, password).build();
return configuration;
}
@Bean(name = "neo4jSessionFactory")
public SessionFactory neo4jSessionFactory() {
return new SessionFactory(neo4jConfiguration(), "org.xxx.neo4j");
}
@Bean(name = "neo4jTransaction")
public Neo4jTransactionManager neo4jTransaction() {
return new Neo4jTransactionManager(neo4jSessionFactory());
}
}
3.4 编写Service
@Service
public class NeoServiceImpl implements INeoService {
@Resource(name = "neo4jSessionFactory")
private SessionFactory sessionFactory;
@Override
public Map<String, Object> getNodeByName(String nodeName, String nodeLabel) {
Session session = sessionFactory.openSession();
//String sql = "MATCH resdata=(n:`" + nodeLabel + "`{node_name:'" + nodeName + "'})-[m]-(q) RETURN resdata limit 10";
String sql = "MATCH (n:`" + nodeLabel + "`{node_name:'" + nodeName + "'}) return n, (n)-[]-() as resData";
Result result = session.query(sql, Maps.newHashMap());
Map<String, Object> resultMap = getListResultNew(result);
return resultMap;
}
private Map<String, Object> getListResultNew(Result result) {
Map<String, Object> resultMap = new HashMap<>();
NodeEntity centerNode = null;
List<NodeResult> nodeResultList = null;
List<InternalPath.SelfContainedSegment> resData = null;
Iterable<Map<String, Object>> maps = result.queryResults();
Iterator<Map<String, Object>> iterator = maps.iterator();
while (iterator.hasNext()) {
Map<String, Object> map = iterator.next();
// 获取中心节点
NodeModel root = (NodeModel) map.get("n");
//获取数据节点
Object obj = map.get("resData");
if (obj != null) {
resData = new ArrayList<InternalPath.SelfContainedSegment>();
InternalPath.SelfContainedSegment[][] internalData = (InternalPath.SelfContainedSegment[][]) map.get("resData");
int index = internalData.length;
for (int i = 0; i < index; i++) {
InternalPath.SelfContainedSegment one = internalData[i][0];
resData.add(one);
}
}
if (root != null) {
// 根节点
centerNode = new NodeEntity();
centerNode.setId(root.getId());
// Map<String,Object> properties = new HashMap<>();
Map<String, Object> propertyMap = new HashMap<>();
// 遍历 propertityList 数组,将 键值对放入 map中
for (Property<String, Object> property : root.getPropertyList()) {
String key = property.getKey();
Object value = property.getValue();
propertyMap.put(key, value);
}
// properties.put("name",root.getPropertyList().get(0).getValue());
centerNode.setProperties(propertyMap);
centerNode.setLabels(root.getLabels());
}
if (CollectionUtils.isNotEmpty(resData)) {
nodeResultList = new ArrayList<>();
for (InternalPath.SelfContainedSegment internalPath : resData) {
//处理start
Node startNode = internalPath.start();
NodeEntity startEntity = new NodeEntity();
startEntity.setId(startNode.id());
List<String> labelStartList = new ArrayList<>();
startNode.labels().forEach(label -> {
labelStartList.add(label);
});
String[] labels = labelStartList.toArray(new String[labelStartList.size()]);
startEntity.setLabels(labels);
startEntity.setProperties(startNode.asMap());
//处理end
Node endNode = internalPath.end();
NodeEntity endEntity = new NodeEntity();
endEntity.setId(endNode.id());
List<String> labelEndList = new ArrayList<>();
endNode.labels().forEach(label -> {
labelEndList.add(label);
});
String[] labelsEnd = labelEndList.toArray(new String[labelEndList.size()]);
endEntity.setLabels(labelsEnd);
endEntity.setProperties(endNode.asMap());
//处理relationship
RelationRef relationRef = new RelationRef();
relationRef.setStart(internalPath.relationship().startNodeId());
relationRef.setEnd(internalPath.relationship().endNodeId());
relationRef.setId(internalPath.relationship().id());
relationRef.setType(internalPath.relationship().type());
NodeResult nodeResult = new NodeResult();
nodeResult.setStart(startEntity);
nodeResult.setEnd(endEntity);
nodeResult.setRelationship(relationRef);
nodeResultList.add(nodeResult);
}
}
// 存放中心节点
resultMap.put("centerNode", centerNode);
// 存放关系节点集合
resultMap.put("relationshipNodeList", nodeResultList);
}
return resultMap;
}
3.5 前端展示
前端的展示推荐使用echarts来绘制展示就可以了
最后开发完成效果如下: