From ee698b636a32fddeea524cb0e6fdcf6742ff8b6d Mon Sep 17 00:00:00 2001 From: Francis Cao Date: Fri, 30 Jan 2026 00:12:13 -0800 Subject: [PATCH] implement exclude bounces feature --- .../websites/[websiteId]/WebsiteControls.tsx | 8 ++++-- .../websites/[websiteId]/WebsiteNav.tsx | 1 + .../websites/[websiteId]/WebsitePage.tsx | 2 +- src/components/hooks/useFilterParameters.ts | 3 +++ src/components/input/BounceFilter.tsx | 26 +++++++++++++++++++ src/components/messages.ts | 1 + src/lib/clickhouse.ts | 20 ++++++++++++++ src/lib/prisma.ts | 20 ++++++++++++++ src/lib/request.ts | 4 +++ src/lib/schema.ts | 1 + src/lib/types.ts | 1 + src/queries/sql/getChannelExpandedMetrics.ts | 13 ++++++---- src/queries/sql/getChannelMetrics.ts | 13 ++++++---- src/queries/sql/getWebsiteStats.ts | 15 +++++++---- src/queries/sql/getWeeklyTraffic.ts | 16 ++++++++---- .../pageviews/getPageviewExpandedMetrics.ts | 19 ++++++++------ .../sql/pageviews/getPageviewMetrics.ts | 20 ++++++++------ src/queries/sql/pageviews/getPageviewStats.ts | 14 ++++++---- .../sql/sessions/getSessionExpandedMetrics.ts | 23 +++++++++------- src/queries/sql/sessions/getSessionMetrics.ts | 24 ++++++++++------- src/queries/sql/sessions/getSessionStats.ts | 14 ++++++---- 21 files changed, 189 insertions(+), 69 deletions(-) create mode 100644 src/components/input/BounceFilter.tsx diff --git a/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx b/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx index 6223dbc0..ea993ac0 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteControls.tsx @@ -1,4 +1,5 @@ import { Column, Grid, Row } from '@umami/react-zen'; +import { BounceFilter } from '@/components/input/BounceFilter'; import { ExportButton } from '@/components/input/ExportButton'; import { FilterBar } from '@/components/input/FilterBar'; import { MonthFilter } from '@/components/input/MonthFilter'; @@ -8,6 +9,7 @@ import { WebsiteFilterButton } from '@/components/input/WebsiteFilterButton'; export function WebsiteControls({ websiteId, allowFilter = true, + allowBounceFilter = false, allowDateFilter = true, allowMonthFilter, allowDownload = false, @@ -15,6 +17,7 @@ export function WebsiteControls({ }: { websiteId: string; allowFilter?: boolean; + allowBounceFilter?: boolean; allowDateFilter?: boolean; allowMonthFilter?: boolean; allowDownload?: boolean; @@ -23,8 +26,9 @@ export function WebsiteControls({ return ( - - {allowFilter ? :
} + + {allowFilter && } + {allowBounceFilter && } {allowDateFilter && ( diff --git a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx index 9f72c303..2bc2fa1c 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx @@ -30,6 +30,7 @@ export function WebsiteNav({ compare: undefined, view: undefined, unit: undefined, + excludeBounce: undefined, }); const items = [ diff --git a/src/app/(main)/websites/[websiteId]/WebsitePage.tsx b/src/app/(main)/websites/[websiteId]/WebsitePage.tsx index 5acc9e68..5412421e 100644 --- a/src/app/(main)/websites/[websiteId]/WebsitePage.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsitePage.tsx @@ -11,7 +11,7 @@ import { WebsitePanels } from './WebsitePanels'; export function WebsitePage({ websiteId }: { websiteId: string }) { return ( - + diff --git a/src/components/hooks/useFilterParameters.ts b/src/components/hooks/useFilterParameters.ts index c141a3be..77edf883 100644 --- a/src/components/hooks/useFilterParameters.ts +++ b/src/components/hooks/useFilterParameters.ts @@ -24,6 +24,7 @@ export function useFilterParameters() { search, segment, cohort, + excludeBounce, }, } = useNavigation(); @@ -47,6 +48,7 @@ export function useFilterParameters() { search, segment, cohort, + excludeBounce, }; }, [ path, @@ -69,5 +71,6 @@ export function useFilterParameters() { search, segment, cohort, + excludeBounce, ]); } diff --git a/src/components/input/BounceFilter.tsx b/src/components/input/BounceFilter.tsx new file mode 100644 index 00000000..e8a4b31b --- /dev/null +++ b/src/components/input/BounceFilter.tsx @@ -0,0 +1,26 @@ +'use client'; +import { Checkbox, Row } from '@umami/react-zen'; +import { useMessages } from '@/components/hooks/useMessages'; +import { useNavigation } from '@/components/hooks/useNavigation'; + +export function BounceFilter() { + const { router, query, updateParams } = useNavigation(); + const { formatMessage, labels } = useMessages(); + const isSelected = query.excludeBounce === 'true'; + + const handleChange = (value: boolean) => { + if (value) { + router.push(updateParams({ excludeBounce: 'true' })); + } else { + router.push(updateParams({ excludeBounce: undefined })); + } + }; + + return ( + + + {formatMessage(labels.excludeBounce)} + + + ); +} diff --git a/src/components/messages.ts b/src/components/messages.ts index de29c306..cebb8c1d 100644 --- a/src/components/messages.ts +++ b/src/components/messages.ts @@ -111,6 +111,7 @@ export const labels = defineMessages({ event: { id: 'label.event', defaultMessage: 'Event' }, events: { id: 'label.events', defaultMessage: 'Events' }, eventName: { id: 'label.event-name', defaultMessage: 'Event name' }, + excludeBounce: { id: 'label.exclude-bounce', defaultMessage: 'Exclude bounces' }, query: { id: 'label.query', defaultMessage: 'Query' }, queryParameters: { id: 'label.query-parameters', defaultMessage: 'Query parameters' }, back: { id: 'label.back', defaultMessage: 'Back' }, diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 0a336f80..3160dbfe 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -131,6 +131,25 @@ function getCohortQuery(filters: Record) { `; } +function getExcludeBounceQuery(filters: Record) { + if (!filters.excludeBounce === true) { + return ''; + } + + return `join + (select distinct session_id, visit_id + from website_event + where website_id = {websiteId:UUID} + and created_at between {startDate:DateTime64} and {endDate:DateTime64} + and event_type = 1 + group by session_id, visit_id + having count(*) > 1 + ) excludeBounce + on excludeBounce.session_id = website_event.session_id + and excludeBounce.visit_id = website_event.visit_id + `; +} + function getDateQuery(filters: Record) { const { startDate, endDate, timezone } = filters; @@ -174,6 +193,7 @@ function parseFilters(filters: Record, options?: QueryOptions) { dateQuery: getDateQuery(filters), queryParams: getQueryParams(filters), cohortQuery: getCohortQuery(cohortFilters), + excludeBounceQuery: getExcludeBounceQuery(filters), }; } diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index c2f98b57..41e42fbc 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -141,6 +141,25 @@ function getCohortQuery(filters: QueryFilters = {}) { `; } +function getExcludeBounceQuery(filters: Record) { + if (!filters.excludeBounce === true) { + return ''; + } + + return `join + (select distinct session_id, visit_id + from website_event + where website_id = {{websiteId}} + and created_at between {{startDate}} and {{endDate}} + and event_type = 1 + group by session_id, visit_id + having count(*) > 1 + ) excludeBounce + on excludeBounce.session_id = website_event.session_id + and excludeBounce.visit_id = website_event.visit_id + `; +} + function getDateQuery(filters: Record) { const { startDate, endDate } = filters; @@ -186,6 +205,7 @@ function parseFilters(filters: Record, options?: QueryOptions) { filterQuery: getFilterQuery(filters, options), queryParams: getQueryParams(filters), cohortQuery: getCohortQuery(cohortFilters), + excludeBounceQuery: getExcludeBounceQuery(filters), }; } diff --git a/src/lib/request.ts b/src/lib/request.ts index 7f9163cc..0b7c1a42 100644 --- a/src/lib/request.ts +++ b/src/lib/request.ts @@ -140,6 +140,10 @@ export async function getQueryFilters( cohort_endDate: endDate, }); } + + if (params.excludeBounce) { + Object.assign(filters, { excludeBounce: true }); + } } return { diff --git a/src/lib/schema.ts b/src/lib/schema.ts index a3c56a0f..020ed0c2 100644 --- a/src/lib/schema.ts +++ b/src/lib/schema.ts @@ -42,6 +42,7 @@ export const filterParams = { segment: z.uuid().optional(), cohort: z.uuid().optional(), eventType: z.coerce.number().int().positive().optional(), + excludeBounce: z.string().optional(), }; export const searchParams = { diff --git a/src/lib/types.ts b/src/lib/types.ts index f3466734..9a6775a0 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -90,6 +90,7 @@ export interface FilterParams { segment?: string; cohort?: string; compare?: string; + excludeBounce?: boolean; } export interface SortParams { diff --git a/src/queries/sql/getChannelExpandedMetrics.ts b/src/queries/sql/getChannelExpandedMetrics.ts index f674d182..2a055ff3 100644 --- a/src/queries/sql/getChannelExpandedMetrics.ts +++ b/src/queries/sql/getChannelExpandedMetrics.ts @@ -41,10 +41,11 @@ async function relationalQuery( filters: QueryFilters, ): Promise { const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; - const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ - ...filters, - websiteId, - }); + const { queryParams, filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, dateQuery } = + parseFilters({ + ...filters, + websiteId, + }); return rawQuery( ` @@ -64,6 +65,7 @@ async function relationalQuery( max(website_event.created_at) max_time from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.event_type != 2 @@ -119,7 +121,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise { const { rawQuery, parseFilters } = clickhouse; - const { queryParams, filterQuery, cohortQuery } = parseFilters({ + const { queryParams, filterQuery, cohortQuery, excludeBounceQuery } = parseFilters({ ...filters, websiteId, }); @@ -166,6 +168,7 @@ async function clickhouseQuery( max(created_at) max_time from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 diff --git a/src/queries/sql/getChannelMetrics.ts b/src/queries/sql/getChannelMetrics.ts index 78e4142e..9d14af05 100644 --- a/src/queries/sql/getChannelMetrics.ts +++ b/src/queries/sql/getChannelMetrics.ts @@ -22,10 +22,11 @@ export async function getChannelMetrics(...args: [websiteId: string, filters?: Q async function relationalQuery(websiteId: string, filters: QueryFilters) { const { rawQuery, parseFilters } = prisma; - const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ - ...filters, - websiteId, - }); + const { queryParams, filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, dateQuery } = + parseFilters({ + ...filters, + websiteId, + }); return rawQuery( ` @@ -41,6 +42,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { website_event.session_id from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.event_type != 2 @@ -81,7 +83,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise<{ x: string; y: number }[]> { const { rawQuery, parseFilters } = clickhouse; - const { queryParams, filterQuery, cohortQuery, dateQuery } = parseFilters({ + const { queryParams, filterQuery, cohortQuery, excludeBounceQuery, dateQuery } = parseFilters({ ...filters, websiteId, }); @@ -116,6 +118,7 @@ async function clickhouseQuery( count(distinct session_id) y from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and event_type != 2 ${dateQuery} diff --git a/src/queries/sql/getWebsiteStats.ts b/src/queries/sql/getWebsiteStats.ts index 69068394..7aceff9f 100644 --- a/src/queries/sql/getWebsiteStats.ts +++ b/src/queries/sql/getWebsiteStats.ts @@ -28,10 +28,11 @@ async function relationalQuery( filters: QueryFilters, ): Promise { const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ - ...filters, - websiteId, - }); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters({ + ...filters, + websiteId, + }); return rawQuery( ` @@ -50,12 +51,14 @@ async function relationalQuery( max(website_event.created_at) as "max_time" from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} and website_event.event_type != 2 ${filterQuery} group by 1, 2 + ) as t `, queryParams, @@ -68,7 +71,7 @@ async function clickhouseQuery( filters: QueryFilters, ): Promise { const { rawQuery, parseFilters } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -92,6 +95,7 @@ async function clickhouseQuery( max(created_at) max_time from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 @@ -115,6 +119,7 @@ async function clickhouseQuery( max(max_time) max_time from website_event_stats_hourly "website_event" ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 diff --git a/src/queries/sql/getWeeklyTraffic.ts b/src/queries/sql/getWeeklyTraffic.ts index 1868b922..f129048f 100644 --- a/src/queries/sql/getWeeklyTraffic.ts +++ b/src/queries/sql/getWeeklyTraffic.ts @@ -16,10 +16,11 @@ export async function getWeeklyTraffic(...args: [websiteId: string, filters: Que async function relationalQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc' } = filters; const { rawQuery, getDateWeeklySQL, parseFilters } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ - ...filters, - websiteId, - }); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters({ + ...filters, + websiteId, + }); return rawQuery( ` @@ -28,6 +29,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { count(distinct website_event.session_id) as value from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} @@ -43,7 +45,10 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { async function clickhouseQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc' } = filters; const { rawQuery, parseFilters } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = await parseFilters({ ...filters, websiteId }); + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = await parseFilters({ + ...filters, + websiteId, + }); let sql = ''; @@ -67,6 +72,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) { count(distinct session_id) as value from website_event_stats_hourly website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} ${filterQuery} diff --git a/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts b/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts index 54164973..29576a47 100644 --- a/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts +++ b/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts @@ -38,13 +38,14 @@ async function relationalQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( - { - ...filters, - websiteId, - }, - { joinSession: SESSION_COLUMNS.includes(type) }, - ); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters( + { + ...filters, + websiteId, + }, + { joinSession: SESSION_COLUMNS.includes(type) }, + ); let entryExitQuery = ''; let excludeDomain = ''; @@ -94,6 +95,7 @@ async function relationalQuery( max(website_event.created_at) as "max_time" from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} ${entryExitQuery} where website_event.website_id = {{websiteId::uuid}} @@ -122,7 +124,7 @@ async function clickhouseQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -171,6 +173,7 @@ async function clickhouseQuery( max(created_at) max_time from website_event ${cohortQuery} + ${excludeBounceQuery} ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} diff --git a/src/queries/sql/pageviews/getPageviewMetrics.ts b/src/queries/sql/pageviews/getPageviewMetrics.ts index b41ea058..748d1366 100644 --- a/src/queries/sql/pageviews/getPageviewMetrics.ts +++ b/src/queries/sql/pageviews/getPageviewMetrics.ts @@ -34,13 +34,14 @@ async function relationalQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( - { - ...filters, - websiteId, - }, - { joinSession: SESSION_COLUMNS.includes(type) }, - ); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters( + { + ...filters, + websiteId, + }, + { joinSession: SESSION_COLUMNS.includes(type) }, + ); let entryExitQuery = ''; let excludeDomain = ''; @@ -75,6 +76,7 @@ async function relationalQuery( count(distinct website_event.session_id) as y from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} ${entryExitQuery} where website_event.website_id = {{websiteId::uuid}} @@ -100,7 +102,7 @@ async function clickhouseQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { rawQuery, parseFilters } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -135,6 +137,7 @@ async function clickhouseQuery( uniq(website_event.session_id) as y from website_event ${cohortQuery} + ${excludeBounceQuery} ${entryExitQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} @@ -174,6 +177,7 @@ async function clickhouseQuery( ${columnQuery} as t from website_event_stats_hourly as website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 diff --git a/src/queries/sql/pageviews/getPageviewStats.ts b/src/queries/sql/pageviews/getPageviewStats.ts index 251d5b14..a0aa2768 100644 --- a/src/queries/sql/pageviews/getPageviewStats.ts +++ b/src/queries/sql/pageviews/getPageviewStats.ts @@ -16,10 +16,11 @@ export async function getPageviewStats(...args: [websiteId: string, filters: Que async function relationalQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc', unit = 'day' } = filters; const { getDateSQL, parseFilters, rawQuery } = prisma; - const { filterQuery, cohortQuery, joinSessionQuery, queryParams } = parseFilters({ - ...filters, - websiteId, - }); + const { filterQuery, cohortQuery, excludeBounceQuery, joinSessionQuery, queryParams } = + parseFilters({ + ...filters, + websiteId, + }); return rawQuery( ` @@ -28,6 +29,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { count(*) y from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} @@ -47,7 +49,7 @@ async function clickhouseQuery( ): Promise<{ x: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; const { parseFilters, rawQuery, getDateSQL } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -65,6 +67,7 @@ async function clickhouseQuery( count(*) as y from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 @@ -84,6 +87,7 @@ async function clickhouseQuery( sum(views) as y from website_event_stats_hourly as website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 diff --git a/src/queries/sql/sessions/getSessionExpandedMetrics.ts b/src/queries/sql/sessions/getSessionExpandedMetrics.ts index 6b85cd45..24cad943 100644 --- a/src/queries/sql/sessions/getSessionExpandedMetrics.ts +++ b/src/queries/sql/sessions/getSessionExpandedMetrics.ts @@ -38,15 +38,16 @@ async function relationalQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery, getTimestampDiffSQL } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( - { - ...filters, - websiteId, - }, - { - joinSession: SESSION_COLUMNS.includes(type), - }, - ); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters( + { + ...filters, + websiteId, + }, + { + joinSession: SESSION_COLUMNS.includes(type), + }, + ); const includeCountry = column === 'city' || column === 'region'; if (type === 'language') { @@ -74,6 +75,7 @@ async function relationalQuery( max(website_event.created_at) as "max_time" from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} @@ -102,7 +104,7 @@ async function clickhouseQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -133,6 +135,7 @@ async function clickhouseQuery( max(created_at) max_time from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 diff --git a/src/queries/sql/sessions/getSessionMetrics.ts b/src/queries/sql/sessions/getSessionMetrics.ts index c519bdd0..32a915c4 100644 --- a/src/queries/sql/sessions/getSessionMetrics.ts +++ b/src/queries/sql/sessions/getSessionMetrics.ts @@ -29,15 +29,16 @@ async function relationalQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( - { - ...filters, - websiteId, - }, - { - joinSession: SESSION_COLUMNS.includes(type), - }, - ); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters( + { + ...filters, + websiteId, + }, + { + joinSession: SESSION_COLUMNS.includes(type), + }, + ); const includeCountry = column === 'city' || column === 'region'; if (type === 'language') { @@ -52,6 +53,7 @@ async function relationalQuery( ${includeCountry ? ', country' : ''} from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} @@ -76,7 +78,7 @@ async function clickhouseQuery( const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; const { parseFilters, rawQuery } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -96,6 +98,7 @@ async function clickhouseQuery( ${includeCountry ? ', country' : ''} from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 @@ -114,6 +117,7 @@ async function clickhouseQuery( ${includeCountry ? ', country' : ''} from website_event_stats_hourly as website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 diff --git a/src/queries/sql/sessions/getSessionStats.ts b/src/queries/sql/sessions/getSessionStats.ts index fd457720..15034681 100644 --- a/src/queries/sql/sessions/getSessionStats.ts +++ b/src/queries/sql/sessions/getSessionStats.ts @@ -16,10 +16,11 @@ export async function getSessionStats(...args: [websiteId: string, filters: Quer async function relationalQuery(websiteId: string, filters: QueryFilters) { const { timezone = 'utc', unit = 'day' } = filters; const { getDateSQL, parseFilters, rawQuery } = prisma; - const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ - ...filters, - websiteId, - }); + const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } = + parseFilters({ + ...filters, + websiteId, + }); return rawQuery( ` @@ -28,6 +29,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { count(distinct website_event.session_id) y from website_event ${cohortQuery} + ${excludeBounceQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} @@ -47,7 +49,7 @@ async function clickhouseQuery( ): Promise<{ x: string; y: number }[]> { const { timezone = 'UTC', unit = 'day' } = filters; const { parseFilters, rawQuery, getDateSQL } = clickhouse; - const { filterQuery, cohortQuery, queryParams } = parseFilters({ + const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({ ...filters, websiteId, }); @@ -65,6 +67,7 @@ async function clickhouseQuery( count(distinct session_id) as y from website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2 @@ -84,6 +87,7 @@ async function clickhouseQuery( uniq(session_id) as y from website_event_stats_hourly as website_event ${cohortQuery} + ${excludeBounceQuery} where website_id = {websiteId:UUID} and created_at between {startDate:DateTime64} and {endDate:DateTime64} and event_type != 2