springboot + neo4j 功能使用

注意:

1、使用jpa处理数据时,如果对象中有 relationship 关系,数据中没有写关系,更新数据时会将原有的关系删除掉(出来方法可以先查询出原来的数据,然后在编辑)

2、

集成

添加依赖

        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-neo4j</artifactId>
		</dependency>
spring:
  # neo4j 图数据库
  neo4j:
    uri: bolt://localhost:7687
    authentication:
      username: neo4j
      password: admin
  # 指定数据库
  data:
    neo4j:
      database: neo4j

如果和数据库一起集成,需要配置多数据源事务,不然事务会失效

import org.neo4j.driver.Driver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;

/**
 * 配置neo4j 和 mysql 事务
 * 1、@EnableNeo4jRepositories 用于扫描指定包下的repository
 * 2、@EntityScan 用于扫描 neo4j实体类
 */
@Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(basePackages = {"com.graph.repository"})
@EntityScan(basePackages = {"com.graph.model.neo4j"})
public class Neo4jConfig {

	/**
	 * 此处为了修改默认事务,必须改。
	 * 加载了Neo4J依赖库之后,transactionManager变成Neo4jTransactionManager
	 *
	 * @param dataSource 数据源
	 * @return
	 */
	@Bean("transactionManager")
	@Primary
	public DataSourceTransactionManager transactionManager(DataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}

	/**
	 * Neo4J的事务管理
	 *
	 * @param driver
	 * @return
	 */
	@Bean("neo4jTransactionManager")
	public Neo4jTransactionManager neo4jTransactionManager(Driver driver) {
		return new Neo4jTransactionManager(driver);
	}

	/**
	 * 需要使用多种事务时
	 *
	 * @param neo4jTransactionManager
	 * @param mysqlTransactionManager
	 * @return
	 */
	@Autowired
	@Bean(name = "multiTransactionManager")
	public PlatformTransactionManager multiTransactionManager(
			Neo4jTransactionManager neo4jTransactionManager,
			DataSourceTransactionManager mysqlTransactionManager) {
		return new ChainedTransactionManager(
				neo4jTransactionManager, mysqlTransactionManager);
	}
}

1.使用Neo4jRepository 进行查询,可以使用jpa进行查询

import lombok.Data;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;

/**
 * 文献
 *
 * @author kou
 */
@Data
@Node(labels = "文献")
public class Literature {

	@Id
	@GeneratedValue
	private Long id;

	@Property(name = "name")
	private String name;


	/**
	 * 文件路径
	 */
	@Property(name = "url")
	private String url;

}
import com.haiwanyoutian.hai.graph.model.neo4j.Literature;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 文献 持久层
 *
 * @author kou
 */
@Repository
public interface LiteratureRepository extends Neo4jRepository<Literature, Long> {

	/**
	 * 按名称查询
	 *
	 * @param name 名称
	 * @return 结果
	 */
//	@Query("MATCH(p:`文献`{name:$name}) return p")
	List<Literature> findByName(@Param("name") String name);

	/**
	 * 按名称模糊查询
	 *
	 * @param name 名称
	 * @return 结果
	 */
	List<Literature> findByNameLike(@Param("name") String name);

	/**
	 * 分页查询
	 *
	 * @param name     名称
	 * @param pageable 分页条件
	 * @return 结果
	 */
	Page<Literature> findByNameLike(@Param("name") String name, Pageable pageable);

	@Query(value = "match (n:`文献`) " +
			" WHERE (n.name CONTAINS $keyword" +
			" or n.full_paper_outline_en CONTAINS $keyword" +
			" or n.full_paper_outline_zh CONTAINS $keyword" +
			" or n.full_paper_summary_en CONTAINS $keyword" +
			" or n.full_paper_summary_zh CONTAINS $keyword) " +
			" return n" +
			" SKIP $skip LIMIT $limit ",
			countQuery = "match (n:`文献`) " +
					" WHERE (n.name CONTAINS $keyword" +
					" or n.full_paper_outline_en CONTAINS $keyword" +
					" or n.full_paper_outline_zh CONTAINS $keyword" +
					" or n.full_paper_summary_en CONTAINS $keyword" +
					" or n.full_paper_summary_zh CONTAINS $keyword) " +
					"return count(*)"
	)
	Page<Literature> findByKeyword(@Param("keyword") String keyword, @Param("pageable") Pageable pageable);

