diff --git a/src/components/metrics/ListTable.tsx b/src/components/metrics/ListTable.tsx index 4c98eb5dc..3aef2b287 100644 --- a/src/components/metrics/ListTable.tsx +++ b/src/components/metrics/ListTable.tsx @@ -4,7 +4,7 @@ import { useSpring, config } from '@react-spring/web'; import { Grid, Row, Column, Text } from '@umami/react-zen'; import { AnimatedDiv } from '@/components/common/AnimatedDiv'; import { Empty } from '@/components/common/Empty'; -import { useMessages } from '@/components/hooks'; +import { useMessages, useMobile } from '@/components/hooks'; import { formatLongCurrency, formatLongNumber } from '@/lib/format'; const ITEM_SIZE = 30; @@ -42,6 +42,7 @@ export function ListTable({ currency, }: ListTableProps) { const { formatMessage, labels } = useMessages(); + const { isMobile } = useMobile(); const getRow = (row: ListData, index: number) => { const { label, count, percent } = row; @@ -56,6 +57,7 @@ export function ListTable({ showPercentage={showPercentage} change={renderChange ? renderChange(row, index) : null} currency={currency} + isMobile={isMobile} /> ); }; @@ -99,6 +101,7 @@ const AnimatedRow = ({ animate, showPercentage = true, currency, + isMobile, }) => { const props = useSpring({ width: percent, @@ -117,7 +120,7 @@ const AnimatedRow = ({ gap > - + {label} diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 220987c79..d441bcec1 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -118,6 +118,7 @@ function getCohortQuery(filters: QueryFilters = {}) { (select distinct website_event.session_id from website_event join session on session.session_id = website_event.session_id + and session.website_id = website_event.website_id where website_event.website_id = {{websiteId}} and website_event.created_at between {{cohort_startDate}} and {{cohort_endDate}} ${filterQuery} @@ -165,7 +166,7 @@ function parseFilters(filters: Record, options?: QueryOptions) { return { joinSessionQuery: options?.joinSession || joinSession - ? `inner join session on website_event.session_id = session.session_id` + ? `inner join session on website_event.session_id = session.session_id and website_event.website_id = session.website_id` : '', dateQuery: getDateQuery(filters), filterQuery: getFilterQuery(filters, options), @@ -225,8 +226,8 @@ async function pagedQuery(model: string, criteria: T, filters?: QueryFilters) async function pagedRawQuery( query: string, - filters: QueryFilters, queryParams: Record, + filters: QueryFilters, name?: string, ) { const { page = 1, pageSize, orderBy, sortDescending = false } = filters; diff --git a/src/queries/sql/events/getEventData.ts b/src/queries/sql/events/getEventData.ts index 52cb53004..42dc2040b 100644 --- a/src/queries/sql/events/getEventData.ts +++ b/src/queries/sql/events/getEventData.ts @@ -19,17 +19,17 @@ async function relationalQuery(websiteId: string, eventId: string) { return rawQuery( ` - select website_id as websiteId, - session_id as sessionId, - event_id as eventId, - url_path as urlPath, - event_name as eventName, - data_key as dataKey, - string_value as stringValue, - number_value as numberValue, - date_value as dateValue, - data_type as dataType, - created_at as createdAt + select website_id as "websiteId", + session_id as "sessionId", + event_id as "eventId", + url_path as "urlPath", + event_name as "eventName", + data_key as "dataKey", + string_value as "stringValue", + number_value as "numberValue", + date_value as "dateValue", + data_type as "dataType", + created_at as "createdAt" from event_data website_id = {{websiteId::uuid}} event_id = {{eventId::uuid}} diff --git a/src/queries/sql/events/getEventExpandedMetrics.ts b/src/queries/sql/events/getEventExpandedMetrics.ts index c36a829a9..fb200c4ae 100644 --- a/src/queries/sql/events/getEventExpandedMetrics.ts +++ b/src/queries/sql/events/getEventExpandedMetrics.ts @@ -37,7 +37,7 @@ async function relationalQuery( ) { const { type, limit = 500, offset = 0 } = parameters; const column = FILTER_COLUMNS[type] || type; - const { rawQuery, parseFilters } = prisma; + const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; const { filterQuery, cohortQuery, joinSessionQuery, queryParams } = parseFilters( { ...filters, @@ -49,16 +49,31 @@ async function relationalQuery( return rawQuery( ` - select ${column} x, - count(*) as y - from website_event - ${cohortQuery} - ${joinSessionQuery} - where website_event.website_id = {{websiteId::uuid}} - and website_event.created_at between {{startDate}} and {{endDate}} - ${filterQuery} - group by 1 - order by 2 desc + select + name, + 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 then 1 else 0 end) as "bounces", + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" + from ( + select + ${column} name, + website_event.session_id, + website_event.visit_id, + count(*) as "c", + min(website_event.created_at) as "min_time", + max(website_event.created_at) as "max_time" + from website_event + ${cohortQuery} + ${joinSessionQuery} + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + ${filterQuery} + group by name, website_event.session_id, website_event.visit_id + ) as t + group by name + order by visitors desc, visits desc limit ${limit} offset ${offset} `, diff --git a/src/queries/sql/events/getWebsiteEvents.ts b/src/queries/sql/events/getWebsiteEvents.ts index 52113daf5..ef4f300c0 100644 --- a/src/queries/sql/events/getWebsiteEvents.ts +++ b/src/queries/sql/events/getWebsiteEvents.ts @@ -18,7 +18,6 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { const { filterQuery, dateQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId, - search: `%${search}%`, }); const searchQuery = search @@ -29,32 +28,33 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return pagedRawQuery( ` select - event_id as "id", - website_id as "websiteId", - session_id as "sessionId", - created_at as "createdAt", - hostname, - url_path as "urlPath", - url_query as "urlQuery", - referrer_path as "referrerPath", - referrer_query as "referrerQuery", - referrer_domain as "referrerDomain", - country as country, + website_event.event_id as "id", + website_event.website_id as "websiteId", + website_event.session_id as "sessionId", + website_event.created_at as "createdAt", + website_event.hostname, + website_event.url_path as "urlPath", + website_event.url_query as "urlQuery", + website_event.referrer_path as "referrerPath", + website_event.referrer_query as "referrerQuery", + website_event.referrer_domain as "referrerDomain", + session.country as country, city as city, device as device, os as os, browser as browser, page_title as "pageTitle", - event_type as "eventType", - event_name as "eventName" + website_event.event_type as "eventType", + website_event.event_name as "eventName" from website_event ${cohortQuery} - join session on website_event.session_id = session.session_id - where website_id = {{websiteId::uuid}} + join session on session.session_id = website_event.session_id + and session.website_id = website_event.website_id + where website_event.website_id = {{websiteId::uuid}} ${dateQuery} ${filterQuery} ${searchQuery} - order by created_at desc + order by website_event.created_at desc `, queryParams, filters, diff --git a/src/queries/sql/getActiveVisitors.ts b/src/queries/sql/getActiveVisitors.ts index 693efd63c..20327424d 100644 --- a/src/queries/sql/getActiveVisitors.ts +++ b/src/queries/sql/getActiveVisitors.ts @@ -18,7 +18,7 @@ async function relationalQuery(websiteId: string) { const result = await rawQuery( ` - select count(distinct session_id) as visitors + select count(distinct session_id) as "visitors" from website_event where website_id = {{websiteId::uuid}} and created_at >= {{startDate}} diff --git a/src/queries/sql/getChannelExpandedMetrics.ts b/src/queries/sql/getChannelExpandedMetrics.ts index 571c9d9d8..5ce884ad0 100644 --- a/src/queries/sql/getChannelExpandedMetrics.ts +++ b/src/queries/sql/getChannelExpandedMetrics.ts @@ -40,7 +40,7 @@ async function relationalQuery( websiteId: string, filters: QueryFilters, ): Promise { - const { rawQuery, parseFilters } = prisma; + const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; const { queryParams, filterQuery, joinSessionQuery, cohortQuery, dateQuery } = parseFilters({ ...filters, websiteId, @@ -48,40 +48,70 @@ async function relationalQuery( return rawQuery( ` - WITH channels as ( - select case when ${toPostgresPositionClause('utm_medium', ['cp', 'ppc', 'retargeting', 'paid'])} then 'paid' else 'organic' end prefix, - case - when referrer_domain = '' and url_query = '' then 'direct' - when ${toPostgresPositionClause('url_query', PAID_AD_PARAMS)} then 'paidAds' - when ${toPostgresPositionClause('utm_medium', ['referral', 'app', 'link'])} then 'referral' - when position(utm_medium, 'affiliate') > 0 then 'affiliate' - when position(utm_medium, 'sms') > 0 or position(utm_source, 'sms') > 0 then 'sms' - when ${toPostgresPositionClause('referrer_domain', SEARCH_DOMAINS)} or position(utm_medium, 'organic') > 0 then concat(prefix, 'Search') - when ${toPostgresPositionClause('referrer_domain', SOCIAL_DOMAINS)} then concat(prefix, 'Social') - when ${toPostgresPositionClause('referrer_domain', EMAIL_DOMAINS)} or position(utm_medium, 'mail') > 0 then 'email' - when ${toPostgresPositionClause('referrer_domain', SHOPPING_DOMAINS)} or position(utm_medium, 'shop') > 0 then concat(prefix, 'Shopping') - when ${toPostgresPositionClause('referrer_domain', VIDEO_DOMAINS)} or position(utm_medium, 'video') > 0 then concat(prefix, 'Video') - else '' end AS x, - count(distinct session_id) y - from website_event - ${cohortQuery} - ${joinSessionQuery} - where website_event.website_id = {{websiteId::uuid}} - and website_event.event_type != 2 - ${dateQuery} - ${filterQuery} - group by 1, 2 - order by y desc) - - select x, sum(y) y - from channels - where x != '' - group by x - order by y desc; - `, + WITH prefix AS ( + select case when website_event.utm_medium LIKE 'p%' OR + website_event.utm_medium LIKE '%ppc%' OR + website_event.utm_medium LIKE '%retargeting%' OR + website_event.utm_medium LIKE '%paid%' then 'paid' else 'organic' end prefix, + website_event.referrer_domain, + website_event.url_query, + website_event.utm_medium, + website_event.utm_source, + website_event.session_id, + website_event.visit_id, + count(*) c, + min(website_event.created_at) min_time, + max(website_event.created_at) max_time + from website_event + ${cohortQuery} + ${joinSessionQuery} + where website_event.website_id = {{websiteId::uuid}} + and website_event.event_type != 2 + ${dateQuery} + ${filterQuery} + group by prefix, + website_event.referrer_domain, + website_event.url_query, + website_event.utm_medium, + website_event.utm_source, + website_event.session_id, + website_event.visit_id), + + channels as ( + select case + when referrer_domain = '' and url_query = '' then 'direct' + when ${toPostgresPositionClause('url_query', PAID_AD_PARAMS)} then 'paidAds' + when ${toPostgresPositionClause('utm_medium', ['referral', 'app', 'link'])} then 'referral' + when utm_medium ilike '%affiliate%' then 'affiliate' + when utm_medium ilike '%sms%' or utm_source ilike '%sms%' then 'sms' + when ${toPostgresPositionClause('referrer_domain', SEARCH_DOMAINS)} or utm_medium ilike '%organic%' then concat(prefix, 'Search') + when ${toPostgresPositionClause('referrer_domain', SOCIAL_DOMAINS)} then concat(prefix, 'Social') + when ${toPostgresPositionClause('referrer_domain', EMAIL_DOMAINS)} or utm_medium ilike '%mail%' then 'email' + when ${toPostgresPositionClause('referrer_domain', SHOPPING_DOMAINS)} or utm_medium ilike '%shop%' then concat(prefix, 'Shopping') + when ${toPostgresPositionClause('referrer_domain', VIDEO_DOMAINS)} or utm_medium ilike '%video%' then concat(prefix, 'Video') + else '' end AS name, + session_id, + visit_id, + c, + min_time, + max_time + from prefix) + + select + name, + sum(c) as "pageviews", + count(distinct session_id) as "visitors", + count(distinct visit_id) as "visits", + sum(case when c = 1 then 1 else 0 end) as "bounces", + sum(${getTimestampDiffSQL('min_time', 'max_time')}) as "totaltime" + from channels + where name != '' + group by name + order by visitors desc, visits desc + `, queryParams, FUNCTION_NAME, - ); + ).then(results => results.map(item => ({ ...item, y: Number(item.y) }))); } async function clickhouseQuery( @@ -156,5 +186,5 @@ function toClickHouseStringArray(arr: string[]): string { } function toPostgresPositionClause(column: string, arr: string[]) { - return arr.map(val => `position(${column}, '${val.replace(/'/g, "''")}') > 0`).join(' OR\n '); + return arr.map(val => `${column} ilike '%${val.replace(/'/g, "''")}%'`).join(' OR\n '); } diff --git a/src/queries/sql/getChannelMetrics.ts b/src/queries/sql/getChannelMetrics.ts index 5fc9187e6..a608ec5ef 100644 --- a/src/queries/sql/getChannelMetrics.ts +++ b/src/queries/sql/getChannelMetrics.ts @@ -29,29 +29,40 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` - WITH channels as ( - select case when ${toPostgresPositionClause('utm_medium', ['cp', 'ppc', 'retargeting', 'paid'])} then 'paid' else 'organic' end prefix, - case - when referrer_domain = '' and url_query = '' then 'direct' - when ${toPostgresPositionClause('url_query', PAID_AD_PARAMS)} then 'paidAds' - when ${toPostgresPositionClause('utm_medium', ['referral', 'app', 'link'])} then 'referral' - when position(utm_medium, 'affiliate') > 0 then 'affiliate' - when position(utm_medium, 'sms') > 0 or position(utm_source, 'sms') > 0 then 'sms' - when ${toPostgresPositionClause('referrer_domain', SEARCH_DOMAINS)} or position(utm_medium, 'organic') > 0 then concat(prefix, 'Search') - when ${toPostgresPositionClause('referrer_domain', SOCIAL_DOMAINS)} then concat(prefix, 'Social') - when ${toPostgresPositionClause('referrer_domain', EMAIL_DOMAINS)} or position(utm_medium, 'mail') > 0 then 'email' - when ${toPostgresPositionClause('referrer_domain', SHOPPING_DOMAINS)} or position(utm_medium, 'shop') > 0 then concat(prefix, 'Shopping') - when ${toPostgresPositionClause('referrer_domain', VIDEO_DOMAINS)} or position(utm_medium, 'video') > 0 then concat(prefix, 'Video') - else '' end AS x, - count(distinct session_id) y + WITH prefix AS ( + select case when website_event.utm_medium LIKE 'p%' OR + website_event.utm_medium LIKE '%ppc%' OR + website_event.utm_medium LIKE '%retargeting%' OR + website_event.utm_medium LIKE '%paid%' then 'paid' else 'organic' end prefix, + website_event.referrer_domain, + website_event.url_query, + website_event.utm_medium, + website_event.utm_source, + website_event.session_id from website_event ${cohortQuery} ${joinSessionQuery} where website_event.website_id = {{websiteId::uuid}} and website_event.event_type != 2 ${dateQuery} - ${filterQuery} - group by 1, 2 + ${filterQuery}), + + channels as ( + select case + when referrer_domain = '' and url_query = '' then 'direct' + when ${toPostgresLikeClause('url_query', PAID_AD_PARAMS)} then 'paidAds' + when ${toPostgresLikeClause('utm_medium', ['referral', 'app', 'link'])} then 'referral' + when utm_medium ilike '%affiliate%' then 'affiliate' + when utm_medium ilike '%sms%' or utm_source ilike '%sms%' then 'sms' + when ${toPostgresLikeClause('referrer_domain', SEARCH_DOMAINS)} or utm_medium ilike '%organic%' then concat(prefix, 'Search') + when ${toPostgresLikeClause('referrer_domain', SOCIAL_DOMAINS)} then concat(prefix, 'Social') + when ${toPostgresLikeClause('referrer_domain', EMAIL_DOMAINS)} or utm_medium ilike '%mail%' then 'email' + when ${toPostgresLikeClause('referrer_domain', SHOPPING_DOMAINS)} or utm_medium ilike '%shop%' then concat(prefix, 'Shopping') + when ${toPostgresLikeClause('referrer_domain', VIDEO_DOMAINS)} or utm_medium ilike '%video%' then concat(prefix, 'Video') + else '' end AS x, + count(distinct session_id) y + from prefix + group by 1 order by y desc) select x, sum(y) y @@ -62,7 +73,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { `, queryParams, FUNCTION_NAME, - ); + ).then(results => results.map(item => ({ ...item, y: Number(item.y) }))); } async function clickhouseQuery( @@ -126,6 +137,6 @@ function toClickHouseStringArray(arr: string[]): string { return arr.map(p => `'${p.replace(/'/g, "\\'")}'`).join(', '); } -function toPostgresPositionClause(column: string, arr: string[]) { - return arr.map(val => `position(${column}, '${val.replace(/'/g, "''")}') > 0`).join(' OR\n '); +function toPostgresLikeClause(column: string, arr: string[]) { + return arr.map(val => `${column} ilike '%${val.replace(/'/g, "''")}%'`).join(' OR\n '); } diff --git a/src/queries/sql/getRealtimeActivity.ts b/src/queries/sql/getRealtimeActivity.ts index ec9a104fe..d7aa06e93 100644 --- a/src/queries/sql/getRealtimeActivity.ts +++ b/src/queries/sql/getRealtimeActivity.ts @@ -35,6 +35,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ${cohortQuery} inner join session on session.session_id = website_event.session_id + and session.website_id = website_event.website_id where website_event.website_id = {{websiteId::uuid}} ${filterQuery} ${dateQuery} diff --git a/src/queries/sql/getValues.ts b/src/queries/sql/getValues.ts index 426731560..6251fe3b8 100644 --- a/src/queries/sql/getValues.ts +++ b/src/queries/sql/getValues.ts @@ -51,6 +51,7 @@ async function relationalQuery(websiteId: string, column: string, filters: Query from website_event inner join session on session.session_id = website_event.session_id + and session.website_id = website_event.website_id where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} ${searchQuery} diff --git a/src/queries/sql/getWebsiteDateRange.ts b/src/queries/sql/getWebsiteDateRange.ts index 2f7f9f190..58d8ab25e 100644 --- a/src/queries/sql/getWebsiteDateRange.ts +++ b/src/queries/sql/getWebsiteDateRange.ts @@ -20,8 +20,8 @@ async function relationalQuery(websiteId: string) { const result = await rawQuery( ` select - min(created_at) as startDate, - max(created_at) as endDate + min(created_at) as "startDate", + max(created_at) as "endDate" from website_event where website_id = {{websiteId::uuid}} and created_at >= {{startDate}} diff --git a/src/queries/sql/getWeeklyTraffic.ts b/src/queries/sql/getWeeklyTraffic.ts index 2ea370670..ea2029935 100644 --- a/src/queries/sql/getWeeklyTraffic.ts +++ b/src/queries/sql/getWeeklyTraffic.ts @@ -24,13 +24,13 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { return rawQuery( ` select - ${getDateWeeklySQL('created_at', timezone)} as time, - count(distinct session_id) as value + ${getDateWeeklySQL('website_event.created_at', timezone)} as time, + count(distinct website_event.session_id) as value from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} ${filterQuery} group by time order by 2 diff --git a/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts b/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts index a718c4113..2e234ab17 100644 --- a/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts +++ b/src/queries/sql/pageviews/getPageviewExpandedMetrics.ts @@ -36,8 +36,8 @@ async function relationalQuery( filters: QueryFilters, ): Promise { const { type, limit = 500, offset = 0 } = parameters; - const column = FILTER_COLUMNS[type] || type; - const { rawQuery, parseFilters } = prisma; + let column = FILTER_COLUMNS[type] || type; + const { rawQuery, parseFilters, getTimestampDiffSQL } = prisma; const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( { ...filters, @@ -52,6 +52,9 @@ async function relationalQuery( if (column === 'referrer_domain') { excludeDomain = `and website_event.referrer_domain != website_event.hostname and website_event.referrer_domain != ''`; + if (type === 'domain') { + column = toPostgresGroupedReferrer(GROUPED_DOMAINS); + } } if (type === 'entry' || type === 'exit') { @@ -74,19 +77,35 @@ async function relationalQuery( return rawQuery( ` - select ${column} x, - count(distinct website_event.session_id) as y - from website_event - ${cohortQuery} - ${joinSessionQuery} - ${entryExitQuery} - where website_event.website_id = {{websiteId::uuid}} + select + name, + 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 then 1 else 0 end) as "bounces", + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" + from ( + select + ${column} name, + website_event.session_id, + website_event.visit_id, + count(*) as "c", + min(website_event.created_at) as "min_time", + max(website_event.created_at) as "max_time" + from website_event + ${cohortQuery} + ${joinSessionQuery} + ${entryExitQuery} + where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} and website_event.event_type != 2 - ${excludeDomain} - ${filterQuery} - group by 1 - order by 2 desc + ${excludeDomain} + ${filterQuery} + group by name, website_event.session_id, website_event.visit_id + ) as t + where name != '' + group by name + order by visitors desc, visits desc limit ${limit} offset ${offset} `, @@ -186,3 +205,23 @@ export function toClickHouseGroupedReferrer( 'END', ].join('\n'); } + +export function toPostgresGroupedReferrer( + domains: any[], + column: string = 'referrer_domain', +): string { + return [ + 'CASE', + ...domains.map(group => { + const matches = Array.isArray(group.match) ? group.match : [group.match]; + + return `WHEN ${toPostgresLikeClause(column, matches)} THEN '${group.domain}'`; + }), + " ELSE 'Other'", + 'END', + ].join('\n'); +} + +function toPostgresLikeClause(column: string, arr: string[]) { + return arr.map(val => `${column} ilike '%${val.replace(/'/g, "''")}%'`).join(' OR\n '); +} diff --git a/src/queries/sql/reports/getAttribution.ts b/src/queries/sql/reports/getAttribution.ts index 3db2ff087..9277bbf7b 100644 --- a/src/queries/sql/reports/getAttribution.ts +++ b/src/queries/sql/reports/getAttribution.ts @@ -69,14 +69,14 @@ async function relationalQuery( const eventQuery = `WITH events AS ( select distinct - session_id, - max(created_at) max_dt + website_event.session_id, + max(website_event.created_at) max_dt from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} - and ${column} = {{step}} + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + and website_event.${column} = {{step}} ${filterQuery} group by 1),`; @@ -234,14 +234,14 @@ async function relationalQuery( ` select count(*) as "pageviews", - count(distinct session_id) as "visitors", - count(distinct visit_id) as "visits" + count(distinct website_event.session_id) as "visitors", + count(distinct website_event.visit_id) as "visits" from website_event ${joinSessionQuery} ${cohortQuery} - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} - and ${column} = {{step}} + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + and website_event.${column} = {{step}} ${filterQuery} `, queryParams, diff --git a/src/queries/sql/reports/getFunnel.ts b/src/queries/sql/reports/getFunnel.ts index a56439dbc..ada5eeb2d 100644 --- a/src/queries/sql/reports/getFunnel.ts +++ b/src/queries/sql/reports/getFunnel.ts @@ -67,12 +67,12 @@ async function relationalQuery( if (levelNumber === 1) { pv.levelOneQuery = ` WITH level1 AS ( - select distinct session_id, created_at + select distinct website_event.session_id, website_event.created_at from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} and ${column} ${operator} {{${i}}} ${filterQuery} )`; diff --git a/src/queries/sql/reports/getGoal.ts b/src/queries/sql/reports/getGoal.ts index 81ec80abe..702c160ab 100644 --- a/src/queries/sql/reports/getGoal.ts +++ b/src/queries/sql/reports/getGoal.ts @@ -42,26 +42,26 @@ async function relationalQuery( return rawQuery( ` - select count(*) as num, + select count(distinct website_event.session_id) as num, ( - select count(distinct session_id) + select count(distinct website_event.session_id) from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} ${dateQuery} ${filterQuery} ) as total from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} + where website_event.website_id = {{websiteId::uuid}} and ${column} = {{value}} ${dateQuery} ${filterQuery} `, queryParams, - ); + ).then(results => results?.[0]); } async function clickhouseQuery( @@ -84,7 +84,7 @@ async function clickhouseQuery( return rawQuery( ` - select count(*) as num, + select count(distinct session_id) as num, ( select count(distinct session_id) from website_event diff --git a/src/queries/sql/reports/getJourney.ts b/src/queries/sql/reports/getJourney.ts index 3ed4acc95..8d1a90d45 100644 --- a/src/queries/sql/reports/getJourney.ts +++ b/src/queries/sql/reports/getJourney.ts @@ -117,16 +117,16 @@ async function relationalQuery( ` WITH events AS ( select distinct - visit_id, - referrer_path, - coalesce(nullIf(event_name, ''), url_path) event, - row_number() OVER (PARTITION BY visit_id ORDER BY created_at) AS event_number + website_event.visit_id, + website_event.referrer_path, + coalesce(nullIf(website_event.event_name, ''), website_event.url_path) event, + row_number() OVER (PARTITION BY visit_id ORDER BY website_event.created_at) AS event_number from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}}), - ${filterQuery} + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + ${filterQuery}), ${sequenceQuery} select * from sequences diff --git a/src/queries/sql/reports/getUTM.ts b/src/queries/sql/reports/getUTM.ts index 61a3c0426..5dee7a7b0 100644 --- a/src/queries/sql/reports/getUTM.ts +++ b/src/queries/sql/reports/getUTM.ts @@ -37,13 +37,13 @@ async function relationalQuery( return rawQuery( ` - select ${column} utm, count(*) as views + select website_event.${column} utm, count(*) as views from website_event ${cohortQuery} ${joinSessionQuery} - where website_id = {{websiteId::uuid}} - and created_at between {{startDate}} and {{endDate}} - and coalesce(${column}, '') != '' + where website_event.website_id = {{websiteId::uuid}} + and website_event.created_at between {{startDate}} and {{endDate}} + and coalesce(website_event.${column}, '') != '' ${filterQuery} group by 1 order by 2 desc diff --git a/src/queries/sql/sessions/getSessionActivity.ts b/src/queries/sql/sessions/getSessionActivity.ts index 6b79338ee..360db530a 100644 --- a/src/queries/sql/sessions/getSessionActivity.ts +++ b/src/queries/sql/sessions/getSessionActivity.ts @@ -21,20 +21,23 @@ async function relationalQuery(websiteId: string, sessionId: string, filters: Qu return rawQuery( ` select - created_at as createdAt, - url_path as urlPath, - url_query as urlQuery, - referrer_domain as referrerDomain, - event_id as eventId, - event_type as eventType, - event_name as eventName, - visit_id as visitId, - event_id IN (SELECT event_id FROM event_data) AS hasData - from website_event e - where e.website_id = {websiteId:UUID} - and e.session_id = {sessionId:UUID} - and e.created_at between {startDate:DateTime64} and {endDate:DateTime64} - order by e.created_at desc + created_at as "createdAt", + url_path as "urlPath", + url_query as "urlQuery", + referrer_domain as "referrerDomain", + event_id as "eventId", + event_type as "eventType", + event_name as "eventName", + visit_id as "visitId", + event_id IN (select event_id + from event_data + where website_id = {{websiteId::uuid}} + and session_id = {{sessionId::uuid}}) AS "hasData" + from website_event + where website_id = {{websiteId::uuid}} + and session_id = {{sessionId::uuid}} + and created_at between {{startDate}} and {{endDate}} + order by created_at desc limit 500 `, { websiteId, sessionId, startDate, endDate }, diff --git a/src/queries/sql/sessions/getSessionDataProperties.ts b/src/queries/sql/sessions/getSessionDataProperties.ts index 78e4cba4a..96cc17bf6 100644 --- a/src/queries/sql/sessions/getSessionDataProperties.ts +++ b/src/queries/sql/sessions/getSessionDataProperties.ts @@ -31,6 +31,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { ${joinSessionQuery} join session_data on session_data.session_id = website_event.session_id + and session_data.website_id = website_event.website_id where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} ${filterQuery} diff --git a/src/queries/sql/sessions/getSessionDataValues.ts b/src/queries/sql/sessions/getSessionDataValues.ts index efc85090b..0093575d8 100644 --- a/src/queries/sql/sessions/getSessionDataValues.ts +++ b/src/queries/sql/sessions/getSessionDataValues.ts @@ -38,6 +38,7 @@ async function relationalQuery( ${joinSessionQuery} join session_data on session_data.session_id = website_event.session_id + and session_data.website_id = website_event.website_id where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} and session_data.data_key = {{propertyName}} diff --git a/src/queries/sql/sessions/getSessionExpandedMetrics.ts b/src/queries/sql/sessions/getSessionExpandedMetrics.ts index 3adfd3464..1d2111377 100644 --- a/src/queries/sql/sessions/getSessionExpandedMetrics.ts +++ b/src/queries/sql/sessions/getSessionExpandedMetrics.ts @@ -37,7 +37,7 @@ async function relationalQuery( ): Promise { const { type, limit = 500, offset = 0 } = parameters; let column = FILTER_COLUMNS[type] || type; - const { parseFilters, rawQuery } = prisma; + const { parseFilters, rawQuery, getTimestampDiffSQL } = prisma; const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters( { ...filters, @@ -55,20 +55,36 @@ async function relationalQuery( return rawQuery( ` - select - ${column} x, - count(distinct website_event.session_id) y + select + name, + ${includeCountry ? 'country,' : ''} + 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 then 1 else 0 end) as "bounces", + sum(${getTimestampDiffSQL('t.min_time', 't.max_time')}) as "totaltime" + from ( + select + ${column} name, + ${includeCountry ? 'country,' : ''} + website_event.session_id, + website_event.visit_id, + count(*) as "c", + min(website_event.created_at) as "min_time", + max(website_event.created_at) as "max_time" + from website_event + ${cohortQuery} + ${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 name, website_event.session_id, website_event.visit_id ${includeCountry ? ', country' : ''} - from website_event - ${cohortQuery} - ${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 - ${includeCountry ? ', 3' : ''} - order by 2 desc + ) as t + group by name + ${includeCountry ? ', country' : ''} + order by visitors desc, visits desc limit ${limit} offset ${offset} `, diff --git a/src/queries/sql/sessions/getWebsiteSessionStats.ts b/src/queries/sql/sessions/getWebsiteSessionStats.ts index be90d4cd2..2e824f0d0 100644 --- a/src/queries/sql/sessions/getWebsiteSessionStats.ts +++ b/src/queries/sql/sessions/getWebsiteSessionStats.ts @@ -44,6 +44,7 @@ async function relationalQuery( from website_event ${cohortQuery} join session on website_event.session_id = session.session_id + and website_event.website_id = session.website_id where website_event.website_id = {{websiteId::uuid}} and website_event.created_at between {{startDate}} and {{endDate}} ${filterQuery} diff --git a/src/queries/sql/sessions/getWebsiteSessions.ts b/src/queries/sql/sessions/getWebsiteSessions.ts index 302deca6e..5ebd1740b 100644 --- a/src/queries/sql/sessions/getWebsiteSessions.ts +++ b/src/queries/sql/sessions/getWebsiteSessions.ts @@ -52,6 +52,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) { from website_event ${cohortQuery} join session on session.session_id = website_event.session_id + and session.website_id = website_event.website_id where website_event.website_id = {{websiteId::uuid}} ${dateQuery} ${filterQuery}