环境准备

  • 开发工具与依赖:JDK 8+、Maven、Neo4j 3及以上(作者使用版本为3.5.35)。
  • neo4j 浏览器可视化地址端口:7474,服务端口:7687,默认账户密码:neo4j/neo4j
  • pom引入 Neo4j Java 驱动依赖(如 org.neo4j.driver)。
  • Java yml文件 spring.data.neo4j 配置 Neo4j 连接参数(URI、用户名、密码)。
spring:
  data:
    neo4j:
      uri: bolt://localhost:7687
      username: neo4j
      password: 123456

参考官网

neo4j 图数据库连接管理工具类

示例代码:

 import org.neo4j.driver.*;
 import org.neo4j.driver.types.Node;
 import org.neo4j.driver.types.Relationship;
 /**
 * neo4j 图数据库连接管理工具类 
 */
public class Neo4jGraphUtils {
  	//neo4j服务驱动
  	static Driver driver;
  	  
  	/**
  	* 获取neo4j连接
  	* @param boltUrl neo4j bolt地址
  	* @param user 用户名
  	* @param password 密码
  	*/ 
  	public void getNeo4jDriver(String boltUrl,String user,String password) {
  		driver = GraphDatabase.driver(boltUrl, AuthTokens.badic(user,password));
  	}

  	//关闭连接
  	public static void closeNeo4j() {
  		if(driver != null){
  			driver.close();
  		}
  	}
  	