	/**
	 * 查询关系
	 *
	 * @param name 名称
	 * @return 结果
	 */
	@Query("MATCH(n:`文献`{name:$name}) return (n)-[]-()")
	Literature queryLiteratureRelation(@Param("name") String name);


	/**
	 * 查询关键字期刊
	 *
	 * @param keyword 关键字
	 * @param limit   数量
	 * @return 结果
	 */
	@Deprecated
	@Query("match(n) where labels(n) in [['文献'],['作者']]  " +
			"with n, [x in keys(n) WHERE n[x]=~'.*'+$keyword+'.*'] as dm " +
			"where size(dm) > 0 " +
			"with n " +
			"LIMIT $limit " +
			"match p=(n)-[]-(k) " +
			"return p ")
	List<Literature> queryLiteratureByKeyword(@Param("keyword") String keyword, @Param("limit") Integer limit);

}

2. 使用cypher进行查询

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 知识接口实现类
 *
 * @author kou
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class LiteratureServiceImpl implements ILiteratureService {

	private final LiteratureRepository literatureRepository;

	private final INeo4jService neo4jService;

	/**
	 * 文献搜索
	 *
	 * @param keyword  关键字
	 * @param pageable 分页查询
	 * @return 结果
	 */
	@Override
	public Page<Literature> search(String keyword, Pageable pageable) {

		if (StringUtils.isNotBlank(keyword)) {
			// return literatureRepository.findByNameLike(keyword, pageable);
			return literatureRepository.findByKeyword(keyword, pageable);
		}
		return literatureRepository.findAll(pageable);
	}

	/**
	 * 文献图库搜索
	 *
	 * @param keyword 关键字
	 * @param limit   数量
	 * @return 结果
	 */
	@Override
	public Object graphSearch(String keyword, Integer limit) {

		StringBuffer sql = new StringBuffer();
		if (StringUtils.isNotBlank(keyword)) {
			sql.append("match(n) where labels(n) in [['文献'],['作者']] ");
			sql.append("with n, [x in keys(n) WHERE n[x]=~'.*").append(keyword).append(".*'] as dm ");
			sql.append("where size(dm) > 0 ");
		} else {
			sql.append("match (n:`文献`) ");
		}
		sql.append("with n ");
		sql.append("LIMIT ").append(limit).append(" ");
		sql.append("match p=(n)-[]-(k) ");
		sql.append("return p ");

		return neo4jService.run(sql.toString());
	}

	/**
	 * 通过文献名查询文献关系
	 *
	 * @param name 文献名
	 * @return 文献关系
	 */
	@Override
	public Object queryGraphByName(String name) {

		StringBuffer sql = new StringBuffer();
		sql.append("match (n:`文献`) ");
		if (StringUtils.isNotBlank(name)) {
			sql.append("where n.name='").append(name).append("' ");
			sql.append("return (n)-[]-() ");
		} else {
			sql.append("return (n)-[]-() ");
			sql.append("limit 200 ");
		}
		return neo4jService.run(sql.toString());
	}

	/**
	 * 通过文献id查询文献知识点关系
	 *
	 * @param id 文献id
	 * @return 结果
	 */
	@Override
	public NodeRelation queryKnowledgePointRelation(Long id) {
		StringBuffer sql = new StringBuffer();
		sql.append("match (p:`文献`)-[rel]-(k:`知识点`) ");
		sql.append(" where id(p) = ").append(id);
		sql.append(" return p, k, rel");

		List<Neo4jData> datas = neo4jService.run(sql.toString());
		return Neo4jNodeUtil.convert(datas);
	}

	/**
	 * 通过节点id查询相关的知识点/文献节点数据
	 *
	 * @param id 节点id
	 * @return 结果
	 */
	@Override
	public NodeRelation queryLiteratureOrKnowledgePointRelation(Long id) {

		StringBuffer sql = new StringBuffer();
		sql.append("match (p)-[rel]-(k) ");
		sql.append(" where id(p) = ").append(id).append(" and labels(k) in [['文献'],['知识点']] ");
		sql.append(" return p, k, rel");

		List<Neo4jData> datas = neo4jService.run(sql.toString());
		return Neo4jNodeUtil.convert(datas);
	}

	/**
	 * 通过文献id查询知识词条
	 *
	 * @param id 节点id
	 * @return 结果
	 */
	@Override
	public List<Neo4jNode> queryKnowledgeEntry(Long id) {
		StringBuffer sql = new StringBuffer();
		sql.append("match (p:`文献`)-[:`知识词条`]-(k) ");
		sql.append(" where id(p) = ").append(id);
		sql.append(" return k");

		List<Neo4jData> datas = neo4jService.run(sql.toString());
		return Neo4jNodeUtil.getNodes(datas);
	}

}
import cn.hutool.core.collection.CollectionUtil;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.neo4j.driver.*;
import org.neo4j.driver.internal.InternalNode;
import org.neo4j.driver.internal.InternalPath;
import org.neo4j.driver.internal.InternalRelationship;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Path;
import org.neo4j.driver.types.Relationship;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * neo4j 业务接口
 *
 * @author kou
 */
