generateGraph 功能初始化

master
xueqingkun 5 months ago
parent 5af8dd8c06
commit 375892f796

@ -11,16 +11,17 @@ import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Relationship;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static org.neo4j.driver.Values.parameters;
@Repository
@RequiredArgsConstructor
public class Neo4jRepository {
/**
* Neo4j
*/
private final Driver driver;
/**
@ -54,6 +55,64 @@ public class Neo4jRepository {
}
}
/**
*
* uniqueKey
* note: properties
* @param label label
* @param uniqueKey
* @param properties
* @return ID
*/
public List<Long> saveOrUpdateEntityNode(String label,String uniqueKey,Map<String, Object> properties) {
try (Session session = driver.session()) {
// MERGE语句确保唯一性
String query = String.format(
"MERGE (n:%s {%s: $uniqueValue}) " +
"SET n += $properties " +
"RETURN id(n) as id ",
label, uniqueKey);
Result result = session.run(query, parameters("uniqueValue", properties.get(uniqueKey), "properties", properties));
return result.list().stream().map(record -> record.get("id").asLong()).collect(Collectors.toList());
}
}
/**
*
* @param sourceId ID
* @param targetId ID
* @param relationType
* @param leftDirection
* @param rightDirection
* @param properties
* @return
*/
public List<Long> saveOrUpdateRelation(Long sourceId,Long targetId,String relationType,
boolean leftDirection,boolean rightDirection,Map<String, Object> properties) {
try (Session session = driver.session()) {
// MERGE关系
String query = "MATCH (a) WHERE id(a) = $sourceId " +
"MATCH (b) WHERE id(b) = $targetId " +
"MERGE (a)%s-[r:" + relationType + "]-%s(b) " +
"SET r += $properties " +
"RETURN id(r) as id";
String format = String.format(query,
leftDirection ? "<" : "",
rightDirection ? ">" : "");
Result result = session.run(format,
parameters("sourceId", sourceId, "targetId", targetId, "properties", properties));
return result.stream().map(record -> record.get("id").asLong()).collect(Collectors.toList());
}
}
private NodeData mapNode(Node node) {
return new NodeData(
node.id(),

@ -1,6 +1,8 @@
package com.supervision.pdfqaserver.dto;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
@ -28,7 +30,7 @@ public class EntityExtractionDTO {
*/
private String name;
private List<ERAttributeDTO> attributes;
private List<ERAttributeDTO> attributes = new ArrayList<>();
public EntityExtractionDTO() {
}

@ -28,4 +28,6 @@ public interface TripleToCypherExecutor {
* @return
*/
void executeCypher(String cypher);
void saveERE(EREDTO eredto);
}

@ -35,7 +35,9 @@ public class ChatServiceImpl implements ChatService {
private static final String PROMPT_PARAM_USER_QUERY = "userQuery";
private final Neo4jRepository neo4jRepository;
private final OllamaChatModel ollamaChatModel;
private final DomainMetadataService domainMetadataService;
private final ChineseEnglishWordsService chineseEnglishWordsService;

@ -122,10 +122,11 @@ public class KnowledgeGraphServiceImpl implements KnowledgeGraphService {
log.info("保存字典完成,新增字典个数:{}", allWords.size() - wordsSize);
// 生成cypher语句
for (EREDTO eredto : mergedList) {
if (CollUtil.isEmpty(eredto.getEntities()) && CollUtil.isEmpty(eredto.getRelations())){
continue;
}
eredto.setEn(allWords);
String insertCypher = tripleToCypherExecutor.generateInsertCypher(eredto);
log.info("insertCypher:{}", insertCypher);
tripleToCypherExecutor.executeCypher(insertCypher);
tripleToCypherExecutor.saveERE(eredto);
}
}

@ -185,9 +185,11 @@ public class TripleConversionPipelineImpl implements TripleConversionPipeline {
leavedEntities.add(entry.getValue());
}
}
if (CollUtil.isNotEmpty(leavedEntities)){
EREDTO eredto = new EREDTO();
eredto.setEntities(leavedEntities);
merged.add(eredto);
}
return merged;
}

@ -1,12 +1,24 @@
package com.supervision.pdfqaserver.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.supervision.pdfqaserver.cache.PromptCache;
import com.supervision.pdfqaserver.dao.Neo4jRepository;
import com.supervision.pdfqaserver.dto.ERAttributeDTO;
import com.supervision.pdfqaserver.dto.EREDTO;
import com.supervision.pdfqaserver.dto.EntityExtractionDTO;
import com.supervision.pdfqaserver.dto.RelationExtractionDTO;
import com.supervision.pdfqaserver.service.TripleToCypherExecutor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.supervision.pdfqaserver.cache.PromptCache.ERE_TO_INSERT_CYPHER;
@Slf4j
@ -15,6 +27,8 @@ import static com.supervision.pdfqaserver.cache.PromptCache.ERE_TO_INSERT_CYPHER
public class TripleToCypherExecutorImpl implements TripleToCypherExecutor {
private final OllamaChatModel ollamaChatModel;
private final Neo4jRepository neo4jRepository;
@Override
public String generateInsertCypher(EREDTO eredto) {
@ -32,4 +46,57 @@ public class TripleToCypherExecutorImpl implements TripleToCypherExecutor {
public void executeCypher(String cypher) {
}
@Override
public void saveERE(EREDTO eredto) {
List<EntityExtractionDTO> entities = eredto.getEntities();
Map<String, List<Long>> nodeCache = new HashMap<>();
if (CollUtil.isNotEmpty(entities)){
// 保存节点
for (EntityExtractionDTO entity : entities) {
if (StrUtil.isEmpty(entity.getName())){
log.info("实体name属性为空,详情:{}", JSONUtil.toJsonStr(entity));
continue;
}
Map<String, Object> attributes = entity.getAttributes().stream().collect(Collectors.toMap(
ERAttributeDTO::getAttributeEn, ERAttributeDTO::getValue
));
attributes.put("name", entity.getName());
log.info("保存节点{},属性:{}", entity.getEntityEn(),JSONUtil.toJsonStr(entity.getAttributes()));
List<Long> nodeIds = neo4jRepository.saveOrUpdateEntityNode(entity.getEntityEn(), "name", attributes);
nodeCache.put(StrUtil.join("_", entity.getEntity(), entity.getName()), nodeIds);
}
}
if (CollUtil.isNotEmpty(eredto.getRelations())){
// 保存关系
for (RelationExtractionDTO relation : eredto.getRelations()) {
String sourceNodeKey = StrUtil.join("_", relation.getSourceType(), relation.getSource());
List<Long> sourceNodeIds = nodeCache.get(sourceNodeKey);
if (CollUtil.isEmpty(sourceNodeIds)) {
log.info("关系{}没有source节点", sourceNodeKey);
continue;
}
String targetNodeKey = StrUtil.join("_", relation.getTargetType(), relation.getTarget());
List<Long> targetNodeIds = nodeCache.get(targetNodeKey);
if (CollUtil.isEmpty(targetNodeIds)) {
log.info("关系{}没有target节点", targetNodeKey);
continue;
}
Map<String, Object> attributes = relation.getAttributes().stream().collect(Collectors.toMap(
ERAttributeDTO::getAttributeEn, ERAttributeDTO::getValue
));
for (Long sourceNodeId : sourceNodeIds) {
for (Long targetNodeId : targetNodeIds) {
if (sourceNodeId.equals(targetNodeId)) {
log.info("关系{}的source和target节点相同", sourceNodeKey);
continue;
}
log.info("保存关系{}-{}-{}的属性:{}", relation.getSourceTypeEn(), relation.getRelationEn(),relation.getTargetTypeEn(), attributes);
neo4jRepository.saveOrUpdateRelation(sourceNodeId, targetNodeId, relation.getRelationEn(), false, false, attributes);
}
}
}
}
}
}

@ -3,8 +3,14 @@ package com.supervision.pdfqaserver;
import com.supervision.pdfqaserver.service.KnowledgeGraphService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.neo4j.driver.*;
import org.neo4j.driver.Record;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.neo4j.driver.Values.parameters;
@Slf4j
@SpringBootTest
@ -18,4 +24,60 @@ class PdfQaServerApplicationTests {
log.info("finish...");
}
@Autowired
private Driver driver;
/**
*
*/
@Test
void testSaveOrUpdateEntityNode() {
List<Long> longs = saveOrUpdateRelation(7838L, 7940L, "REL_TYPE", true, false, Map.of("name", "test1"));
System.out.println(longs);
}
public void saveOrUpdateEntityNode(String label, String uniqueKey, Map<String, Object> properties) {
try (Session session = driver.session()) {
// MERGE语句确保唯一性
String query = String.format(
"MERGE (n:%s {%s: $uniqueValue}) " +
"SET n += $properties " +
"RETURN id(n) as id",
label, uniqueKey);
Result result = session.run(query, parameters("uniqueValue", properties.get(uniqueKey), "properties", properties));
if (result.hasNext()) {
Record next = result.next();
Value value = next.get("id");
long aLong = value.asLong();
System.out.printf("已处理 %s 节点: %s%n", label, next.keys());
}
}
}
private List<Long> saveOrUpdateRelation(Long sourceId, Long targetId, String relationType,
boolean leftDirection, boolean rightDirection, Map<String, Object> relation) {
try (Session session = driver.session()) {
// MERGE关系
String query = "MATCH (a) WHERE id(a) = $sourceId " +
"MATCH (b) WHERE id(b) = $targetId " +
"MERGE (a)%s-[r:" + relationType + "]-%s(b) " +
"SET r += $properties " +
"RETURN id(r) as id";
String format = String.format(query,
leftDirection ? "<" : "",
rightDirection ? ">" : "");
Result result = session.run(format,
parameters("sourceId", sourceId, "targetId", targetId, "properties", relation));
return result.stream().map(record -> record.get("id").asLong()).collect(Collectors.toList());
}
}
}

Loading…
Cancel
Save