implement exclude bounces feature
Some checks failed
Node.js CI / build (push) Has been cancelled

This commit is contained in:
Francis Cao 2026-01-30 00:12:13 -08:00
parent 4a3190b2da
commit ee698b636a
21 changed files with 189 additions and 69 deletions

View file

@ -1,4 +1,5 @@
import { Column, Grid, Row } from '@umami/react-zen'; import { Column, Grid, Row } from '@umami/react-zen';
import { BounceFilter } from '@/components/input/BounceFilter';
import { ExportButton } from '@/components/input/ExportButton'; import { ExportButton } from '@/components/input/ExportButton';
import { FilterBar } from '@/components/input/FilterBar'; import { FilterBar } from '@/components/input/FilterBar';
import { MonthFilter } from '@/components/input/MonthFilter'; import { MonthFilter } from '@/components/input/MonthFilter';
@ -8,6 +9,7 @@ import { WebsiteFilterButton } from '@/components/input/WebsiteFilterButton';
export function WebsiteControls({ export function WebsiteControls({
websiteId, websiteId,
allowFilter = true, allowFilter = true,
allowBounceFilter = false,
allowDateFilter = true, allowDateFilter = true,
allowMonthFilter, allowMonthFilter,
allowDownload = false, allowDownload = false,
@ -15,6 +17,7 @@ export function WebsiteControls({
}: { }: {
websiteId: string; websiteId: string;
allowFilter?: boolean; allowFilter?: boolean;
allowBounceFilter?: boolean;
allowDateFilter?: boolean; allowDateFilter?: boolean;
allowMonthFilter?: boolean; allowMonthFilter?: boolean;
allowDownload?: boolean; allowDownload?: boolean;
@ -23,8 +26,9 @@ export function WebsiteControls({
return ( return (
<Column gap> <Column gap>
<Grid columns={{ xs: '1fr', md: 'auto 1fr' }} gap> <Grid columns={{ xs: '1fr', md: 'auto 1fr' }} gap>
<Row alignItems="center" justifyContent="flex-start"> <Row alignItems="center" justifyContent="flex-start" gap="4">
{allowFilter ? <WebsiteFilterButton websiteId={websiteId} /> : <div />} {allowFilter && <WebsiteFilterButton websiteId={websiteId} />}
{allowBounceFilter && <BounceFilter />}
</Row> </Row>
<Row alignItems="center" justifyContent={{ xs: 'flex-start', md: 'flex-end' }}> <Row alignItems="center" justifyContent={{ xs: 'flex-start', md: 'flex-end' }}>
{allowDateFilter && ( {allowDateFilter && (

View file

@ -30,6 +30,7 @@ export function WebsiteNav({
compare: undefined, compare: undefined,
view: undefined, view: undefined,
unit: undefined, unit: undefined,
excludeBounce: undefined,
}); });
const items = [ const items = [

View file

@ -11,7 +11,7 @@ import { WebsitePanels } from './WebsitePanels';
export function WebsitePage({ websiteId }: { websiteId: string }) { export function WebsitePage({ websiteId }: { websiteId: string }) {
return ( return (
<Column gap> <Column gap>
<WebsiteControls websiteId={websiteId} /> <WebsiteControls websiteId={websiteId} allowBounceFilter={true} />
<WebsiteMetricsBar websiteId={websiteId} showChange={true} /> <WebsiteMetricsBar websiteId={websiteId} showChange={true} />
<Panel minHeight="520px"> <Panel minHeight="520px">
<Row justifyContent="end"> <Row justifyContent="end">

View file

@ -24,6 +24,7 @@ export function useFilterParameters() {
search, search,
segment, segment,
cohort, cohort,
excludeBounce,
}, },
} = useNavigation(); } = useNavigation();
@ -47,6 +48,7 @@ export function useFilterParameters() {
search, search,
segment, segment,
cohort, cohort,
excludeBounce,
}; };
}, [ }, [
path, path,
@ -69,5 +71,6 @@ export function useFilterParameters() {
search, search,
segment, segment,
cohort, cohort,
excludeBounce,
]); ]);
} }

View file

@ -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 (
<Row alignItems="center" gap>
<Checkbox isSelected={isSelected} onChange={handleChange}>
{formatMessage(labels.excludeBounce)}
</Checkbox>
</Row>
);
}

View file

@ -111,6 +111,7 @@ export const labels = defineMessages({
event: { id: 'label.event', defaultMessage: 'Event' }, event: { id: 'label.event', defaultMessage: 'Event' },
events: { id: 'label.events', defaultMessage: 'Events' }, events: { id: 'label.events', defaultMessage: 'Events' },
eventName: { id: 'label.event-name', defaultMessage: 'Event name' }, eventName: { id: 'label.event-name', defaultMessage: 'Event name' },
excludeBounce: { id: 'label.exclude-bounce', defaultMessage: 'Exclude bounces' },
query: { id: 'label.query', defaultMessage: 'Query' }, query: { id: 'label.query', defaultMessage: 'Query' },
queryParameters: { id: 'label.query-parameters', defaultMessage: 'Query parameters' }, queryParameters: { id: 'label.query-parameters', defaultMessage: 'Query parameters' },
back: { id: 'label.back', defaultMessage: 'Back' }, back: { id: 'label.back', defaultMessage: 'Back' },

View file

@ -131,6 +131,25 @@ function getCohortQuery(filters: Record<string, any>) {
`; `;
} }
function getExcludeBounceQuery(filters: Record<string, any>) {
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<string, any>) { function getDateQuery(filters: Record<string, any>) {
const { startDate, endDate, timezone } = filters; const { startDate, endDate, timezone } = filters;
@ -174,6 +193,7 @@ function parseFilters(filters: Record<string, any>, options?: QueryOptions) {
dateQuery: getDateQuery(filters), dateQuery: getDateQuery(filters),
queryParams: getQueryParams(filters), queryParams: getQueryParams(filters),
cohortQuery: getCohortQuery(cohortFilters), cohortQuery: getCohortQuery(cohortFilters),
excludeBounceQuery: getExcludeBounceQuery(filters),
}; };
} }

View file

@ -141,6 +141,25 @@ function getCohortQuery(filters: QueryFilters = {}) {
`; `;
} }
function getExcludeBounceQuery(filters: Record<string, any>) {
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<string, any>) { function getDateQuery(filters: Record<string, any>) {
const { startDate, endDate } = filters; const { startDate, endDate } = filters;
@ -186,6 +205,7 @@ function parseFilters(filters: Record<string, any>, options?: QueryOptions) {
filterQuery: getFilterQuery(filters, options), filterQuery: getFilterQuery(filters, options),
queryParams: getQueryParams(filters), queryParams: getQueryParams(filters),
cohortQuery: getCohortQuery(cohortFilters), cohortQuery: getCohortQuery(cohortFilters),
excludeBounceQuery: getExcludeBounceQuery(filters),
}; };
} }

View file

@ -140,6 +140,10 @@ export async function getQueryFilters(
cohort_endDate: endDate, cohort_endDate: endDate,
}); });
} }
if (params.excludeBounce) {
Object.assign(filters, { excludeBounce: true });
}
} }
return { return {

View file

@ -42,6 +42,7 @@ export const filterParams = {
segment: z.uuid().optional(), segment: z.uuid().optional(),
cohort: z.uuid().optional(), cohort: z.uuid().optional(),
eventType: z.coerce.number().int().positive().optional(), eventType: z.coerce.number().int().positive().optional(),
excludeBounce: z.string().optional(),
}; };
export const searchParams = { export const searchParams = {

View file

@ -90,6 +90,7 @@ export interface FilterParams {
segment?: string; segment?: string;
cohort?: string; cohort?: string;
compare?: string; compare?: string;
excludeBounce?: boolean;
} }
export interface SortParams { export interface SortParams {

View file

@ -41,7 +41,8 @@ async function relationalQuery(
filters: QueryFilters, filters: QueryFilters,
): Promise<ChannelExpandedMetricsData[]> { ): Promise<ChannelExpandedMetricsData[]> {
const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma;
const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ const { queryParams, filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, dateQuery } =
parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -64,6 +65,7 @@ async function relationalQuery(
max(website_event.created_at) max_time max(website_event.created_at) max_time
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type != 2 and website_event.event_type != 2
@ -119,7 +121,7 @@ async function clickhouseQuery(
filters: QueryFilters, filters: QueryFilters,
): Promise<ChannelExpandedMetricsData[]> { ): Promise<ChannelExpandedMetricsData[]> {
const { rawQuery, parseFilters } = clickhouse; const { rawQuery, parseFilters } = clickhouse;
const { queryParams, filterQuery, cohortQuery } = parseFilters({ const { queryParams, filterQuery, cohortQuery, excludeBounceQuery } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -166,6 +168,7 @@ async function clickhouseQuery(
max(created_at) max_time max(created_at) max_time
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2

View file

@ -22,7 +22,8 @@ export async function getChannelMetrics(...args: [websiteId: string, filters?: Q
async function relationalQuery(websiteId: string, filters: QueryFilters) { async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { rawQuery, parseFilters } = prisma; const { rawQuery, parseFilters } = prisma;
const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ const { queryParams, filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, dateQuery } =
parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -41,6 +42,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
website_event.session_id website_event.session_id
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type != 2 and website_event.event_type != 2
@ -81,7 +83,7 @@ async function clickhouseQuery(
filters: QueryFilters, filters: QueryFilters,
): Promise<{ x: string; y: number }[]> { ): Promise<{ x: string; y: number }[]> {
const { rawQuery, parseFilters } = clickhouse; const { rawQuery, parseFilters } = clickhouse;
const { queryParams, filterQuery, cohortQuery, dateQuery } = parseFilters({ const { queryParams, filterQuery, cohortQuery, excludeBounceQuery, dateQuery } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -116,6 +118,7 @@ async function clickhouseQuery(
count(distinct session_id) y count(distinct session_id) y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and event_type != 2 and event_type != 2
${dateQuery} ${dateQuery}

View file

@ -28,7 +28,8 @@ async function relationalQuery(
filters: QueryFilters, filters: QueryFilters,
): Promise<WebsiteStatsData[]> { ): Promise<WebsiteStatsData[]> {
const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma; const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -50,12 +51,14 @@ async function relationalQuery(
max(website_event.created_at) as "max_time" max(website_event.created_at) as "max_time"
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}} and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2 and website_event.event_type != 2
${filterQuery} ${filterQuery}
group by 1, 2 group by 1, 2
) as t ) as t
`, `,
queryParams, queryParams,
@ -68,7 +71,7 @@ async function clickhouseQuery(
filters: QueryFilters, filters: QueryFilters,
): Promise<WebsiteStatsData[]> { ): Promise<WebsiteStatsData[]> {
const { rawQuery, parseFilters } = clickhouse; const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -92,6 +95,7 @@ async function clickhouseQuery(
max(created_at) max_time max(created_at) max_time
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2
@ -115,6 +119,7 @@ async function clickhouseQuery(
max(max_time) max_time max(max_time) max_time
from website_event_stats_hourly "website_event" from website_event_stats_hourly "website_event"
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2

View file

@ -16,7 +16,8 @@ export async function getWeeklyTraffic(...args: [websiteId: string, filters: Que
async function relationalQuery(websiteId: string, filters: QueryFilters) { async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc' } = filters; const { timezone = 'utc' } = filters;
const { rawQuery, getDateWeeklySQL, parseFilters } = prisma; const { rawQuery, getDateWeeklySQL, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -28,6 +29,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
count(distinct website_event.session_id) as value count(distinct website_event.session_id) as value
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}} 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) { async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc' } = filters; const { timezone = 'utc' } = filters;
const { rawQuery, parseFilters } = clickhouse; const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = await parseFilters({ ...filters, websiteId }); const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = await parseFilters({
...filters,
websiteId,
});
let sql = ''; let sql = '';
@ -67,6 +72,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
count(distinct session_id) as value count(distinct session_id) as value
from website_event_stats_hourly website_event from website_event_stats_hourly website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery} ${filterQuery}

View file

@ -38,7 +38,8 @@ async function relationalQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters(
{ {
...filters, ...filters,
websiteId, websiteId,
@ -94,6 +95,7 @@ async function relationalQuery(
max(website_event.created_at) as "max_time" max(website_event.created_at) as "max_time"
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
${entryExitQuery} ${entryExitQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
@ -122,7 +124,7 @@ async function clickhouseQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { rawQuery, parseFilters } = clickhouse; const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -171,6 +173,7 @@ async function clickhouseQuery(
max(created_at) max_time max(created_at) max_time
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${entryExitQuery} ${entryExitQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}

View file

@ -34,7 +34,8 @@ async function relationalQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { rawQuery, parseFilters } = prisma; const { rawQuery, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters(
{ {
...filters, ...filters,
websiteId, websiteId,
@ -75,6 +76,7 @@ async function relationalQuery(
count(distinct website_event.session_id) as y count(distinct website_event.session_id) as y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
${entryExitQuery} ${entryExitQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
@ -100,7 +102,7 @@ async function clickhouseQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { rawQuery, parseFilters } = clickhouse; const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -135,6 +137,7 @@ async function clickhouseQuery(
uniq(website_event.session_id) as y uniq(website_event.session_id) as y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${entryExitQuery} ${entryExitQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
@ -174,6 +177,7 @@ async function clickhouseQuery(
${columnQuery} as t ${columnQuery} as t
from website_event_stats_hourly as website_event from website_event_stats_hourly as website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2

View file

@ -16,7 +16,8 @@ export async function getPageviewStats(...args: [websiteId: string, filters: Que
async function relationalQuery(websiteId: string, filters: QueryFilters) { async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc', unit = 'day' } = filters; const { timezone = 'utc', unit = 'day' } = filters;
const { getDateSQL, parseFilters, rawQuery } = prisma; const { getDateSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, cohortQuery, joinSessionQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, joinSessionQuery, queryParams } =
parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -28,6 +29,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
count(*) y count(*) y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}} and website_event.created_at between {{startDate}} and {{endDate}}
@ -47,7 +49,7 @@ async function clickhouseQuery(
): Promise<{ x: string; y: number }[]> { ): Promise<{ x: string; y: number }[]> {
const { timezone = 'UTC', unit = 'day' } = filters; const { timezone = 'UTC', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateSQL } = clickhouse; const { parseFilters, rawQuery, getDateSQL } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -65,6 +67,7 @@ async function clickhouseQuery(
count(*) as y count(*) as y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2
@ -84,6 +87,7 @@ async function clickhouseQuery(
sum(views) as y sum(views) as y
from website_event_stats_hourly as website_event from website_event_stats_hourly as website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2

View file

@ -38,7 +38,8 @@ async function relationalQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { parseFilters, rawQuery, getTimestampDiffSQL } = prisma; const { parseFilters, rawQuery, getTimestampDiffSQL } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters(
{ {
...filters, ...filters,
websiteId, websiteId,
@ -74,6 +75,7 @@ async function relationalQuery(
max(website_event.created_at) as "max_time" max(website_event.created_at) as "max_time"
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}} and website_event.created_at between {{startDate}} and {{endDate}}
@ -102,7 +104,7 @@ async function clickhouseQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { parseFilters, rawQuery } = clickhouse; const { parseFilters, rawQuery } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -133,6 +135,7 @@ async function clickhouseQuery(
max(created_at) max_time max(created_at) max_time
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2

View file

@ -29,7 +29,8 @@ async function relationalQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { parseFilters, rawQuery } = prisma; const { parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters(
{ {
...filters, ...filters,
websiteId, websiteId,
@ -52,6 +53,7 @@ async function relationalQuery(
${includeCountry ? ', country' : ''} ${includeCountry ? ', country' : ''}
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}} and website_event.created_at between {{startDate}} and {{endDate}}
@ -76,7 +78,7 @@ async function clickhouseQuery(
const { type, limit = 500, offset = 0 } = parameters; const { type, limit = 500, offset = 0 } = parameters;
let column = FILTER_COLUMNS[type] || type; let column = FILTER_COLUMNS[type] || type;
const { parseFilters, rawQuery } = clickhouse; const { parseFilters, rawQuery } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -96,6 +98,7 @@ async function clickhouseQuery(
${includeCountry ? ', country' : ''} ${includeCountry ? ', country' : ''}
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2
@ -114,6 +117,7 @@ async function clickhouseQuery(
${includeCountry ? ', country' : ''} ${includeCountry ? ', country' : ''}
from website_event_stats_hourly as website_event from website_event_stats_hourly as website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2

View file

@ -16,7 +16,8 @@ export async function getSessionStats(...args: [websiteId: string, filters: Quer
async function relationalQuery(websiteId: string, filters: QueryFilters) { async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc', unit = 'day' } = filters; const { timezone = 'utc', unit = 'day' } = filters;
const { getDateSQL, parseFilters, rawQuery } = prisma; const { getDateSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, joinSessionQuery, cohortQuery, excludeBounceQuery, queryParams } =
parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -28,6 +29,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
count(distinct website_event.session_id) y count(distinct website_event.session_id) y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
${joinSessionQuery} ${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}} where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}} and website_event.created_at between {{startDate}} and {{endDate}}
@ -47,7 +49,7 @@ async function clickhouseQuery(
): Promise<{ x: string; y: number }[]> { ): Promise<{ x: string; y: number }[]> {
const { timezone = 'UTC', unit = 'day' } = filters; const { timezone = 'UTC', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateSQL } = clickhouse; const { parseFilters, rawQuery, getDateSQL } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ const { filterQuery, cohortQuery, excludeBounceQuery, queryParams } = parseFilters({
...filters, ...filters,
websiteId, websiteId,
}); });
@ -65,6 +67,7 @@ async function clickhouseQuery(
count(distinct session_id) as y count(distinct session_id) as y
from website_event from website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2
@ -84,6 +87,7 @@ async function clickhouseQuery(
uniq(session_id) as y uniq(session_id) as y
from website_event_stats_hourly as website_event from website_event_stats_hourly as website_event
${cohortQuery} ${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64} and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2 and event_type != 2