@AllArgsConstructor
@Service
public class Neo4jServiceImpl implements INeo4jService {

	private final Driver driver;

	@Override
	public List<Neo4jData> run(String cypherSql) {

		try (Session session = driver.session()) {
			Result result = session.run(cypherSql);
			return this.parseResult(result);
		}
	}

	/**
	 * 开启事务执行脚本
	 *
	 * @param cypherSql 脚本
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> runTx(String cypherSql) {
		try (Session session = driver.session()) {
			// 开启事务
			try (Transaction tx = session.beginTransaction()) {
				Result result = tx.run(cypherSql);
				List<Neo4jData> dataList = this.parseResult(result);
				// 提交事务
				tx.commit();
				return dataList;
			}
		}
	}

	/**
	 * 开启事务执行脚本
	 *
	 * @param cypherSql  脚本
	 * @param parameters 参数
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> runTx(String cypherSql, Map<String, Object> parameters) {
		try (Session session = driver.session()) {
			// 开启事务
			try (Transaction tx = session.beginTransaction()) {
				Result result = tx.run(cypherSql, parameters);
				List<Neo4jData> dataList = this.parseResult(result);
				// 提交事务
				tx.commit();
				return dataList;
			}
		}
	}

	/**
	 * 开启事务执行脚本
	 *
	 * @param tx        事务
	 * @param cypherSql 脚本
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> runTx(Transaction tx, String cypherSql) {
		// 开启事务
		Result result = tx.run(cypherSql);
		return this.parseResult(result);
	}

	/**
	 * 开启事务执行脚本
	 *
	 * @param tx         事务
	 * @param cypherSql  脚本
	 * @param parameters 参数
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> runTx(Transaction tx, String cypherSql, Map<String, Object> parameters) {
		// 开启事务
		Result result = tx.run(cypherSql, parameters);
		return this.parseResult(result);
	}

	/**
	 * 批量创建节点
	 *
	 * @param label        节点类型
	 * @param nodeDataList 参数,{"name": "", "properties": Map<String, Object>}, label:标签类型,name:节点名称,properties: 节点数据
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> batchCreateNode(String label, List<Map<String, Object>> nodeDataList) {

		// 数据不为空进行入图库
		Map<String, Object> parameters = new HashMap<>(1);
		parameters.put("props", nodeDataList);
		// sql格式:UNWIND $props AS p create/merge(n:Person{name: p.name} set n += p.properties)
		String cypherSql = "UNWIND $props AS p merge(n:" + label + "{name: p.name}) set n += p.properties";

		return this.runTx(cypherSql, parameters);
	}

	/**
	 * 批量创建节点
	 *
	 * @param tx           事务
	 * @param label        节点类型
	 * @param nodeDataList 参数,{"name": "", "properties": Map<String, Object>}, label:标签类型,name:节点名称,properties: 节点数据
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> batchCreateNode(Transaction tx, String label, List<Map<String, Object>> nodeDataList) {

		// 数据不为空进行入图库
		Map<String, Object> parameters = new HashMap<>(1);
		parameters.put("props", nodeDataList);
		// sql格式:UNWIND $props AS p create/merge(n:Person{name: p.name} set n += p.properties)
		String cypherSql = "UNWIND $props AS p merge(n:" + label + "{name: p.name}) set n += p.properties";

		return this.runTx(tx, cypherSql, parameters);
	}

	/**
	 * 批量创建节点
	 *
	 * @param tx           事务
	 * @param labels       节点类型
	 * @param nodeDataList 参数,{"name": "", "properties": Map<String, Object>}, label:标签类型,name:节点名称,properties: 节点数据
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> batchCreateNode(Transaction tx, List<String> labels, List<Map<String, Object>> nodeDataList) {

		// 数据不为空进行入图库
		Map<String, Object> parameters = new HashMap<>(1);
		parameters.put("props", nodeDataList);
		// sql格式:UNWIND $props AS p create/merge(n:Person:Country{name: p.name} set n += p.properties)
		StringBuffer multipleLabels = new StringBuffer();
		for (int i = 0; i < labels.size(); i++) {
			if (StringUtils.isNotBlank(labels.get(i))) {
				multipleLabels.append(":").append(labels.get(i));
			}
		}
		String cypherSql = "UNWIND $props AS p merge(n" + multipleLabels + "{name: p.name}) set n += p.properties";

		return this.runTx(tx, cypherSql, parameters);
	}

	/**
	 * 批量创建关系
	 *
	 * @param headLabel    头节点类型
	 * @param tailLabel    尾节点类型
	 * @param relation     关系
	 * @param relationList 参数,{"head": "", tail:"", "properties": Map<String, Object>}, head:头节点名称,tail:尾节点名称,properties: 关系数据,必须有,可以写{}
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> batchCreateRelationship(String headLabel, String tailLabel, String relation, List<Map<String, Object>> relationList) {

		Map<String, Object> parameters = new HashMap<>(1);
		parameters.put("props", relationList);
		// sql格式:UNWIND $props AS p match (n:文献{name: p.head}), (m:作者{name: p.tail}) create (n)-[r:连接]->(m)  SET r += p.properties
		String relationSql = "UNWIND $props AS p match (n: " + headLabel + "{name: p.head}), (m: " + tailLabel + "{name: p.tail}) MERGE (n)-[r:" + relation + "]->(m) SET r += p.properties";

		return this.runTx(relationSql, parameters);
	}

	/**
	 * 批量创建关系
	 *
	 * @param tx           事务
	 * @param headLabel    头节点类型
	 * @param tailLabel    尾节点类型
	 * @param relation     关系
	 * @param relationList 参数,{"head": "", tail:"", "properties": Map<String, Object>}, head:头节点名称,tail:尾节点名称,properties: 关系数据,必须有,可以写{}
	 * @return 结果
	 */
	@Override
	public List<Neo4jData> batchCreateRelationship(Transaction tx, String headLabel, String tailLabel, String relation, List<Map<String, Object>> relationList) {

		Map<String, Object> parameters = new HashMap<>(1);
		parameters.put("props", relationList);
		// sql格式:UNWIND $props AS p match (n:文献{name: p.head}), (m:作者{name: p.tail}) create (n)-[r:连接]->(m)  SET r += p.properties
		String relationSql = "UNWIND $props AS p match (n: " + headLabel + "{name: p.head}), (m: " + tailLabel + "{name: p.tail}) MERGE (n)-[r:" + relation + "]->(m) SET r += p.properties";

		return this.runTx(tx, relationSql, parameters);
	}