  	/**
  	* 添加节点
  	* @param node 节点名称
  	* @param properties 节点属性信息
  	*/ 
  	public static void addNode(String node ,String properties ) {
  		try(Session session = driver.session()){
  			String cypher = "Merge (a:"+node+" "joinMap(properties)+")";
  			session.writeTransaction(tx -> tx.run(cypher));
  		}catch(Exception e){
  			e.printStacktrace();
  		}
  	}
  	/**
  	* 添加节点联系关系
  	* @param node 节点名称数组
  	* @param nodePropertyShip 节点属性 关联字段数组
  	* @param relationshipType 联系关系类型名称
  	* @param relationship 联系关系属性内容
  	*/ 
  	public static void addRelationship(String[] node ,String[] nodePropertyShip,String relationshipType ,Map<String,Object> relationship) {
  		try(Session session = driver.session()){
  			String relationshipStr = ObjectUtils.isEmpty(relationship) ? relationshipType : relationshipType  + " " + joinMap(relationship);
  			String cypher = "MATCH (n1:"+node[0]+"),(n2:"+node[1]+") WHERE n1.+nodePropertyShip[0]+" = n2.nodePropertyShip[1]+
  			" CREATE (n1)-[: lx_"+relationshipStr+"]->(n2)";
  			session.writeTransaction(tx -> tx.run(cypher));
  		}catch(Exception e){
  			e.printStacktrace();
  		}
  	}	
  	/**
  	* 删除节点
  	* @param node 节点名称
  	*/ 
  	public static void deleteNodeAll(String node) {
  		try(Session session = driver.session()){
  			//删除节点 保留关系
  			String cypher = "Merge (n:"+node+" ) delete n ";
  			session.writeTransaction(tx -> tx.run(cypher));
  		}catch(Exception e){
  			e.printStacktrace();
  		}
  	}
  	/**
  	* 删除连线关系
  	* @param ship 连线关系名称
  	*/ 
  	public static void deleteShipAll(String ship) {
  		try(Session session = driver.session()){
  			String cypher = "Merge (n)-[r: "+ship+" ]-() delete r ";
  			session.writeTransaction(tx -> tx.run(cypher));
  		}catch(Exception e){
  			e.printStacktrace();
  		}
  	}
  	/**
  	* 删除节点和连线关系
  	* @param node 节点名称
  	*/ 
  	public static void deleteNodeAll(String node) {
  		try(Session session = driver.session()){
  			//去除 detach 关系
  			String cypher = "Merge (n:"+node+" ) detach  delete n ";
  			session.writeTransaction(tx -> tx.run(cypher));
  		}catch(Exception e){
  			e.printStacktrace();
  		}
  	}
  	/**
  	* 获取节点和连线关系信息
  	* @param mainNode 主表节点
  	* @param mainNodePropertyName 主表节点属性名称
  	* @param mainNodePropertyId 主表节点属性id
  	* @param level 查询最大层级数
  	*/ 
  	public static List<Map<String,Object>> getNodesInfo(String mainNode,String mainNodePropertyName,String mainNodePropertyId ,String cypher,String level) {
  		List<Map<String,Object>> result = new ArrayList<>();
  		Set<Map<String,Object>> set;
  		try(Session session = driver.session()){
  			//校验未传cypher时,按前面参数赋值
  			if(StringUtils.isEmpty(cypher)) {
  				if(StringUtils.isEmpty(level)) {
  					//默认查询全部
  					level = “”;
  				}
  				cypher = "match (n:"+mainNode+" {"+mainNodePropertyName+":\""+mainNodePropertyId+“\"})-[r*0.."+level+"]-(n1) return n,r,n1";
  			}
  			//执行cypher语句后 遍历结果
  			session.run(cypher).forEachRemaining(record ->{
  				//获取节点、联系信息key
  				List<String> keys = record.keys();
  				for(String key : keys){
  					//处理节点关系信息获取
  					if(key.contains("r")){
  						if(ObejctUtils.isEmpty(record.get(key))){
  							continue;
  						}
  						for(int i=0;i<record.get(key).size();i++){
  							Relationship relationship = record.get(key).get(i).asRelationship();
  							long relId = relationship.id();
  							String type = relationship.type();
  							long startNodeId = relationship.startNodeId();
  							long endNodeId = relationship.endNodeId();
  							Map<String,Object> relProperties = relationship.asMap();
  							Map<String,Object> resultMap = new HashMap<>();
  							resultMap.put("id",relId);
  							resultMap.put("type","relationship");
  							resultMap.put("zstpShowName",relProperties.get("关系").toString());
  							resultMap.put("startNodeId",startNodeId);
  							resultMap.put("endNodeId",endNodeId);
  							resultMap.put("properties",relProperties);
  							result.add(resultMap);
  						}
  						continue;
  					}
  					//处理节点信息获取
  					Node node = record.get(key).asNode();
  					long nodeId = node.id();
  					List<String> labels = (List<String) node.labels();
  					Map<String,Object> nodeProperties = node.asMap();
  					Map<String,Object> resultMap = new HashMap();
  					resultMap.put("id",nodeId);
  					resultMap.put("ty1pe","node");
  					//获取预览回显值
  					String zszdLabel = nodeProperties.get("zszdLabel").toString();
  					resultMap.put("zstpShowName",nodeProperties.get(zszdLabel).toString());
  					resultMap.put("properties",nodeProperties);
  					result.add(resultMap);
  				}
  			});
  		}catch(Exception e){
  			e.printStacktrace();
  		}finally{
  			//去重
  			set = new HashSet<>(result);
  			List<Map<String,Object>> noDuplicateList = new ArrayList<>(set);
  			return noDuplicateList;
  		}
  	}

  	//map转str 适配neo4j格式
  	public static <K,V> String joinMap(Map<K,V> map){
  		if(map == null || map.isEmpty()) return "";
  		StringBuilder sb = new StringBuilder();
  		sb.append("{");
  		boolean firstEntry = true;
  		for(Map.Entry<K,V> entry : map.entrySet()){
  			K key = entry.getKey();
  			V value = entry.getValue();
  			// 如果不是第一个条目,添加逗号
  			if(!firstEntry){
  				sb.append(",");
  			}else{
  				firstEntry = false;
  			}
  			// 添加键和值,并将=替换成:
  			sb.append(key).append(":").append("\""+value+"\"");
  		}
  		sb.append("}");
  		return sb.toString();
  	}

