인기 콘텐츠 참여 지표 원장 보정
This commit is contained in:
@@ -773,6 +773,42 @@ const getPostContentSessionAggregate = async (sql, postId, startDay, endDay) =>
|
||||
return rows[0] || {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 여러 게시물 콘텐츠 세션 집계를 조회한다.
|
||||
* @param {import('postgres').Sql} sql - DB 클라이언트
|
||||
* @param {string[]} postIds - 게시물 ID 목록
|
||||
* @param {string} startDay - 시작일
|
||||
* @param {string} endDay - 종료일
|
||||
* @returns {Promise<Map<string, Object>>} 게시물 ID별 원장 집계
|
||||
*/
|
||||
const getPostContentSessionAggregateMap = async (sql, postIds, startDay, endDay) => {
|
||||
const normalizedPostIds = [...new Set(postIds.map((postId) => String(postId || '').trim()).filter(Boolean))]
|
||||
|
||||
if (!normalizedPostIds.length) {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
const rows = await sql`
|
||||
SELECT
|
||||
post_id,
|
||||
COUNT(*) FILTER (WHERE view_recorded)::int AS views,
|
||||
COUNT(DISTINCT visitor_hash) FILTER (WHERE view_recorded)::int AS visitors,
|
||||
COUNT(*) FILTER (WHERE read_recorded)::int AS reads,
|
||||
COUNT(*) FILTER (WHERE engaged_recorded)::int AS engaged_views,
|
||||
COALESCE(SUM(duration_seconds), 0)::int AS total_engaged_seconds,
|
||||
COUNT(*) FILTER (WHERE scroll_50_recorded)::int AS scroll_50,
|
||||
COUNT(*) FILTER (WHERE scroll_75_recorded)::int AS scroll_75,
|
||||
COUNT(*) FILTER (WHERE scroll_100_recorded)::int AS scroll_100,
|
||||
COUNT(*)::int AS content_sessions
|
||||
FROM analytics_content_sessions
|
||||
WHERE post_id IN ${sql(normalizedPostIds)}
|
||||
AND day BETWEEN ${startDay}::date AND ${endDay}::date
|
||||
GROUP BY post_id
|
||||
`
|
||||
|
||||
return new Map(rows.map((row) => [String(row.post_id), row]))
|
||||
}
|
||||
|
||||
/**
|
||||
* 페이지 콘텐츠 세션 집계를 조회한다.
|
||||
* @param {import('postgres').Sql} sql - DB 클라이언트
|
||||
@@ -800,6 +836,41 @@ const getPageContentSessionAggregate = async (sql, pageId, startDay, endDay) =>
|
||||
return rows[0] || {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 여러 페이지 콘텐츠 세션 집계를 조회한다.
|
||||
* @param {import('postgres').Sql} sql - DB 클라이언트
|
||||
* @param {string[]} pageIds - 페이지 ID 목록
|
||||
* @param {string} startDay - 시작일
|
||||
* @param {string} endDay - 종료일
|
||||
* @returns {Promise<Map<string, Object>>} 페이지 ID별 원장 집계
|
||||
*/
|
||||
const getPageContentSessionAggregateMap = async (sql, pageIds, startDay, endDay) => {
|
||||
const normalizedPageIds = [...new Set(pageIds.map((pageId) => String(pageId || '').trim()).filter(Boolean))]
|
||||
|
||||
if (!normalizedPageIds.length) {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
const rows = await sql`
|
||||
SELECT
|
||||
page_id,
|
||||
COUNT(*) FILTER (WHERE view_recorded)::int AS views,
|
||||
COUNT(DISTINCT visitor_hash) FILTER (WHERE view_recorded)::int AS visitors,
|
||||
COUNT(*) FILTER (WHERE engaged_recorded)::int AS engaged_views,
|
||||
COALESCE(SUM(duration_seconds), 0)::int AS total_engaged_seconds,
|
||||
COUNT(*) FILTER (WHERE scroll_50_recorded)::int AS scroll_50,
|
||||
COUNT(*) FILTER (WHERE scroll_75_recorded)::int AS scroll_75,
|
||||
COUNT(*) FILTER (WHERE scroll_100_recorded)::int AS scroll_100,
|
||||
COUNT(*)::int AS content_sessions
|
||||
FROM analytics_content_sessions
|
||||
WHERE page_id IN ${sql(normalizedPageIds)}
|
||||
AND day BETWEEN ${startDay}::date AND ${endDay}::date
|
||||
GROUP BY page_id
|
||||
`
|
||||
|
||||
return new Map(rows.map((row) => [String(row.page_id), row]))
|
||||
}
|
||||
|
||||
/**
|
||||
* 게시물 전체 콘텐츠 세션 스크롤 집계를 조회한다.
|
||||
* @param {import('postgres').Sql} sql - DB 클라이언트
|
||||
@@ -2714,15 +2785,32 @@ export const getAnalyticsTopPosts = async (options = {}) => {
|
||||
LIMIT ${limit}
|
||||
`
|
||||
|
||||
const shouldUseContentRows = canUseContentSessionAggregateForRange(rangeStartDay, today)
|
||||
const contentRowMap = shouldUseContentRows
|
||||
? await getPostContentSessionAggregateMap(sql, rows.map((row) => row.id), rangeStartDay, today)
|
||||
: new Map()
|
||||
|
||||
return rows.map((row) => {
|
||||
const engagedViews = Number(row.engaged_views || 0)
|
||||
const totalEngagedSeconds = Number(row.total_engaged_seconds || 0)
|
||||
const contentRow = contentRowMap.get(String(row.id))
|
||||
const metricRow = hasContentSessionAggregateRows(contentRow)
|
||||
? {
|
||||
...row,
|
||||
reads: contentRow.reads,
|
||||
engaged_views: contentRow.engaged_views,
|
||||
total_engaged_seconds: contentRow.total_engaged_seconds,
|
||||
scroll_50: contentRow.scroll_50,
|
||||
scroll_75: contentRow.scroll_75,
|
||||
scroll_100: contentRow.scroll_100
|
||||
}
|
||||
: row
|
||||
const engagedViews = Number(metricRow.engaged_views || 0)
|
||||
const totalEngagedSeconds = Number(metricRow.total_engaged_seconds || 0)
|
||||
const engagementStats = normalizeContentEngagementStats({
|
||||
views: row.views,
|
||||
reads: row.reads,
|
||||
scroll50: row.scroll_50,
|
||||
scroll75: row.scroll_75,
|
||||
scroll100: row.scroll_100
|
||||
reads: metricRow.reads,
|
||||
scroll50: metricRow.scroll_50,
|
||||
scroll75: metricRow.scroll_75,
|
||||
scroll100: metricRow.scroll_100
|
||||
})
|
||||
|
||||
return {
|
||||
@@ -2782,14 +2870,30 @@ export const getAnalyticsTopPages = async (options = {}) => {
|
||||
LIMIT ${limit}
|
||||
`
|
||||
|
||||
const shouldUseContentRows = canUseContentSessionAggregateForRange(rangeStartDay, today)
|
||||
const contentRowMap = shouldUseContentRows
|
||||
? await getPageContentSessionAggregateMap(sql, rows.map((row) => row.id), rangeStartDay, today)
|
||||
: new Map()
|
||||
|
||||
return rows.map((row) => {
|
||||
const engagedViews = Number(row.engaged_views || 0)
|
||||
const totalEngagedSeconds = Number(row.total_engaged_seconds || 0)
|
||||
const contentRow = contentRowMap.get(String(row.id))
|
||||
const metricRow = hasContentSessionAggregateRows(contentRow)
|
||||
? {
|
||||
...row,
|
||||
engaged_views: contentRow.engaged_views,
|
||||
total_engaged_seconds: contentRow.total_engaged_seconds,
|
||||
scroll_50: contentRow.scroll_50,
|
||||
scroll_75: contentRow.scroll_75,
|
||||
scroll_100: contentRow.scroll_100
|
||||
}
|
||||
: row
|
||||
const engagedViews = Number(metricRow.engaged_views || 0)
|
||||
const totalEngagedSeconds = Number(metricRow.total_engaged_seconds || 0)
|
||||
const engagementStats = normalizeContentEngagementStats({
|
||||
views: row.views,
|
||||
scroll50: row.scroll_50,
|
||||
scroll75: row.scroll_75,
|
||||
scroll100: row.scroll_100
|
||||
scroll50: metricRow.scroll_50,
|
||||
scroll75: metricRow.scroll_75,
|
||||
scroll100: metricRow.scroll_100
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user