	/**
	 * 批量创建节点及关系
	 *
	 * @param nodeRelationDataDto 节点关系数据
	 * @return 结果
	 */
	@Override
	public boolean createNodeAndRelation(NodeRelationDataDto nodeRelationDataDto) {

		try (Session session = driver.session()) {
			// 开启事务
			try (Transaction tx = session.beginTransaction()) {
				// 创建节点
				if (CollectionUtil.isNotEmpty(nodeRelationDataDto.getNodes())) {
					nodeRelationDataDto.getNodes().stream().forEach(d -> {
						this.batchCreateNode(tx, d.getLabels(), d.getProperties());
					});
				}

				// 创建关系
				if (CollectionUtil.isNotEmpty(nodeRelationDataDto.getRelations())) {
					nodeRelationDataDto.getRelations().stream().forEach(r -> {
						this.batchCreateRelationship(tx, r.getHeadLabel(), r.getTailLabel(), r.getRelation(), r.getProperties());
					});
				}
				// 提交事务
				tx.commit();
				return true;
			}
		}
	}

	/**
	 * 解析结果数据
	 *
	 * @param result 执行结果
	 * @return 解析结果
	 */
	private List<Neo4jData> parseResult(Result result) {

		List<Neo4jData> dataList = new ArrayList<>();
		while (result.hasNext()) {
			dataList.add(parseRecord(result.next()));
		}
		return dataList;
	}

