This commit is contained in:
lunaticbum 2025-09-29 10:19:05 +09:00
parent d2a1f37f39
commit 00bba0bc39

View File

@ -223,7 +223,22 @@ interface PostRepository : ReactiveMongoRepository<Post, String> {
// @org.springframework.data.mongodb.repository.Query("{ '\$and': [ { 'posting': true }, { '\$expr': { '\$gte': [ { '\$strLenCP': '\$id' }, 4 ] } } ] }") // @org.springframework.data.mongodb.repository.Query("{ '\$and': [ { 'posting': true }, { '\$expr': { '\$gte': [ { '\$strLenCP': '\$id' }, 4 ] } } ] }")
fun findAllByOrderByModifyTimeDesc(pageable: Pageable): Flux<Post> fun findAllByOrderByModifyTimeDesc(pageable: Pageable): Flux<Post>
fun countByOrderByModifyTimeDesc(): Mono<Long> fun countByOrderByModifyTimeDesc(): Mono<Long>
@Aggregation(pipeline = [
"{ \$sort: { modifyTime: -1 } }",
"{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
"{ \$replaceRoot: { newRoot: \"\$post\" } }",
"{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
"{ \$sort: { \"modifyTime\": -1 } }"
])
fun findTop5ByOrderByReadCountDesc(): Flux<Post> fun findTop5ByOrderByReadCountDesc(): Flux<Post>
@Aggregation(pipeline = [
"{ \$sort: { modifyTime: -1 } }",
"{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
"{ \$replaceRoot: { newRoot: \"\$post\" } }",
"{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
"{ \$sort: { \"modifyTime\": -1 } }"
])
fun findTop5ByOrderByModifyTimeDesc(): Flux<Post> fun findTop5ByOrderByModifyTimeDesc(): Flux<Post>
fun findByPostTypeOrderByModifyTimeDesc(postType: String): Flux<Post> fun findByPostTypeOrderByModifyTimeDesc(postType: String): Flux<Post>
@ -302,7 +317,7 @@ interface PostRepository : ReactiveMongoRepository<Post, String> {
fun countLatestUniqueOrigin(): Mono<AggregationCount> // 헬퍼 클래스로 매핑 fun countLatestUniqueOrigin(): Mono<AggregationCount> // 헬퍼 클래스로 매핑
@Aggregation(pipeline = [ @Aggregation(pipeline = [
"{ \$match: { \$or: [ { writer: ?0 }, { posting: true } ] } }", "{ \$match: { \$and: [ { \$or: [ { writer: ?0 }, { posting: true } ] }, { 'postType': { \$ne: 'GIBBERISH' } } ] } }",
"{ \$sort: { modifyTime: -1 } }", "{ \$sort: { modifyTime: -1 } }",
"{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }", "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
"{ \$replaceRoot: { newRoot: \"\$post\" } }", "{ \$replaceRoot: { newRoot: \"\$post\" } }",
@ -311,7 +326,7 @@ interface PostRepository : ReactiveMongoRepository<Post, String> {
fun findLatestUniqueForWriterPaginated(username: String, pageable: Pageable): Flux<Post> fun findLatestUniqueForWriterPaginated(username: String, pageable: Pageable): Flux<Post>
@Aggregation(pipeline = [ @Aggregation(pipeline = [
"{ \$match: { \$or: [ { writer: ?0 }, { posting: true } ] } }", "{ \$match: { \$and: [ { \$or: [ { writer: ?0 }, { posting: true } ] }, { 'postType': { \$ne: 'GIBBERISH' } } ] } }",
"{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] } } }", "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] } } }",
"{ \$count: \"totalCount\" }" "{ \$count: \"totalCount\" }"
]) ])
@ -319,41 +334,29 @@ interface PostRepository : ReactiveMongoRepository<Post, String> {
/** /**
* [수정] posting이 true인 문서만 필터링하는 $match 단계를 추가합니다. * [수정] GIBBERISH 타입을 제외하고, posting이 true인 문서만 필터링하는 $match 단계를 추가합니다.
* [[[[[ FIXED LOGIC]]]]]
*/ */
@Aggregation(pipeline = [ @Aggregation(pipeline = [
// 1. Sort ALL posts first to find the absolute most recent version of each.
"{ \$sort: { modifyTime: -1 } }", "{ \$sort: { modifyTime: -1 } }",
// 2. Group by the original ID to get only the latest version.
"{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }", "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
// 3. Restore the post document structure.
"{ \$replaceRoot: { newRoot: \"\$post\" } }", "{ \$replaceRoot: { newRoot: \"\$post\" } }",
// 4. NOW, filter this list of latest posts to show only the public ones. "{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
"{ \$match: { posting: true } }",
// 5. Finally, sort the remaining public posts for display.
"{ \$sort: { \"modifyTime\": -1 } }" "{ \$sort: { \"modifyTime\": -1 } }"
]) ])
fun findLatestUniquePublishedPaginated(pageable: Pageable): Flux<Post> // 메서드 이름 변경 fun findLatestUniquePublishedPaginated(pageable: Pageable): Flux<Post>
/** /**
* '고유 최신 ' 공개된 글의 개수를 카운트합니다. * '고유 최신 ' 공개된 글의 개수를 카운트합니다.
* [수정] posting이 true인 문서만 필터링하는 $match 단계를 추가합니다. * [수정] GIBBERISH 타입을 제외하고, posting이 true인 문서만 필터링하는 $match 단계를 추가합니다.
* [[[[[FIXED LOGIC]]]]]
*/ */
@Aggregation(pipeline = [ @Aggregation(pipeline = [
// 1. Sort ALL posts.
"{ \$sort: { modifyTime: -1 } }", "{ \$sort: { modifyTime: -1 } }",
// 2. Group to get the latest version of each.
"{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }", "{ \$group: { _id: { \$ifNull: [\"\$originId\", \"\$_id\"] }, post: { \$first: \"\$\$ROOT\" } } }",
// 3. Restore the document.
"{ \$replaceRoot: { newRoot: \"\$post\" } }", "{ \$replaceRoot: { newRoot: \"\$post\" } }",
// 4. Filter for public posts. "{ \$match: { posting: true, postType: { \$ne: 'GIBBERISH' } } }",
"{ \$match: { posting: true } }",
// 5. Count the final result.
"{ \$count: \"totalCount\" }" "{ \$count: \"totalCount\" }"
]) ])
fun countLatestUniquePublished(): Mono<AggregationCount> // 메서드 이름 변경 fun countLatestUniquePublished(): Mono<AggregationCount>
fun findByWriterOrderByModifyTimeDesc(writer: String, pageable: Pageable): Flux<Post> // [신규 추가] fun findByWriterOrderByModifyTimeDesc(writer: String, pageable: Pageable): Flux<Post> // [신규 추가]
@ -383,6 +386,10 @@ interface PostRepository : ReactiveMongoRepository<Post, String> {
"{ \$match: { _id: { \$ne: \"\" } } }" "{ \$match: { _id: { \$ne: \"\" } } }"
]) ])
fun findDistinctTags(): Flux<org.bson.Document> // 반환 타입을 Document로 변경 fun findDistinctTags(): Flux<org.bson.Document> // 반환 타입을 Document로 변경
// [신규 추가] GIBBERISH 제외하고 조회
fun findByPostTypeNotOrderByModifyTimeDesc(postType: String, pageable: Pageable): Flux<Post>
fun countByPostTypeNot(postType: String): Mono<Long>
} }
@ -401,26 +408,30 @@ class PostManager(
return postRepository.deleteById(postId) return postRepository.deleteById(postId)
} }
// [수정] 익명 사용자용 목록 조회 // [수정] 익명 사용자용 목록 조회 (Aggregation 사용)
fun findLatestUniquePaginated(pageable: Pageable) : Mono<List<Post>> { fun findLatestUniquePaginated(pageable: Pageable) : Mono<List<Post>> {
return postRepository.findByPostingIsTrueOrderByModifyTimeDesc(pageable) return postRepository.findLatestUniquePublishedPaginated(pageable)
.collectList() .collectList()
} }
// [수정] 익명 사용자용 글 개수 // [수정] 익명 사용자용 글 개수 (Aggregation 사용)
fun countLatestUnique(): Mono<Long> { fun countLatestUnique(): Mono<Long> {
return postRepository.countByPostingIsTrue() return postRepository.countLatestUniquePublished()
.map { it.totalCount }
.switchIfEmpty(Mono.just(0L))
} }
// [수정] '글쓰기' 권한 사용자용 목록 조회 // [수정] '글쓰기' 권한 사용자용 목록 조회 (Aggregation 사용)
fun findLatestUniqueForWriter(username: String, pageable: Pageable) : Mono<List<Post>> { fun findLatestUniqueForWriter(username: String, pageable: Pageable) : Mono<List<Post>> {
return postRepository.findByPostingIsTrueOrWriterOrderByModifyTimeDesc(username, pageable) return postRepository.findLatestUniqueForWriterPaginated(username, pageable)
.collectList() .collectList()
} }
// [수정] '글쓰기' 권한 사용자용 글 개수 // [수정] '글쓰기' 권한 사용자용 글 개수 (Aggregation 사용)
fun countLatestUniqueForWriter(username: String): Mono<Long> { fun countLatestUniqueForWriter(username: String): Mono<Long> {
return postRepository.countByPostingIsTrueOrWriter(username) return postRepository.countLatestUniqueForWriter(username)
.map { it.totalCount }
.switchIfEmpty(Mono.just(0L))
} }
// [수정] 익명 사용자용 인기글 // [수정] 익명 사용자용 인기글
@ -541,11 +552,10 @@ class PostManager(
/** /**
* 인증된 사용자를 위한 메서드 (모든 버전 조회) * 인증된 사용자를 위한 메서드 (모든 버전 조회, GIBBERISH 제외)
* [FIX]: Change return type to Mono<List<Post>> and remove the blocking call.
*/ */
fun findAllVersionsPaginated(pageable :Pageable) : Mono<List<Post>> { fun findAllVersionsPaginated(pageable :Pageable) : Mono<List<Post>> {
return postRepository.findAllByOrderByModifyTimeDesc(pageable) return postRepository.findByPostTypeNotOrderByModifyTimeDesc(PostType.GIBBERISH.name, pageable)
.map { post -> .map { post ->
// 1. 제목을 UTF-8로 디코딩합니다. // 1. 제목을 UTF-8로 디코딩합니다.
post.title = post.title?.let { URLDecoder.decode(it, "UTF-8") } ?: "" post.title = post.title?.let { URLDecoder.decode(it, "UTF-8") } ?: ""
@ -560,32 +570,13 @@ class PostManager(
.collectList() .collectList()
} }
// /**
// * 익명 사용자를 위한 메서드 (고유 최신 글 페이지네이션 조회)
// * [FIX]: This function should already be correct from the previous step.
// */
// fun findLatestUniquePaginated(pageable: Pageable) : Mono<List<Post>> { // <-- Should already return Mono
// return postRepository.findLatestUniquePublishedPaginated(pageable)
// .collectList()
// }
/** /**
* 인증된 사용자가 보는 글의 개수 * 인증된 사용자가 보는 글의 개수 (GIBBERISH 제외)
*/ */
fun countAllVersions(): Mono<Long> { fun countAllVersions(): Mono<Long> {
return postRepository.countByOrderByModifyTimeDesc() return postRepository.countByPostTypeNot(PostType.GIBBERISH.name)
} }
// /**
// * 익명 사용자가 보는 글의 총 개수
// */
// fun countLatestUnique(): Mono<Long> {
// // AggregationCount(totalCount=N) 객체에서 Long 값만 추출합니다. 결과가 없으면 0L을 반환합니다.
// return postRepository.countLatestUniquePublished()
// .map { it.totalCount }
// .switchIfEmpty(Mono.just(0L))
// }
/** /**
* 좋아요 카운트를 1 증가시키고, JS에서 즉시 업데이트할 있도록 *업데이트된* 문서를 반환합니다. * 좋아요 카운트를 1 증가시키고, JS에서 즉시 업데이트할 있도록 *업데이트된* 문서를 반환합니다.
*/ */
@ -634,13 +625,10 @@ class PostManager(
/** /**
* 화면은 이제 "익명 사용자용 최신 글" 0 페이지, 8 아이템을 명시적으로 요청합니다. * 화면은 이제 "익명 사용자용 최신 글" 0 페이지, 8 아이템을 명시적으로 요청합니다.
* [FIX]: Return Mono<List<Post>> to match the updated callee.
*/ */
fun find8() : Mono<List<Post>> { // <-- 3. Change return type to Mono<List<Post>> fun find8() : Mono<List<Post>> {
// 홈 화면은 항상 0번 페이지의 8개 아이템을 요청합니다.
val pageRequest = PageRequest.of(0, 8) // Page 0, Size 8 val pageRequest = PageRequest.of(0, 8) // Page 0, Size 8
// 하드코딩된 쿼리 대신, 익명사용자용 페이지네이션 메서드를 호출합니다. return this.findLatestUniquePaginated(pageRequest)
return this.findLatestUniquePaginated(pageRequest) // <-- 4. This now correctly returns the Mono
} }
fun save(post: Post): Mono<Post> { fun save(post: Post): Mono<Post> {
@ -674,30 +662,6 @@ class PostManager(
p p
} }
} }
// // [신규 추가] 익명 사용자용 인기글
// fun getTop5UniquePublishedByViews(): Flux<Post> {
// return postRepository.findTop5UniquePublishedByReadCountDesc().map { p ->
// p.title = URLDecoder.decode(p.title, "UTF-8")
// if (p.title?.isEmpty() == true) {
// p.title = "무제(無題)"
// }
// println("${p.id} p.posting >> ${p.posting}")
// p
// }
// }
// // [신규 추가] 익명 사용자용 최신글
// fun getRecent5UniquePublished(): Flux<Post> {
// return postRepository.findTop5UniquePublishedByModifyTimeDesc().map { p ->
// p.title = URLDecoder.decode(p.title, "UTF-8")
// if (p.title?.isEmpty() == true) {
// p.title = "무제(無題)"
// }
// p
// }
// }
} }