Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/#228] 아티클 목록 조회(무한스크롤) API 추가 #259

Merged
merged 46 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
cadc248
docs: flyway V1.00.0.15 배포
hun-ca Jul 27, 2024
02ea8a7
refactor: article_main_card 테이블에서 content 삭제
hun-ca Jul 27, 2024
451d7f3
refactor: LocalDateTime에 대한 Json serialize 설정 추가
hun-ca Jul 27, 2024
48657e6
feat: 아티클 목록 조회에서 카테고리 코드 파람 추가
hun-ca Jul 27, 2024
c591f8a
test: 아티클 목록 조회 API 카테고리 코드 추가 반영
hun-ca Jul 27, 2024
a4ca18c
feat: 아티클 카테고리 조회 API 추가
hun-ca Jul 27, 2024
2bfb33e
fix: PK 수정
hun-ca Jul 27, 2024
e64b341
fix: 아티클 카테고리 조회시 displayname을 보내도록 수정
hun-ca Jul 28, 2024
d5b51bc
feat: 아티클 목록 조회 API 1차 구현(step1: 조회수 및 카테고리 기반 정렬 상위 10개 조회)
hun-ca Jul 28, 2024
74a9829
feat: 아티클 최초 생성시 조회수 테이블 0으로 초기화하도록 변경
hun-ca Jul 28, 2024
053007d
feat: isLast에 대한 로직 처리를 UC로 이동
hun-ca Jul 28, 2024
54d8186
test: article 카테고리 조회 API 테스트 구현
hun-ca Jul 28, 2024
2510219
fix: ARTICLE_MAIN_CARD 컬럼 수정
hun-ca Jul 28, 2024
d9027cd
fix: ARTICLE_MAIN_CARD 테이블 컬럼 타입 변경
hun-ca Jul 28, 2024
0213ead
fix: ARTICLE_MAIN_CARD 테이블 컬럼 타입 변경 (description 컬럼 json으로 유지)
hun-ca Jul 28, 2024
c2d5fde
feat: 아티클 목록 조회, 저장 SQL 추가
hun-ca Jul 28, 2024
29743a0
feat: 아티클 컨텐츠 조회 SQL 생성
hun-ca Jul 28, 2024
394620b
feat: 아티클 Main card DB 접근의 경우 한정 구현
hun-ca Jul 28, 2024
3329b64
chore: TODO 주석 추가
hun-ca Jul 28, 2024
2c10688
fix: selectArticlesOrderByViewsQuery 문법 수정
hun-ca Jul 28, 2024
04d5f6e
fix: article main card 뷰 조합 SQL 수정(테이블명 수정)
hun-ca Jul 28, 2024
787c6b6
fix: workbooks 삭제
hun-ca Jul 29, 2024
7e29ca9
fix: json query
hun-ca Jul 29, 2024
6546c36
fix: ARTICLE_MAIN_CARD 테이블에서 workbooks 컬럼 삭제
hun-ca Jul 29, 2024
8539a26
fix: 트랜잭션 readOnly 옵션 일시 삭제(insert 쿼리)
hun-ca Jul 29, 2024
c7c1d79
fix: 워크북 응답 삭제
hun-ca Jul 29, 2024
a0ec2a1
feat: 조회수가 같을 경우 최신 아티클이 우선순위가 높도록 수정
hun-ca Jul 29, 2024
d22c0fa
merge from main
hun-ca Jul 29, 2024
50c8c48
test: [GET] /api/v1/articles/categories 테스트 수정
hun-ca Jul 29, 2024
5c9e316
fix: 아티클이 이미 테이블에 있느 경우에도 컨텐츠 조회되도록 변경
hun-ca Jul 29, 2024
e546788
refactor: 쿼리 수정
hun-ca Jul 29, 2024
5e05b09
refactor: 아티클 카테고리 조회 API 수정 (DTO 삭제)
hun-ca Jul 29, 2024
a657727
fix: row rank 쿼리 문법 수정
hun-ca Jul 29, 2024
0ddb026
feat: record class <-> 쿼리 수행 결과 변환 매퍼 ArticleMainCardMapper 구현
hun-ca Jul 30, 2024
d97fe44
feat: ARTICLE_MAIN_CARD 테이블 workbooks 컬럼 추가(아티클이 포함된 워크북 정보 json)
hun-ca Jul 30, 2024
0bc1aa2
feat:JDBC URL 옵션 추가 - allowMultiQueries=true
hun-ca Jul 30, 2024
7ffc0f7
feat: record 필드(workbooks) 추가 및 워크북 json mapper 적용
hun-ca Jul 30, 2024
fd3e7b7
feat: 아티클 목록 조회 응답 바디 수정 (workbooks[] 추가)
hun-ca Jul 30, 2024
d4279e2
test: data.articles[].workbooks 응답 바디 추가 테스트 코드 수정
hun-ca Jul 30, 2024
b1255ee
Merge remote-tracking branch 'origin/main' into feat/#228_hunca
hun-ca Jul 30, 2024
58265dc
fix: selectArticlesOrderByViewsQuery 문법 일부 수정
hun-ca Jul 30, 2024
af42b1a
test: Duplicate entry '[email protected]' for key 'member.email' 에러 해결
hun-ca Jul 31, 2024
c657c0a
Merge remote-tracking branch 'origin/main' into feat/#228_hunca
hun-ca Jul 31, 2024
365c02d
refacotr: kotlin logger로 변경
hun-ca Jul 31, 2024
e3dd2b9
test: insertMember 메소드로 트랜잭션 분리
hun-ca Jul 31, 2024
3749da9
test: #e3dd2b9 원복
hun-ca Jul 31, 2024
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 @@ -6,6 +6,7 @@ import com.few.api.repo.dao.article.command.InsertFullArticleRecordCommand
import com.few.api.repo.dao.article.query.SelectArticleRecordQuery
import com.few.api.repo.dao.article.query.SelectWorkBookArticleRecordQuery
import com.few.api.repo.dao.article.query.SelectWorkbookMappedArticleRecordsQuery
import com.few.api.repo.dao.article.record.SelectArticleContentsRecord
import com.few.api.repo.dao.article.record.SelectArticleRecord
import com.few.api.repo.dao.article.record.SelectWorkBookArticleRecord
import com.few.api.repo.dao.article.record.SelectWorkBookMappedArticleRecord
Expand Down Expand Up @@ -108,4 +109,15 @@ class ArticleDao(

return mstId.getValue(ArticleMst.ARTICLE_MST.ID)
}