	/**
	 * 解析结果数据
	 *
	 * @param record 执行结果
	 * @return 解析结果
	 */
	private Neo4jData parseRecord(Record record) {

		Neo4jData data = new Neo4jData();
		data.setKeys(record.keys());
		Map<String, Integer> keysIndex = new HashMap<>(record.keys().size());
		data.setLength(record.values().size());
		// 字段信息
		List fields = new ArrayList(data.getLength());

		for (Map.Entry<String, Object> item : record.asMap().entrySet()) {
			keysIndex.put(item.getKey(), record.index(item.getKey()));
			if (item.getValue() instanceof InternalNode) {
				// 节点
				fields.add(this.parseNode((InternalNode) item.getValue()));
			} else if (item.getValue() instanceof InternalRelationship) {
				// 关系, 如: MATCH (n)-[rel:`作者`]->(r) return rel
				fields.add(this.parseRelationship((InternalRelationship) item.getValue()));
			} else if (item.getValue() instanceof InternalPath) {
				// 路径, 如: MATCH p=()-[r:`作者`]->() RETURN p LIMIT 25
				InternalPath internalPath = (InternalPath) item.getValue();
				Iterator<Node> iterNode = internalPath.nodes().iterator();
				int count = 0;
				while (iterNode.hasNext()) {
					iterNode.next();
					count++;
				}
				// 当前节点数据
				Map<String, Object> path = new HashMap<>();
				// 获取节点
				List<Neo4jNode> nodeList = new ArrayList<>(count);
				Iterator<Node> nodes = internalPath.nodes().iterator();
				while (nodes.hasNext()) {
					nodeList.add(this.parseNode(nodes.next()));
				}
				if (CollectionUtil.isNotEmpty(nodeList)) {
					path.put("start", nodeList.get(0));
					if (nodeList.size() > 1) {
						path.put("end", nodeList.get(1));
					}
				}

				// 获取segment
				List<Neo4jSegment> segmentsList = new ArrayList<>(count);
				Iterator<Path.Segment> segments = internalPath.iterator();
				while (segments.hasNext()) {
					segmentsList.add(this.parseSegment(segments.next()));
				}
				path.put("segments", segmentsList);
				fields.add(path);
			} else if (item.getValue() instanceof List) {
				if (CollectionUtil.isNotEmpty((Collection<?>) item.getValue())) {
					if (((List<?>) item.getValue()).get(0) instanceof InternalNode) {
						// 节点列表
						fields.add(this.parseNodeList((List<InternalNode>) item.getValue()));
					} else if (((List<?>) item.getValue()).get(0) instanceof InternalRelationship) {
						// 关系列表
						fields.add(this.parseRelationship((List<InternalRelationship>) item.getValue()));
					} else if (((List<?>) item.getValue()).get(0) instanceof InternalPath) {
						// 路径
						List<InternalPath> internalPathList = ((List<InternalPath>) item.getValue());
						Iterator<InternalPath> ipNode = internalPathList.iterator();
						InternalPath ipath = null;
						while (ipNode.hasNext()) {
							ipath = ipNode.next();
							Iterator<Node> iterNode = ipath.nodes().iterator();
							int count = 0;
							while (iterNode.hasNext()) {
								iterNode.next();
								count++;
							}
							// 当前节点数据
							Map<String, Object> path = new HashMap<>();
							// 获取节点
							List<Neo4jNode> nodeList = new ArrayList<>(count);
							Iterator<Node> nodes = ipath.nodes().iterator();
							while (nodes.hasNext()) {
								nodeList.add(this.parseNode(nodes.next()));
							}
							if (CollectionUtil.isNotEmpty(nodeList)) {
								path.put("start", nodeList.get(0));
								if (nodeList.size() > 1) {
									path.put("end", nodeList.get(1));
								}
							}

							// 获取segment
							List<Neo4jSegment> segmentsList = new ArrayList<>(count);
							Iterator<Path.Segment> segments = ipath.iterator();
							while (segments.hasNext()) {
								segmentsList.add(this.parseSegment(segments.next()));
							}
							path.put("segments", segmentsList);
							fields.add(path);
						}
					}
				}
			}
		}

		data.setFieldLookup(keysIndex);
		data.setFields(fields);
		return data;
	}

	/**
	 * 解析节点
	 * 不解析直接返回拿不到数据
	 *
	 * @param nodeList 节点
	 * @return 节点
	 */
	private List<Neo4jNode> parseNodeList(List<InternalNode> nodeList) {

		List<Neo4jNode> dataList = new ArrayList<>(nodeList.size());
		nodeList.stream().forEach(n -> {
			dataList.add(this.parseNode(n));
		});
		return dataList;
	}

