springboot + neo4j 功能使用

集成

添加依赖

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

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

java 复制代码
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进行查询

java 复制代码
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;

}
java 复制代码
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);

}
  1. 使用cypher进行查询
java 复制代码
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);
	}

}
java 复制代码
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;
	}
}
java 复制代码
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;

}
java 复制代码
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;

}
java 复制代码
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;

}
java 复制代码
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进行查询

java 复制代码
    /**
	 * 文献搜索
	 *
	 * @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);
    }
相关推荐
optimistic_chen2 分钟前
【Java EE进阶 --- SpringBoot】统一功能处理(拦截器)
spring boot·后端·java-ee·log4j·拦截器
AI大模型学徒11 分钟前
Chatbox 安装 for Windows
windows·语言模型·chatgpt
没有bug.的程序员1 小时前
Spring Boot 常见性能与配置优化
java·spring boot·后端·spring·动态代理
没有bug.的程序员2 小时前
Spring Boot Actuator 监控机制解析
java·前端·spring boot·spring·源码
骇客野人2 小时前
Spring Boot项目快速稳健架构指南
spring boot·后端·架构
千里马学框架2 小时前
windows系统上aosp15上winscope离线html如何使用?
android·windows·html·framework·安卓窗口系统·winscope
小猪绝不放弃.2 小时前
Spring Boot项目的核心依赖
java·spring boot·后端
2501_938963963 小时前
Flutter 3.19 桌面应用开发:适配 Windows/macOS 端窗口大小与菜单栏自定义
windows·flutter·macos
G31135422733 小时前
云服务器系统 选择Windows和Linux的理由
linux·服务器·windows
ss2733 小时前
基于Springboot + vue3实现的药材中药资源共享平台
java·spring boot·后端