fun selectArticleContents(articleIds: Set<Long>): List<SelectArticleContentsRecord> =
selectArticleContentsQuery(articleIds)
.fetchInto(SelectArticleContentsRecord::class.java)

fun selectArticleContentsQuery(articleIds: Set<Long>) = dslContext.select(
ArticleIfo.ARTICLE_IFO.ARTICLE_MST_ID.`as`(SelectArticleContentsRecord::articleId.name),
ArticleIfo.ARTICLE_IFO.CONTENT.`as`(SelectArticleContentsRecord::content.name)
).from(ArticleIfo.ARTICLE_IFO)
.where(ArticleIfo.ARTICLE_IFO.ARTICLE_MST_ID.`in`(articleIds))
.and(ArticleIfo.ARTICLE_IFO.DELETED_AT.isNull)
Comment on lines +121 to +126
Copy link
Member Author

@hun-ca hun-ca Jul 31, 2024

Choose a reason for hiding this comment

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

아티클 컨텐츠만 뽑아오는건데 생각해보니까 이건 매번 스크롤마다 10개 아티클에 대해서 모두 수행되기 때문에 disk I/O 부하가 다소 있을거 같습니다

Copy link
Collaborator

Choose a reason for hiding this comment

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

그런데 어딘가에서는 저장되고 io로 불러와야하니까 선택의 문제가 아닐까요?

우선 db에서 불러오고 쫌 더 좋은 방법 고민해봐요!

Copy link
Collaborator

Choose a reason for hiding this comment

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