	/**
	 * 解析节点
	 * 不解析直接返回拿不到数据
	 *
	 * @param node 节点
	 * @return 节点
	 */
	private Neo4jNode parseNode(Node node) {
		Neo4jNode neo4jNode = new Neo4jNode();
		neo4jNode.setElementId(node.id());
		neo4jNode.setLabels(CollectionUtil.toCollection(node.labels()));
		neo4jNode.setProperties(node.asMap());
		neo4jNode.setIdentity(new Neo4jIdentity(node.id(), 0L));
		return neo4jNode;
	}

	/**
	 * 解析节点
	 * 不解析直接返回拿不到数据
	 *
	 * @param ir 节点
	 * @return 节点
	 */
	private Neo4jNode parseNode(InternalNode ir) {
		Neo4jNode node = new Neo4jNode();
		node.setElementId(ir.id());
		node.setLabels(ir.labels());
		node.setProperties(ir.asMap());
		node.setIdentity(new Neo4jIdentity(ir.id(), 0L));
		return node;
	}

	/**
	 * 解析关系
	 * 不解析直接返回拿不到数据
	 *
	 * @param relList 关系列表
	 * @return 关系
	 */
	private List<Neo4jRelation> parseRelationship(List<InternalRelationship> relList) {

		List<Neo4jRelation> dataList = new ArrayList<>(relList.size());
		relList.stream().forEach(n -> {
			dataList.add(this.parseRelationship(n));
		});
		return dataList;
	}

	/**
	 * 解析关系
	 * 不解析直接返回拿不到数据
	 *
	 * @param rel 关系
	 * @return 关系
	 */
	private Neo4jRelation parseRelationship(InternalRelationship rel) {
		Neo4jRelation relation = new Neo4jRelation();
		relation.setElementId(rel.id());
		relation.setStartNodeElementId(rel.startNodeId());
		relation.setEndNodeElementId(rel.endNodeId());
		relation.setType(rel.type());
		relation.setProperties(rel.asMap());
		relation.setIdentity(new Neo4jIdentity(rel.id(), 0L));
		relation.setStart(new Neo4jIdentity(rel.startNodeId(), 0L));
		relation.setEnd(new Neo4jIdentity(rel.endNodeId(), 0L));
		return relation;
	}

	/**
	 * 解析关系
	 * 不解析直接返回拿不到数据
	 *
	 * @param rel 关系
	 * @return 关系
	 */
	private Neo4jRelationship parseRelationship(Relationship rel) {

		Neo4jRelationship relation = new Neo4jRelationship();
		relation.setElementId(rel.id());
		relation.setStartNodeElementId(rel.startNodeId());
		relation.setEndNodeElementId(rel.endNodeId());
		relation.setIdentity(new Neo4jIdentity(rel.id(), 0L));
		relation.setStart(new Neo4jIdentity(rel.startNodeId(), 0L));
		relation.setEnd(new Neo4jIdentity(rel.endNodeId(), 0L));
		relation.setType(rel.type());
		relation.setProperties(rel.asMap());
		return relation;
	}

	/**
	 * 解析关系
	 * 不解析直接返回拿不到数据
	 *
	 * @param segment
	 * @return 关系
	 */
	private Neo4jSegment parseSegment(Path.Segment segment) {
		Neo4jSegment seg = new Neo4jSegment();
		seg.setStart(this.parseNode(segment.start()));
		seg.setEnd(this.parseNode(segment.end()));
		seg.setRelationship(this.parseRelationship(segment.relationship()));
		return seg;
	}
}
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

import java.util.List;
import java.util.Map;

/**
 * neo4j执行cypher语句返回结果
 *
 * @author kou
 */
@Data
public class Neo4jData {

	/**
	 * 查询字段
	 */
	private List<String> keys;

	/**
	 * 长度,fields个数
	 */
	private Integer length;

	/**
	 * 数据字段
	 */
	@JsonProperty(value = "_fields")
	private List fields;

	/**
	 * keys索引,字段keys在fields的位置
	 */
	@JsonProperty(value = "_fieldLookup")
	private Map<String, Integer> fieldLookup;

}
import lombok.Data;

import java.util.Collection;
import java.util.Map;

/**
 * 结点信息
 *
 * @author kou
 */
@Data
public class Neo4jNode {

	/**
	 * 结点id
	 */
	private Long elementId;

	private Neo4jIdentity identity;

	/**
	 * label名称
	 */
	private Collection<String> labels;

