Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6b2ff23
:sparkles: feature: 인덱스 생성 기능 추
catturtle123 Aug 18, 2025
675f87c
:sparkles: feature: 인덱스 리스트 조회 기능 추가
catturtle123 Aug 18, 2025
88dcf54
:sparkles: feature: 인덱스 수정 기능 추
catturtle123 Aug 18, 2025
f91166a
:sparkles: feature: 인덱스 상세 조회 기능 추가
catturtle123 Aug 18, 2025
562da35
:sparkles: feature: 인덱스 삭제 기능 추가
catturtle123 Aug 18, 2025
9d2952c
:recycle: refactor: indexId 수
catturtle123 Aug 18, 2025
5a3c8aa
:bug: fix: createIndex valid 추가
catturtle123 Aug 18, 2025
3c0a439
:recycle: refactor: getIndex null 허용 제거
catturtle123 Aug 18, 2025
47529f8
:recycle: refactor: updateIndexes 이름 변경
catturtle123 Aug 18, 2025
de99d88
:recycle: refactor: deleteIndex 상태 수정
catturtle123 Aug 18, 2025
7e5f981
:recycle: refactor: 인덱스 생성시 trim 추가
catturtle123 Aug 18, 2025
498a209
:recycle: refactor: 인덱스 embeddingModel val로 변경
catturtle123 Aug 18, 2025
9ad88a8
:recycle: refactor: 인덱스 리스트 조회 정렬 변경
catturtle123 Aug 18, 2025
9dddf80
:recycle: refactor: index 조회 기본 메서드로 변경
catturtle123 Aug 18, 2025
4d77119
:recycle: refactor: chunking size 보강
catturtle123 Aug 18, 2025
83c1099
:recycle: refactor: 생성 시 검증 삭제
catturtle123 Aug 18, 2025
e70fe40
:test_tube: test: 인덱스 생성 테스트 코드 추가
catturtle123 Aug 18, 2025
372ef3f
:test_tube: test: 인덱스 리스트 조회 테스트 추가
catturtle123 Aug 18, 2025
462fff4
:test_tube: test: 인덱스 상세 조회
catturtle123 Aug 18, 2025
de40020
:test_tube: test: 인덱스 상세 조회 시 잘못된 값이 들어갈 때
catturtle123 Aug 18, 2025
a963ab4
:test_tube: test: 인덱스 수정
catturtle123 Aug 18, 2025
001e6d4
:test_tube: test: 인덱스 수정 시 에러
catturtle123 Aug 18, 2025
c10896a
:test_tube: test: 인덱스 삭제
catturtle123 Aug 18, 2025
b376f5c
:test_tube: test: 테스트 포메팅 통일
catturtle123 Aug 18, 2025
bf3a5b6
:recycle: refactor: 중복된 overlap 검증 로직을 헬퍼로 추출
catturtle123 Aug 18, 2025
3db1d9f
:recycle: refactor: 세미 콜론 제거
catturtle123 Aug 18, 2025
222d038
:recycle: refactor: 예측값, 실측값 수정.
catturtle123 Aug 18, 2025
31c2066
:recycle: refactor: 인덱스 수정 에러
catturtle123 Aug 18, 2025
480f4d8
:test_tube: test: size 수정
catturtle123 Aug 18, 2025
8b8ee56
:recycle: refactor: sonarqube 수정
catturtle123 Aug 18, 2025
37da14f
:recycle: refactor: sonarqube 수정
catturtle123 Aug 18, 2025
0906b39
:sparkles: feature: 인덱스 삭제시 chunkingEmbedding 삭제
catturtle123 Aug 18, 2025
d0375ab
:recycle: refactor: 삭제 로직 변경
catturtle123 Aug 18, 2025
11531c3
:recycle: refactor: transactional 삭제
catturtle123 Aug 18, 2025
7b64209
:recycle: refactor: 포메팅 수
catturtle123 Aug 18, 2025
5ae2ac3
:recycle: refactor: 리팩토링
catturtle123 Aug 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ class Model(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "models_id")
val id: Long? = null,
): BaseEntity()
) : BaseEntity()
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package simplerag.ragback.domain.document.entity

