1. 事物问题
我们学习的neo4j的时候,一般都是对neo4j数据库直接进行操作,而实际生产过程中,neo4j的数据则来源于很多地方,比如我现在用到的mysql、redis、clickhouse等等。
而看过我之前博客的同学应该知道,我是基于spring jpa对neo4j数据库进行操作的,spring jpa已经对事物进行了默认封装,而我们此时用到了多数据源,那么势必会存在事物切换的问题
比如我先对mysql数据库进行操作,再对neo4j进行操作,那么这两步操作可以使用一个事物吗?显然是不可能的。那么,我们如何解决这种问题呢?
解决方案:
1. 配置mysql的数据源
2. 配置mysql数据源的默认事物
3. 配置neo4j的Session对象(session对象类似于mysql创建的jdbc连接对象,拿到对象可以直接对数据库进行操作)
4. 先操作mysql查询,再进行保存neo4j操作
2. 入库效率问题
使用java对neo4j数据库进行直接插入的效率非常底下,经测试,5万个节点插入,耗时半个小时以上,即便采用事物批量提交也是一样,且java bolt存在一个管道buff的大小问题,一次提交的数据不能超过buff最大长度。
解决办法:
1. 将需要插入的节点去重
2. 将去重的节点数据写入本地磁盘,已csv的格式保存数据
3. 通过执行LOAD CSV命令导入磁盘中的csv文件,设置事物提交条数
导入节点:
session.run(“using periodic commit 100 LOAD CSV WITH HEADERS FROM “file:///” + nodeFileName + “.csv” AS line merge (a:BasicNode {name:line.name, value: line.value}) return a”);
导入关系:session.run("using periodic commit 10 LOAD CSV WITH HEADERS FROM “file:///” + relationshipFileName + “.csv” AS line " + “match (from:BasicNode{name:line.name}),(to:BasicNode{name:line.value}) merge (from) - [:relation{type:line.type}] -> (to)”);
3. 导入CVS文件的问题
neo4j在导入数据文件的时候,默认是去neo4j安装路径下的import文件夹下读取要导入的数据文件,如果在生成文件的时候,路径不对,就会导致导入失败。
解决办法:
1. 在application.properties文件中,写入neo4j的安装路径
2. 在代码中注入安装路径
或者修改neo4j的配置文件
#下面的意思是只能从import目录下导入,可以改变这个地址,或者去掉,去掉的话可以从任意位置导入
dbms.directories.import=import
4. spring jpa复杂查询返回对象的问题
spring jpa的出现,大大减少了程序猿在对数据库进行操作时的工作量,减少了很多不必要写的代码,而spring jpa并不是什么新技术,它只是基于ORM框架所开发的一种规约、规范,只能操作一些比较常规且简单的数据库操作,虽然大部分情况下,这些规约能够满足我们日常的使用。
而neo4j因为关系和节点、属性之间的复杂关系,往往在使用spring jpa的数据,会因为返回参数而大伤脑筋。那么,如果复杂的查询及结果如何去做?
解决方案:
1. 配置neo4j的session对象
2. 通过指定节点数据,查询节点的关系
@Autowired
private Session session;
/**
* 根据开始节点查询关联关系节点,可指定结束节点查询
* @param startNode 开始节点名称
* @param endNode 结束节点名称
* @return
*/
public Map<String, List<Map<String, Object>>> findByNameAndRelation(String startNode, String endNode) {
String cypherSql = String.format("match p = ((a:BasicNode {name:\"%s\"}) - [*..5] - ()) return p limit 1000", startNode);
if (StringUtils.isNotEmpty(endNode)) {
cypherSql = String.format("match (a:Sheeps {name:\"%s\"}) - [b] -> (c:Sheeps {name:\"%s\"}) return a,b,c limit 1000", startNode, endNode);
}
Result result = session.run(cypherSql);
Map<String, List<Map<String, Object>>> resultMap = new HashMap<String, List<Map<String, Object>>>();
List<Map<String, Object>> nodesList = new ArrayList<Map<String, Object>>();
List<Map<String, Object>> relationshipList = new ArrayList<Map<String, Object>>();
Map<Long, String> nodeIds = new HashMap<Long, String>(); // 保存id,用于去重
Map<Long, String> relationIds = new HashMap<Long, String>(); // 保存id,用于去重
while (result.hasNext()) {
Record record = result.next();
List<Value> values = record.values();
for (Value value : values) {
if ("NODE".equals(value.type().name())) {
Node node = value.asNode();
if (!nodeIds.containsKey(node.id())) {
Map<String, Object> map = setMap(node);
nodesList.add(map);
nodeIds.put(node.id(), "");
}
} else if ("RELATIONSHIP".equals(value.type().name())) {
Relationship relationship = value.asRelationship();
Map<String, Object> map = setMap(relationship);
relationshipList.add(map);
} else if ("PATH".equals(value.type().name())) {
Path path = value.asPath();
Iterable<Node> nodes = path.nodes();
for (Node node : nodes) {
if (!nodeIds.containsKey(node.id())) {
Map<String, Object> map = setMap(node);
nodesList.add(map);
nodeIds.put(node.id(), "");
}
}
Iterable<Relationship> relationships = path.relationships();
for (Relationship relationship : relationships) {
if (!relationIds.containsKey(relationship.id())) {
Map<String, Object> map = setMap(relationship);
relationshipList.add(map);
relationIds.put(relationship.id(), "");
}
}
}
}
}
resultMap.put("relationship", relationshipList);
resultMap.put("nodes", nodesList);
relationIds.clear();
relationIds = null;
nodeIds.clear();
nodeIds = null;
return resultMap;
}