	/**
	 * 属性
	 */
	private Map<String, Object> properties;

}
import lombok.Data;

import java.util.Map;

/**
 * neo4j关系
 *
 * @author kou
 */
@Data
public class Neo4jRelation {

	/**
	 * 关系id
	 */
	private Long elementId;

	/**
	 * 关系类型
	 */
	private String type;

	/**
	 * id信息
	 */
	private Neo4jIdentity identity;

	/**
	 * 头节点信息
	 */
	private Neo4jIdentity start;

	/**
	 * 尾节点信息
	 */
	private Neo4jIdentity end;

	/**
	 * 头节点id
	 */
	private Long startNodeElementId;

	/**
	 * 尾节点id
	 */
	private Long endNodeElementId;

	/**
	 * 属性
	 */
	private Map<String, Object> properties;

}
import cn.hutool.core.collection.CollectionUtil;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * cypher脚本结果工具类
 *
 * @author kou
 */
public class Neo4jNodeUtil {

	/**
	 * 将cypher脚本查询出的结果转换为节点关系
	 *
	 * @param datas cypher脚本查询出的结果
	 * @return 节点关系
	 */
	public static NodeRelation convert(List<Neo4jData> datas) {

		if (CollectionUtil.isEmpty(datas)) {
			return null;
		}

		int length = datas.size() * 3;
		List<Neo4jNode> nodes = new ArrayList<>(length);
		List<Neo4jRelation> relations = new ArrayList<>(length);

		Map<Long, Long> nodeIds = new HashMap<>(length);
		Map<Long, Long> relationIds = new HashMap<>(length);

		datas.stream().forEach(d -> {
			d.getFields().stream().forEach(f -> {
				if (f instanceof Neo4jNode) {
					Neo4jNode node = (Neo4jNode) f;
					if (null == nodeIds.get(node.getElementId())) {
						nodeIds.put(node.getElementId(), node.getElementId());
						Map<String, Object> properties = node.getProperties();
						if (CollectionUtil.isNotEmpty(properties) && properties.containsKey("embedding")) {
							properties = ImmutableMap.copyOf(Maps.filterKeys(properties, key -> !key.equals("embedding")));
							node.setProperties(properties);
						}
						nodes.add(node);
					}
				} else if (f instanceof Neo4jRelation) {
					Neo4jRelation rel = (Neo4jRelation) f;
					if (null == relationIds.get(rel.getElementId())) {
						relationIds.put(rel.getElementId(), rel.getElementId());
						relations.add(rel);
					}
				}
			});
		});

		NodeRelation nodeRelation = new NodeRelation();
		nodeRelation.setNodes(nodes);
		nodeRelation.setRelations(relations);
		return nodeRelation;
	}

	/**
	 * 获取节点
	 *
	 * @param datas cypher脚本查询出的结果
	 * @return 节点关系
	 */
	public static List<Neo4jNode> getNodes(List<Neo4jData> datas) {

		if (CollectionUtil.isEmpty(datas)) {
			return null;
		}

		List<Neo4jNode> nodes = new ArrayList<>(datas.size());
		Map<Long, Long> nodeIds = new HashMap<>(datas.size());

		datas.stream().forEach(d -> {
			d.getFields().stream().forEach(f -> {
				if (f instanceof Neo4jNode) {
					Neo4jNode node = (Neo4jNode) f;
					if (null == nodeIds.get(node.getElementId())) {
						nodeIds.put(node.getElementId(), node.getElementId());
						Map<String, Object> properties = node.getProperties();
						if (CollectionUtil.isNotEmpty(properties) && properties.containsKey("embedding")) {
							properties = ImmutableMap.copyOf(Maps.filterKeys(properties, key -> !key.equals("embedding")));
							node.setProperties(properties);
						}
						nodes.add(node);
					}
				}
			});
		});

		return nodes;
	}

}

使用jpa进行查询

    /**
	 * 文献搜索
	 *
	 * @param keyword  关键字
	 * @param pageable 分页查询
	 * @return 结果
	 */
	@Override
	public Page<Literature> search(String keyword, Pageable pageable) {
            ExampleMatcher matcher = ExampleMatcher.matching()
					.withMatcher("name", match -> match.contains())
					.withMatcher("fullPaperOutlineEn", match -> match.contains())
					.withMatcher("fullPaperOutlineZh", match -> match.contains())
					.withMatcher("fullPaperSummaryEn", match -> match.contains())
					.withMatcher("fullPaperSummaryZh", match -> match.contains());
			Literature literature = new Literature();
			literature.setName(keyword);
			literature.setFullPaperOutlineEn(keyword);
			literature.setFullPaperOutlineZh(keyword);
			literature.setFullPaperSummaryEn(keyword);
			literature.setFullPaperSummaryZh(keyword);

			// return literatureRepository.findByNameLike(keyword, pageable);
			return literatureRepository.findAll(Example.of(literature, matcher), pageable);
    }

