Fix excludeBounce query, simplify bounce definition for SPA websites, query routing for performance, add tests

This commit is contained in:
Francis Cao committed 2026-07-30 09:34:35 -07:00
1 parent 0a80d09d0b
commit b3aec54bb3
12 files changed
+410 -150

No files matched your search

@@ -1,5 +1,10 @@
import clickhouse from '@/lib/clickhouse';
import { BOUNCE_THRESHOLD, EVENT_TYPE, FILTER_COLUMNS, GROUPED_DOMAINS, SESSION_COLUMNS } from '@/lib/constants';
import {
EVENT_TYPE,
FILTER_COLUMNS,
GROUPED_DOMAINS,
SESSION_COLUMNS,
} from '@/lib/constants';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import type { QueryFilters } from '@/lib/types';
@@ -46,6 +51,7 @@ async function relationalQuery(
},
{ joinSession: SESSION_COLUMNS.includes(type) },
);
const needsBounceEvents = filters.excludeBounce !== true;
const fullPathSearchQuery =
type === 'fullPath' && filters.search
? `and (case when website_event.url_query != '' then website_event.url_path || '?' || website_event.url_query else website_event.url_path end) ilike {{fullPathSearch}}`
@@ -88,6 +94,21 @@ async function relationalQuery(
type === 'fullPath'
? `case when website_event.url_query != '' then website_event.url_path || '?' || website_event.url_query else website_event.url_path end`
: column;
const bounceQuery = needsBounceEvents
? `sum(case when t.c = 1 and coalesce(e.has_custom_event, 0) = 0 then 1 else 0 end) as "bounces",`
: '0 as "bounces",';
const visitEventsJoin = needsBounceEvents
? `left join (
select session_id, visit_id, 1 as "has_custom_event"
from website_event
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
and event_type = ${EVENT_TYPE.customEvent}
group by 1, 2
) as e
on e.session_id = t.session_id
and e.visit_id = t.visit_id`
: '';
return rawQuery(
`
@@ -96,7 +117,7 @@ async function relationalQuery(
sum(t.c) as "pageviews",
count(distinct t.session_id) as "visitors",
count(distinct t.visit_id) as "visits",
sum(case when t.c = 1 and t.events_count < ${BOUNCE_THRESHOLD} then 1 else 0 end) as "bounces",
${bounceQuery}
sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime"
from (
select
@@ -105,16 +126,7 @@ async function relationalQuery(
website_event.visit_id,
count(*) as "c",
min(website_event.created_at) as "min_time",
max(website_event.created_at) as "max_time",
max((
select count(*)
from website_event we2
where we2.website_id = website_event.website_id
and we2.session_id = website_event.session_id
and we2.visit_id = website_event.visit_id
and we2.created_at between {{startDate}} and {{endDate}}
and we2.event_type = ${EVENT_TYPE.customEvent}
)) as "events_count"
max(website_event.created_at) as "max_time"
from website_event
${cohortQuery}
${excludeBounceQuery}
@@ -128,6 +140,7 @@ async function relationalQuery(
${filterQuery}
group by ${groupByColumn}, website_event.session_id, website_event.visit_id
) as t
${visitEventsJoin}
where name != ''
group by name
order by visitors desc, visits desc
@@ -154,6 +167,7 @@ async function clickhouseQuery(
...filters,
websiteId,
});
const needsBounceEvents = filters.excludeBounce !== true;
const fullPathSearchQuery =
type === 'fullPath' && filters.search
? `and positionCaseInsensitive(if(url_query != '', concat(url_path, '?', url_query), url_path), {fullPathSearch:String}) > 0`
@@ -189,6 +203,19 @@ async function clickhouseQuery(
} else if (type === 'fullPath') {
selectColumn = `if(url_query != '', concat(url_path, '?', url_query), url_path)`;
}
const bounceQuery = needsBounceEvents
? `sumIf(1, t.c = 1 and ifNull(e.has_custom_event, 0) = 0) as "bounces",`
: '0 as "bounces",';
const visitEventsJoin = needsBounceEvents
? `left join (
select session_id, visit_id, toUInt8(1) as has_custom_event
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = ${EVENT_TYPE.customEvent}
group by session_id, visit_id
) as e using (session_id, visit_id)`
: '';
return rawQuery(
`
@@ -197,7 +224,7 @@ async function clickhouseQuery(
sum(t.c) as "pageviews",
uniq(t.session_id) as "visitors",
uniq(t.visit_id) as "visits",
sumIf(1, t.c = 1 and ifNull(e.events_count, 0) < ${BOUNCE_THRESHOLD}) as "bounces",
${bounceQuery}
sum(max_time-min_time) as "totaltime"
from (
select
@@ -220,14 +247,7 @@ async function clickhouseQuery(
${filterQuery}
group by name, session_id, visit_id
) as t
left join (
select session_id, visit_id, toUInt32(count()) as events_count
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type = ${EVENT_TYPE.customEvent}
group by session_id, visit_id
) as e using (session_id, visit_id)
${visitEventsJoin}
group by name
order by visitors desc, visits desc
limit ${limit}