diff --git a/src/app/(main)/reports/insights/InsightsTable.tsx b/src/app/(main)/reports/insights/InsightsTable.tsx
index 2cccc24c..692d7824 100644
--- a/src/app/(main)/reports/insights/InsightsTable.tsx
+++ b/src/app/(main)/reports/insights/InsightsTable.tsx
@@ -3,6 +3,7 @@ import { GridTable, GridColumn } from 'react-basics';
import { useFormat, useMessages } from 'components/hooks';
import { ReportContext } from '../[reportId]/Report';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
+import { formatShortTime } from 'lib/format';
export function InsightsTable() {
const [fields, setFields] = useState([]);
@@ -31,6 +32,12 @@ export function InsightsTable() {
);
})}
+
+ {row => row?.views?.toLocaleString()}
+
+
+ {row => row?.visits?.toLocaleString()}
+
{row => row?.visitors?.toLocaleString()}
-
- {row => row?.views?.toLocaleString()}
+
+ {row => {
+ const n = (Math.min(row?.visits, row?.bounces) / row?.visits) * 100;
+ return Math.round(+n) + '%';
+ }}
+
+
+ {row => {
+ const n = row?.totaltime / row?.visits;
+ return `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`;
+ }}
);
diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts
index e5cb40c0..c1b9d25f 100644
--- a/src/lib/clickhouse.ts
+++ b/src/lib/clickhouse.ts
@@ -119,7 +119,7 @@ async function parseFilters(websiteId: string, filters: QueryFilters = {}, optio
};
}
-async function rawQuery(query: string, params: Record = {}): Promise {
+async function rawQuery(query: string, params: Record = {}): Promise {
if (process.env.LOG_QUERY) {
log('QUERY:\n', query);
log('PARAMETERS:\n', params);
diff --git a/src/queries/analytics/reports/getInsights.ts b/src/queries/analytics/reports/getInsights.ts
index 282ed755..7313e436 100644
--- a/src/queries/analytics/reports/getInsights.ts
+++ b/src/queries/analytics/reports/getInsights.ts
@@ -23,7 +23,7 @@ async function relationalQuery(
y: number;
}[]
> {
- const { parseFilters, rawQuery } = prisma;
+ const { getTimestampDiffQuery, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSession, params } = await parseFilters(
websiteId,
{
@@ -37,15 +37,31 @@ async function relationalQuery(
return rawQuery(
`
- select
+ select
+ sum(t.c) as "views",
+ 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(${getTimestampDiffQuery('t.min_time', 't.max_time')}) as "totaltime",
${parseFields(fields)}
- from website_event
- ${joinSession}
- where website_event.website_id = {{websiteId::uuid}}
- and website_event.created_at between {{startDate}} and {{endDate}}
- and website_event.event_type = {{eventType}}
- ${filterQuery}
- ${parseGroupBy(fields)}
+ from (
+ select
+ ${parseFields(fields)},
+ 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
+ ${joinSession}
+ where website_event.website_id = {{websiteId::uuid}}
+ and website_event.created_at between {{startDate}} and {{endDate}}
+ and event_type = {{eventType}}
+ ${filterQuery}
+ group by ${parseFields(fields)},
+ website_event.session_id, website_event.visit_id
+ ) as t
+ group by ${parseFields(fields)}
order by 1 desc, 2 desc
limit 500
`,
@@ -71,14 +87,30 @@ async function clickhouseQuery(
return rawQuery(
`
- select
+ select
+ sum(t.c) as "views",
+ count(distinct t.session_id) as "visitors",
+ count(distinct t.visit_id) as "visits",
+ sum(if(t.c = 1, 1, 0)) as "bounces",
+ sum(max_time-min_time) as "totaltime",
${parseFields(fields)}
- from website_event
- where website_id = {websiteId:UUID}
- and created_at between {startDate:DateTime64} and {endDate:DateTime64}
- and event_type = {eventType:UInt32}
- ${filterQuery}
- ${parseGroupBy(fields)}
+ from (
+ select
+ ${parseFields(fields)},
+ session_id,
+ visit_id,
+ count(*) c,
+ min(created_at) min_time,
+ max(created_at) max_time
+ from website_event
+ where website_id = {websiteId:UUID}
+ and created_at between {startDate:DateTime64} and {endDate:DateTime64}
+ and event_type = {eventType:UInt32}
+ ${filterQuery}
+ group by ${parseFields(fields)},
+ session_id, visit_id
+ ) as t
+ group by ${parseFields(fields)}
order by 1 desc, 2 desc
limit 500
`,
@@ -89,27 +121,14 @@ async function clickhouseQuery(
...a,
views: Number(a.views),
visitors: Number(a.visitors),
+ visits: Number(a.visits),
+ bounces: Number(a.bounces),
+ totaltime: Number(a.totaltime),
};
});
});
}
-function parseFields(fields: any[]) {
- const query = fields.reduce(
- (arr, field) => {
- const { name } = field;
-
- return arr.concat(`${FILTER_COLUMNS[name]} as "${name}"`);
- },
- ['count(*) as views', 'count(distinct website_event.session_id) as visitors'],
- );
-
- return query.join(',\n');
-}
-
-function parseGroupBy(fields: { name: any }[]) {
- if (!fields.length) {
- return '';
- }
- return `group by ${fields.map(({ name }) => FILTER_COLUMNS[name]).join(',')}`;
+function parseFields(fields: { name: any }[]) {
+ return `${fields.map(({ name }) => FILTER_COLUMNS[name]).join(',')}`;
}