3、动态多标签,即不确定当前节点有什么标签,如果类不写 

@Node(labels = "知识词条")

则默认类名作为了节点类型,所以有多个标签时最好确定一个主标签,然后使用

@DynamicLabels
private Set<String> labels;

注解多标签,将其他标签添加进去即可

import lombok.Data;
import org.springframework.data.neo4j.core.schema.*;

import java.util.Set;

/**
 * 知识词条
 *
 * @author kou
 */
@Data
@Node(labels = "知识词条")
public class KnowledgeEntry {

	@Id
	@GeneratedValue
	private Long id;

	/**
	 * 多标签
	 */
	@DynamicLabels
	private Set<String> labels;

	/**
	 * 文件id
	 */
	@Property(name = "file_id")
	private String fileId;

	/**
	 * 文件名
	 */
	@Property(name = "fullfilename")
	private String fullfilename;

	/**
	 * 内容
	 */
	@Property(name = "name")
	private String name;

	/**
	 * 知识词条 关系
	 */
	@Relationship(type = "知识词条", direction = Relationship.Direction.INCOMING)
	private Literature literature;

}

动态标签操作,如果多标签更新数据时,使用id更新会报错,这时需要先通过id将数据查询出来,在更新数据

import com.graph.model.dto.KnowledgeEntryDto;
import com.graph.model.neo4j.KnowledgeEntry;
import com.graph.model.neo4j.Literature;
import com.graph.repository.KnowledgeEntryRepository;
import com.graph.repository.LiteratureRepository;
import com.graph.service.IKnowledgeEntryService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

/**
 * 知识词条业务接口实现类
 *
 * @author kou
 */
@RequiredArgsConstructor
@Service
public class KnowledgeEntryServiceImpl implements IKnowledgeEntryService {

	private final KnowledgeEntryRepository knowledgeEntryRepository;

	private final LiteratureRepository literatureRepository;

	/**
	 * 添加知识词条
	 *
	 * @param knowledgeEntryDto 知识词条信息
	 * @return 结果
	 */
	@Override
	public KnowledgeEntry addKnowledgeEntry(KnowledgeEntryDto knowledgeEntryDto) {

		Optional<Literature> literatureOptional = literatureRepository.findById(knowledgeEntryDto.getLiteratureId());
		if (!literatureOptional.isPresent()) {
			throw new RuntimeException("文献不存在");
		}
		Literature literature = literatureOptional.get();
		KnowledgeEntry knowledgeEntry = new KnowledgeEntry();
		knowledgeEntry.setName(knowledgeEntryDto.getName());
		knowledgeEntry.setFullfilename(literature.getFullfilename());
		knowledgeEntry.setFileId(literature.getFileId());
		knowledgeEntry.setLiterature(literature);

		// 添加多标签
		Set<String> labels = new HashSet<>();
		labels.add(knowledgeEntryDto.getKnowledgePoint());
		knowledgeEntry.setLabels(labels);

		knowledgeEntryRepository.save(knowledgeEntry);

		return knowledgeEntry;
	}

	/**
	 * 更新知识词条
	 *
	 * @param knowledgeEntry 知识词条信息
	 * @return 结果
	 */
	@Override
	public boolean updateKnowledgeEntry(KnowledgeEntry knowledgeEntry) {
		Optional<KnowledgeEntry> literatureOptional = knowledgeEntryRepository.findById(knowledgeEntry.getId());
		if (!literatureOptional.isPresent()) {
			throw new RuntimeException("知识点不存在");
		}
		KnowledgeEntry exist = literatureOptional.get();
		exist.setName(knowledgeEntry.getName());
		knowledgeEntryRepository.save(exist);
		return true;
	}

	/**
	 * 删除知识词条
	 *
	 * @param id 知识词条id
	 * @return 结果
	 */
	@Override
	public boolean deleteKnowledgeEntry(Long id) {
		knowledgeEntryRepository.deleteById(id);
		return true;
	}

}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值