글고 생각해보니까 요거는 정책으로 풀 수 있는 문제가 아닌가 합니다.

썸넬에서는 최대 5줄 보여준다 하면 우리가 미리 잘라서 넣어둘 수 있으니까요

Copy link
Member Author

Choose a reason for hiding this comment

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

맞아요 일단 메모리에 저장하면DB에서 꺼내는것보단 빠르긴 함. 말씀대로 5줄로 쭐이면 좋긴할듯

Copy link
Member Author

Choose a reason for hiding this comment

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

잴 많이 호출되는 api에서 잴 많이 disk I/O 부하를 주는 셈. 그렇다고 이걸 로컬 캐시에 넣는게 맞을까요?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.few.api.repo.dao.article

import jooq.jooq_dsl.tables.ArticleMst.ARTICLE_MST
import jooq.jooq_dsl.tables.MappingWorkbookArticle.MAPPING_WORKBOOK_ARTICLE
import jooq.jooq_dsl.tables.Member.MEMBER
import jooq.jooq_dsl.tables.Workbook.WORKBOOK
import com.few.api.repo.dao.article.record.ArticleMainCardRecord
import com.few.api.repo.dao.article.support.CommonJsonMapper
import jooq.jooq_dsl.tables.ArticleMainCard.ARTICLE_MAIN_CARD
import org.jooq.*
import org.jooq.impl.DSL.*
import org.springframework.stereotype.Repository
import java.time.LocalDateTime

