feat(home): AI 캐릭터 스냅샷 집계 쿼리를 변경한다
This commit is contained in:
@@ -483,74 +483,89 @@ class DefaultHomeRecommendationQueryRepository(
|
||||
|
||||
override fun findAiCharacterSnapshots(
|
||||
windowStart: LocalDateTime,
|
||||
snapshotAt: LocalDateTime,
|
||||
windowEndExclusive: LocalDateTime,
|
||||
limit: Int
|
||||
): List<RecommendationSnapshotRecord> {
|
||||
val sql = """
|
||||
select cc.id as target_id,
|
||||
(((select count(cm.id)
|
||||
from chat_message cm
|
||||
join chat_participant cp on cp.id = cm.participant_id
|
||||
where cp.character_id = cc.id
|
||||
and cm.created_at >= :windowStart
|
||||
and cm.created_at <= :snapshotAt
|
||||
and cm.is_active = true
|
||||
and cp.is_active = true
|
||||
and cp.participant_type = 'CHARACTER') * ${RecommendationScoreSpec.AI_RECENT_CHAT_WEIGHT} +
|
||||
(select count(distinct up.member_id)
|
||||
from chat_message cm
|
||||
join chat_participant up on up.id = cm.participant_id
|
||||
join chat_participant cp on cp.chat_room_id = cm.chat_room_id
|
||||
join member m on m.id = up.member_id
|
||||
where cp.character_id = cc.id
|
||||
and cm.created_at >= :windowStart
|
||||
and cm.created_at <= :snapshotAt
|
||||
and cm.is_active = true
|
||||
and up.is_active = true
|
||||
and cp.is_active = true
|
||||
and up.participant_type = 'USER'
|
||||
and cp.participant_type = 'CHARACTER'
|
||||
and m.is_active = true) * ${RecommendationScoreSpec.AI_RECENT_ACTIVE_USER_WEIGHT}) *
|
||||
case
|
||||
when cc.created_at >= :boost10Start then ${RecommendationScoreSpec.NEW_BOOST_10_DAYS}
|
||||
when cc.created_at >= :boost20Start then ${RecommendationScoreSpec.NEW_BOOST_20_DAYS}
|
||||
when cc.created_at >= :boost30Start then ${RecommendationScoreSpec.NEW_BOOST_30_DAYS}
|
||||
else ${RecommendationScoreSpec.DEFAULT_NEW_BOOST}
|
||||
end) as score,
|
||||
rand() as random_tie_breaker
|
||||
from chat_character cc
|
||||
where cc.is_active = true
|
||||
and (exists (
|
||||
select 1
|
||||
select ranked.target_id,
|
||||
ranked.score,
|
||||
ranked.snapshot_rank as random_tie_breaker
|
||||
from (
|
||||
select scored.target_id,
|
||||
scored.score,
|
||||
row_number() over (order by scored.score desc, scored.character_created_at desc, scored.target_id desc) as snapshot_rank
|
||||
from (
|
||||
select cc.id as target_id,
|
||||
cc.created_at as character_created_at,
|
||||
((coalesce(acs.ai_chat_count, 0) * ${RecommendationScoreSpec.AI_CHAT_WEIGHT} +
|
||||
coalesce(aus.active_user_count, 0) * ${RecommendationScoreSpec.AI_ACTIVE_USER_WEIGHT} +
|
||||
coalesce(fs.follow_increase_count, 0) * ${RecommendationScoreSpec.AI_FOLLOW_INCREASE_WEIGHT}) *
|
||||
case
|
||||
when cc.created_at >= :boost10Start then ${RecommendationScoreSpec.AI_NEW_BOOST_10_DAYS}
|
||||
when cc.created_at >= :boost20Start then ${RecommendationScoreSpec.AI_NEW_BOOST_20_DAYS}
|
||||
when cc.created_at >= :boost30Start then ${RecommendationScoreSpec.AI_NEW_BOOST_30_DAYS}
|
||||
else ${RecommendationScoreSpec.DEFAULT_NEW_BOOST}
|
||||
end) as score
|
||||
from chat_character cc
|
||||
join member creator_member on creator_member.id = cc.creator_member_id
|
||||
left join (
|
||||
select cp.character_id as character_id, count(cm.id) as ai_chat_count
|
||||
from chat_message cm
|
||||
join chat_participant cp on cp.id = cm.participant_id
|
||||
where cp.character_id = cc.id
|
||||
and cm.created_at >= :windowStart
|
||||
and cm.created_at <= :snapshotAt
|
||||
where cm.created_at >= :windowStart
|
||||
and cm.created_at < :windowEndExclusive
|
||||
and cm.is_active = true
|
||||
and cp.is_active = true
|
||||
and cp.participant_type = 'CHARACTER'
|
||||
) or exists (
|
||||
select 1
|
||||
group by cp.character_id
|
||||
) acs on acs.character_id = cc.id
|
||||
left join (
|
||||
select cp.character_id as character_id, count(distinct up.member_id) as active_user_count
|
||||
from chat_message cm
|
||||
join chat_participant up on up.id = cm.participant_id
|
||||
join chat_participant cp on cp.chat_room_id = cm.chat_room_id
|
||||
join member m on m.id = up.member_id
|
||||
where cp.character_id = cc.id
|
||||
and cm.created_at >= :windowStart
|
||||
and cm.created_at <= :snapshotAt
|
||||
where cm.created_at >= :windowStart
|
||||
and cm.created_at < :windowEndExclusive
|
||||
and cm.is_active = true
|
||||
and up.is_active = true
|
||||
and cp.is_active = true
|
||||
and up.participant_type = 'USER'
|
||||
and cp.participant_type = 'CHARACTER'
|
||||
and m.is_active = true
|
||||
))
|
||||
order by score desc, random_tie_breaker asc
|
||||
group by cp.character_id
|
||||
) aus on aus.character_id = cc.id
|
||||
left join (
|
||||
select cf.creator_id as creator_id, count(cf.id) as follow_increase_count
|
||||
from creator_following cf
|
||||
join member follower_member on follower_member.id = cf.member_id
|
||||
where cf.created_at >= :windowStart
|
||||
and cf.created_at < :windowEndExclusive
|
||||
and cf.is_active = true
|
||||
and follower_member.is_active = true
|
||||
group by cf.creator_id
|
||||
) fs on fs.creator_id = cc.creator_member_id
|
||||
where cc.is_active = true
|
||||
and cc.created_at is not null
|
||||
and cc.created_at < :windowEndExclusive
|
||||
and creator_member.is_active = true
|
||||
and creator_member.role = 'CREATOR'
|
||||
and creator_member.member_kind = 'AI_CHARACTER'
|
||||
) scored
|
||||
where scored.score > 0
|
||||
) ranked
|
||||
order by ranked.score desc, ranked.snapshot_rank asc
|
||||
limit :limit
|
||||
""".trimIndent()
|
||||
|
||||
return executeSnapshotQuery(sql, RecommendedSectionType.AI_CHARACTER, windowStart, snapshotAt, limit)
|
||||
return executeSnapshotQuery(
|
||||
sql,
|
||||
RecommendedSectionType.AI_CHARACTER,
|
||||
windowStart,
|
||||
windowEndExclusive.minusSeconds(1),
|
||||
limit,
|
||||
windowEndExclusive
|
||||
)
|
||||
}
|
||||
|
||||
override fun findCheerCreatorSnapshots(
|
||||
@@ -1129,12 +1144,19 @@ class DefaultHomeRecommendationQueryRepository(
|
||||
sectionType: RecommendedSectionType,
|
||||
windowStart: LocalDateTime,
|
||||
snapshotAt: LocalDateTime,
|
||||
limit: Int
|
||||
limit: Int,
|
||||
windowEndExclusive: LocalDateTime? = null
|
||||
): List<RecommendationSnapshotRecord> {
|
||||
val query = entityManager.createNativeQuery(sql)
|
||||
.setParameter("windowStart", windowStart)
|
||||
.setParameter("snapshotAt", snapshotAt)
|
||||
.setParameter("limit", limit)
|
||||
if (sql.contains(":snapshotAt")) {
|
||||
query.setParameter("snapshotAt", snapshotAt)
|
||||
}
|
||||
if (windowEndExclusive != null) {
|
||||
query.setParameter("windowEndExclusive", windowEndExclusive)
|
||||
}
|
||||
query
|
||||
.setParameter(
|
||||
"boost10Start",
|
||||
snapshotAt.toLocalDate().minusDays(RecommendationScoreSpec.NEW_BOOST_10_DAY_LIMIT).atStartOfDay()
|
||||
|
||||
@@ -40,7 +40,7 @@ interface HomeRecommendationQueryPort {
|
||||
|
||||
fun findAiCharacterSnapshots(
|
||||
windowStart: LocalDateTime,
|
||||
snapshotAt: LocalDateTime,
|
||||
windowEndExclusive: LocalDateTime,
|
||||
limit: Int
|
||||
): List<RecommendationSnapshotRecord>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user