Skip to content

Conversation

@catturtle123
Copy link
Contributor

@catturtle123 catturtle123 commented Aug 18, 2025

📌 PR 개요

index 메타데이터 CRUD 기능 추가

🔍 관련 이슈

✨ 변경 사항

📸 스크린샷 / 동작 확인 결과 (선택)

필요하다면 이미지나 동영상 첨부.

✅ 체크리스트

  • 코드 스타일을 준수했습니다.
  • 불필요한 주석이나 콘솔 로그를 제거했습니다.
  • 관련 테스트를 작성/수정했습니다.
  • 로컬 환경에서 모든 기능이 정상 작동함을 확인했습니다.

🗒️ 추가 메모

기타 참고 사항을 적어주세요.

Summary by CodeRabbit

  • New Features
    • 인덱스 생성, 목록/상세 조회, 수정, 삭제 REST API 추가 (/api/v1/indexes).
  • Bug Fixes
    • 청킹 크기보다 큰 overlap 요청 시 유효성 검증으로 요청 거부 및 명확한 오류 메시지 제공.
    • 인덱스 삭제 시 성공 메시지 응답.
  • Tests
    • 인덱스 서비스 CRUD 및 예외 상황에 대한 통합 테스트 추가.
  • Style
    • 전반적인 코드 포맷팅 개선 및 불필요한 import 정리.

@catturtle123 catturtle123 self-assigned this Aug 18, 2025
@catturtle123 catturtle123 added the feature New feature or request label Aug 18, 2025
@catturtle123 catturtle123 linked an issue Aug 18, 2025 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link

coderabbitai bot commented Aug 18, 2025

Walkthrough

인덱스 메타데이터 CRUD 기능을 신규 추가했다. 컨트롤러·서비스·리포지토리·컨버터·DTO·예외/에러코드 및 테스트를 도입하고, 엔티티에 업데이트 메서드와 연관관계를 추가했다. overlapSize < chunkingSize 유효성 검증 로직과 정렬 조회(findAllByOrderByCreatedAtDesc)를 포함한다. 기타 파일은 포맷팅/불필요 import 정리.

Changes

Cohort / File(s) Summary
Index API (Controller)
src/main/kotlin/.../index/controller/IndexController.kt
인덱스 CRUD REST 엔드포인트 추가(생성/목록/상세/수정/삭제), ApiResponse 래핑, @validated 적용.
Index Service & Validation
src/main/kotlin/.../index/service/IndexService.kt
트랜잭션 CRUD 로직 구현, overlapSize < chunkingSize 검증, NOT_FOUND/OVERLAP_OVERFLOW 예외 처리.
DTOs & Converter
src/main/kotlin/.../index/dto/IndexRequestDTO.kt, src/main/kotlin/.../index/dto/IndexResponseDTO.kt, src/main/kotlin/.../index/converter/IndexConverter.kt
요청/응답 DTO 추가(생성/수정/프리뷰/상세/리스트), 엔티티-DTO 매핑 유틸 추가.
Persistence (Entity & Repos)
src/main/kotlin/.../index/entity/Index.kt, .../entity/ChunkEmbedding.kt, .../entity/DataFileIndex.kt, src/main/kotlin/.../index/repository/IndexRepository.kt, .../repository/ChunkEmbeddingRepository.kt
Index 엔티티에 가변 필드·update 메서드·연관관계 추가, 정렬 조회 리포지토리 및 ChunkEmbedding 리포지토리 추가, 일부 포맷 정리.
Error Handling
src/main/kotlin/.../global/error/CustomException.kt, .../global/error/ErrorCode.kt
IndexException 추가, OVERLAP_OVERFLOW 에러코드 추가.
Tests
src/test/kotlin/.../index/service/IndexServiceTest.kt
서비스 CRUD 및 검증/예외 시나리오 통합 테스트 추가.
Misc Formatting/Cleanup
src/main/kotlin/.../chat/entity/Model.kt, .../document/entity/{DataFile.kt,DataFileTag.kt,Tag.kt}, .../document/repository/DataFileTagRepository.kt, .../document/service/DataFileService.kt, .../prompt/entity/{FewShot.kt,Prompt.kt}, src/test/kotlin/.../document/service/DataFileServiceTest.kt
기능 변화 없이 공백/포맷/불필요 import 정리.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant IndexController
  participant IndexService
  participant IndexRepository
  participant Converter
  participant ErrorHandling

  Client->>IndexController: POST /api/v1/indexes (IndexCreateRequest)
  IndexController->>IndexService: createIndex(req)
  IndexService->>IndexService: validateOverlap(req.overlap, req.chunk)
  alt invalid overlap
    IndexService-->>ErrorHandling: throw IndexException(OVERLAP_OVERFLOW)
  else valid
    IndexService->>Converter: toIndex(req)
    Converter-->>IndexService: Index
    IndexService->>IndexRepository: save(Index)
    IndexRepository-->>IndexService: Index
    IndexService->>Converter: toIndexPreviewResponse(Index)
    Converter-->>IndexService: IndexPreviewResponse
    IndexService-->>IndexController: IndexPreviewResponse
    IndexController-->>Client: 201 ApiResponse
  end
Loading
sequenceDiagram
  autonumber
  participant Client
  participant IndexController
  participant IndexService
  participant IndexRepository
  participant Converter
  participant ErrorHandling

  Client->>IndexController: GET /api/v1/indexes
  IndexController->>IndexService: getIndexes()
  IndexService->>IndexRepository: findAllByOrderByCreatedAtDesc()
  IndexRepository-->>IndexService: List<Index>
  IndexService->>Converter: toIndexPreviewResponseList(list)
  Converter-->>IndexService: IndexPreviewResponseList
  IndexService-->>IndexController: IndexPreviewResponseList
  IndexController-->>Client: 200 ApiResponse
