변경2
This commit is contained in:
@@ -34,6 +34,8 @@ public class DocumentIndexingService {
|
||||
private final DocumentParserService documentParserService;
|
||||
private final ChunkingService chunkingService;
|
||||
private final EmbeddingService embeddingService;
|
||||
private final SmartChunkingService smartChunkingService;
|
||||
private final VisionService visionService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Value("${file.upload-dir:./uploads}")
|
||||
@@ -89,34 +91,61 @@ public class DocumentIndexingService {
|
||||
private void processIndexing(Long docId, TopicInfo topicInfo, MultipartFile file) throws Exception {
|
||||
log.info("인덱싱 시작: docId={}, fileName={}", docId, file.getOriginalFilename());
|
||||
|
||||
// 1. 문서 파싱
|
||||
String content = documentParserService.parseDocument(file);
|
||||
// 문서 정보 조회
|
||||
DocInfo docInfo = docInfoRepository.findById(docId)
|
||||
.orElseThrow(() -> new RuntimeException("문서를 찾을 수 없습니다."));
|
||||
|
||||
String content;
|
||||
|
||||
// 1. Vision 처리 (PDF + Vision 활성화된 경우)
|
||||
String fileType = getFileExtension(file.getOriginalFilename());
|
||||
if ("pdf".equalsIgnoreCase(fileType) && visionService.isEnabled()) {
|
||||
log.info("[Vision] PDF Vision 분석 시작...");
|
||||
content = visionService.processPdfWithVision(docInfo.getFilePath());
|
||||
|
||||
if (content == null || content.isBlank()) {
|
||||
log.warn("[Vision] Vision 분석 실패, 기본 파서로 대체");
|
||||
content = documentParserService.parseDocument(file);
|
||||
} else {
|
||||
log.info("[Vision] Vision 분석 완료: {} 글자", content.length());
|
||||
}
|
||||
} else {
|
||||
// 2. 기본 문서 파싱 (Tika)
|
||||
content = documentParserService.parseDocument(file);
|
||||
}
|
||||
|
||||
if (content == null || content.isBlank()) {
|
||||
throw new RuntimeException("문서 내용이 비어있습니다.");
|
||||
}
|
||||
|
||||
// 2. 청킹
|
||||
// 3. 청킹
|
||||
List<ChunkingService.ChunkResult> chunks = chunkingService.chunkText(content);
|
||||
if (chunks.isEmpty()) {
|
||||
throw new RuntimeException("청크 생성 실패");
|
||||
}
|
||||
log.info("청크 생성 완료: {} chunks", chunks.size());
|
||||
|
||||
// 3. 각 청크에 대해 임베딩 생성 및 저장
|
||||
DocInfo docInfo = docInfoRepository.findById(docId)
|
||||
.orElseThrow(() -> new RuntimeException("문서를 찾을 수 없습니다."));
|
||||
|
||||
for (ChunkingService.ChunkResult chunk : chunks) {
|
||||
// 4. 각 청크에 대해 임베딩 생성 및 저장
|
||||
for (int i = 0; i < chunks.size(); i++) {
|
||||
ChunkingService.ChunkResult chunk = chunks.get(i);
|
||||
|
||||
// 임베딩 생성
|
||||
String embeddingVector = embeddingService.createEmbeddingAsString(chunk.getContent());
|
||||
|
||||
// Native Query로 벡터 저장
|
||||
saveChunkWithEmbedding(docInfo, topicInfo, chunk, embeddingVector);
|
||||
// 스마트 청킹: 메타데이터 생성 (활성화된 경우)
|
||||
SmartChunkingService.ChunkMetadata metadata = null;
|
||||
if (smartChunkingService.isEnabled()) {
|
||||
log.info("[SmartChunking] 메타데이터 생성 중... ({}/{})", i + 1, chunks.size());
|
||||
metadata = smartChunkingService.generateMetadata(chunk.getContent());
|
||||
}
|
||||
|
||||
// Native Query로 벡터 + 메타데이터 저장
|
||||
saveChunkWithEmbedding(docInfo, topicInfo, chunk, embeddingVector, metadata);
|
||||
|
||||
log.debug("청크 저장 완료: index={}", chunk.getIndex());
|
||||
}
|
||||
|
||||
// 4. 문서 상태 업데이트
|
||||
// 5. 문서 상태 업데이트
|
||||
updateDocStatus(docId, "INDEXED", null);
|
||||
updateChunkCount(docId, chunks.size());
|
||||
|
||||
@@ -124,16 +153,29 @@ public class DocumentIndexingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 청크 + 벡터 저장 (Native Query 사용)
|
||||
* 청크 + 벡터 + 메타데이터 저장 (Native Query 사용)
|
||||
*/
|
||||
private void saveChunkWithEmbedding(DocInfo docInfo, TopicInfo topicInfo,
|
||||
ChunkingService.ChunkResult chunk, String embedding) {
|
||||
ChunkingService.ChunkResult chunk, String embedding,
|
||||
SmartChunkingService.ChunkMetadata metadata) {
|
||||
String sql = """
|
||||
INSERT INTO TB_DOC_CHUNK
|
||||
(doc_id, topic_id, chunk_content, chunk_embedding, chunk_index, token_count, created_at)
|
||||
VALUES (?, ?, ?, ?::vector, ?, ?, ?)
|
||||
(doc_id, topic_id, chunk_content, chunk_embedding, chunk_index, token_count,
|
||||
chunk_summary, chunk_keywords, chunk_questions, chunk_type, created_at)
|
||||
VALUES (?, ?, ?, ?::vector, ?, ?, ?, ?, ?, ?, ?)
|
||||
""";
|
||||
|
||||
// 메타데이터 처리
|
||||
String summary = null;
|
||||
String keywords = null;
|
||||
String questions = null;
|
||||
|
||||
if (metadata != null) {
|
||||
summary = metadata.getSummary();
|
||||
keywords = metadata.getKeywords() != null ? String.join(", ", metadata.getKeywords()) : null;
|
||||
questions = metadata.getQuestions() != null ? toJson(metadata.getQuestions()) : null;
|
||||
}
|
||||
|
||||
jdbcTemplate.update(sql,
|
||||
docInfo.getDocId(),
|
||||
topicInfo.getTopicId(),
|
||||
@@ -141,9 +183,26 @@ public class DocumentIndexingService {
|
||||
embedding,
|
||||
chunk.getIndex(),
|
||||
chunk.getTokenCount(),
|
||||
summary,
|
||||
keywords,
|
||||
questions,
|
||||
"text",
|
||||
LocalDateTime.now()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* List를 JSON 문자열로 변환
|
||||
*/
|
||||
private String toJson(java.util.List<String> list) {
|
||||
if (list == null || list.isEmpty()) return null;
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
return mapper.writeValueAsString(list);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일 저장
|
||||
|
||||
Reference in New Issue
Block a user