小程序原生组件—提升你的小程序体验
1699
2022-11-19
SpringBoot结合Neo4j自定义cypherSql的方法
前言
SpringBoot引入neo4j
大多数时候Neo4j结合springboot都是按照实体类的映射,进行对象形式的创建节点,导致了节点属性的不可配性。结合这种问题,结合目前开发经验,可以使用neo4j 框架的session 进行原生cypherSql的执行。所以结合使用session进行动态可配等思路,建立一个Neo4jUtil..
Neo4jUtil作用
SpringBoot结合neo4j,自定义封装cypherSql进行操作。实际就是使用neo4j的Session.执行一些cypherSql操作,然后封装了一些方法供大家在既可以使用springboot的对象化操作方式的前提创建节点或者关系,也可以自定义继续封装一些特殊需求的方法,避免大家造轮子。
相关代码
application.yml
spring:
data:
neo4j:
uri: bolt://127.0.0.1:7688
username: neo4j
password: neo4j
config
package com.troy.keeper.modules.desk.config;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
@Configuration
@EnableNeo4jRepositories("com.troy.keeper.desc.repository") // 声明neo4j repository存放地址
public class Neo4jConfig {
@Value("${spring.data.neo4j.uri}")
private String uri;
@Value("${spring.data.neo4j.username}")
private String userName;
@Value("${spring.data.neo4j.password}")
private String password;
@Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration.Builder().uri(uri).connectionPoolSize(100).credentials(userName, password).withBasePackages("com.troy.keeper.desc.repository").build();
return configuration;
}
@Bean
public SessionFactory sessionFactory() {
return new SessionFactory(getConfiguration());
}
@Bean("neo4jTransaction")
public Neo4jTransactionManager neo4jTransactionManager(SessionFactory sessionFactory) {
return new Neo4jTransactionManager(sessionFactory);
}
}
Entity
Neo4jBasicNode
//节点实体类
package com.troy.keeper.modules.desk.entity.neo4j;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* neo4j 节点实体类
* @author YangBM
*/
@Data
public class Neo4jBasicNode implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 标签
*/
private List
/**
* 标签属性
*/
private Map
}
Neo4jBaiscRelation
//关系实体类
package com.troy.keeper.modules.desk.entity.neo4j;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* 关系
*/
@Data
public class Neo4jBaiscRelation implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
private Long id;
/**
* 标签
*/
private String type;
/**
* 标签属性
*/
private Map
}
Neo4jqueryRelation
//查询关系的时候返回的对象封装的实体类
package com.troy.keeper.modules.desk.entity.neo4j;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* 关系
* @author YangBM
*/
@Data
public class Neo4jQueryRelation implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 开始节点id
*/
private Long start;
/**
* 结束节点id
*/
private Long end;
/**
* 关系类型
*/
private String type;
/**
* id
*/
private Long id;
/**
* 标签属性
*/
private Map
}
VO
Neo4jBasicRelationReturnVO
package com.troy.keeper.modules.desk.vo.neo4j;
import com.troy.keeper.modules.desk.entity.neo4j.Neo4jBasicNode;
import com.troy.keeper.modules.desk.entity.neo4j.Neo4jQueryRelation;
import lombok.Data;
import java.io.Serializable;
/**
* 基础返回关系VO
* 关系
* @author YangBM
*/
@Data
public class Neo4jBasicRelationReturnVO implements Serializable {
private static final long serialVersionUID = 1L;
private Neo4jBasicNode start;
private Neo4jQueryRelation relationship;
private Neo4jBasicNode end;
}
RelationVO
package com.troy.keeper.modules.desk.vo.neo4j;
import lombok.Data;
/**
* 将关系的实体类,转换换成cypherSql需要字符串类型的vo
* Util里面会用到
* @author YangBM
*/
@Data
public class RelationVO {
/**
* 关系名称
*/
private String relationLabelName;
/**
* 开始标签名称
*/
private String startLabelName;
/**
* 开始节点条件
*/
private String startNodeProperties;
/**
* 关系的属性
*/
private String relationProperties;
/**
* 结束节点条件
*/
private String endNodeProperties;
/**
* 结束标签名称
*/
private String endLabelName;
/**
* 查询层级
*/
private String level;
}
Util
Neo4jUtil
package com.troy.keeper.modules.desk.util;
import com.fasterxml.jackson.core.jsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.troy.keeper.core.tool.utils.BeanUtil;
import com.troy.keeper.core.tool.utils.Func;
import com.troy.keeper.modules.desk.dto.neo4j.Neo4jSaveRelationDTO;
import com.troy.keeper.modules.desk.dto.neo4j.RelationDTO;
import com.troy.keeper.modules.desk.entity.neo4j.Neo4jBaiscRelation;
import com.troy.keeper.modules.desk.entity.neo4j.Neo4jBasicNode;
import com.troy.keeper.modules.desk.entity.neo4j.Neo4jQueryRelation;
import com.troy.keeper.modules.desk.vo.neo4j.Neo4jBasicRelationReturnVO;
import com.troy.keeper.modules.desk.vo.neo4j.RelationVO;
import lombok.SneakyThrows;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.driver.internal.InternalPath;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.neo4j.ogm.model.Property;
import org.neo4j.ogm.model.Result;
import org.neo4j.ogm.response.model.NodeModel;
import org.neo4j.ogm.session.Session;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
/**
* @author YangBM
* Neo4J工具类
*/
@Component
public class Neo4jUtil {
/**
* init map序列号
*/
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, false);
}
@SneakyThrows
public static String propertiesMapToPropertiesStr(Map
map.entrySet().removeIf(entry -> Func.isEmpty(entry.getValue()));
return mapper.writeValueAsString(map);
}
@Resource
private Session session;
public Session getSession() {
return this.session;
}
/**
* 获取所有的标签名称
*
* @return
*/
public List
String cypherSql = "match (n) return distinct labels(n) as name";
Result query = session.query(cypherSql, new HashMap<>());
ArrayList
for (Map
String[] names = (String[]) map.get("name");
for (String name : names) {
labelNames.add(name);
}
}
return labelNames;
}
/**
* 获取所有的关系名称
*
* @return
*/
public List
String cypherSql = "MATCH ()-[r]-() RETURN distinct type(r) as name";
Result query = session.query(cypherSql, new HashMap<>());
ArrayList
for (Map
relationNames.add(map.get("name").toString());
}
return relationNames;
}
/**
* 按条件查询节点
*
* @param node
* @return 返回节点集合
*/
public List
String cypherSql = "";
if (Func.isNotEmpty(node.getId())) {
cypherSql = String.format("MATCH (n) where id(n)=%s return n", node.getId());
} else {
String labels = "";
if (Func.isNotEmpty(node.getLabels())) {
labels = ":`" + String.join("`:`", node.getLabels()) + "`";
}
String property = "";
if (Func.isNotEmpty(node.getProperty())) {
property = Neo4jUtil.propertiesMapToPropertiesStr(node.getProperty());
}
cypherSql = String.format("match(n%s%s) return n", labels, property);
}
Result query = session.query(cypherSql, new HashMap<>());
ArrayList
Iterable
for (Map
NodeModel queryNode = (NodeModel) map.get("n");
Neo4jBasicNode startNodeVo = new Neo4jBasicNode();
startNodeVo.setId(queryNode.getId());
startNodeVo.setLabels(Arrays.asList(queryNode.getLabels()));
List
HashMap
for (Property
if (proMap.containsKey(stringObjectProperty.getKey())) {
throw new RuntimeException("数据重复");
}
proMap.put(stringObjectProperty.getKey(), stringObjectProperty.getValue());
}
startNodeVo.setProperty(proMap);
nodeList.add(startNodeVo);
}
session.clear();
return nodeList;
}
/**
* 创建节点
*
* @param node 节点
* @param nodup 是否去重。 true去重 false不去重
* @return
*/
public boolean createNode(Neo4jBasicNode node, Boolean nodup) {
String labels = "";
if (Func.isNotEmpty(node.getLabels())) {
labels = ":`" + String.join("`:`", node.getLabels()) + "`";
}
String property = "";
if (Func.isNotEmpty(node.getProperty())) {
property = Neo4jUtil.propertiesMapToPropertiesStr(node.getProperty());
}
String cypherSql = String.format("%s(%s%s)", nodup ? "MERGE" : "create", labels, property);
Result query = session.query(cypherSql, new HashMap<>());
session.clear();
return query.queryStatistics().getNodesCreated() > 0;
}
/**
* 创建节点(不去重)
*
* @param node 节点
* @param
* @return
*/
public boolean createNode(Neo4jBasicNode node) {
return this.createNode(node, false);
}
/**
* 创建节点,(去重增强型)
* 创建节点,如果节点存在,先把它删除,在重新创建
* 这个方法的目的是因为 createNode方法所谓的去重,是指如果 ,已有节点A,需要创建的节点B,如果A的属性个数大于B的属性且属性对应的值一模一样,就会创建一个新的A。所以现在的方式是对B新增A中B缺少的属性
* @param node
* @return
*/
public boolean recreateNode(Neo4jBasicNode node){
List
Map
Set
//查询用属性查询节点是不是存在。
//存在比较标签的lable1是不是一样。不一样就这个查询到的节点(少了就新增标签,多了就删除标签)
Neo4jBasicNode queryNode= BeanUtil.copy(node,Neo4jBasicNode.class);
queryNode.setLabels(null);
List
if (queryNodeList.isEmpty()){
return createNode(node,true);
}
for (Neo4jBasicNode neo4jBasicNode : queryNodeList) {
//处理标签
List
ArrayList
for (String saveLabel : saveLabels) {
if (!queryLabels.contains(saveLabel)){
//新增标签
addLabels.add(saveLabel);
}
}
String addLabelStr=addLabels.isEmpty()?"":("e:"+String.join(":",addLabels));
//处理属性
Map
Set
HashMap
for (String savePropertyKey: savePropertyKeySet) {
if (!queryPropertyKeySet.contains(savePropertyKey)){
addPropertyMap.put(savePropertyKey,saveProperty.get(savePropertyKey));
}
}
String addPropertyStr=addPropertyMap.isEmpty()?"":(",e+="+ Neo4jUtil.propertiesMapToPropertiesStr(addPropertyMap));
if(StringUtils.isAllEmpty(addPropertyStr,addPropertyStr)){
return true;
}
String addLabelCypherSql =String.format("MERGE (e) with e where id(e)=%s set %s %s return count(e) as count",neo4jBasicNode.getId(),addLabelStr,addPropertyStr);
Result query = session.query(addLabelCypherSql, new HashMap<>());
System.out.println("跟新了:"+neo4jBasicNode.getId());
session.clear();
}
//创建不重复节点
return true;
};
/**
* 批量创建节点(存在的节点将会被重复创建)
*
* @param nodeList 节点的list集合
* @return 创建成功条数
*
*/
public Long batchCreateNode(List
return this.batchCreateNode(nodeList, false);
}
/**
* 批量创建节点
*
* @param nodeList 节点的list集合
* @param nodup 是否去重。 true去重(存在的节点将不会被创建) false不去重
* @return 创建成功条数
*/
public Long batchCreateNode(List
ArrayList
//验证
for (Neo4jBasicNode neo4jBasicNode : nodeList) {
if ((!nodup) || this.queryNode(neo4jBasicNode).size() == 0) {
addNode.add(neo4jBasicNode);
}
}
String cypherSql = "create";
ArrayList
for (Neo4jBasicNode node : addNode) {
String labels = "";
if (Func.isNotEmpty(node.getLabels())) {
labels = ":`" + String.join("`:`", node.getLabels()) + "`";
}
String property = "";
if (Func.isNotEmpty(node.getProperty())) {
property = Neo4jUtil.propertiesMapToPropertiesStr(node.getProperty());
}
content.add(String.format("(%s%s)", labels, property));
}
cypherSql += String.join(",", content);
if (content.size() == 0) {
return 0L;
}
Result query = session.query(cypherSql, new HashMap<>());
session.clear();
return Long.valueOf(query.queryStatistics().getNodesCreated());
}
/**
* 删除节点和相关关系
*
* @param node 节点条件
http:// * @param delRelation true 删除节点相关的关系;false 只删除不存在关系的,存在关系的节点将不会被删除关系
* @return
*/
public Integer delNode(Neo4jBasicNode node, boolean delRelation) {
String cypherSql = "";
if (Func.isNotEmpty(node.getId())) {
cypherSql = String.format("MATCH (n) where id(n)=%s ", node.getId());
} else {
String labels = "";
if (Func.isNotEmpty(node.getLabels())) {
labels = ":`" + String.join("`:`", node.getLabels()) + "`";
}
String property = "";
if (Func.isNotEmpty(node.getProperty())) {
property = Neo4jUtil.propertiesMapToPropertiesStr(node.getProperty());
}
cypherSql = String.format("match(n%s%s) ", labels, property);
}
if (delRelation) {
cypherSql += "DETACH DELETE n";
} else {
//删除不存在关系的节点
cypherSql += " where not exists((n)-[]-()) DELETE n";
}
Result query = session.query(cypherSql, new HashMap<>());
session.clear();
return query.queryStatistics().getNodesDeleted();
}
/**
* 删除节点和相关关系
* 只删除不存在关系的,存在关系的节点将不会被删除关系
*
* @param node 节点条件 有关系的节点不会删除
* @return
*/
public Integer delNode(Neo4jBasicNode node) {
return this.delNode(node, false);
}
public int queryNodeCreateRelation(Neo4jBasicNode start, Neo4jBasicNode end, Neo4jBaiscRelation relation) {
Neo4jSaveRelationDTO dto = new Neo4jSaveRelationDTO();
dto.setStart(start);
dto.setEnd(end);
dto.setRelationship(relation);
return queryNodeCreateRelation(dto);
}
/**
* 查询节点然后创建关系
* 创建关系(查询开始节点和结束节点然后创造关系)
* 注意:开始节点和结束节点以及创建的关系参数一定要存在!
* 关系如果存在,不会重复创建
* 因为需要返回创建条数 当前方法未做条件判断
*
* @param saveRelation 关系构造类
* @return 返回创建关系的个数
*/
public int queryNodeCreateRelation(Neo4jSaveRelationDTO saveRelation) {
//开始节点和结束节点验证
String cypherSql = "";
String startLable = "";
if (Func.isNotEmpty(saveRelation.getStart().getLabels())) {
startLable = ":`" + String.join("`:`", saveRelation.getStart().getLabels()) + "`";
}
String startProperty = "";
if (Func.isNotEmpty(saveRelation.getStart().getProperty())) {
startProperty = Neo4jUtil.propertiesMapToPropertiesStr(saveRelation.getStart().getProperty());
}
String endLable = "";
if (Func.isNotEmpty(saveRelation.getEnd().getLabels())) {
endLable = ":`" + String.join("`:`", saveRelation.getEnd().getLabels()) + "`";
}
String endProperty = "";
if (Func.isNotEmpty(saveRelation.getEnd().getProperty())) {
endProperty = Neo4jUtil.propertiesMapToPropertiesStr(saveRelation.getEnd().getProperty());
}
String startWhere = "";
if (Func.isNotEmpty(saveRelation.getStart().getId())) {
startWhere += " where id(start)=" + saveRelation.getStart().getId();
startLable = "";
startProperty = "";
}
String endWhere = "";
if (Func.isNotEmpty(saveRelation.getEnd().getId())) {
endWhere += " where id(end)=" + saveRelation.getEnd().getId();
endLable = "";
endProperty = "";
}
String relationType = "";
if (Func.isNotEmpty(saveRelation.getRelationship().getType())) {
relationType = ":`" + saveRelation.getRelationship().getType() + "`";
}
if (Func.isEmpty(relationType)) {
throw new RuntimeException("关系名称不能为空!");
}
String relationProperty = "";
if (Func.isNotEmpty(saveRelation.getRelationship().getProperty())) {
relationProperty = Neo4jUtil.propertiesMapToPropertiesStr(saveRelation.getRelationship().getProperty());
}
cypherSql = String.format("MATCH (start%s%s) %s with start MATCH (end%s%s) %s MERGE (start)-[rep%s%s]->(end)", startLable, startProperty, startWhere, endLable, endProperty, endWhere, relationType, relationProperty);
Result query = session.query(cypherSql, new HashMap<>());
session.clear();
return query.queryStatistics().getRelationshipsCreated();
}
/**
* 创建节点同时创建关系
* 重复的不会被创建
*
* @param saveRelation
* @return
*/
public boolean creteNodeAndRelation(Neo4jSaveRelationDTO saveRelation) {
String cypherSql = "";
String startLable = "";
if (Func.isNotEmpty(saveRelation.getStart().getLabels())) {
startLable = ":`" + String.join("`:`", saveRelation.getStart().getLabels()) + "`";
}
String startProperty = "";
if (Func.isNotEmpty(saveRelation.getStart().getProperty())) {
startProperty = Neo4jUtil.propertiesMapToPropertiesStr(saveRelation.getStart().getProperty());
}
String endLable = "";
if (Func.isNotEmpty(saveRelation.getEnd().getLabels())) {
endLable = ":`" + String.join("`:`", saveRelation.getEnd().getLabels()) + "`";
}
String endProperty = "";
if (Func.isNotEmpty(saveRelation.getEnd().getProperty())) {
endProperty = Neo4jUtil.propertiesMapToPropertiesStr(saveRelation.getEnd().getProperty());
}
String relationType = "";
if (Func.isNotEmpty(saveRelation.getRelationship().getType())) {
relationType = ":`" + saveRelation.getRelationship().getType() + "`";
}
if (Func.isEmpty(relationType)) {
throw new RuntimeException("关系名称不能为空!");
}
String relationProperty = "";
if (Func.isNotEmpty(saveRelation.getRelationship().getProperty())) {
relationProperty = Neo4jUtil.propertiesMapToPropertiesStr(saveRelation.getRelationship().getProperty());
}
cypherSql = String.format("MERGE (start%s%s)-[rep%s%s]->(end%s%s)", startLable, startProperty, relationType, relationProperty, endLable, endProperty);
Result query = session.query(cypherSql, new HashMap<>());
session.clear();
return query.queryStatistics().getRelationshipsCreated() > 0;
}
/**
* 查询关系
*
* @param relationDTO
* @return
*/
public List
RelationVO relationVO = formatRelation(relationDTO);
//拼接sql
String cypherSql = String.format("MATCH p=(a%s%s)-[r%s%s]->(b%s%s)-[*0..%s]->() RETURN p", relationVO.getStartLabelName(), relationVO.getStartNodeProperties(), relationVO.getRelationLabelName(), relationVO.getRelationProperties(), relationVO.getEndLabelName(), relationVO.getEndNodeProperties(), relationVO.getLevel());
System.out.println(cypherSql);
long startTime = System.currentTimeMillis();
Result query = session.query(cypherSql, new HashMap<>());
System.out.println(String.format("耗时%d秒", System.currentTimeMillis() - startTime));
Iterable
ArrayList
for (Map
InternalPath.SelfContainedSegment[] ps = (InternalPath.SelfContainedSegment[]) map.get("p");
for (InternalPath.SelfContainedSegment p : ps) {
returnList.add(changeToNeo4jBasicRelationReturnVO(p));
}
}
session.clear();
return returnList;
}
/**
* 格式化
*
* @param relationDTO
* @return
*/
public RelationVO formatRelation(RelationDTO relationDTO) {
RelationVO relationVO = new RelationVO();
//验证
if (Func.isNotEmpty(relationDTO.getRelationLabelName())) {
relationVO.setRelationLabelName(":`" + relationDTO.getRelationLabelName()+"`");
} else {
relationVO.setRelationLabelName("");
}
if (Func.isNotEmpty(relationDTO.getStartLabelName())) {
relationVO.setStartLabelName(":`" + relationDTO.getStartLabelName()+"`");
} else {
relationVO.setStartLabelName("");
}
if (Func.isNotEmpty(relationDTO.getEndLabelName())) {
relationVO.setEndLabelName(":`" + relationDTO.getEndLabelName()+"`");
} else {
relationVO.setEndLabelName("");
}
if (Func.isNotEmpty(relationDTO.getStartNodeProperties())) {
relationVO.setStartNodeProperties(Neo4jUtil.propertiesMapToPropertiesStr(relationDTO.getStartNodeProperties()));
} else {
relationVO.setStartNodeProperties("");
}
if (Func.isNotEmpty(relationDTO.getRelationProperties())) {
relationVO.setRelationProperties(Neo4jUtil.propertiesMapToPropertiesStr(relationDTO.getRelationProperties()));
} else {
relationVO.setRelationProperties("");
}
if (Func.isNotEmpty(relationDTO.getEndNodeProperties())) {
relationVO.setEndNodeProperties(Neo4jUtil.propertiesMapToPropertiesStr(relationDTO.getEndNodeProperties()));
} else {
relationVO.setEndNodeProperties("");
}
if (Func.isNotEmpty(relationDTO.getLevel())) {
relationVO.setLevel(relationDTO.getLevel().toString());
} else {
relationVO.setLevel("");
}
return relationVO;
}
/**
* 转化neo4j默认查询的参数为自定返回类型
*
* @param selfContainedSegment
* @return Neo4jBasicRelationReturn
*/
public Neo4jBasicRelationReturnVO changeToNeo4jBasicRelationReturnVO(InternalPath.SelfContainedSegment selfContainedSegment) {
Neo4jBasicRelationReturnVO neo4JBasicRelationReturnVO = new Neo4jBasicRelationReturnVO();
//start
Node start = selfContainedSegment.start();
Neo4jBasicNode startNodeVo = new Neo4jBasicNode();
startNodeVo.setId(start.id());
startNodeVo.setLabels(IteratorUtils.toList(start.labels().iterator()));
startNodeVo.setProperty(start.asMap());
neo4JBasicRelationReturnVO.setStart(startNodeVo);
//end
Node end = selfContainedSegment.end();
Neo4jBasicNode endNodeVo = new Neo4jBasicNode();
endNodeVo.setId(end.id());
endNodeVo.setLabels(IteratorUtils.toList(end.labels().iterator()));
endNodeVo.setProperty(end.asMap());
neo4JBasicRelationReturnVO.setEnd(endNodeVo);
//relationship
Neo4jQueryRelation neo4JQueryRelation = new Neo4jQueryRelation();
Relationship relationship = selfContainedSegment.relationship();
neo4JQueryRelation.setStart(relationship.startNodeId());
neo4JQueryRelation.setEnd(relationship.endNodeId());
neo4JQueryRelation.setId(relationship.id());
neo4JQueryRelation.setType(relationship.type());
neo4JQueryRelation.setProperty(relationship.asMap());
neo4JBasicRelationReturnVO.setRelationship(neo4JQueryRelation);
return neo4JBasicRelationReturnVO;
}
}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~