({
projectId,
startDate,
endDate,
timezone,
search,
limit,
}: IGetPagesInput)
| 34 | constructor(private client: typeof ch) {} |
| 35 | |
| 36 | async getTopPages({ |
| 37 | projectId, |
| 38 | startDate, |
| 39 | endDate, |
| 40 | timezone, |
| 41 | search, |
| 42 | limit, |
| 43 | }: IGetPagesInput): Promise<ITopPage[]> { |
| 44 | // CTE: Get titles from the last 30 days for faster retrieval |
| 45 | const titlesCte = clix(this.client, timezone) |
| 46 | .select([ |
| 47 | 'concat(origin, path) as page_key', |
| 48 | "anyLast(properties['__title']) as title", |
| 49 | ]) |
| 50 | .from(TABLE_NAMES.events, false) |
| 51 | .where('project_id', '=', projectId) |
| 52 | .where('name', '=', 'screen_view') |
| 53 | .where('created_at', '>=', clix.exp('now() - INTERVAL 30 DAY')) |
| 54 | .groupBy(['origin', 'path']); |
| 55 | |
| 56 | // CTE: compute screen_view durations via window function (leadInFrame gives next event's timestamp) |
| 57 | const screenViewDurationsCte = clix(this.client, timezone) |
| 58 | .select([ |
| 59 | 'project_id', |
| 60 | 'session_id', |
| 61 | 'path', |
| 62 | 'origin', |
| 63 | `dateDiff('millisecond', created_at, lead(created_at, 1, created_at) OVER (PARTITION BY session_id ORDER BY created_at)) AS duration`, |
| 64 | ]) |
| 65 | .from(TABLE_NAMES.events, false) |
| 66 | .where('project_id', '=', projectId) |
| 67 | .where('name', '=', 'screen_view') |
| 68 | .where('path', '!=', '') |
| 69 | .where('created_at', 'BETWEEN', [ |
| 70 | clix.datetime(startDate, 'toDateTime'), |
| 71 | clix.datetime(endDate, 'toDateTime'), |
| 72 | ]); |
| 73 | |
| 74 | // Pre-filtered sessions subquery for better performance |
| 75 | const sessionsSubquery = clix(this.client, timezone) |
| 76 | .select(['id', 'project_id', 'is_bounce']) |
| 77 | .from(TABLE_NAMES.sessions, true) // FINAL |
| 78 | .where('project_id', '=', projectId) |
| 79 | .where('created_at', 'BETWEEN', [ |
| 80 | clix.datetime(startDate, 'toDateTime'), |
| 81 | clix.datetime(endDate, 'toDateTime'), |
| 82 | ]) |
| 83 | .where('sign', '=', 1); |
| 84 | |
| 85 | // Main query: aggregate events and calculate bounce rate from pre-filtered sessions |
| 86 | const query = clix(this.client, timezone) |
| 87 | .with('page_titles', titlesCte) |
| 88 | .with('screen_view_durations', screenViewDurationsCte) |
| 89 | .select<ITopPage>([ |
| 90 | 'e.origin as origin', |
| 91 | 'e.path as path', |
| 92 | "coalesce(pt.title, '') as title", |
| 93 | 'uniq(e.session_id) as sessions', |
no test coverage detected