Loading
sequenceDiagram
  autonumber
  participant Client
  participant Controller
  participant Service
  participant Repo
  participant Converter
  participant ErrorHandling

  Client->>Controller: PUT /api/v1/indexes/{id} (IndexUpdateRequest)
  Controller->>Service: updateIndex(id, req)
  Service->>Repo: findById(id)
  alt not found
    Service-->>ErrorHandling: throw IndexException(NOT_FOUND)
  else found
    Service->>Service: validateOverlap(req.overlap, req.chunk)
    alt invalid
      Service-->>ErrorHandling: throw IndexException(OVERLAP_OVERFLOW)
    else valid
      Service->>Service: index.update(req)
      Service->>Converter: toIndexPreviewResponse(index)
      Converter-->>Service: IndexPreviewResponse
      Service-->>Controller: IndexPreviewResponse
      Controller-->>Client: 200 ApiResponse
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
메타데이터 저장 구현 (#9)
리스트 조회 구현 (#9)
삭제 구현 (#9)
overlapSize가 chunkingSize를 넘지 못하도록 검증 (#9)

Assessment against linked issues: Out-of-scope changes

(없음)

Possibly related PRs

  • ✨ Feature: erd 수정 #6: 인덱스 도메인(엔티티 및 연관 클래스) 변경과 직접적으로 관련된 리팩터링/기능 추가로 보임.

Poem

깡충, 깡충! 인덱스가 폈네, 새싹처럼 🌱
저장·조회·삭제, 길 따라 또로록—
겹침은 넘지 말 것! 토끼 규칙 철저히 🥕
예외는 휙, 응답은 착—
오늘도 레코드에 흔적을 남기네, 푸릇푸릇 데이터 숲에서.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#9-index-meta

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1d0aeea and 562da35.

📒 Files selected for processing (9)
  • src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/dto/IndexRequestDTO.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/global/error/CustomException.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
src/main/kotlin/simplerag/ragback/domain/index/dto/IndexRequestDTO.kt (1)
src/main/kotlin/simplerag/ragback/domain/document/dto/DataFileRequestDTO.kt (2)
  • min (7-11)
  • message (13-20)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
src/main/kotlin/simplerag/ragback/global/error/CustomException.kt (1)
src/main/kotlin/simplerag/ragback/global/error/GlobalExceptionHandler.kt (2)
  • handleCustomException (80-86)
  • handleFileException (72-78)
src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1)
src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt (1)
  • dataFileService (17-44)
src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (3)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (3)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (4)
  • toIndex (7-17)
  • toIndexPreviewResponse (26-31)
  • toIndexPreviewResponseList (19-24)
  • toIndexDetailResponse (33-44)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/enums/SimilarityMetric.kt (1)
  • description (3-9)
🔇 Additional comments (9)
src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt (1)

26-27: INDEX 도메인 에러코드 추가 적절

PR 요구사항(오버랩이 청킹을 초과하지 못함)에 정확히 매핑되는 에러코드 추가입니다. GlobalExceptionHandler의 CustomException 흐름과도 호환됩니다.

src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt (1)

6-8: 필드명 변경: indexDetailResponseindexes 적용
IndexPreviewResponseList가 Preview 리스트를 담고 있음에도 Detail 용어가 혼동을 줄 수 있어 다음과 같이 수정을 권장합니다.

 data class IndexPreviewResponseList(
-    val indexDetailResponse: List<IndexPreviewResponse>,
+    val indexes: List<IndexPreviewResponse>,
 )

위 변경에 따라 관련 참조를 모두 업데이트해주세요:

  • src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt
  • src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (생성자 호출 시 키워드 인자 사용 시 indexes = … 적용)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt
  • src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt
  • (있는 경우) 테스트 코드 내 indexDetailResponse 참조

참조 위치 검색 스크립트:

rg -nP '\bindexDetailResponse\b|IndexPreviewResponseList\b' --glob '!**/build/**'

반영 후 API 스키마 변경으로 인한 클라이언트 영향 여부도 검증해주세요.

src/main/kotlin/simplerag/ragback/domain/index/dto/IndexRequestDTO.kt (1)

28-29: IndexCreateRequest에 교차 검증 추가

 data class IndexCreateRequest(
@@
     val embeddingModel: EmbeddingModel,
 
     val reranker: Boolean,
+    
+    @get:AssertTrue(message = "overlapSize는 chunkingSize를 초과할 수 없습니다")
+    val isOverlapValid: Boolean
+        get() = overlapSize <= chunkingSize,
 )
⛔ Skipped due to learnings
Learnt from: catturtle123
PR: Simple-RAG/BACK#6
File: src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt:16-22
Timestamp: 2025-08-17T13:12:02.200Z
Learning: catturtle123이 Index 엔티티의 overlapSize ≤ chunkingSize 검증을 엔티티 레벨이 아닌 서비스 레이어에서 구현하기로 결정했으며, chunking embedding 저장 관련 PR에서 이 검증 로직을 추가할 예정이므로 해당 PR에서 언급해야 함 (Simple-RAG/BACK 프로젝트)
src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (2)

11-16: 컨트롤러 골격/의존성 주입 구성은 적절합니다

REST 베이스 경로와 @validated 적용, 생성자 주입 모두 적절합니다.


27-31: 목록 조회 엔드포인트 구성은 간결하고 적절합니다

서비스 위임과 응답 래핑이 일관적입니다.

src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (3)

19-24: 리스트 변환 로직은 간결하며 재사용성이 좋습니다

map + 전용 프리뷰 변환으로 적절하게 분리되어 있습니다.


33-44: 디테일 변환은 필드 매핑이 명확합니다

필수 필드가 누락 없이 매핑되며, 응답 DTO와 일치합니다.


26-31: indexesId null 허용 여부 및 API 계약 검토 필요

현재 IndexPreviewResponseIndexDetailResponse에서
indexesId: Long?
로 선언되어 있어, JPA 비영속 상태의 index.id == null 값을 그대로 반환합니다.
API 클라이언트 측에서 null을 처리할 수 있는지 확인 후, 다음 중 하나를 선택해주세요:

  • null 허용 시: 현행대로 두고 별도 처리 불필요
  • null 비허용 시:
    1. DTO 타입을 Long(Non-null)으로 변경
    2. 매핑 시 requireNotNull(index.id) 로 조기 실패 처리

점검 대상:

  • src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt
    • data class IndexPreviewResponse(var indexesId: Long?, …)
    • data class IndexDetailResponse(var indexesId: Long?, …)
  • src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt
    • toIndexPreviewResponse / toIndexDetailResponse 매핑 로직
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1)

27-31: 정렬 조회 + 프리뷰 변환 플로우는 적절합니다

읽기 전용 트랜잭션과 변환 계층 분리가 잘 되어 있습니다.

Comment on lines 33 to 39
@GetMapping("/{indexesId}")
fun getIndex(
@PathVariable indexesId: Long
): ApiResponse<IndexDetailResponse> {
val indexDetailResponse = indexService.getIndex(indexesId)
return ApiResponse.ok(indexDetailResponse)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

서비스 시그니처와 nullability 불일치 가능성

컨트롤러는 ApiResponse를 반환하지만, 서비스의 getIndex는 현재 IndexDetailResponse?를 리턴하도록 선언되어 있습니다. 이 경우 컴파일 불일치(Nullable → Non-null) 또는 불필요한 안전 연산 추가가 필요해집니다. 서비스 시그니처를 Non-null로 정합시키는 것을 권장합니다.

서비스 파일에 다음 수정을 함께 적용해 주세요(해당 파일 코멘트에도 제안합니다).

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt
around lines 33 to 39, the controller returns ApiResponse<IndexDetailResponse>
but the service method indexService.getIndex currently declares a nullable
return type (IndexDetailResponse?), causing a mismatch; change the service
signature to return a non-null IndexDetailResponse and update its implementation
to either always return a valid IndexDetailResponse or throw a controlled
exception (e.g., EntityNotFoundException) when not found, then adjust any
callers/tests accordingly so the controller can safely wrap the non-null result
in ApiResponse.ok.

Comment on lines 41 to 48
@PutMapping("/{indexesId}")
fun updateIndexes(
@PathVariable indexesId: Long,
@RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
): ApiResponse<IndexPreviewResponse> {
val indexPreviewResponse = indexService.updateIndexes(indexesId, indexUpdateRequest)
return ApiResponse.ok(indexPreviewResponse)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

메서드 네이밍 일관성: updateIndex 권장

단일 리소스 업데이트인데 메서드명이 updateIndexes(복수형)입니다. 다른 엔드포인트와의 가독성을 위해 updateIndex로의 변경을 권장합니다.

-    fun updateIndexes(
+    fun updateIndex(
         @PathVariable indexesId: Long,
         @RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
     ): ApiResponse<IndexPreviewResponse> {
-        val indexPreviewResponse = indexService.updateIndexes(indexesId, indexUpdateRequest)
+        val indexPreviewResponse = indexService.updateIndexes(indexesId, indexUpdateRequest) // 서비스도 rename 시 함께 변경
         return ApiResponse.ok(indexPreviewResponse)
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PutMapping("/{indexesId}")
fun updateIndexes(
@PathVariable indexesId: Long,
@RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
): ApiResponse<IndexPreviewResponse> {
val indexPreviewResponse = indexService.updateIndexes(indexesId, indexUpdateRequest)
return ApiResponse.ok(indexPreviewResponse)
}
@PutMapping("/{indexesId}")
fun updateIndex(
@PathVariable indexesId: Long,
@RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
): ApiResponse<IndexPreviewResponse> {
val indexPreviewResponse = indexService.updateIndexes(indexesId, indexUpdateRequest) // 서비스도 rename 시 함께 변경
return ApiResponse.ok(indexPreviewResponse)
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt
around lines 41 to 48, the controller method is named updateIndexes (plural) but
updates a single resource; rename the method to updateIndex and update its
internal service call to the corresponding singular service method (e.g.,
indexService.updateIndex) or keep the existing service name if preferred —
ensure you also rename all references/usages (service interface/implementation,
tests, callers) to match the new method name so compilation and routing remain
correct.

Comment on lines 56 to 61
@Transactional
fun deleteIndex(indexesId: Long) {
val index = indexRepository.findIndexById(indexesId) ?: throw IndexException(ErrorCode.NOT_FOUND)

indexRepository.delete(index)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

삭제 시 참조 무결성 점검 필요(DataFileIndex 등 참조 존재 가능)

Index는 DataFileIndex 등에서 참조될 수 있습니다. cascade 설정이 없다면 FK 제약으로 삭제 실패가 발생할 수 있으니, 사전 체크(참조 존재 시 차단) 또는 cascade = REMOVE(orphanRemoval) 전략을 검토해 주세요.

다음 스크립트로 Index ↔ DataFileIndex 연관 및 cascade 설정을 확인하세요.

[offer_architecture_advice]
참조가 존재하면:

  • 비즈니스 제약: 참조 존재 시 삭제 금지(409/400 반환)
  • 운영 편의: Index.oneToMany(DataFileIndex, cascade = REMOVE, orphanRemoval = true)로 영속성 계단 삭제
    둘 중 정책을 명확히 하여 반영해 주세요.

🏁 Script executed:

#!/bin/bash
# Index 엔티티에 DataFileIndex 연관이 있는지와 cascade 설정 확인
rg -n -C3 'class\s+Index\b|@OneToMany|@ManyToOne|cascade\s*=' --type=kt
rg -n -C2 'class\s+DataFileIndex\b|@ManyToOne|@JoinColumn|indexes_id' --type=kt

Length of output: 209


다음과 같이 rg 명령에서 --type=kt 대신 -g '*.kt'를 사용해 Kotlin 파일만 검색하도록 스크립트를 수정하고, 연관 및 cascade 설정을 재확인해 보세요.


🏁 Script executed:

#!/bin/bash
# Index 엔티티에 DataFileIndex 연관 및 cascade 설정 확인 (Kotlin 파일 대상)
rg -n -C3 'class\s+Index\b|@OneToMany|@ManyToOne|cascade\s*=' -g '*.kt'
rg -n -C2 'class\s+DataFileIndex\b|@ManyToOne|@JoinColumn|indexes_id' -g '*.kt'

Length of output: 10484


Index 삭제 시 참조 무결성 처리 필요

현재 Index 엔티티에는 DataFileIndex에 대한 역방향 매핑(@OneToMany)이나 cascade 설정이 없어, 연관된 레코드가 남아 있으면 삭제 시 FK 제약 위반 예외가 발생합니다. 비즈니스 요구사항에 맞춰 아래 중 하나를 선택해 반영해 주세요:

– 사전 체크 방식
IndexService.deleteIndex() 호출 직후에 index.dataFileIndexes.isNotEmpty() 등으로 참조 존재 여부 확인
• 참조가 있을 경우 IndexException(ErrorCode.CONFLICT) 등으로 차단

– 영속성 계단 삭제(cascade) 방식
Index 엔티티에 @OneToMany(mappedBy = "index", cascade = [CascadeType.REMOVE], orphanRemoval = true) 추가

수정 위치

  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt

예시 diff

 class Index(
   // 기존 필드…
 ) : BaseEntity() {
+  @OneToMany(
+    mappedBy = "index",
+    cascade = [CascadeType.REMOVE],
+    orphanRemoval = true
+  )
+  val dataFileIndexes: MutableList<DataFileIndex> = mutableListOf()
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Transactional
fun deleteIndex(indexesId: Long) {
val index = indexRepository.findIndexById(indexesId) ?: throw IndexException(ErrorCode.NOT_FOUND)
indexRepository.delete(index)
}
class Index(
// 기존 필드…
) : BaseEntity() {
@OneToMany(
mappedBy = "index",
cascade = [CascadeType.REMOVE],
orphanRemoval = true
)
val dataFileIndexes: MutableList<DataFileIndex> = mutableListOf()
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt around
lines 56 to 61, add a reference-integrity pre-check before deleting an Index:
after loading the index, verify if index.dataFileIndexes is not empty (ensure
the collection is initialized/loaded) and if so throw
IndexException(ErrorCode.CONFLICT) to block deletion; only call
indexRepository.delete(index) when there are no related DataFileIndex entries.
This implements the "pre-check" approach suggested in the review without
changing entity cascade settings.

Comment on lines +14 to +17
class IndexException(
override val errorCode: ErrorCode,
override val cause: Throwable? = null,
) : CustomException(errorCode, errorCode.message, cause)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

중복 제거: 부모의 기본 메시지를 활용해 생성자 단순화

현재 IndexExceptionerrorCode.message를 중복 전달합니다. 부모의 기본값을 사용하면 코드가 더 간결해집니다.

적용 예:

 class IndexException(
     override val errorCode: ErrorCode,
     override val cause: Throwable? = null,
-) : CustomException(errorCode, errorCode.message, cause)
+) : CustomException(errorCode, cause = cause)

참고: 동일 패턴의 S3Exception에도 같은 방식 적용 가능(변경 범위 밖이므로 참고 코드만 첨부).

class S3Exception(
    override val errorCode: ErrorCode,
    override val cause: Throwable? = null,
) : CustomException(errorCode, cause = cause)
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/global/error/CustomException.kt around
lines 14 to 17, the IndexException constructor redundantly passes
errorCode.message to the parent; simplify by removing the explicit message
argument and rely on the parent's default message parameter (i.e., change the
super call to use only the errorCode and optional cause so the constructor
becomes: override val errorCode: ErrorCode, override val cause: Throwable? =
null, ) : CustomException(errorCode, cause = cause). Apply the same pattern for
similar exceptions (e.g., S3Exception) where applicable.

S3_UNSUPPORTED_CONTENT_TYPE(HttpStatus.BAD_REQUEST, "S3_007", "지원하지 않는 Content-Type 입니다.")
S3_UNSUPPORTED_CONTENT_TYPE(HttpStatus.BAD_REQUEST, "S3_007", "지원하지 않는 Content-Type 입니다."),

// index
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

주석 스타일 일관성 제안: // S3// index

기존 섹션 주석(// S3)과 동일한 스타일로 통일하면 가독성이 좋아집니다.

적용 예:

-    // index
+    // INDEX
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// index
// INDEX
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/global/error/ErrorCode.kt around line 26,
the section comment currently uses "// index" which is inconsistent with the
existing section comment style (e.g., "// S3"); update the comment to match the
established style/format used elsewhere (use the same casing and spacing, e.g.
"// Index" or the same uppercase token pattern as other headings) so all section
comments are consistent.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

♻️ Duplicate comments (10)
src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt (2)

10-13: 식별자는 불변(val)/비널(Long)로 고정하세요

응답 DTO의 식별자는 생성/조회 시점에 비널이 자연스럽고, 변경될 여지가 없어 val indexId: Long이 적합합니다. 컨버터에서 requireNotNull(index.id)로 매핑해 NPE 대신 명시적 예외로 fail-fast 하길 권장합니다.

 data class IndexPreviewResponse(
-    var indexId: Long?,
+    val indexId: Long,
     val snapshotName: String
 )

15-24: Detail 응답도 동일하게 불변/비널로 통일

Detail에서도 val indexId: Long로 고정해 Preview/Detail 간 정합성을 확보하세요.

 data class IndexDetailResponse(
-    var indexId: Long?,
+    val indexId: Long,
     val snapshotName: String,
     val chunkingSize: Int,
     val overlapSize: Int,
     val similarityMetric: SimilarityMetric,
     val topK: Int,
     val embeddingModel: EmbeddingModel,
     val reranker: Boolean,
 )
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)

7-17: 생성 시 snapshotName 트리밍 누락 — update와 일관성 유지 필요

엔티티 업데이트 로직이 trim을 적용한다면, 생성 시에도 동일하게 trim해야 합니다.

 fun toIndex(createRequest: IndexCreateRequest): Index {
     return Index(
-        snapshotName = createRequest.snapshotName,
+        snapshotName = createRequest.snapshotName.trim(),
         overlapSize = createRequest.overlapSize,
         chunkingSize = createRequest.chunkingSize,
         similarityMetric = createRequest.similarityMetric,
         topK = createRequest.topK,
         embeddingModel = createRequest.embeddingModel,
         reranker = createRequest.reranker
     )
 }
src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (4)

18-25: @Valid 누락으로 DTO Bean Validation이 동작하지 않습니다

생성 요청에 대해 DTO 레벨 제약(@min 등)을 적용하려면 @Valid를 추가하세요.

 @PostMapping
 @ResponseStatus(HttpStatus.CREATED)
 fun createIndex(
-    @RequestBody indexCreateRequest: IndexCreateRequest
+    @RequestBody @Valid indexCreateRequest: IndexCreateRequest
 ): ApiResponse<IndexPreviewResponse> {
     val createdIndex = indexService.createIndex(indexCreateRequest)
     return ApiResponse.ok(createdIndex)
 }

33-39: 서비스 시그니처와 nullability 정합성 맞추기

컨트롤러는 ApiResponse<IndexDetailResponse>(Non-null)를 반환합니다. 서비스의 getIndexIndexDetailResponse?이면 불필요한 널 처리/컴파일 불일치가 생깁니다. 서비스 시그니처를 Non-null로 변경하세요.

서비스 측 제안(diff는 서비스 파일 코멘트 참고):

  • fun getIndex(indexId: Long): IndexDetailResponse
  • not found 시 예외를 던지므로 null 반환 경로는 없습니다.

41-48: 단일 리소스 업데이트인데 메서드명이 복수형입니다 — updateIndex로 정리

메서드명과 서비스 호출을 단수형으로 정리해 가독성과 일관성을 높이세요.

 @PutMapping("/{indexId}")
-fun updateIndexes(
+fun updateIndex(
     @PathVariable indexId: Long,
     @RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
 ): ApiResponse<IndexPreviewResponse> {
-    val indexPreviewResponse = indexService.updateIndexes(indexId, indexUpdateRequest)
+    val indexPreviewResponse = indexService.updateIndex(indexId, indexUpdateRequest)
     return ApiResponse.ok(indexPreviewResponse)
 }

50-56: DELETE 응답: 204 No Content로 반환 정리 권장

ApiResponse<Unit>에 null을 싣는 패턴은 지양하는 것이 좋습니다. 204로 명확히 하세요.

 @DeleteMapping("/{indexId}")
-fun deleteIndex(
-    @PathVariable indexId: Long
-): ApiResponse<Unit> {
-    indexService.deleteIndex(indexId)
-    return ApiResponse.ok(null, "인덱스가 삭제 되었습니다.")
-}
+@ResponseStatus(HttpStatus.NO_CONTENT)
+fun deleteIndex(@PathVariable indexId: Long) {
+    indexService.deleteIndex(indexId)
+}
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (3)

19-21: 경계값 버그: overlap == chunking 허용됨 — 요구사항/도메인과 불일치

요구/도메인 규칙(이전 커밋 기준)과 통일하려면 >=로 변경해 equality도 차단하세요. 업데이트 로직도 동일.

-        if (indexCreateRequest.overlapSize > indexCreateRequest.chunkingSize) {
+        if (indexCreateRequest.overlapSize >= indexCreateRequest.chunkingSize) {
             throw IndexException(ErrorCode.OVERLAP_OVERFLOW)
         }

33-38: getIndex 반환 타입을 Non-null로 정정

not found 시 예외를 던지므로 nullable일 이유가 없습니다. 컨트롤러와 정합성도 맞출 수 있습니다.

-    fun getIndex(indexId: Long): IndexDetailResponse? {
+    fun getIndex(indexId: Long): IndexDetailResponse {
         val index = indexRepository.findIndexById(indexId) ?: throw IndexException(ErrorCode.NOT_FOUND)

         return toIndexDetailResponse(index)
     }

41-54: update 시그니처/검증 통일: 이름 단수화 + '>=' 검증

  • 메서드명을 단수형으로
  • overlap 경계값을 생성과 동일하게 >=로 통일
-    fun updateIndexes(
+    fun updateIndex(
         indexId: Long,
         indexUpdateRequest: IndexUpdateRequest
     ): IndexPreviewResponse {
         val index = indexRepository.findIndexById(indexId) ?: throw IndexException(ErrorCode.NOT_FOUND)

-        if (indexUpdateRequest.overlapSize > indexUpdateRequest.chunkingSize) {
+        if (indexUpdateRequest.overlapSize >= indexUpdateRequest.chunkingSize) {
             throw IndexException(ErrorCode.OVERLAP_OVERFLOW)
         }

         index.update(indexUpdateRequest)

         return toIndexPreviewResponse(index)
     }

다음 스크립트로 레포 전역에서 updateIndexes 사용처를 찾아 일괄 치환 범위를 점검하세요(테스트/컨트롤러/인터페이스 등).

#!/bin/bash
rg -n -C2 --glob '!**/build/**' '\bupdateIndexes\s*\('
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 562da35 and 9d2952c.

📒 Files selected for processing (4)
  • src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-17T13:12:02.200Z
Learnt from: catturtle123
PR: Simple-RAG/BACK#6
File: src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt:16-22
Timestamp: 2025-08-17T13:12:02.200Z
Learning: catturtle123이 Index 엔티티의 overlapSize ≤ chunkingSize 검증을 엔티티 레벨이 아닌 서비스 레이어에서 구현하기로 결정했으며, chunking embedding 저장 관련 PR에서 이 검증 로직을 추가할 예정이므로 해당 PR에서 언급해야 함 (Simple-RAG/BACK 프로젝트)

Applied to files:

  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt
🧬 Code Graph Analysis (4)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)
  • name (9-42)
src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt (3)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)
  • name (9-42)
src/main/kotlin/simplerag/ragback/domain/index/entity/enums/SimilarityMetric.kt (1)
  • description (3-9)
src/main/kotlin/simplerag/ragback/domain/index/entity/enums/EmbeddingModel.kt (1)
  • dim (3-49)
src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (3)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)
  • name (9-42)
src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt (1)
  • dataFileService (17-44)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (3)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (4)
  • toIndex (7-17)
  • toIndexPreviewResponse (26-31)
  • toIndexPreviewResponseList (19-24)
  • toIndexDetailResponse (33-44)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)
  • name (9-42)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)

Comment on lines +19 to +24
fun toIndexPreviewResponseList(
indexes: List<Index>
): IndexPreviewResponseList {
val indexList = indexes.map { toIndexPreviewResponse(it) }
return IndexPreviewResponseList(indexList)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

리스트 DTO 네이밍 변경 반영

DTO의 필드명을 items로 변경할 경우, 생성자 호출도 함께 정정해야 합니다.

 fun toIndexPreviewResponseList(
     indexes: List<Index>
 ): IndexPreviewResponseList {
     val indexList = indexes.map { toIndexPreviewResponse(it) }
-    return IndexPreviewResponseList(indexList)
+    return IndexPreviewResponseList(items = indexList)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun toIndexPreviewResponseList(
indexes: List<Index>
): IndexPreviewResponseList {
val indexList = indexes.map { toIndexPreviewResponse(it) }
return IndexPreviewResponseList(indexList)
}
fun toIndexPreviewResponseList(
indexes: List<Index>
): IndexPreviewResponseList {
val indexList = indexes.map { toIndexPreviewResponse(it) }
return IndexPreviewResponseList(items = indexList)
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt
around lines 19 to 24, the IndexPreviewResponseList constructor call must be
updated to match the DTO field rename to items; change the return to pass the
list using the named parameter (e.g., IndexPreviewResponseList(items =
indexList)) so the correct property is populated.

Comment on lines +26 to +31
fun toIndexPreviewResponse(index: Index): IndexPreviewResponse {
return IndexPreviewResponse(
indexId = index.id,
snapshotName = index.snapshotName,
)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

식별자 비널화에 맞춘 매핑: requireNotNull로 fail-fast

DTO에서 val indexId: Long로 강화할 경우, 여기서 비널 보장을 해야 합니다.

 fun toIndexPreviewResponse(index: Index): IndexPreviewResponse {
     return IndexPreviewResponse(
-        indexId = index.id,
+        indexId = requireNotNull(index.id) { "Index.id should not be null after persistence" },
         snapshotName = index.snapshotName,
     )
 }

참고: 만약 생성 직후 IDENTITY 전략으로 id가 null 가능성이 있으면, 서비스에서 saveAndFlush를 사용해 id를 보장하거나 트랜잭션 flush 시점을 조정하세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun toIndexPreviewResponse(index: Index): IndexPreviewResponse {
return IndexPreviewResponse(
indexId = index.id,
snapshotName = index.snapshotName,
)
}
fun toIndexPreviewResponse(index: Index): IndexPreviewResponse {
return IndexPreviewResponse(
indexId = requireNotNull(index.id) { "Index.id should not be null after persistence" },
snapshotName = index.snapshotName,
)
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt
around lines 26 to 31, the mapping returns index.id which may be null while the
DTO expects a non-null Long; change the mapping to perform a fail-fast null
check by using requireNotNull(index.id) when assigning indexId so a clear
exception is thrown if id is missing, and if IDs can legitimately be null
immediately after creation ensure the caller/service uses saveAndFlush or
adjusts transaction/flush timing so the entity has an id before converting.

Comment on lines +33 to +44
fun toIndexDetailResponse(index: Index): IndexDetailResponse {
return IndexDetailResponse(
indexId = index.id,
chunkingSize = index.chunkingSize,
overlapSize = index.overlapSize,
similarityMetric = index.similarityMetric,
topK = index.topK,
embeddingModel = index.embeddingModel,
reranker = index.reranker,
snapshotName = index.snapshotName,
)
} No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Detail 매핑도 동일하게 비널 보장

Detail 응답도 requireNotNull(index.id)로 일관되게 처리하세요.

 fun toIndexDetailResponse(index: Index): IndexDetailResponse {
     return IndexDetailResponse(
-        indexId = index.id,
+        indexId = requireNotNull(index.id) { "Index.id should not be null for detail response" },
         chunkingSize = index.chunkingSize,
         overlapSize = index.overlapSize,
         similarityMetric = index.similarityMetric,
         topK = index.topK,
         embeddingModel = index.embeddingModel,
         reranker = index.reranker,
         snapshotName = index.snapshotName,
     )
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun toIndexDetailResponse(index: Index): IndexDetailResponse {
return IndexDetailResponse(
indexId = index.id,
chunkingSize = index.chunkingSize,
overlapSize = index.overlapSize,
similarityMetric = index.similarityMetric,
topK = index.topK,
embeddingModel = index.embeddingModel,
reranker = index.reranker,
snapshotName = index.snapshotName,
)
}
fun toIndexDetailResponse(index: Index): IndexDetailResponse {
return IndexDetailResponse(
indexId = requireNotNull(index.id) { "Index.id should not be null for detail response" },
chunkingSize = index.chunkingSize,
overlapSize = index.overlapSize,
similarityMetric = index.similarityMetric,
topK = index.topK,
embeddingModel = index.embeddingModel,
reranker = index.reranker,
snapshotName = index.snapshotName,
)
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt
around lines 33 to 44, the IndexDetailResponse mapping uses index.id directly;
change it to requireNotNull(index.id) to consistently enforce non-null IDs like
other mappings. Update the indexId assignment to requireNotNull(index.id, "Index
id must not be null") and leave other fields as-is.

Comment on lines +6 to +8
data class IndexPreviewResponseList(
val indexDetailResponse: List<IndexPreviewResponse>,
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

리스트 필드 네이밍 오류: Preview 목록인데 indexDetailResponse 라는 이름입니다

의미상 혼동됩니다. items(또는 previews)처럼 중립적·일관된 이름으로 교체하세요. 컨버터/컨트롤러 반환에도 파급 수정 필요합니다.

적용 예:

 data class IndexPreviewResponseList(
-    val indexDetailResponse: List<IndexPreviewResponse>,
+    val items: List<IndexPreviewResponse>,
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
data class IndexPreviewResponseList(
val indexDetailResponse: List<IndexPreviewResponse>,
)
data class IndexPreviewResponseList(
val items: List<IndexPreviewResponse>,
)
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt around
lines 6 to 8, the list field is misnamed as `indexDetailResponse` though it
holds preview items; rename the property to a neutral plural such as `items` or
`previews` (e.g., `val items: List<IndexPreviewResponse>`) and update all usages
accordingly: adjust the DTO declaration, any converter/mapping code, controller
return types and JSON serializers/clients that reference the old name, and run
compilation/tests to ensure no remaining references to `indexDetailResponse`.

Comment on lines 56 to 61
@Transactional
fun deleteIndex(indexId: Long) {
val index = indexRepository.findIndexById(indexId) ?: throw IndexException(ErrorCode.NOT_FOUND)

indexRepository.delete(index)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

삭제 시 참조 무결성 전략 명확화 필요(DataFileIndex 등 연관 고려)

Index가 다른 엔티티에서 참조될 가능성이 있으면 FK 제약으로 삭제 실패가 납니다. 정책을 선택해 반영하세요.

옵션:

  • 사전 체크: 참조 존재 시 IndexException(CONFLICT)로 차단
  • 계단 삭제: @OneToMany(mappedBy="index", cascade=REMOVE, orphanRemoval=true) 추가

도메인 정책을 결정하고 서비스/엔티티 레벨 중 한 곳에서 일관되게 처리하시길 권장합니다.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt around
lines 56-61, the deleteIndex method does not handle referential integrity (e.g.,
DataFileIndex) and may fail due to FK constraints; choose a domain policy and
implement it consistently: either (A) add a pre-delete existence check in the
service that queries related entities (e.g.,
dataFileIndexRepository.existsByIndexId(indexId)) and throw
IndexException(ErrorCode.CONFLICT) if any references exist, or (B) update the
Index entity mapping to cascade removals (add @OneToMany(mappedBy="index",
cascade = [CascadeType.REMOVE], orphanRemoval = true) on the collection) so
related rows are deleted automatically; apply the chosen approach and ensure
transactionality and tests reflect the behavior.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

♻️ Duplicate comments (10)
src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (1)

8-8: 정렬 방향 반영은 OK. 다만 리스트 전량 반환 대신 페이징 지원을 권장합니다

생성일 내림차순 메서드 채택은 목적에 부합합니다. 실제 운영 데이터량을 고려하면 전량 조회(List) 대신 Pageable을 받아 Page로 반환하는 시그니처가 바람직합니다. 기본 findAll(pageable) + Sort를 쓰면 파생 메서드 자체도 불필요합니다.

적용안 A(파생 메서드 유지 + 페이징):

-    fun findAllByOrderByCreatedAtDesc(): List<Index>
+    fun findAllByOrderByCreatedAtDesc(pageable: org.springframework.data.domain.Pageable): org.springframework.data.domain.Page<Index>

적용안 B(파생 메서드 제거 + 표준 메서드 사용):

  • Repository: 커스텀 메서드 제거
  • Service: indexRepository.findAll(PageRequest.of(page, size, Sort.by(DESC, "createdAt")))
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (3)

9-16: 생성 시 snapshotName 트리밍 반영 Good

생성/수정 간 일관성이 확보되었습니다.


22-24: 리스트 DTO 생성자/프로퍼티명 확인 필요(items 여부)

IndexPreviewResponseList(indexList) 호출이 DTO 정의와 불일치할 수 있습니다. 과거 논의대로 필드명이 items라면 명시적 이름 인자 사용이 안전합니다.

권장 수정:

-    return IndexPreviewResponseList(indexList)
+    return IndexPreviewResponseList(items = indexList)

검증 스크립트(프로퍼티명 확인):

#!/bin/bash
# IndexPreviewResponseList 정의 및 프로퍼티 확인
rg -n -C2 -g '!**/build/**' -P 'data\s+class\s+IndexPreviewResponseList\b.*\(' --type=kt
rg -n -C2 -g '!**/build/**' -P '\bclass\s+IndexPreviewResponseList\b' --type=kt

27-31: id 널 가능성 방어 필요 — DTO는 비널 Long일 가능성이 큼

영속 전 엔티티 id는 null일 수 있습니다. DTO가 비널을 기대한다면 fail-fast로 명확히 드러내세요.

권장 수정:

 fun toIndexPreviewResponse(index: Index): IndexPreviewResponse {
     return IndexPreviewResponse(
-        indexId = index.id,
+        indexId = requireNotNull(index.id) { "Index.id should not be null after persistence" },
         snapshotName = index.snapshotName,
     )
 }
 fun toIndexDetailResponse(index: Index): IndexDetailResponse {
     return IndexDetailResponse(
-        indexId = index.id,
+        indexId = requireNotNull(index.id) { "Index.id should not be null for detail response" },
         chunkingSize = index.chunkingSize,
         overlapSize = index.overlapSize,
         similarityMetric = index.similarityMetric,
         topK = index.topK,
         embeddingModel = index.embeddingModel,
         reranker = index.reranker,
         snapshotName = index.snapshotName,
     )
 }

Also applies to: 33-44

src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (3)

21-23: POST 요청에 @Valid 추가된 점 확인

요청 DTO 제약 검증이 이제 정상 동작합니다.


42-48: 메서드명 단수형으로 정리 권장: updateIndexes → updateIndex

단일 리소스 업데이트이므로 명명 일관성을 맞추세요.

-    fun updateIndexes(
+    fun updateIndex(
         @PathVariable indexId: Long,
         @RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
     ): ApiResponse<IndexPreviewResponse> {
         val indexPreviewResponse = indexService.updateIndex(indexId, indexUpdateRequest)
         return ApiResponse.ok(indexPreviewResponse)
     }

50-56: 204 No Content와 본문 동시 반환 — HTTP 규약 위반, 즉시 수정 필요

@ResponseStatus(NO_CONTENT)인 경우 바디를 보내면 안 됩니다. ApiResponse<Unit> 반환/본문 제거가 필요합니다.

     @DeleteMapping("/{indexId}")
     @ResponseStatus(HttpStatus.NO_CONTENT)
-    fun deleteIndex(
-        @PathVariable indexId: Long
-    ): ApiResponse<Unit> {
-        indexService.deleteIndex(indexId)
-        return ApiResponse.ok(null, "인덱스가 삭제 되었습니다.")
-    }
+    fun deleteIndex(@PathVariable indexId: Long) {
+        indexService.deleteIndex(indexId)
+    }
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2)

33-35: embeddingModel 불변(val) 유지 확인

업데이트에서 변경되지 않는 필드를 불변으로 둔 점 좋습니다.


45-52: 도메인 불변식 검증 보강 권장(overlap < chunking 등)

서비스에서 예외를 던지더라도, 엔티티 레벨에서도 불변식을 보장하면 일관성이 좋아집니다. 최소한 업데이트 시 아래 검증을 추가하세요. IllegalArgumentException 매핑이 없다면 글로벌 핸들러에 400 매핑을 추가하거나 도메인 예외로 통일하는 것도 고려 바랍니다.

     fun update(req: IndexUpdateRequest) {
+        require(req.chunkingSize >= 1) { "chunkingSize는 1 이상이어야 합니다." }
+        require(req.overlapSize >= 0) { "overlapSize는 0 이상이어야 합니다." }
+        require(req.overlapSize < req.chunkingSize) { "overlap 크기는 chunking 크기를 넘을 수 없습니다." }
+        require(req.topK >= 1) { "topK는 1 이상이어야 합니다." }
+        require(req.snapshotName.isNotBlank()) { "snapshotName은 비어 있을 수 없습니다." }
         snapshotName = req.snapshotName.trim()
         chunkingSize = req.chunkingSize
         overlapSize = req.overlapSize
         similarityMetric = req.similarityMetric
         topK = req.topK
         reranker = req.reranker
     }
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1)

58-62: 삭제 시 참조 무결성 처리 누락 — FK 제약으로 삭제 실패 가능. 사전 체크 또는 cascade 전략 필요

Index가 다른 엔티티(예: DataFileIndex)에서 참조 중이면 FK 제약으로 delete가 실패할 수 있습니다. 도메인 정책을 결정해 일관되게 반영하세요.

옵션:

  • 사전 체크(권장): 관련 레코드 존재 시 CONFLICT 예외로 차단
  • 계단 삭제: Index@OneToMany(cascade = REMOVE, orphanRemoval = true)로 연관 엔티티 자동 삭제

서비스 레벨 사전 체크 예시(diff):

@@
-class IndexService(
-    private val indexRepository: IndexRepository
-) {
+class IndexService(
+    private val indexRepository: IndexRepository,
+    private val dataFileIndexRepository: DataFileIndexRepository
+) {
@@
     @Transactional
     fun deleteIndex(indexId: Long) {
         val index = indexRepository.findByIdOrNull(indexId) ?: throw IndexException(ErrorCode.NOT_FOUND)
 
-        indexRepository.delete(index)
+        if (dataFileIndexRepository.existsByIndexId(indexId)) {
+            throw IndexException(ErrorCode.CONFLICT)
+        }
+        indexRepository.delete(index)
     }

DataFileIndexRepository가 없다면 아래와 같이 정의해 활용하세요(별도 파일):

interface DataFileIndexRepository : JpaRepository<DataFileIndex, Long> {
    fun existsByIndexId(indexId: Long): Boolean
}

또는 Index 엔티티에 역방향 컬렉션이 있다면, 초기화되도록 fetch 전략/쿼리를 보장한 뒤 아래처럼 체크할 수 있습니다:

  • if (index.dataFileIndexes.isNotEmpty()) throw IndexException(ErrorCode.CONFLICT)

검증 스크립트로 현 상태를 확인하세요.

#!/bin/bash
# DataFileIndex 연관 및 Repository 존재 여부 점검

echo "== 엔티티/매핑 존재 여부 =="
rg -n -C3 -g '*.kt' 'class\s+DataFileIndex\b|@ManyToOne|@OneToMany|cascade\s*=|orphanRemoval\s*='

echo
echo "== Repository 존재 여부 =="
rg -n -C2 -g '*.kt' 'interface\s+\w*DataFileIndexRepository\b|existsByIndexId'

echo
echo "== Index 엔티티에 역방향 컬렉션이 있는지 =="
rg -n -C3 -g '*.kt' '(class\s+Index\b|@OneToMany|dataFileIndexes)'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9d2952c and b376f5c.

📒 Files selected for processing (6)
  • src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1 hunks)
  • src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-17T13:12:02.200Z
Learnt from: catturtle123
PR: Simple-RAG/BACK#6
File: src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt:16-22
Timestamp: 2025-08-17T13:12:02.200Z
Learning: catturtle123이 Index 엔티티의 overlapSize ≤ chunkingSize 검증을 엔티티 레벨이 아닌 서비스 레이어에서 구현하기로 결정했으며, chunking embedding 저장 관련 PR에서 이 검증 로직을 추가할 예정이므로 해당 PR에서 언급해야 함 (Simple-RAG/BACK 프로젝트)

Applied to files:

  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt
🧬 Code Graph Analysis (5)
src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (3)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (2)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (4)
  • toIndex (7-17)
  • toIndexPreviewResponse (26-31)
  • toIndexPreviewResponseList (19-24)
  • toIndexDetailResponse (33-44)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1)
src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt (1)
  • dataFileService (17-44)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
🔇 Additional comments (7)
src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (1)

6-9: JpaRepository 확장 및 기본 형태 적절

도메인 엔티티에 대한 표준 CRUD 활용에 문제가 없습니다.

src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1)

33-39: 서비스 반환타입 nullability 일치 확인 완료

IndexService.getIndex(indexId: Long): IndexDetailResponse 메소드가 Non-null IndexDetailResponse를 반환하도록 선언되어 있으며, 조회 결과가 없을 경우 IndexException을 던져 예외 처리로 일관성을 유지하므로 컨트롤러와 반환 타입의 nullability가 일치합니다.

src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (1)

93-95: 테스트와 DTO 프로퍼티명 일치 확인
검증 결과, IndexPreviewResponseList DTO에 val indexDetailResponse: List<IndexPreviewResponse>로 정의되어 있어 테스트에서 indexes.indexDetailResponse.size를 사용하는 것이 올바릅니다. 수정이 필요 없습니다.

src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (4)

20-22: overlap 경계값을 '>='로 통일 — 요구사항과 일치

overlapSize >= chunkingSize 시 예외를 던지도록 수정되어 요구사항 및 엔티티 측 정책과 정합입니다. 이전 경계값 불일치 지적사항이 깔끔히 해소됐습니다.


48-50: update에도 동일한 경계값 검증 적용 — 일관성 OK

수정(update) 시에도 '>=' 검증을 동일하게 적용하여 생성과 일관성을 확보했습니다.


35-39: getIndex 반환 타입 Non-null 일치 — 예외 흐름과 정합

NOT_FOUND 시 예외를 던지므로 Non-null 시그니처가 맞습니다. 컨트롤러/호출부와의 계약 일치 측면에서도 적절합니다.


17-26: 검증 애너테이션 적용 확인 완료
IndexCreateRequest/IndexUpdateRequest DTO에 @field:NotBlank, @field:Length, @field:Positive, @field:PositiveOrZero 등이 이미 적용되어 있으며, IndexController에는 @validated@RequestBody @Valid 어노테이션이 사용되고 있음을 확인했습니다. 서비스 레벨 검증과 병행하여 현재 설정만으로도 충분합니다.

Comment on lines +18 to +25
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun createIndex(
@RequestBody @Valid indexCreateRequest: IndexCreateRequest
): ApiResponse<IndexPreviewResponse> {
val createdIndex = indexService.createIndex(indexCreateRequest)
return ApiResponse.ok(createdIndex)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

201 Created의 Location 헤더 제공 고려

리소스 생성 시 Location: /api/v1/indexes/{id} 헤더를 추가하면 REST 사용성이 좋아집니다. ApiResponse 래퍼 정책에 따라 ResponseEntity 사용을 검토하세요.

예시:

@PostMapping
fun createIndex(@RequestBody @Valid indexCreateRequest: IndexCreateRequest): ResponseEntity<ApiResponse<IndexPreviewResponse>> {
    val created = indexService.createIndex(indexCreateRequest)
    val uri = URI.create("/api/v1/indexes/${created.indexId}")
    return ResponseEntity.created(uri).body(ApiResponse.ok(created))
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt
around lines 18–25, the createIndex endpoint returns ApiResponse directly and
omits a Location header for the newly created resource; change the method to
return ResponseEntity<ApiResponse<IndexPreviewResponse>> (remove or replace
@ResponseStatus), call indexService.createIndex(...) to get the created object,
build the resource URI with the created.indexId (e.g.
URI.create("/api/v1/indexes/${created.indexId}")), and return
ResponseEntity.created(uri).body(ApiResponse.ok(created)) so the response
includes the 201 Location header while preserving the ApiResponse wrapper.

Comment on lines +27 to +31
@GetMapping
fun getIndexes(): ApiResponse<IndexPreviewResponseList> {
val indexPreviewResponseList = indexService.getIndexes()
return ApiResponse.ok(indexPreviewResponseList)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

목록 조회에 페이징/정렬 파라미터 도입 권장

대량 데이터에서 전체 반환은 비효율적입니다. Pageable 주입 + 서비스/레포지토리 연동으로 페이지네이션을 제공하세요.

컨트롤러 예시:

-    @GetMapping
-    fun getIndexes(): ApiResponse<IndexPreviewResponseList> {
-        val indexPreviewResponseList = indexService.getIndexes()
+    @GetMapping
+    fun getIndexes(pageable: org.springframework.data.domain.Pageable): ApiResponse<IndexPreviewResponseList> {
+        val indexPreviewResponseList = indexService.getIndexes(pageable)
         return ApiResponse.ok(indexPreviewResponseList)
     }

서비스/레포지토리(참고):

// Service
fun getIndexes(pageable: Pageable): IndexPreviewResponseList {
    val page = indexRepository.findAll(pageable.sortOr(Sort.by(Sort.Direction.DESC, "createdAt")))
    // page.content → DTO 매핑 + total, hasNext 등 노출 필요 시 확장
    ...
}

// Helper
private fun Pageable.sortOr(defaultSort: Sort) =
    if (sort.isSorted) this else PageRequest.of(pageNumber, pageSize, defaultSort)

Comment on lines 41 to 55
@Transactional
fun updateIndex(
indexId: Long,
indexUpdateRequest: IndexUpdateRequest
): IndexPreviewResponse {
val index = indexRepository.findByIdOrNull(indexId) ?: throw IndexException(ErrorCode.NOT_FOUND)

if (indexUpdateRequest.overlapSize >= indexUpdateRequest.chunkingSize) {
throw IndexException(ErrorCode.OVERLAP_OVERFLOW)
}

index.update(indexUpdateRequest)

return toIndexPreviewResponse(index)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

업데이트 응답 스키마 재검토(선택)

update가 Preview를 반환하는 것은 일관성 있지만, 클라이언트가 갱신된 상세값을 즉시 필요로 한다면 Detail을 반환하거나 ETag/버전 필드를 포함하는 것이 실용적입니다. API 사용 패턴에 맞춰 결정하세요.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt around
lines 41 to 55, the updateIndex function currently returns an
IndexPreviewResponse but clients may need the full updated details or a
version/ETag; change the method to return the appropriate updated representation
by either (A) returning a Detail response (e.g., IndexDetailResponse) built from
the updated entity via the existing mapper, or (B) keep returning Preview but
include the index version/ETag in the response object; update the method
signature, the mapper call (toIndexDetailResponse or
toIndexPreviewResponseWithVersion), and any callers/tests to expect the new
response shape.

Comment on lines 186 to 189
@DisplayName("인덱스 수정 시 없는 인덱스를 조회하면 에러가 터진다.")
fun getUpdateTestWithInvalidIndex() {
// given
val savedIndex = indexRepository.save(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

테스트 메서드 네이밍 정리 권장

의도에 맞게 updateIndexTestWithInvalidIndex로 변경하면 가독성이 좋아집니다.

-    fun getUpdateTestWithInvalidIndex() {
+    fun updateIndexTestWithInvalidIndex() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@DisplayName("인덱스 수정 시 없는 인덱스를 조회하면 에러가 터진다.")
fun getUpdateTestWithInvalidIndex() {
// given
val savedIndex = indexRepository.save(
@DisplayName("인덱스 수정 시 없는 인덱스를 조회하면 에러가 터진다.")
fun updateIndexTestWithInvalidIndex() {
// given
val savedIndex = indexRepository.save(
🤖 Prompt for AI Agents
In src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
around lines 186 to 189, the test method name getUpdateTestWithInvalidIndex does
not reflect its intention; rename the method to updateIndexTestWithInvalidIndex
and update any references/imports or test annotations accordingly so the method
name clearly indicates it tests updating an index with an invalid id.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🔭 Outside diff range comments (1)
src/main/kotlin/simplerag/ragback/domain/document/service/DataFileService.kt (1)

83-96: getDataFiles 내부 N+1 쿼리 가능성 — 배치 조회로 성능 개선 권장

Line 84에서 각 데이터파일마다 findTagsByDataFile(dataFile)를 호출합니다. 데이터 수가 많을 경우 N+1 쿼리 이슈가 발생할 수 있습니다. 리포지토리에 “여러 DataFile에 대한 태그를 한 번에 조인 조회”하는 메서드를 추가하고, 서비스에서 결과를 매핑하는 방식으로 개선을 권장합니다.

아래와 같이 변경을 제안드립니다.

  • 서비스 변경(해당 블록 대체):
-        val dataFileList: MutableList<DataFileDetailResponse> = ArrayList()
-        dataSlice.forEach { dataFile ->
-            val dataFileTags: List<DataFileTag> = dataFileTagRepository.findTagsByDataFile(dataFile)
-
-            val tagDtos: List<TagDTO> = dataFileTags.map { dataFileTag ->
-                val tag = dataFileTag.tag
-                TagDTO(tag.id, tag.name)
-            }
-
-            dataFileList.add(DataFileDetailResponse.of(dataFile, tagDtos))
-        }
+        val dataFiles = dataSlice.toList()
+        val tagMap: Map<Long, List<DataFileTag>> =
+            dataFileTagRepository.findAllByDataFileInWithTag(dataFiles)
+                .groupBy({ it.dataFile.id!! }, { it })
+
+        val dataFileList: List<DataFileDetailResponse> = dataFiles.map { dataFile ->
+            val dataFileTags = tagMap[dataFile.id] ?: emptyList()
+            val tagDtos = dataFileTags.map { TagDTO(it.tag.id, it.tag.name) }
+            DataFileDetailResponse.of(dataFile, tagDtos)
+        }
  • 리포지토리 추가 메서드(새 코드, 참고용):
@Query(
    """
    SELECT dft
    FROM DataFileTag dft
    JOIN FETCH dft.tag t
    WHERE dft.dataFile IN :dataFiles
    """
)
fun findAllByDataFileInWithTag(@Param("dataFiles") dataFiles: List<DataFile>): List<DataFileTag>

이렇게 하면 태그 조회가 1회 조인 쿼리로 축약되어 getDataFiles의 전체 쿼리 수를 크게 줄일 수 있습니다.

♻️ Duplicate comments (4)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)

29-33: ID null-safety 보강: requireNotNull로 fail-fast 처리

JPA save 이후엔 id가 채워지지만, 방어적 코딩 차원에서 null 불가를 명시해 두는 것이 안전합니다. 아래처럼 변경을 제안합니다.

 return IndexPreviewResponse(
-        indexId = index.id,
+        indexId = requireNotNull(index.id) { "Index.id should not be null after persistence" },
         snapshotName = index.snapshotName,
 )
 return IndexDetailResponse(
-        indexId = index.id,
+        indexId = requireNotNull(index.id) { "Index.id should not be null for detail response" },
         chunkingSize = index.chunkingSize,
         overlapSize = index.overlapSize,
         similarityMetric = index.similarityMetric,
         topK = index.topK,
         embeddingModel = index.embeddingModel,
         reranker = index.reranker,
         snapshotName = index.snapshotName,
 )

Also applies to: 36-46

src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (3)

29-33: 목록 조회에 페이징/필터 도입 고려

현재 전량 로딩이므로 데이터 증가 시 병목이 됩니다. Pageable을 인자로 받고, 저장소에 대응 오버로드를 추가하는 것을 권장합니다.

예시:

-    fun getIndexes(): IndexPreviewResponseList {
-        val indexes = indexRepository.findAllByOrderByCreatedAtDesc()
-        return toIndexPreviewResponseList(indexes)
-    }
+    fun getIndexes(pageable: Pageable): IndexPreviewResponseList {
+        val page = indexRepository.findAllByOrderByCreatedAtDesc(pageable)
+        return toIndexPreviewResponseList(page.content)
+    }

추가로 컨트롤러에서 Pageable을 받아 전달하도록 변경하면 대규모 데이터에서도 안정적으로 동작합니다.


56-61: 삭제 시 참조 무결성 정책 명확화 필요

Index가 다른 엔티티에서 참조될 경우 FK 제약으로 삭제 실패할 수 있습니다. 비즈니스 정책에 맞춰 아래 중 하나를 택해 일관 처리하세요.

  • 사전 체크: 관련 리포지토리(existsByIndexId 등)로 참조 존재 시 IndexException(CONFLICT) 반환
  • 계단 삭제: Index에 @onetomany(mappedBy="index", cascade=REMOVE, orphanRemoval=true) 추가

서비스 쪽 사전 체크 예시(리포지토리 존재 시):

if (dataFileIndexRepository.existsByIndexId(indexId)) {
    throw IndexException(ErrorCode.CONFLICT)
}
indexRepository.delete(index)

정책을 선택해 테스트로 기대 동작을 고정해 두는 것을 권장합니다.


43-54: update 응답 스키마 재검토(선택): Detail 반환 혹은 ETag 포함

업데이트 직후 클라이언트가 최신 상세 값을 필요로 한다면 Detail 응답이 실용적입니다. 미니멀 변경을 원하면 Preview 유지 + 버전/ETag 포함도 옵션입니다.

Detail로 전환 예:

-    ): IndexPreviewResponse {
+    ): IndexDetailResponse {
@@
-        return toIndexPreviewResponse(index)
+        return toIndexDetailResponse(index)

동일한 변경을 컨트롤러/테스트에 반영해 주세요.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b376f5c and 37da14f.

📒 Files selected for processing (16)
  • src/main/kotlin/simplerag/ragback/domain/chat/entity/Model.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/document/entity/DataFile.kt (0 hunks)
  • src/main/kotlin/simplerag/ragback/domain/document/entity/DataFileTag.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/document/entity/Tag.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/document/repository/DataFileTagRepository.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/document/service/DataFileService.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/prompt/entity/FewShot.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt (1 hunks)
  • src/test/kotlin/simplerag/ragback/domain/document/service/DataFileServiceTest.kt (1 hunks)
  • src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (1 hunks)
💤 Files with no reviewable changes (1)
  • src/main/kotlin/simplerag/ragback/domain/document/entity/DataFile.kt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-17T13:12:02.200Z
Learnt from: catturtle123
PR: Simple-RAG/BACK#6
File: src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt:16-22
Timestamp: 2025-08-17T13:12:02.200Z
Learning: catturtle123이 Index 엔티티의 overlapSize ≤ chunkingSize 검증을 엔티티 레벨이 아닌 서비스 레이어에서 구현하기로 결정했으며, chunking embedding 저장 관련 PR에서 이 검증 로직을 추가할 예정이므로 해당 PR에서 언급해야 함 (Simple-RAG/BACK 프로젝트)

Applied to files:

  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt
🧬 Code Graph Analysis (8)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)
src/main/kotlin/simplerag/ragback/global/util/FloatArrayToPgVectorStringConverter.kt (1)
  • autoApply (6-34)
src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (3)
src/main/kotlin/simplerag/ragback/domain/document/repository/TagRepository.kt (1)
  • findByName (6-11)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/document/repository/DataFileRepository.kt (1)
  • existsBySha256 (6-8)
src/main/kotlin/simplerag/ragback/domain/chat/entity/Model.kt (2)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (1)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
src/main/kotlin/simplerag/ragback/domain/document/entity/DataFileTag.kt (1)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
src/main/kotlin/simplerag/ragback/domain/prompt/entity/FewShot.kt (1)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (2)
src/test/kotlin/simplerag/ragback/RagBackApplicationTests.kt (2)
  • contextLoads (6-13)
  • contextLoads (9-11)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (2)
src/main/kotlin/simplerag/ragback/domain/document/entity/DataFile.kt (1)
  • name (7-32)
src/main/kotlin/simplerag/ragback/global/entity/BaseEntity.kt (1)
  • name (11-21)
🔇 Additional comments (15)
src/main/kotlin/simplerag/ragback/domain/document/entity/Tag.kt (1)

19-19: LGTM: 클래스 헤더 공백 스타일이 Kotlin 컨벤션에 부합합니다.

의미적 변경 없이 가독성만 개선되었습니다. 일관된 스타일 유지에도 도움이 됩니다.

src/main/kotlin/simplerag/ragback/domain/chat/entity/Model.kt (1)

29-29: LGTM: 클래스 상속 표기 공백 수정이 적절합니다.

Kotlin 코딩 컨벤션에 맞는 형식으로 변경되었고 동작 변화는 없습니다.

src/main/kotlin/simplerag/ragback/domain/prompt/entity/Prompt.kt (1)

25-25: LGTM: 포맷팅 변경(콜론 앞 공백)만 존재하며 안전합니다.

컨벤션 정렬로 가독성 향상, 기능상 영향 없습니다.

src/main/kotlin/simplerag/ragback/domain/prompt/entity/FewShot.kt (1)

28-28: LGTM: 클래스 헤더 포맷팅 정리 OK.

의도된 스타일 통일이며 런타임 영향은 없습니다.

src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (1)

30-30: LGTM: 상속 표기 공백 수정만 반영되었습니다.

의미적 변화 없이 컨벤션에 맞춘 정리로 보입니다.

src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)

22-22: 형식 정리(Lint) 변경만 수행 — 문제 없음

기능적 변화 없이 기본 클래스 지정부의 공백 정렬만 변경되었습니다. 다른 엔티티들과의 스타일 일관성도 좋아졌습니다.

src/test/kotlin/simplerag/ragback/domain/document/service/DataFileServiceTest.kt (1)

230-235: 테스트 본문의 포맷팅 정리 — 가독성 향상, 동작 영향 없음

DataFile(...) 초기화 구문 들여쓰기 정리가 이루어졌으며, 테스트 의미나 검증 로직에는 영향이 없습니다.

src/main/kotlin/simplerag/ragback/domain/document/repository/DataFileTagRepository.kt (1)

12-19: @query 포맷 재정렬 — 동작 동일, 가독성 개선

쿼리 문자열을 멀티라인으로 분리하여 가독성이 좋아졌습니다. 기능적 변화는 없습니다.

src/main/kotlin/simplerag/ragback/domain/document/entity/DataFileTag.kt (1)

24-24: 형식 정리(Lint) 변경만 수행 — 문제 없음

상속부 공백 정렬 변경만 있으며, 런타임 동작에는 영향이 없습니다. 프로젝트 전반의 스타일과 일관적입니다.

src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt (1)

8-8: 정렬 방향 Desc 전환은 좋습니다

최신 생성순으로 내려주는 구현은 실사용 패턴에 부합합니다.

src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (1)

12-12: 생성 시 snapshotName trim 적용 일관성 확보 Good

update와 동일하게 생성 시에도 trim 적용되어 데이터 일관성이 좋아졌습니다.

src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (3)

33-35: embeddingModel을 val로 고정한 점 좋습니다

업데이트 시 변경되지 않는 속성을 불변으로 유지해 실수 변경 가능성을 줄였습니다.


45-52: 검증 위치(서비스 레이어) 결정과 정합성 OK

retrieved_learnings에 따라 overlap 등 교차 필드 검증을 서비스 레이어에서 처리하기로 한 결정을 코드가 잘 반영되어 있습니다(update는 순수 상태 변경에 집중).


10-16: 플러그인 적용 확인 완료

  • build.gradleorg.jetbrains.kotlin.plugin.springorg.jetbrains.kotlin.plugin.jpa 플러그인 선언(id 'org.jetbrains.kotlin.plugin.spring', id 'org.jetbrains.kotlin.plugin.jpa')이 확인되었습니다.
  • allOpen {} 블록도 존재해 JPA 엔티티의 open 및 No-Arg 생성자 요구사항을 충족합니다.

별도 조치가 필요 없습니다.

src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1)

63-65: 경계값 검증 로직 정확하고 헬퍼로 추출된 점 좋습니다

overlapSize >= chunkingSize 차단이 요구사항과 일치하며, 중복을 헬퍼로 제거해 가독성이 좋습니다.

Comment on lines +26 to +27
return IndexPreviewResponseList(indexList)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

리스트 DTO 생성자: 명시적 파라미터 사용 및 DTO 구조 확인 권장

생성자 위치 의존보다 이름 기반 전달이 안전합니다. DTO 필드명이 items/contents 등으로 변경될 가능성에 대비해 명시적 파라미터를 권장합니다. 현재 DTO 시그니처가 무엇인지 확인해 주세요.
[suggest_optional_refactor]

다음 스크립트로 DTO 정의를 확인해 주세요(필드명/생성자 파라미터 명 확인):


🏁 Script executed:

#!/bin/bash
# DTO 정의 위치와 필드명을 출력
rg -n -C3 'data\s+class\s+IndexPreviewResponseList' --glob '!**/build/**'
rg -n -C3 'class\s+IndexPreviewResponseList|data\s+class\s+IndexPreviewResponseList' --glob '!**/build/**'

Length of output: 1670


리스트 DTO 생성자: 명시적 파라미터 사용 제안

확인된 DTO 시그니처:

data class IndexPreviewResponseList(
    val indexDetailResponse: List<IndexPreviewResponse>
)

생성자 호출 시 위치 의존 대신 이름 기반 전달을 권장드립니다:

- return IndexPreviewResponseList(indexList)
+ return IndexPreviewResponseList(indexDetailResponse = indexList)

• 파일: src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt
• 대상 라인: 26–27

🤖 Prompt for AI Agents
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt
around lines 26–27: the DTO IndexPreviewResponseList has a single property
indexDetailResponse, so replace the positional constructor call with a named
argument to avoid positional-dependency; update the return to use
IndexPreviewResponseList(indexDetailResponse = indexList) to make the intent
explicit and robust against future constructor changes.

Comment on lines +6 to +9
interface IndexRepository : JpaRepository<Index, Long> {

fun findAllByOrderByCreatedAtDesc(): List<Index>
} No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

목록 조회에 페이징 오버로드 추가 제안

데이터가 증가하면 List 전량 로딩은 부담이 큽니다. Desc 정렬을 유지하되 Pageable 오버로드를 추가해 확장성을 확보하는 것을 권장합니다.

적용 예(인터페이스에 오버로드 추가):

+import org.springframework.data.domain.Page
+import org.springframework.data.domain.Pageable
 interface IndexRepository : JpaRepository<Index, Long> {
 
     fun findAllByOrderByCreatedAtDesc(): List<Index>
+    fun findAllByOrderByCreatedAtDesc(pageable: Pageable): Page<Index>
 }

서비스/컨트롤러에서 pageable을 받아 활용하도록 점진 적용할 수 있습니다(기존 List 메서드는 하위호환 유지).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface IndexRepository : JpaRepository<Index, Long> {
fun findAllByOrderByCreatedAtDesc(): List<Index>
}
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
interface IndexRepository : JpaRepository<Index, Long> {
fun findAllByOrderByCreatedAtDesc(): List<Index>
fun findAllByOrderByCreatedAtDesc(pageable: Pageable): Page<Index>
}
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/repository/IndexRepository.kt
around lines 6-9, the current findAllByOrderByCreatedAtDesc() returns a full
List which will not scale; add a pageable overload to keep the same descending
sort but return a Page for efficient pagination (e.g. declare a method that
accepts a org.springframework.data.domain.Pageable and returns
org.springframework.data.domain.Page<Index>), keep the existing List-returning
method for backward compatibility, and then progressively wire Pageable through
service/controller layers where needed.

Comment on lines +33 to +48
@Test
@DisplayName("인덱스 생성이 정상 작동한다")
fun createIndexTest() {
// given
val indexCreateRequest =
IndexCreateRequest("test", 1, 0, SimilarityMetric.COSINE, 1, EmbeddingModel.TEXT_EMBEDDING_3_LARGE, true)

// when
val createIndexResponse = indexService.createIndex(indexCreateRequest)

// then
val indices = indexRepository.findAll()
val index = indices[0]
assertEquals(index.id, createIndexResponse.indexId)
assertEquals(index.snapshotName, createIndexResponse.snapshotName)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

생성/수정 시 snapshotName trim 동작 테스트 추가 제안

Converter/Entity에서 trim을 수행하므로 공백 입력에 대한 동작을 테스트로 보강하면 회귀를 막을 수 있습니다.

예시 테스트 메서드(추가):

@Test
@DisplayName("생성 시 snapshotName 앞뒤 공백이 제거된다")
fun createIndexTrimsSnapshotName() {
    val req = IndexCreateRequest("  spaced  ", 1, 0, SimilarityMetric.COSINE, 1, EmbeddingModel.TEXT_EMBEDDING_3_LARGE, true)
    val res = indexService.createIndex(req)
    assertThat(res.snapshotName).isEqualTo("spaced")
}

@Test
@DisplayName("수정 시 snapshotName 앞뒤 공백이 제거된다")
fun updateIndexTrimsSnapshotName() {
    val saved = indexRepository.save(Index("orig", 1, 0, SimilarityMetric.COSINE, 1, EmbeddingModel.TEXT_EMBEDDING_3_LARGE, true))
    indexService.updateIndex(saved.id!!, IndexUpdateRequest("  fixed  ", 2, 1, SimilarityMetric.EUCLIDEAN, 3, false))
    val reloaded = indexRepository.findById(saved.id!!).get()
    assertThat(reloaded.snapshotName).isEqualTo("fixed")
}

필요하시다면 PR에 반영 가능한 형태로 패치를 준비해 드릴게요.

Comment on lines +92 to +97
// when
val indexes = indexService.getIndexes()

// then
assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

리스트 DTO 필드명 점검: Preview 목록인데 indexDetailResponse라는 명칭 혼동

getIndexes는 Preview 리스트를 반환하지만 test에서는 indexDetailResponse 필드를 참조합니다. 타입/명칭 간 불일치 소지가 있어 DTO/테스트 중 하나의 네이밍 정리를 권장합니다(items 혹은 previews 등).
[suggest_optional_refactor]

DTO 구조 확인 스크립트:


🏁 Script executed:

#!/bin/bash
# IndexPreviewResponseList 및 그 프로퍼티 명을 확인
rg -n -C3 'data\s+class\s+IndexPreviewResponseList' --glob '!**/build/**'
rg -n -C2 'val\s+\w+\s*:\s*List<IndexPreviewResponse>' --glob '!**/build/**'

Length of output: 1360


네이밍 통일 권장: indexDetailResponsepreviews

getIndexes()가 Preview 리스트를 반환하지만, DTO와 테스트의 필드명이 indexDetailResponse로 되어 있어 직관성이 떨어집니다. 아래 위치를 참고해 필드명을 보다 명확한 previews(또는 items)로 변경하는 것을 추천드립니다.

  • 변경 대상
    • src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt
      • data class IndexPreviewResponseListval indexDetailResponse: List<IndexPreviewResponse>val previews: List<IndexPreviewResponse>
    • src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
      • indexes.indexDetailResponse.sizeindexes.previews.size
🤖 Prompt for AI Agents
In src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
around lines 92-97 and the DTO at
src/main/kotlin/simplerag/ragback/domain/index/dto/IndexResponseDTO.kt, rename
the field indexDetailResponse to a clearer name like previews: update the data
class IndexPreviewResponseList to use val previews: List<IndexPreviewResponse>
(or items) and then update all usages in tests and production code, e.g., change
indexes.indexDetailResponse.size to indexes.previews.size, ensuring
imports/serialization names are adjusted if necessary to keep compatibility.

Comment on lines +96 to +97
assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

정렬 보장에 대한 단언 추가 제안

리스트 조회가 createdAt DESC임을 보장하려면 순서까지 검증하는 단언을 추가하는 것이 좋습니다.

-        assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
+        assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
+        // 생성 순서: test(먼저) → test2(나중). DESC 정렬이므로 test2, test 순이어야 합니다.
+        assertThat(indexes.indexDetailResponse.map { it.snapshotName })
+            .containsExactly("test2", "test")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
}
assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
// 생성 순서: test(먼저) → test2(나중). DESC 정렬이므로 test2, test 순이어야 합니다.
assertThat(indexes.indexDetailResponse.map { it.snapshotName })
.containsExactly("test2", "test")
}
🤖 Prompt for AI Agents
In src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
around lines 96-97, the test asserts the list size but does not verify ordering;
add an assertion that the returned list is ordered by createdAt descending by
comparing the createdAt of adjacent entries (e.g., assert that
indexDetailResponse[0].createdAt is after or equal to
indexDetailResponse[1].createdAt or extract the createdAt list and assert it is
sorted descending) so the test guarantees the expected sort order.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (9)
src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (3)

92-97: 리스트 정렬 검증 단언 추가 + Preview 리스트 필드명 정비 필요

  • 정렬 보장을 테스트에 포함해 회귀를 막으세요.
  • 필드명이 indexDetailResponse인데 Preview 리스트를 담고 있어 혼동됩니다. DTO/테스트 모두 previews 등으로 통일 권장(이전 코멘트와 동일).

정렬 단언 추가 예시:

         // then
-        assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
+        assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
+        // 생성 순서: test(먼저) → test2(나중). DESC 정렬이면 ["test2", "test"]
+        assertThat(indexes.indexDetailResponse.map { it.snapshotName })
+            .containsExactly("test2", "test")

186-189: 테스트 메서드 네이밍 정리: updateIndexTestWithInvalidIndex 권장

의도를 명확히 드러내도록 메서드명을 정비하세요(이전 코멘트 동일).

-    fun updateTestWithInvalidIndex() {
+    fun updateIndexTestWithInvalidIndex() {

33-48: snapshotName trim 동작 테스트 케이스 보강 제안

도메인/컨버터에서 trim을 수행하므로 회귀 방지를 위해 테스트를 추가하세요(이전 코멘트 동일).

추가 테스트 예시(파일 말미에 추가):

@Test
@DisplayName("생성 시 snapshotName 앞뒤 공백이 제거된다")
fun createIndexTrimsSnapshotName() {
    val req = IndexCreateRequest("  spaced  ", 1, 0, SimilarityMetric.COSINE, 1, EmbeddingModel.TEXT_EMBEDDING_3_LARGE, true)
    val res = indexService.createIndex(req)
    assertThat(res.snapshotName).isEqualTo("spaced")
}

@Test
@DisplayName("수정 시 snapshotName 앞뒤 공백이 제거된다")
fun updateIndexTrimsSnapshotName() {
    val saved = indexRepository.save(Index("orig", 1, 0, SimilarityMetric.COSINE, 1, EmbeddingModel.TEXT_EMBEDDING_3_LARGE, true))
    indexService.updateIndex(saved.id!!, IndexUpdateRequest("  fixed  ", 2, 1, SimilarityMetric.EUCLIDEAN, 3, false))
    val reloaded = indexRepository.findById(saved.id!!).get()
    assertThat(reloaded.snapshotName).isEqualTo("fixed")
}

필요하면 패치 형태로 준비해 드리겠습니다.

src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (4)

41-47: 메서드 네이밍 일관성: updateIndexes → updateIndex 권장

단일 리소스 업데이트이므로 복수형은 혼동을 유발합니다(이전 코멘트 동일).

-    fun updateIndexes(
+    fun updateIndex(
         @PathVariable indexId: Long,
         @RequestBody @Valid indexUpdateRequest: IndexUpdateRequest,
     ): ApiResponse<IndexPreviewResponse> {
-        val indexPreviewResponse = indexService.updateIndex(indexId, indexUpdateRequest)
+        val indexPreviewResponse = indexService.updateIndex(indexId, indexUpdateRequest)
         return ApiResponse.ok(indexPreviewResponse)
     }

50-56: DELETE 응답: 204 No Content로 단순화 권장

ApiResponse<Unit>에 null을 싣기보다 204로 반환하는 것이 REST/스키마 일관성에 더 적합합니다(이전 코멘트 동일).

-    @DeleteMapping("/{indexId}")
-    fun deleteIndex(
-        @PathVariable indexId: Long
-    ): ApiResponse<Unit> {
-        indexService.deleteIndex(indexId)
-        return ApiResponse.ok(null, "인덱스가 삭제 되었습니다.")
-    }
+    @DeleteMapping("/{indexId}")
+    @ResponseStatus(HttpStatus.NO_CONTENT)
+    fun deleteIndex(@PathVariable indexId: Long) {
+        indexService.deleteIndex(indexId)
+    }

27-31: 목록 조회에 페이징 도입 권장

전체 반환은 확장성에 불리합니다. Pageable을 받아 서비스/레포지토리까지 전파하세요(이전 코멘트 동일).

컨트롤러 변경 예시:

-    @GetMapping
-    fun getIndexes(): ApiResponse<IndexPreviewResponseList> {
-        val indexPreviewResponseList = indexService.getIndexes()
+    @GetMapping
+    fun getIndexes(pageable: org.springframework.data.domain.Pageable): ApiResponse<IndexPreviewResponseList> {
+        val indexPreviewResponseList = indexService.getIndexes(pageable)
         return ApiResponse.ok(indexPreviewResponseList)
     }

서비스/레포지토리 시그니처도 함께 조정이 필요합니다(아래 서비스 파일 코멘트 참고).


18-25: 201 Created 시 Location 헤더 제공 고려

생성 리소스의 URI를 Location으로 제공하면 클라이언트 사용성이 좋아집니다(이전 코멘트 동일).

-    @PostMapping
-    @ResponseStatus(HttpStatus.CREATED)
-    fun createIndex(
-        @RequestBody @Valid indexCreateRequest: IndexCreateRequest
-    ): ApiResponse<IndexPreviewResponse> {
-        val createdIndex = indexService.createIndex(indexCreateRequest)
-        return ApiResponse.ok(createdIndex)
-    }
+    @PostMapping
+    fun createIndex(
+        @RequestBody @Valid indexCreateRequest: IndexCreateRequest
+    ): org.springframework.http.ResponseEntity<ApiResponse<IndexPreviewResponse>> {
+        val createdIndex = indexService.createIndex(indexCreateRequest)
+        val uri = java.net.URI.create("/api/v1/indexes/${createdIndex.indexId}")
+        return org.springframework.http.ResponseEntity.created(uri).body(ApiResponse.ok(createdIndex))
+    }
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (2)

58-63: 삭제 시 참조 무결성 정책 명확화 필요(DataFileIndex 등 연관 고려)

Index는 다른 엔티티에서 참조될 수 있습니다. FK 제약으로 삭제 실패 가능성이 있으므로 정책을 선택해 구현하세요(이전 코멘트 동일).

옵션:

  • 사전 체크: dataFileIndexRepository.existsByIndexId(indexId)가 true면 IndexException(CONFLICT)로 차단
  • 계단 삭제: Index@OneToMany(mappedBy="index", cascade=REMOVE, orphanRemoval=true) 컬렉션 추가

현재 ChunkEmbedding은 cascade로 커버되지만, DataFileIndex 등 다른 연관도 정책에 맞춰 처리하는 것을 권장합니다.


45-56: 업데이트 응답 스키마 재검토(선택)

업데이트 후 클라이언트가 상세값을 즉시 필요로 한다면 Preview 대신 Detail 반환이 더 실용적일 수 있습니다(이전 코멘트 동일).

대안:

  • A: IndexDetailResponse 반환으로 변경
  • B: Preview 유지 + 버전/ETag 추가
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 37da14f and 11531c3.

📒 Files selected for processing (5)
  • src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/repository/ChunkingEmbeddingRepository.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1 hunks)
  • src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-17T13:12:02.200Z
Learnt from: catturtle123
PR: Simple-RAG/BACK#6
File: src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt:16-22
Timestamp: 2025-08-17T13:12:02.200Z
Learning: catturtle123이 Index 엔티티의 overlapSize ≤ chunkingSize 검증을 엔티티 레벨이 아닌 서비스 레이어에서 구현하기로 결정했으며, chunking embedding 저장 관련 PR에서 이 검증 로직을 추가할 예정이므로 해당 PR에서 언급해야 함 (Simple-RAG/BACK 프로젝트)

Applied to files:

  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt
🧬 Code Graph Analysis (5)
src/main/kotlin/simplerag/ragback/domain/index/repository/ChunkingEmbeddingRepository.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (2)
  • name (8-42)
  • updateEmbedding (35-40)
src/main/kotlin/simplerag/ragback/domain/index/controller/IndexController.kt (2)
src/main/kotlin/simplerag/ragback/domain/document/controller/DataFileController.kt (1)
  • dataFileService (17-44)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (3)
src/test/kotlin/simplerag/ragback/domain/document/service/DataFileServiceTest.kt (2)
  • dataFileService (28-221)
  • it (164-164)
src/test/kotlin/simplerag/ragback/RagBackApplicationTests.kt (2)
  • contextLoads (6-13)
  • contextLoads (9-11)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (2)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (4)
  • toIndex (10-20)
  • toIndexPreviewResponse (29-34)
  • toIndexPreviewResponseList (22-27)
  • toIndexDetailResponse (36-47)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2)
src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (2)
  • name (8-42)
  • updateEmbedding (35-40)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
🔇 Additional comments (2)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)

48-55: 엔티티 레이어 검증에 대해

현재 update는 단순 할당만 수행하고, overlap 검증은 서비스에서 수행합니다. 과거 합의(learned preference)에 따라 검증을 서비스 레이어로 유지하는 현재 접근이 적절합니다. snapshotName trim을 적용한 점도 👍

src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1)

22-29: 검증 로직 분리 잘 했습니다

overlap 검증을 헬퍼로 추출해 중복을 제거한 구조가 깔끔합니다. 컨버터에서 snapshotName trim을 적용한 것도 👍

Comment on lines 40 to 41
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
val chunkingEmbeddingList: MutableList<ChunkEmbedding> = mutableListOf(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

컬렉션 프로퍼티 네이밍 개선: chunkingEmbeddingList → chunkEmbeddings 권장

도메인 용어 및 엔티티명(ChunkEmbedding)과 일관되게 간결하게 정리하세요.

-    @OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
-    val chunkingEmbeddingList: MutableList<ChunkEmbedding> = mutableListOf(),
+    @OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
+    val chunkEmbeddings: MutableList<ChunkEmbedding> = mutableListOf(),

참고: 종속 코드의 프로퍼티 참조도 함께 변경 필요합니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
val chunkingEmbeddingList: MutableList<ChunkEmbedding> = mutableListOf(),
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
val chunkEmbeddings: MutableList<ChunkEmbedding> = mutableListOf(),
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt around lines
40-41, rename the collection property from chunkingEmbeddingList to
chunkEmbeddings to align with domain naming and the ChunkEmbedding entity;
update the @OneToMany declaration to use the new property name and keep its
cascade/orphanRemoval/mappedBy attributes. Then search-and-replace all usages
across the codebase (entity references, DTOs, service/repository code, tests,
JSON mappings, and any serialization names) to use chunkEmbeddings so
compilation and runtime behavior remain correct.

Comment on lines 7 to 11
interface ChunkingEmbeddingRepository: JpaRepository<ChunkEmbedding, Long> {

fun deleteAllByIndex(index: Index)

} No newline at end of file
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

네이밍 혼동: Chunking vs Chunk — 리포지토리명 일관화 권장

엔티티명이 ChunkEmbedding인데 리포지토리/파일명은 ChunkingEmbeddingRepository입니다. 팀 컨벤션 상 일관성을 위해 ChunkEmbeddingRepository로 리네임하세요.

적용 예시:

-interface ChunkingEmbeddingRepository: JpaRepository<ChunkEmbedding, Long> {
+interface ChunkEmbeddingRepository: JpaRepository<ChunkEmbedding, Long> {

추가로 파일명도 ChunkEmbeddingRepository.kt로 변경하는 것을 권장합니다.

🤖 Prompt for AI Agents
In
src/main/kotlin/simplerag/ragback/domain/index/repository/ChunkingEmbeddingRepository.kt
around lines 7 to 11, the repository interface name uses
"ChunkingEmbeddingRepository" while the entity is named ChunkEmbedding; rename
the interface to ChunkEmbeddingRepository and update the filename to
ChunkEmbeddingRepository.kt, then update any imports/usages across the codebase
(including tests and DI configuration) to reference the new name to maintain
consistency with project naming conventions.

Comment on lines +31 to +35
@Transactional(readOnly = true)
fun getIndexes(): IndexPreviewResponseList {
val indexes = indexRepository.findAllByOrderByCreatedAtDesc()
return toIndexPreviewResponseList(indexes)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

목록 조회 페이징/정렬 지원 제안

데이터 증가 시 전체 조회는 비효율적입니다. Pageable을 받아 기본 정렬을 createdAt DESC로 강제(미지정 시)하는 형태로 확장하세요(컨트롤러 코멘트와 쌍).

예시(개념):

@Transactional(readOnly = true)
fun getIndexes(pageable: Pageable): IndexPreviewResponseList {
    val sorted = if (pageable.sort.isSorted) pageable
                 else PageRequest.of(pageable.pageNumber, pageable.pageSize, Sort.by(Sort.Direction.DESC, "createdAt"))
    val page = indexRepository.findAll(sorted)
    return toIndexPreviewResponseList(page.content)
}

레포지토리에 findAll(Pageable) 시그니처가 기본 제공됩니다.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt around
lines 31-35, change the no-arg getIndexes() to accept a Pageable, enforce a
default Sort.by(DESC, "createdAt") when pageable.sort is not sorted, call
indexRepository.findAll(pageable) to get a Page and pass page.content to
toIndexPreviewResponseList; ensure the method remains @Transactional(readOnly =
true) and update the controller to pass through Pageable so paging and sorting
are supported and defaulted as requested.

Comment on lines +145 to +149
// when * then
val message = assertThrows<IndexException> { indexService.getIndex(savedIndex.id!! + 1L) }.message

assertEquals("리소스를 찾을 수 없습니다.", message)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Not Found 검증에서 id + 1L 사용 지양 — 더 견고한 미존재 ID 사용

단일 테스트 컨텍스트에서도 시퀀스/식별자 정책에 따라 +1이 우연히 존재할 수 있습니다. Long.MAX_VALUE 등의 명백한 미존재 ID를 사용하세요.

-        val message = assertThrows<IndexException> { indexService.getIndex(savedIndex.id!! + 1L) }.message
+        val message = assertThrows<IndexException> { indexService.getIndex(Long.MAX_VALUE) }.message
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// when * then
val message = assertThrows<IndexException> { indexService.getIndex(savedIndex.id!! + 1L) }.message
assertEquals("리소스를 찾을 수 없습니다.", message)
}
// when * then
val message = assertThrows<IndexException> { indexService.getIndex(Long.MAX_VALUE) }.message
assertEquals("리소스를 찾을 수 없습니다.", message)
🤖 Prompt for AI Agents
In src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
around lines 145 to 149, the test uses savedIndex.id!! + 1L to assert a Not
Found case which can accidentally collide with existing IDs; replace the
computed id with an explicit guaranteed-nonexistent ID such as Long.MAX_VALUE
(or a constant clearly out-of-range) when calling indexService.getIndex(...)
inside assertThrows, and update the assertion accordingly to keep the test
robust.

Comment on lines +204 to +209
// when * then
val message =
assertThrows<IndexException> { indexService.updateIndex(savedIndex.id!! + 1L, indexUpdateRequest) }.message

assertEquals("리소스를 찾을 수 없습니다.", message)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Not Found 검증에서 id + 1L 사용 지양 — 더 견고한 미존재 ID 사용

업데이트 실패 테스트도 동일하게 정비하세요.

-        val message =
-            assertThrows<IndexException> { indexService.updateIndex(savedIndex.id!! + 1L, indexUpdateRequest) }.message
+        val message =
+            assertThrows<IndexException> { indexService.updateIndex(Long.MAX_VALUE, indexUpdateRequest) }.message
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// when * then
val message =
assertThrows<IndexException> { indexService.updateIndex(savedIndex.id!! + 1L, indexUpdateRequest) }.message
assertEquals("리소스를 찾을 수 없습니다.", message)
}
// when * then
val message =
assertThrows<IndexException> { indexService.updateIndex(Long.MAX_VALUE, indexUpdateRequest) }.message
assertEquals("리소스를 찾을 수 없습니다.", message)
}
🤖 Prompt for AI Agents
In src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
around lines 204 to 209, the test uses savedIndex.id!! + 1L as a “not found” id
which is brittle; replace that with a guaranteed-nonexistent id (e.g.,
Long.MAX_VALUE or compute an id not present by querying the repository) and
assert against that value instead; update the corresponding update-failure test
the same way so both tests use a robust non-existent id (optionally assert
repository.findById(nonExistentId).isEmpty before calling the service).

Comment on lines +277 to +281
// when * then
val message = assertThrows<IndexException> { indexService.deleteIndex(savedIndex.id!! + 1L) }.message

assertEquals("리소스를 찾을 수 없습니다.", message)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Not Found 검증에서 id + 1L 사용 지양 — 더 견고한 미존재 ID 사용

삭제 실패 테스트도 동일하게 정비하세요.

-        val message = assertThrows<IndexException> { indexService.deleteIndex(savedIndex.id!! + 1L) }.message
+        val message = assertThrows<IndexException> { indexService.deleteIndex(Long.MAX_VALUE) }.message
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// when * then
val message = assertThrows<IndexException> { indexService.deleteIndex(savedIndex.id!! + 1L) }.message
assertEquals("리소스를 찾을 수 없습니다.", message)
}
// when * then
val message = assertThrows<IndexException> { indexService.deleteIndex(Long.MAX_VALUE) }.message
assertEquals("리소스를 찾을 수 없습니다.", message)
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (8)
src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (5)

95-96: 정렬 보장 단언 추가 권장(createdAt DESC)

리스트 반환이 createdAt 내림차순임을 테스트로 고정하면 회귀를 방지할 수 있습니다.

         // then
-        assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
+        assertThat(indexes.indexDetailResponse.size).isEqualTo(2)
+        // 생성 순서: "test"(먼저) → "test2"(나중). DESC 정렬이므로 ["test2", "test"] 이어야 함.
+        assertThat(indexes.indexDetailResponse.map { it.snapshotName })
+            .containsExactly("test2", "test")

145-147: Not Found 케이스의 미존재 ID를 확실한 값으로 교체(Long.MAX_VALUE)

id + 1L는 상황에 따라 실제 존재할 수 있어 취약합니다.

-        val message = assertThrows<IndexException> { indexService.getIndex(savedIndex.id!! + 1L) }.message
+        val message = assertThrows<IndexException> { indexService.getIndex(Long.MAX_VALUE) }.message

203-207: Not Found 케이스의 미존재 ID를 확실한 값으로 교체(Long.MAX_VALUE)

업데이트 실패 테스트에도 동일하게 적용하세요.

-        val message =
-            assertThrows<IndexException> { indexService.updateIndex(savedIndex.id!! + 1L, indexUpdateRequest) }.message
+        val message =
+            assertThrows<IndexException> { indexService.updateIndex(Long.MAX_VALUE, indexUpdateRequest) }.message

276-279: Not Found 케이스의 미존재 ID를 확실한 값으로 교체(Long.MAX_VALUE)

삭제 실패 테스트에도 동일하게 적용하세요.

-        val message = assertThrows<IndexException> { indexService.deleteIndex(savedIndex.id!! + 1L) }.message
+        val message = assertThrows<IndexException> { indexService.deleteIndex(Long.MAX_VALUE) }.message

186-188: 테스트 메서드 네이밍 정리 — 가독성 향상

의도를 명확히 드러내도록 rename을 권장합니다.

-    fun updateTestWithInvalidIndex() {
+    fun updateIndexTestWithInvalidIndex() {
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (3)

29-33: 목록 조회에 페이징/정렬 파라미터 지원 제안(확장성)

데이터 증가 시 전체 조회는 비용이 큽니다. Pageable을 받아 기본 정렬을 createdAt DESC로 강제(미지정 시)하는 형태를 권장합니다.

-    @Transactional(readOnly = true)
-    fun getIndexes(): IndexPreviewResponseList {
-        val indexes = indexRepository.findAllByOrderByCreatedAtDesc()
-        return toIndexPreviewResponseList(indexes)
-    }
+    @Transactional(readOnly = true)
+    fun getIndexes(pageable: org.springframework.data.domain.Pageable): IndexPreviewResponseList {
+        val sorted = if (pageable.sort.isSorted) pageable
+        else org.springframework.data.domain.PageRequest.of(
+            pageable.pageNumber,
+            pageable.pageSize,
+            org.springframework.data.domain.Sort.by(org.springframework.data.domain.Sort.Direction.DESC, "createdAt")
+        )
+        val page = indexRepository.findAll(sorted)
+        return toIndexPreviewResponseList(page.content)
+    }

컨트롤러/테스트 시그니처 업데이트가 필요합니다.


42-54: 업데이트 응답 스키마 재검토(클라이언트 사용성 관점)

업데이트 후 Preview만 반환하면 추가 조회가 필요할 수 있습니다. 즉시 상세가 필요한 사용 패턴이라면 Detail 반환을 고려하세요.

-    ): IndexPreviewResponse {
+    ): IndexDetailResponse {
         val index = indexRepository.findByIdOrNull(indexId) ?: throw IndexException(ErrorCode.NOT_FOUND)
 
         validateOverlap(indexUpdateRequest.overlapSize, indexUpdateRequest.chunkingSize)
 
         index.update(indexUpdateRequest)
 
-        return toIndexPreviewResponse(index)
+        return toIndexDetailResponse(index)
     }

56-61: 삭제 시 참조 무결성 정책 명확화 필요(DataFileIndex 등 연관 고려)

Index는 다른 엔티티에서 참조될 수 있습니다. FK 제약으로 삭제 실패 시 일관된 도메인 정책을 적용하세요.

정책 옵션:

  • 사전 체크: dataFileIndexRepository.existsByIndexId(indexId)가 true면 IndexException(CONFLICT)로 차단
  • 계단 삭제: Index에 @OneToMany(mappedBy="index", cascade=[REMOVE], orphanRemoval=true)로 연관 컬렉션 추가

서비스 사전 체크 예시(개념):

if (dataFileIndexRepository.existsByIndexId(indexId)) {
    throw IndexException(ErrorCode.CONFLICT)
}
indexRepository.delete(index)

(참고: 해당 레포지토리 의존성 주입 및 테스트 보강 필요)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 11531c3 and 5ae2ac3.

📒 Files selected for processing (4)
  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/repository/ChunkEmbeddingRepository.kt (1 hunks)
  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (1 hunks)
  • src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-17T13:12:02.200Z
Learnt from: catturtle123
PR: Simple-RAG/BACK#6
File: src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt:16-22
Timestamp: 2025-08-17T13:12:02.200Z
Learning: catturtle123이 Index 엔티티의 overlapSize ≤ chunkingSize 검증을 엔티티 레벨이 아닌 서비스 레이어에서 구현하기로 결정했으며, chunking embedding 저장 관련 PR에서 이 검증 로직을 추가할 예정이므로 해당 PR에서 언급해야 함 (Simple-RAG/BACK 프로젝트)

Applied to files:

  • src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt
  • src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt
🧬 Code Graph Analysis (3)
src/main/kotlin/simplerag/ragback/domain/index/repository/ChunkEmbeddingRepository.kt (1)
src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (2)
  • name (8-42)
  • updateEmbedding (35-40)
src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (2)
src/main/kotlin/simplerag/ragback/domain/index/converter/IndexConverter.kt (4)
  • toIndex (10-20)
  • toIndexPreviewResponse (29-34)
  • toIndexPreviewResponseList (22-27)
  • toIndexDetailResponse (36-47)
src/main/kotlin/simplerag/ragback/RagBackApplication.kt (1)
  • @SpringBootApplication (7-9)
src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (2)
src/main/kotlin/simplerag/ragback/domain/index/entity/ChunkEmbedding.kt (2)
  • name (8-42)
  • updateEmbedding (35-40)
src/main/kotlin/simplerag/ragback/domain/index/entity/DataFileIndex.kt (1)
  • name (7-22)
🔇 Additional comments (4)
src/main/kotlin/simplerag/ragback/domain/index/repository/ChunkEmbeddingRepository.kt (1)

6-6: LGTM — 표준 CRUD에 충분한 최소 정의입니다.

Spring Data JPA의 관례에 맞는 심플 레포지토리 선언으로, 현재 요구사항을 충족합니다.

src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt (1)

48-55: 엔티티는 상태 변경만, 검증은 서비스 레이어 — 현재 구현이 팀 합의와 일치합니다.

retrieved learnings에 따른 “overlap 검증은 서비스에서, 엔티티는 순수 상태 변경” 원칙을 잘 지키고 있습니다. update에서 trim 처리도 적절합니다.

src/main/kotlin/simplerag/ragback/domain/index/service/IndexService.kt (2)

20-27: 생성 플로우 및 검증 흐름 적절

  • overlap 경계 검증(>=)을 사전 수행
  • Converter에서 trim 후 저장
  • 생성 후 Preview 반환

요구사항에 부합합니다.


63-65: overlap 경계 검증(>=) 일관 — LGTM

요구사항에 맞는 경계값 처리입니다.

Comment on lines 14 to 16
@Column(name = "snapshot_name", length = 255, nullable = false)
val snapshotName: String,
var snapshotName: String,

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

입력 유효성 강화: snapshotName에 @notblank 추가 권장

빈 문자열/공백만 있는 경우를 방지하려면 Bean Validation을 병행하세요(서비스/컨트롤러 @Valid와 함께 사용 권장).

     @Column(name = "snapshot_name", length = 255, nullable = false)
-    var snapshotName: String,
+    @field:jakarta.validation.constraints.NotBlank
+    var snapshotName: String,

추가로, 상단 import에 다음을 포함하세요(필요 시):

import jakarta.validation.constraints.NotBlank
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt around lines
14 to 16, add Bean Validation to snapshotName by annotating the property with
@NotBlank and add the import jakarta.validation.constraints.NotBlank at the top
of the file; ensure controllers/services validate incoming DTOs with @Valid so
blank or whitespace-only values are rejected.

Comment on lines +40 to +41
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
val chunkEmbeddings: MutableList<ChunkEmbedding> = mutableListOf(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

엔티티 컬렉션 캡슐화: MutableList 직접 노출 지양

엔티티의 연관 컬렉션을 외부에 MutableList로 노출하면 불변식이 깨지기 쉽습니다(임의 추가/삭제, 양방향 일관성 붕괴). JPA 매핑 대상 필드는 private로 숨기고, 읽기 전용 뷰와 편의 메서드를 제공하는 패턴을 권장합니다.

아래처럼 백킹 필드로 매핑하고, 읽기 전용 getter 및 편의 메서드를 추가하세요.

-    @OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
-    val chunkEmbeddings: MutableList<ChunkEmbedding> = mutableListOf(),
+    @OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
+    private val _chunkEmbeddings: MutableList<ChunkEmbedding> = mutableListOf(),

추가(동일 파일 내 다른 위치에 배치):

// 읽기 전용 뷰 제공
val chunkEmbeddings: List<ChunkEmbedding> get() = _chunkEmbeddings.toList()

// 편의 메서드: 양방향 일관성 보장
fun addChunkEmbedding(embedding: ChunkEmbedding) {
    require(embedding.index === this) { "ChunkEmbedding.index must be this Index" }
    _chunkEmbeddings.add(embedding)
}
fun removeChunkEmbedding(embedding: ChunkEmbedding) {
    _chunkEmbeddings.remove(embedding)
}

Comment on lines +56 to +62
// when * then
val message = assertThrows<IndexException> {
indexService.createIndex(indexCreateRequest)
}.message

assertEquals("overlap 크기는 chunking 크기를 넘을 수 없습니다.", message)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

에러 메시지 대신 에러 코드 단언 고려(내구성 향상)

현 테스트는 한글 메시지 문자열에 의존해 취약합니다. ErrorCode를 노출하는 예외 설계라면 코드 단언으로 전환을 고려하세요.

예시(참고용):

val e = assertThrows<IndexException> { indexService.createIndex(indexCreateRequest) }
assertThat(e.errorCode).isEqualTo(ErrorCode.OVERLAP_OVERFLOW)

필요 시 import: import simplerag.ragback.global.error.ErrorCode

🤖 Prompt for AI Agents
In src/test/kotlin/simplerag/ragback/domain/index/service/IndexServiceTest.kt
around lines 56 to 62, the test asserts against a Korean error message string
which is brittle; change it to capture the thrown IndexException into a variable
and assert on its errorCode (e.g., ErrorCode.OVERLAP_OVERFLOW) instead of the
message, and add the necessary import: import
simplerag.ragback.global.error.ErrorCode.

@catturtle123 catturtle123 merged commit fb25b2d into develop Aug 18, 2025
1 check passed
@catturtle123 catturtle123 deleted the feature/#9-index-meta branch August 18, 2025 07:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ [Feature] 인덱스 메타 데이터 저장, 조회, 삭제

2 participants