  	/**
  	 *
  	 * 获取节点最大层级数
  	 * @param mainNode  主表节点
  	 * @param mainNodePropertyName 主表节点属性名称
  	 * @param mainNodePropertyId 主表节点属性id
  	 */
  	 public static Integer getNodeMaxLevel(String mainNode,String mainNodePropertyName,String mainNodePropertyId){
  	 	//返回结果信息
  	 	final Integet[] maxLevel = new Integer[1];
  	 	try(Session session = driver.session()){
  	 		//执行最大层级数查询
  	 		String cypher = "match (n:"+mainNode+" {"+mainNodePropertyName+":\""+mainNodePropertyId+"\"})-[r*0..]-(n1) return max(length(r)) as level";
  	 		//执行cypher语句后 遍历结果
  			session.run(cypher).forEachRemaining(record ->{
  				maxLevel[0] = Integer.parseInt(record,values().get(0).toString());
  			);
  		}catch(Exception e){
  			e.printStacktrace();
  		}
  		return maxLevel[0];
  	 }

常见Cypher语句

  1. 删除所有关系
    MATCH (n)-[r]-() DELETE r
    MATCH (n)-[r:指定关系名]-() DELETE r
  2. 删除所有节点 (DETACH 先将所有与该节点相连的边(relationship)从图数据库中移除然后再进行节点本身的删除操作)
    MATCH (n) DETACH DELETE n
    MATCH (n:Person) DETACH DELETE n
  3. 添加节点(该节点存在则不更改,否则新建并返回)
    MERGE (p:Person {id:521,name:"李四"}) RETURN p//3.5.35用MATCH 4.4.36版本使用MERGE
  4. 添加关系
    MATCH (n:Person {name:"张三"}),(p:Movie {名称:"魔幻手机"}) CREATE (u)-[:看过]->(p)
    MATCH (n:Person {name:"张三"}),(p:Movie {名称:"魔幻手机"}) WHERE n.id = p.nId CREATE (u)-[:看过]->(p)
    //下面俩是4.4.36版本使用
    MERGE (n:Person {name:"张三"}) MERGE (p:Movie {name:"魔幻手机"}) MERGE (n)-[:看过]->(p) // 关系已存在则不创建,不存在则创建 RETURN n, p;
    CREATE (n:Person {name:"李四"})-[:看过]->(p:Movie {name:"魔幻手机"}) //都是新创建的
  5. 查看全部节点及关系
    MATCH (n) RETURN n LIMIT 10
  6. 查看指定条件的节点及关系
    MATCH (n:Person {name:"张三"})-[r:看过]->(m:Movie) return n,r,m
  7. 查看某一实体节点开始的深度遍历 无层级限制的深度遍历 1…1是从第几层到第几层
    MATCH (n:Person {id:"2"})-[r*0..]-(n1) return n,r,n1
    MATCH (n:Person {id:"2"})-[r*1..1]-(n1) return n,r,n1
  8. 获取某一节点开始往下深度遍历最大的层级数
    MATCH (n:Person {id:"2"})-[r*0..]-(n1) return max(length(r))
    MATCH p=(n:Movie {名称:"魔幻手机"})-[*0..]-(n1) RETURN max(length(p)) AS max_path_length;// 4.4.36版本使用
  9. 查看所有节点名称标签
    MATCH (n) RETURN DISTINCT labels(n)
  10. 查看所有关系名称
    MATCH ()-[r]-() RETURN DISTINCT type(r)
  11. 修改节点属性
    MATCH (n:Person {id:"2"}) SET n.age=30 RETURN n
  12. 条件存在则修改 否则新增
    MERGE (p:Person {name: "Alice"}) ON CREATE SET p.created = timestamp(), p.age = 25, p.status = "new" ON MATCH SET p.lastSeen = timestamp(), p.status = "updated" RETURN p.name, p.age, p.status, p.created, p.lastSeen
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