@Repository
class ArticleMainCardDao(
private val dslContext: DSLContext,
private val commonJsonMapper: CommonJsonMapper,
) {

fun selectArticleMainCardsRecord(articleIds: Set<Long>): Set<ArticleMainCardRecord> {
return selectArticleMainCardsRecordQuery(articleIds)
.fetchInto(ArticleMainCardRecord::class.java)
.toSet()
}

private fun selectArticleMainCardsRecordQuery(articleIds: Set<Long>) = dslContext.select(
ARTICLE_MAIN_CARD.ID.`as`(ArticleMainCardRecord::articleId.name),
ARTICLE_MAIN_CARD.TITLE.`as`(ArticleMainCardRecord::articleTitle.name),
ARTICLE_MAIN_CARD.MAIN_IMAGE_URL.`as`(ArticleMainCardRecord::mainImageUrl.name),
ARTICLE_MAIN_CARD.CATEGORY_CD.`as`(ArticleMainCardRecord::categoryCd.name),
ARTICLE_MAIN_CARD.CREATED_AT.`as`(ArticleMainCardRecord::createdAt.name),
ARTICLE_MAIN_CARD.WRITER_ID.`as`(ArticleMainCardRecord::writerId.name),
ARTICLE_MAIN_CARD.WRITER_EMAIL.`as`(ArticleMainCardRecord::writerEmail.name),
jsonGetAttributeAsText(ARTICLE_MAIN_CARD.WRITER_DESCRIPTION, "name").`as`(ArticleMainCardRecord::writerName.name),
jsonGetAttribute(ARTICLE_MAIN_CARD.WRITER_DESCRIPTION, "url").`as`(ArticleMainCardRecord::writerImgUrl.name)
).from(ARTICLE_MAIN_CARD)
.where(ARTICLE_MAIN_CARD.ID.`in`(articleIds))
.query
Copy link
Member Author

Choose a reason for hiding this comment

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

기존에 조회가 되어서 ARTICLE_MAIN_CARD테이블(미리 조인된 테이블)에 들어가 있을 경우 바로 셀렉트해옴


fun selectByArticleMstAndMemberAndMappingWorkbookArticleAndWorkbook(articleIds: Set<Long>): Set<ArticleMainCardRecord> {
return selectByArticleMstAndMemberAndMappingWorkbookArticleAndWorkbookQuery(articleIds)
.fetchInto(ArticleMainCardRecord::class.java)
.toSet()
}

private fun selectByArticleMstAndMemberAndMappingWorkbookArticleAndWorkbookQuery(articleIds: Set<Long>):
SelectQuery<Record9<Long, String, String, Byte, LocalDateTime, Long, String, String, JSON>> {
val a = ARTICLE_MST.`as`("a")
val m = MEMBER.`as`("m")
val mwa = MAPPING_WORKBOOK_ARTICLE.`as`("mwa")
val w = WORKBOOK.`as`("w")

return dslContext.select(
a.ID.`as`(ArticleMainCardRecord::articleId.name),
a.TITLE.`as`(ArticleMainCardRecord::articleTitle.name),
a.MAIN_IMAGE_URL.`as`(ArticleMainCardRecord::mainImageUrl.name),
a.CATEGORY_CD.`as`(ArticleMainCardRecord::categoryCd.name),
a.CREATED_AT.`as`(ArticleMainCardRecord::createdAt.name),
m.ID.`as`(ArticleMainCardRecord::writerId.name),
m.EMAIL.`as`(ArticleMainCardRecord::writerEmail.name),
jsonGetAttributeAsText(m.DESCRIPTION, "name").`as`(ArticleMainCardRecord::writerName.name),
jsonGetAttribute(m.DESCRIPTION, "url").`as`(ArticleMainCardRecord::writerImgUrl.name)
)
.from(a)
.join(m).on(a.MEMBER_ID.eq(m.ID)).and(a.DELETED_AT.isNull).and(m.DELETED_AT.isNull)
.leftJoin(mwa).on(a.ID.eq(mwa.ARTICLE_ID)).and(mwa.DELETED_AT.isNull)
.leftJoin(w).on(mwa.WORKBOOK_ID.eq(w.ID)).and(w.DELETED_AT.isNull)
.where(a.ID.`in`(articleIds))
.groupBy(a.ID)
.query
Copy link
Member Author

Choose a reason for hiding this comment

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

조인해서 ARTICLE_MAIN_CARD 테이블에 넣기 위한 쿼리

Copy link
Member Author

Choose a reason for hiding this comment

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

RAW SQL

SELECT a.id                                  AS article_id,
       a.title                               AS article_title,
       a.main_image_url,
       a.category_cd,
       a.created_at                          AS article_created_at,
       m.id                                  AS writer_id,
       m.email                               AS writer_email,
       m.description                         AS writer_description,
       json_extract(m.description, '$.url')  AS writer_description_url,
       json_extract(m.description, '$.name') AS writer_dname,
       JSON_ARRAYAGG(
               JSON_OBJECT(
                       'title', IFNULL(w.title, NULL),
                       'id', IFNULL(w.id, NULL)
               )
       )                                     AS workbooks
FROM ARTICLE_MST a
         JOIN MEMBER m ON a.member_id = m.id
    AND a.deleted_at IS NULL
    AND m.deleted_at IS NULL
         LEFT JOIN MAPPING_WORKBOOK_ARTICLE mwa
                   ON a.id = mwa.article_id
                       AND mwa.deleted_at IS NULL
         LEFT JOIN WORKBOOK w
                   ON mwa.workbook_id = w.id
                       AND w.deleted_at IS NULL
where a.id in (1, 2, 3)
GROUP BY a.id;

Copy link
Member Author

Choose a reason for hiding this comment

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

위 쿼리에서 해당 아티클이 속한 워크북 리스트를 하나의 컬럼으로 가져올라다 보니 json으로 만들어서 가져오려고

       JSON_ARRAYAGG(
               JSON_OBJECT(
                       'title', IFNULL(w.title, NULL),
                       'id', IFNULL(w.id, NULL)
               )
       )                                     AS workbooks

이게 들어간건데 jooq에서 어떤 노력을 해도 계속 에러나서 빠지게 됨...

Copy link
Member Author

Choose a reason for hiding this comment

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

결국 최종 쿼리는 아래와 같이 workbook 관련 2개 테이블이 빠지게 됨

SELECT a.id                                  AS article_id,
       a.title                               AS article_title,
       a.main_image_url,
       a.category_cd,
       a.created_at                          AS article_created_at,
       m.id                                  AS writer_id,
       m.email                               AS writer_email,
       m.description                         AS writer_description,
       json_extract(m.description, '$.url')  AS writer_description_url,
       json_extract(m.description, '$.name') AS writer_dname
FROM ARTICLE_MST a
         JOIN MEMBER m ON a.member_id = m.id
    AND a.deleted_at IS NULL
    AND m.deleted_at IS NULL
where a.id in (1, 2, 3)
GROUP BY a.id;

Copy link
Collaborator

Choose a reason for hiding this comment

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

스크린샷 2024-07-30 오후 1 40 57

요 사진의 로그로 allowMultiQueries 문제라는거 감잡고

https://blog.jooq.org/mysqls-allowmultiqueries-flag-with-jdbc-and-jooq/
에서 확신하고

jdbc url 끝에 &allowMultiQueries=true 추가하고

jsonArrayAgg(
    jsonObject(
        key("id").value(Workbook.WORKBOOK.ID),
        key("title").value(Workbook.WORKBOOK.TITLE),
    )
).`as`(ArticleMainCardRecord::temp.name), // 우선 temp로 실험함

위와 같이 수정하니까 본래 쿼리와 같이 동작합니다.

jsonArrayAgg 문서: https://www.jooq.org/doc/latest/manual/sql-building/column-expressions/json-functions/json-object-function/

스크린샷 2024-07-30 오후 1 38 31

Copy link
Collaborator

Choose a reason for hiding this comment

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

본래 쿼리가 생각하신 개발 방향에 맞으면 수정하면 될 것 가타요!!!!!

Copy link
Member Author

Choose a reason for hiding this comment

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

종준님.. ㄹㅇ 쵝오다 이걸 바로 캐치해버리시네

Copy link
Member Author

Choose a reason for hiding this comment

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

말씀해주신대로 해보고 말씀드릴게요

}

fun insertArticleMainCardsBulk(commands: Set<ArticleMainCardRecord>) {
dslContext.batch(
insertArticleMainCardsBulkQuery(commands)
).execute()
}

fun insertArticleMainCardsBulkQuery(commands: Set<ArticleMainCardRecord>):
InsertValuesStep8<jooq.jooq_dsl.tables.records.ArticleMainCardRecord, Long, String, String, Byte, LocalDateTime, Long, String, JSON> {
val insertStep = dslContext.insertInto(
ARTICLE_MAIN_CARD,
ARTICLE_MAIN_CARD.ID,
ARTICLE_MAIN_CARD.TITLE,
ARTICLE_MAIN_CARD.MAIN_IMAGE_URL,
ARTICLE_MAIN_CARD.CATEGORY_CD,
ARTICLE_MAIN_CARD.CREATED_AT,
ARTICLE_MAIN_CARD.WRITER_ID,
ARTICLE_MAIN_CARD.WRITER_EMAIL,
ARTICLE_MAIN_CARD.WRITER_DESCRIPTION
)

for (command in commands) {
insertStep.values(
command.articleId,
command.articleTitle,
command.mainImageUrl.toString(),
command.categoryCd,
command.createdAt,
command.writerId,
command.writerEmail,
JSON.valueOf(
commonJsonMapper.toJsonStr(
mapOf(
"name" to command.writerName,
"url" to command.writerImgUrl
)
)
)
)
}

return insertStep
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ package com.few.api.repo.dao.article

import com.few.api.repo.dao.article.command.ArticleViewCountCommand
import com.few.api.repo.dao.article.query.ArticleViewCountQuery
import com.few.api.repo.dao.article.query.SelectArticlesOrderByViewsQuery
import com.few.api.repo.dao.article.query.SelectRankByViewsQuery
import com.few.api.repo.dao.article.record.SelectArticleViewsRecord
import jooq.jooq_dsl.tables.ArticleViewCount.ARTICLE_VIEW_COUNT
import org.jooq.DSLContext
import org.jooq.Record2
import org.jooq.SelectQuery
import org.jooq.impl.DSL.*
import org.springframework.stereotype.Repository

@Repository
Expand All @@ -21,6 +27,13 @@ class ArticleViewCountDao(
.execute()
}

fun insertArticleViewCountToZero(query: ArticleViewCountQuery) = insertArticleViewCountToZeroQuery(query).execute()

fun insertArticleViewCountToZeroQuery(query: ArticleViewCountQuery) = dslContext.insertInto(ARTICLE_VIEW_COUNT)
.set(ARTICLE_VIEW_COUNT.ARTICLE_ID, query.articleId)
.set(ARTICLE_VIEW_COUNT.VIEW_COUNT, 0)
.set(ARTICLE_VIEW_COUNT.CATEGORY_CD, query.categoryType.code)

fun selectArticleViewCount(command: ArticleViewCountCommand): Long? {
return dslContext.select(
ARTICLE_VIEW_COUNT.VIEW_COUNT
Expand All @@ -29,4 +42,47 @@ class ArticleViewCountDao(
.and(ARTICLE_VIEW_COUNT.DELETED_AT.isNull)
.fetchOneInto(Long::class.java)
}

fun selectRankByViews(query: SelectRankByViewsQuery): Long? {
return selectRankByViewsQuery(query)
.fetchOneInto(Long::class.java)
}

fun selectRankByViewsQuery(query: SelectRankByViewsQuery) = dslContext
.select(field("offset"))
.from(
dslContext.select(
ARTICLE_VIEW_COUNT.ARTICLE_ID,
rowNumber().over(orderBy(ARTICLE_VIEW_COUNT.VIEW_COUNT.desc())).`as`("offset")
).from(ARTICLE_VIEW_COUNT.`as`("RankedRows"))
)
.where(field("RankedRows.article_id").eq(query.articleId))
.query
Copy link
Member Author

Choose a reason for hiding this comment

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

  • ARTICLE_VIEW_COUNT 테이블에서 이전 스크롤에서 읽은 마지막 아티클 ID가 몇번째 row인지 판단하는 쿼리.
  • 쿼리 조회 결과로 해당 row가 몇 번째 row인지 리턴됩니다
  • 해당 값은 selectArticlesOrderByViewsQuery 에서 사용되는데 밑에서 설명드릴게요

Copy link
Member Author

Choose a reason for hiding this comment

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

a657727 에서 쿼리 문법 수정


fun selectArticlesOrderByViews(query: SelectArticlesOrderByViewsQuery): Set<SelectArticleViewsRecord> {
return selectArticlesOrderByViewsQuery(query)
.fetchInto(SelectArticleViewsRecord::class.java)
.toSet()
}

fun selectArticlesOrderByViewsQuery(query: SelectArticlesOrderByViewsQuery): SelectQuery<Record2<Long, Long>> {
val articleViewCountOffsetTb = select()
.from(ARTICLE_VIEW_COUNT)
.where(ARTICLE_VIEW_COUNT.DELETED_AT.isNull)
.orderBy(ARTICLE_VIEW_COUNT.VIEW_COUNT.desc())
.limit(query.offset, Long.MAX_VALUE)
.asTable("article_view_count_offset_tb")

val sql = dslContext.select(
articleViewCountOffsetTb.field(ARTICLE_VIEW_COUNT.ARTICLE_ID.`as`(SelectArticleViewsRecord::articleId.name)),
articleViewCountOffsetTb.field(ARTICLE_VIEW_COUNT.VIEW_COUNT.`as`(SelectArticleViewsRecord::views.name))
)
.from(articleViewCountOffsetTb)

if (query.category != null) {
sql.where(field("article_view_count_offset_tb.category_cd").eq(query.category.code))
}

return sql.limit(10).query
Copy link
Member Author

Choose a reason for hiding this comment

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

  • 위 selectRankByViewsQuery 쿼리에서 이전까지 읽은 아티클이 몇번째 Row인지 구했고 이걸 Offset 이라고 둠
  • 이제 이 offset을 기준으로 몇번째부터 10개를 읽을지 결정하는데,,,
  • 예시
select *
from ARTICLE_VIEW_COUNT
order by view_count desc
limit 20, 10;

위 처럼 쿼리가 작성되면 offset이 20이고 맨 앞20개 row를 스킵한 뒤 10개를 읽는 쿼리가 됩니다. (참고로 테이블 끝까지 읽기 위해 Long.MAX_VALUE을 사용함)

최종 쿼리 예시

select *
from (select *
      from ARTICLE_VIEW_COUNT
      order by view_count desc
      limit 20, 9999999) as article_view_count_offset_tb
where article_view_count_offset_tb.category_cd = 10
limit 10;
  • where 카테고리 부분은 동적쿼리 적용 가능한 부분인데 뭐 저렇게 안하고 방법있을까요?

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.few.api.repo.dao.article.query

import com.few.data.common.code.CategoryType

data class SelectArticlesOrderByViewsQuery(
val offset: Long,
val category: CategoryType?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.few.api.repo.dao.article.query

data class SelectRankByViewsQuery(
val articleId: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.few.api.repo.dao.article.record

import java.net.URL
import java.time.LocalDateTime

data class ArticleMainCardRecord(
val articleId: Long,
val articleTitle: String,
val mainImageUrl: URL,
val categoryCd: Byte,
val createdAt: LocalDateTime,
val writerId: Long,
val writerEmail: String,
val writerName: String,
val writerImgUrl: URL,
) {
var content: String = ""
set(value) {
field = value
}

var views: Long = 0L
set(value) {
field = value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.repo.dao.article.record

data class SelectArticleContentsRecord(
val articleId: Long,
val content: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.few.api.repo.dao.article.record

data class SelectArticleViewsRecord(
val articleId: Long,
val views: Long,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.few.api.repo.dao.article.support

import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.stereotype.Component

@Component
class CommonJsonMapper( // TODO: common 성 패키지 위치로 이동
private val objectMapper: ObjectMapper,
) {
fun toJsonStr(map: Map<String, Any>): String {
return objectMapper.writeValueAsString(map)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.junit.jupiter.api.Test

import java.net.URL

class WriterDescriptionJsonMapperTest {
class CommonJsonMapperTest {

private var writerDescriptionJsonMapper: WriterDescriptionJsonMapper = WriterDescriptionJsonMapper(ObjectMapper())

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import com.few.api.domain.admin.document.utils.ObjectPathGenerator
import com.few.api.exception.common.ExternalIntegrationException
import com.few.api.exception.common.NotFoundException
import com.few.api.repo.dao.article.ArticleDao
import com.few.api.repo.dao.article.ArticleViewCountDao
import com.few.api.repo.dao.article.command.InsertFullArticleRecordCommand
import com.few.api.repo.dao.article.query.ArticleViewCountQuery
import com.few.api.repo.dao.document.DocumentDao
import com.few.api.repo.dao.document.command.InsertDocumentIfoCommand
import com.few.api.repo.dao.member.MemberDao
Expand All @@ -31,6 +33,7 @@ class AddArticleUseCase(
private val memberDao: MemberDao,
private val problemDao: ProblemDao,
private val documentDao: DocumentDao,
private val articleViewCountDao: ArticleViewCountDao,
private val convertDocumentService: ConvertDocumentService,
private val putDocumentService: PutDocumentService,
private val getUrlService: GetUrlService,
Expand Down Expand Up @@ -78,9 +81,11 @@ class AddArticleUseCase(

htmlSource
}

useCaseIn.contentType.lowercase(Locale.getDefault()) == "html" -> {
useCaseIn.contentSource
}

else -> {
throw IllegalArgumentException("Unsupported content type: ${useCaseIn.contentType}")
}
Expand Down Expand Up @@ -116,6 +121,12 @@ class AddArticleUseCase(
problemDao.insertProblems(commands)
}

ArticleViewCountQuery(
articleMstId,
CategoryType.fromCode(CategoryType.convertToCode(useCaseIn.category))
?: throw NotFoundException("article.invalid.category")
).let { articleViewCountDao.insertArticleViewCountToZero(it) }

return AddArticleUseCaseOut(articleMstId)
}
}
Loading
Loading