import jakarta.persistence.*
import simplerag.ragback.global.entity.BaseEntity
import java.time.LocalDateTime

@Entity
@Table(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ class DataFileTag(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "data_files_tags_id")
val id: Long? = null,
): BaseEntity()
) : BaseEntity()
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ class Tag(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "tags_id")
val id: Long? = null,
): BaseEntity()
) : BaseEntity()
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import simplerag.ragback.domain.document.entity.DataFileTag
interface DataFileTagRepository : JpaRepository<DataFileTag, Long> {
fun existsByDataFileIdAndTagId(dataFileId: Long, tagId: Long): Boolean

@Query("""
@Query(
"""
SELECT dft
FROM DataFileTag dft
JOIN FETCH dft.tag t
WHERE dft.dataFile = :dataFile
""")
"""
)
fun findTagsByDataFile(@Param("dataFile") dataFile: DataFile): List<DataFileTag>

fun deleteAllByDataFile(dataFile: DataFile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package simplerag.ragback.domain.document.service

import org.springframework.dao.DataIntegrityViolationException
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.transaction.support.TransactionSynchronization
Expand All @@ -22,9 +21,7 @@ import simplerag.ragback.global.util.S3Type
import simplerag.ragback.global.util.S3Util
import simplerag.ragback.global.util.computeMetricsStreaming
import simplerag.ragback.global.util.resolveContentType
import java.time.LocalDateTime
import java.util.*
import kotlin.collections.ArrayList

@Service
class DataFileService(
Expand Down Expand Up @@ -83,11 +80,10 @@ class DataFileService(
val dataSlice = dataFileRepository.findByIdGreaterThanOrderById(cursor, PageRequest.of(0, take))

val dataFileList: MutableList<DataFileDetailResponse> = ArrayList()
dataSlice.forEach{ dataFile ->
dataSlice.forEach { dataFile ->
val dataFileTags: List<DataFileTag> = dataFileTagRepository.findTagsByDataFile(dataFile)

val tagDtos: List<TagDTO> = dataFileTags.map{
dataFileTag ->
val tagDtos: List<TagDTO> = dataFileTags.map { dataFileTag ->
val tag = dataFileTag.tag
TagDTO(tag.id, tag.name)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package simplerag.ragback.domain.index.controller

import jakarta.validation.Valid
import org.springframework.http.HttpStatus
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import simplerag.ragback.domain.index.dto.*
import simplerag.ragback.domain.index.service.IndexService
import simplerag.ragback.global.response.ApiResponse

@RestController
@RequestMapping("/api/v1/indexes")
@Validated
class IndexController(
private val indexService: IndexService
) {

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


@GetMapping
fun getIndexes(): ApiResponse<IndexPreviewResponseList> {
val indexPreviewResponseList = indexService.getIndexes()
return ApiResponse.ok(indexPreviewResponseList)
}
Comment on lines +27 to +31
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)


@GetMapping("/{indexId}")
fun getIndex(
@PathVariable indexId: Long
): ApiResponse<IndexDetailResponse> {
val indexDetailResponse = indexService.getIndex(indexId)
return ApiResponse.ok(indexDetailResponse)
}

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

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


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package simplerag.ragback.domain.index.converter

import simplerag.ragback.domain.index.dto.IndexCreateRequest
import simplerag.ragback.domain.index.dto.IndexDetailResponse
import simplerag.ragback.domain.index.dto.IndexPreviewResponse
import simplerag.ragback.domain.index.dto.IndexPreviewResponseList
import simplerag.ragback.domain.index.entity.Index


fun toIndex(createRequest: IndexCreateRequest): Index {
return Index(
snapshotName = createRequest.snapshotName.trim(),
overlapSize = createRequest.overlapSize,
chunkingSize = createRequest.chunkingSize,
similarityMetric = createRequest.similarityMetric,
topK = createRequest.topK,
embeddingModel = createRequest.embeddingModel,
reranker = createRequest.reranker
)
}

fun toIndexPreviewResponseList(
indexes: List<Index>
): IndexPreviewResponseList {
val indexList = indexes.map { toIndexPreviewResponse(it) }
return IndexPreviewResponseList(indexList)
}
Comment on lines +22 to +27
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 +27
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.


fun toIndexPreviewResponse(index: Index): IndexPreviewResponse {
return IndexPreviewResponse(
indexId = index.id,
snapshotName = index.snapshotName,
)
}
Comment on lines +29 to +34
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.


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,
)
}
Comment on lines +36 to +47
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package simplerag.ragback.domain.index.dto

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Positive
import jakarta.validation.constraints.PositiveOrZero
import org.hibernate.validator.constraints.Length
import simplerag.ragback.domain.index.entity.enums.EmbeddingModel
import simplerag.ragback.domain.index.entity.enums.SimilarityMetric

Comment on lines +3 to +9
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

교차 필드 유효성 검증 추가 제안: overlapSize ≤ chunkingSize

요구사항을 DTO 레벨에서 선제 검증하면 컨트롤러 단에서 일관된 400 응답을 보장할 수 있습니다. 서비스 레이어 검증은 유지하되, 입력 경계에서도 막는 것을 권장합니다.

추가 import:

 import jakarta.validation.constraints.NotBlank
+import jakarta.validation.constraints.AssertTrue
 import jakarta.validation.constraints.Positive
 import jakarta.validation.constraints.PositiveOrZero
📝 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
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Positive
import jakarta.validation.constraints.PositiveOrZero
import org.hibernate.validator.constraints.Length
import simplerag.ragback.domain.index.entity.enums.EmbeddingModel
import simplerag.ragback.domain.index.entity.enums.SimilarityMetric
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.AssertTrue
import jakarta.validation.constraints.Positive
import jakarta.validation.constraints.PositiveOrZero
import org.hibernate.validator.constraints.Length
import simplerag.ragback.domain.index.entity.enums.EmbeddingModel
import simplerag.ragback.domain.index.entity.enums.SimilarityMetric
🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/dto/IndexRequestDTO.kt around
lines 3 to 9, add cross-field validation to enforce overlapSize ≤ chunkingSize
at the DTO level: create a class-level constraint (or an @AssertTrue method)
that checks overlapSize <= chunkingSize and returns a validation message when
violated; register the validator by adding the necessary imports (e.g.,
jakarta.validation.Constraint, jakarta.validation.Payload,
jakarta.validation.ConstraintValidator,
jakarta.validation.ConstraintValidatorContext or
jakarta.validation.constraints.AssertTrue) and annotate the DTO with the new
constraint (or annotate the boolean getter), keeping existing field-level
annotations intact so controllers return 400 on invalid input.

data class IndexCreateRequest(
@field:Length(max = 255)
@field:NotBlank
val snapshotName: String,

@field:Positive
val chunkingSize: Int,

@field:PositiveOrZero
val overlapSize: Int,

val similarityMetric: SimilarityMetric,

@field:Positive
val topK: Int,

val embeddingModel: EmbeddingModel,

val reranker: Boolean,
)

data class IndexUpdateRequest(
@field:Length(max = 255)
@field:NotBlank
val snapshotName: String,

@field:Positive
val chunkingSize: Int,

@field:PositiveOrZero
val overlapSize: Int,

val similarityMetric: SimilarityMetric,

@field:Positive
val topK: Int,

val reranker: Boolean,
)
Comment on lines +47 to +48
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

IndexUpdateRequest에 교차 검증 추가

업데이트에서도 동일 제약을 보장해야 합니다.

 data class IndexUpdateRequest(
@@
     val topK: Int,
 
     val reranker: Boolean,
+    
+    @get:AssertTrue(message = "overlapSize는 chunkingSize를 초과할 수 없습니다")
+    val isOverlapValid: Boolean
+        get() = overlapSize <= chunkingSize,
 )

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/kotlin/simplerag/ragback/domain/index/dto/IndexRequestDTO.kt around
lines 47-48, the IndexUpdateRequest currently lacks the cross-field validation
present on create; add the same constraint checks to the update DTO (e.g.,
mutually-exclusive/required-field rules) by implementing a cross-field validator
or an @AssertTrue validation method on the DTO so the same business rules are
enforced on updates, and ensure it returns/throws a clear validation error
(IllegalArgumentException or ConstraintViolation) when violated; update or add
unit tests to cover the failing and passing cases.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package simplerag.ragback.domain.index.dto

import simplerag.ragback.domain.index.entity.enums.EmbeddingModel
import simplerag.ragback.domain.index.entity.enums.SimilarityMetric

data class IndexPreviewResponseList(
val indexDetailResponse: List<IndexPreviewResponse>,
)
Comment on lines +6 to +8
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`.


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

data class IndexDetailResponse(
var indexId: Long?,
val snapshotName: String,
val chunkingSize: Int,
val overlapSize: Int,
val similarityMetric: SimilarityMetric,
val topK: Int,
val embeddingModel: EmbeddingModel,
val reranker: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ChunkEmbedding(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "chunk_embeddings_id")
val id: Long? = null,
): BaseEntity() {
) : BaseEntity() {

@get:Transient
val embedding: FloatArray get() = _embedding.copyOf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ class DataFileIndex(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "data_files_indexes_id")
val id: Long? = null,
): BaseEntity()
) : BaseEntity()
31 changes: 23 additions & 8 deletions src/main/kotlin/simplerag/ragback/domain/index/entity/Index.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package simplerag.ragback.domain.index.entity

import jakarta.persistence.*
import jakarta.validation.constraints.Min
import simplerag.ragback.domain.index.dto.IndexUpdateRequest
import simplerag.ragback.domain.index.entity.enums.EmbeddingModel
import simplerag.ragback.domain.index.entity.enums.SimilarityMetric
import simplerag.ragback.global.entity.BaseEntity
Expand All @@ -11,32 +12,46 @@ import simplerag.ragback.global.entity.BaseEntity
class Index(

@Column(name = "snapshot_name", length = 255, nullable = false)
val snapshotName: String,
var snapshotName: String,

Comment on lines 14 to 16
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.

@Column(name = "chunking_size", nullable = false)
@Min(1)
val chunkingSize: Int,
var chunkingSize: Int,

@Column(name = "overlap_size", nullable = false)
@Min(0)
val overlapSize: Int,
var overlapSize: Int,

@Column(name = "similarity_metric", nullable = false)
@Enumerated(EnumType.STRING)
val similarityMetric: SimilarityMetric,
var similarityMetric: SimilarityMetric,

@Column(name = "top_k", nullable = false)
@Min(1)
val topK: Int,
var topK: Int,

@Column(name = "embedding_model", nullable = false, length = 255)
@Enumerated(EnumType.STRING)
val embeddingModel: EmbeddingModel,

@Column(name = "reranker", nullable = false)
val reranker: Boolean,
var reranker: Boolean,

@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, mappedBy = "index")
val chunkEmbeddings: MutableList<ChunkEmbedding> = mutableListOf(),
Comment on lines +40 to +41
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)
}


@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "indexes_id")
val id: Long? = null,
): BaseEntity()
var id: Long? = null,
) : BaseEntity() {

fun update(req: IndexUpdateRequest) {
snapshotName = req.snapshotName.trim()
chunkingSize = req.chunkingSize
overlapSize = req.overlapSize
similarityMetric = req.similarityMetric
topK = req.topK
reranker = req.reranker
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package simplerag.ragback.domain.index.repository

import org.springframework.data.jpa.repository.JpaRepository
import simplerag.ragback.domain.index.entity.ChunkEmbedding

interface ChunkEmbeddingRepository : JpaRepository<ChunkEmbedding, Long>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package simplerag.ragback.domain.index.repository

import org.springframework.data.jpa.repository.JpaRepository
import simplerag.ragback.domain.index.entity.Index

interface IndexRepository : JpaRepository<Index, Long> {

fun findAllByOrderByCreatedAtDesc(): List<Index>
}
Comment on lines +6 to +9
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.

Loading