Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions src/main/java/com/going/server/domain/graph/entity/Edge.java

This file was deleted.

9 changes: 4 additions & 5 deletions src/main/java/com/going/server/domain/graph/entity/Graph.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.going.server.domain.graph.entity;

import com.going.server.global.common.BaseEntity;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
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.Relationship;
import org.springframework.data.neo4j.core.schema.*;

import java.util.List;

@Node("KnowledgeGraph")
@Node("Graph")
@Getter
@Setter
@Builder
public class Graph extends BaseEntity {
@Id
@GeneratedValue
Expand Down
33 changes: 17 additions & 16 deletions src/main/java/com/going/server/domain/graph/entity/GraphEdge.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.going.server.domain.graph.entity;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.neo4j.core.schema.*;
Expand All @@ -9,33 +10,33 @@
@RelationshipProperties
@Getter
@Setter
@Builder
public class GraphEdge {

@Id
@GeneratedValue
private Long id; // Neo4j 내부 ID

private String source;

private String label; // 관계 라벨

@TargetNode
private GraphNode target; // 연결 대상 노드

@Property
private Long edgeId; // JSON에서 받은 숫자형 edge ID (ex. 12)

private GraphEdge createEdge(Long edgeId, String label, GraphNode source, GraphNode target) {
GraphEdge edge = new GraphEdge();
edge.setEdgeId(edgeId);
edge.setLabel(label);
edge.setTarget(target);

// outbound edge를 source에 연결
if (source.getEdges() == null) {
source.setEdges(new ArrayList<>());
}
source.getEdges().add(edge);
return edge;
}
// private GraphEdge createEdge(Long edgeId, String label, GraphNode source, GraphNode target) {
// GraphEdge edge = new GraphEdge();
// edge.setEdgeId(edgeId);
// edge.setLabel(label);
// edge.setTarget(target);
//
// // outbound edge를 source에 연결
// if (source.getEdges() == null) {
// source.setEdges(new ArrayList<>());
// }
// source.getEdges().add(edge);
// return edge;
// }

// Long → String 변환 (프론트 전송 시)
public String getIdAsString() {
Expand Down
16 changes: 10 additions & 6 deletions src/main/java/com/going/server/domain/graph/entity/GraphNode.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
package com.going.server.domain.graph.entity;

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
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.Relationship;
import org.springframework.data.neo4j.core.schema.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Node("GraphNode")
@Getter
@Setter
@Builder
public class GraphNode {

@Id
@GeneratedValue
private Long id;

@Property("node_id")
private Long nodeId;

private String label;
private int level;
private String group;
private Long level;
private String includeSentence; //해당 노드(단어)가 포함된 문장

@Relationship(type = "HAS_GRAPH", direction = Relationship.Direction.INCOMING)
private Graph graph;

@Relationship(type = "RELATED", direction = Relationship.Direction.OUTGOING)
private List<GraphEdge> edges = new ArrayList<>();
private Set<GraphEdge> edges;

public String getIdAsString() {
return id != null ? String.valueOf(id) : null;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.going.server.domain.graph.repository;

import com.going.server.domain.graph.entity.GraphEdge;
import org.springframework.data.neo4j.repository.Neo4jRepository;

public interface GraphEdgeRepository extends Neo4jRepository<GraphEdge, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.going.server.domain.graph.repository;

import com.going.server.domain.graph.entity.GraphNode;
import org.springframework.data.neo4j.repository.Neo4jRepository;

public interface GraphNodeRepository extends Neo4jRepository<GraphNode, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@
@Builder
public class UploadResponseDto {
private Long graphId;
private String title;
private Boolean easy; //쉬움 모드
private Boolean hard; //어려움 모드
private Long nodes; //그래프 노드 개수
private Long edges; //그래프 관계 개수

public static UploadResponseDto from(Long graphId, String title, Boolean easy, Boolean haed, Long nodes, Long edges) {
return UploadResponseDto.builder().graphId(graphId).title(title).easy(easy).hard(haed).nodes(nodes).edges(edges).build();
public static UploadResponseDto from(Long graphId) {
return UploadResponseDto.builder().graphId(graphId).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.going.server.domain.upload.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.going.server.domain.graph.dto.edgeDto;
import com.going.server.domain.graph.dto.nodeDto;
import com.going.server.domain.graph.entity.Edge;
import com.going.server.domain.graph.repository.EdgeRepository;
import com.going.server.domain.graph.entity.Graph;
import com.going.server.domain.graph.entity.GraphEdge;
import com.going.server.domain.graph.entity.GraphNode;
import com.going.server.domain.graph.repository.GraphEdgeRepository;
import com.going.server.domain.graph.repository.GraphNodeRepository;
import com.going.server.domain.graph.repository.GraphRepository;
import com.going.server.domain.ocr.OcrService;
import com.going.server.domain.ocr.PdfOcrService;
import com.going.server.domain.upload.dto.UploadRequestDto;
Expand All @@ -19,16 +20,16 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;

@Service
@RequiredArgsConstructor
public class UploadServiceImpl implements UploadService {
private final OcrService ocrService;
private final PdfOcrService pdfOcrService;
private final EdgeRepository edgeRepository;
private final GraphNodeRepository graphNodeRepository;
private final GraphEdgeRepository graphEdgeRepository;
private final GraphRepository graphRepository;
@Value("${ocr.api.url}")
private String apiUrl;
@Value("${ocr.api.secret-key}")
Expand All @@ -45,40 +46,90 @@ public UploadResponseDto uploadFile(UploadRequestDto dto) {
ObjectMapper mapper = new ObjectMapper();
InputStream is = getClass().getClassLoader().getResourceAsStream("data.json");
JsonNode root = mapper.readTree(is);
JsonNode dataNode = root.get("data");
JsonNode edgesNode = dataNode.get("edges");
JsonNode graph = root.get("data");
JsonNode nodesNode = graph.get("nodes");
JsonNode edgesNode = graph.get("edges");

List<Edge> edgeList = new ArrayList<>();
//node 데이터 파싱코드
List<GraphNode> nodeList = new ArrayList<>();
for (JsonNode node : nodesNode) {
GraphNode graphNode = GraphNode.builder()
.nodeId(Long.parseLong(node.get("id").asText()))
.label(node.get("label").asText())
.group(node.get("group").asText())
.level(node.get("level").asLong())
.includeSentence(node.get("includeSentence").asText())
.build();
nodeList.add(graphNode);
}

//DB에 저장
graphNodeRepository.saveAll(nodeList);

//노드 ID로 빠르게 찾을 수 있게 Map 생성
Map<String, GraphNode> nodeIdToNode = new HashMap<>();
for (GraphNode graphNode : nodeList) {
nodeIdToNode.put(String.valueOf(graphNode.getNodeId()), graphNode);
}

//edge 데이터 파싱 코드
List<GraphEdge> edgeList = new ArrayList<>();
for (JsonNode edgeNode : edgesNode) {
if (!edgeNode.has("source") || !edgeNode.has("target") || !edgeNode.has("label")) {
System.out.println("필드 누락: " + edgeNode.toPrettyString());
continue;
}

String source = edgeNode.get("source").asText();
String target = edgeNode.get("target").asText();
String sourceId = edgeNode.get("source").asText();
String targetId = edgeNode.get("target").asText();
String label = edgeNode.get("label").asText();

Edge edge = Edge.builder()
.source(source)
.target(target)
GraphNode sourceNode = nodeIdToNode.get(sourceId);
GraphNode targetNode = nodeIdToNode.get(targetId);

if (sourceNode == null || targetNode == null) {
System.out.println("노드 매칭 실패: source=" + sourceId + ", target=" + targetId);
continue;
}

//edge 엔티티 생성
GraphEdge edge = GraphEdge.builder()
.source(sourceId)
.label(label)
.target(targetNode)
.build();

edgeList.add(edge);
// sourceNode에 edge 연결
if (sourceNode.getEdges() == null) {
sourceNode.setEdges(new HashSet<>());
}
sourceNode.getEdges().add(edge);
}

//edge db에 저장
graphNodeRepository.saveAll(nodeList);

//그래프 생성
String title = dto.getTitle();
Graph graphEntity = Graph.builder()
.title(title)
.listenUpPerfect(false)
.connectPerfect(false)
.picturePerfect(false)
.nodes(nodeList)
.build();
graphRepository.save(graphEntity);

// 양방향 연결 설정 (GraphNode → Graph)
for (GraphNode graphNode : nodeList) {
graphNode.setGraph(graphEntity);
}

edgeRepository.saveAll(edgeList);
System.out.println("총 " + edgeList.size() + "개의 edge가 저장되었습니다.");
// 저장 (이제 연결된 상태로 저장됨)
graphRepository.save(graphEntity);
graphNodeRepository.saveAll(nodeList);

return UploadResponseDto.from(
null,
null,
null,
null,
null,
null
graphEntity.getId()
);
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
Expand Down
14 changes: 7 additions & 7 deletions src/main/resources/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,50 +9,50 @@
"label": "동물",
"group": 1,
"level": 0,
"description": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
"includeSentence": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
},
{
"id": "2",
"label": "고양이",
"group": 2,
"level": 1,
"description": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
"includeSentence": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
},
{
"id": "3",
"label": "토끼",
"group": 2,
"level": 1,
"description": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
"includeSentence": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."

},
{
"id": "4",
"label": "기린",
"group": 2,
"level": 1,
"description": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
"includeSentence": "기린은 세계에서 가장 키가 큰 동물로, 길게 뻗은 목을 이용해 높은 나무의 잎을 먹습니다. 초식동물이며 아프리카 사바나에서 주로 서식하고, 길고 튼튼한 다리로 천천히 이동하며, 천적을 피할 때는 매우 빠른 속도로 달릴 수 있습니다."
},
{
"id": "5",
"label": "원숭이",
"group": 2,
"level": 1,
"description": "원숭이는 사람과 유전적으로 가까운 영장류에 속하며, 높은 지능과 손을 이용한 다양한 도구 사용 능력을 갖추고 있습니다. 나무를 잘 타며 무리를 지어 생활하고, 감정 표현이 풍부하며 사회적 행동이 발달해 있습니다."
"includeSentence": "원숭이는 사람과 유전적으로 가까운 영장류에 속하며, 높은 지능과 손을 이용한 다양한 도구 사용 능력을 갖추고 있습니다. 나무를 잘 타며 무리를 지어 생활하고, 감정 표현이 풍부하며 사회적 행동이 발달해 있습니다."
},
{
"id": "6",
"label": "추론",
"group": 2,
"level": 2,
"description": "추론은 관찰하거나 알고 있는 사실을 바탕으로 새로운 사실이나 결론을 이끌어내는 사고 과정입니다. 예를 들어, 고양이가 생선을 먹는다는 사실을 안다면, 생선을 보고 고양이가 있을 가능성을 추론할 수 있습니다. 과학적 탐구나 문제 해결에서 매우 중요한 능력입니다."
"includeSentence": "추론은 관찰하거나 알고 있는 사실을 바탕으로 새로운 사실이나 결론을 이끌어내는 사고 과정입니다. 예를 들어, 고양이가 생선을 먹는다는 사실을 안다면, 생선을 보고 고양이가 있을 가능성을 추론할 수 있습니다. 과학적 탐구나 문제 해결에서 매우 중요한 능력입니다."
},
{
"id": "7",
"label": "생선",
"group": 2,
"level": 2,
"description": "생선은 물속에 사는 척추동물로, 지느러미와 비늘을 가지고 있으며 아가미로 숨을 쉽니다. 다양한 종류가 있으며, 많은 동물과 사람에게 중요한 단백질 공급원입니다. 고양이는 생선을 매우 좋아하는 동물로 자주 묘사됩니다."
"includeSentence": "생선은 물속에 사는 척추동물로, 지느러미와 비늘을 가지고 있으며 아가미로 숨을 쉽니다. 다양한 종류가 있으며, 많은 동물과 사람에게 중요한 단백질 공급원입니다. 고양이는 생선을 매우 좋아하는 동물로 자주 묘사됩니다."
}
],
"edges": [
Expand Down