diff --git a/src/components/hooks/queries/useWebsiteListChartsQuery.ts b/src/components/hooks/queries/useWebsiteListChartsQuery.ts index 967d29bf6..89aec198e 100644 --- a/src/components/hooks/queries/useWebsiteListChartsQuery.ts +++ b/src/components/hooks/queries/useWebsiteListChartsQuery.ts @@ -12,7 +12,7 @@ function useListChartsQuery(queryKey: string, path: string, idsInput: string[]) const { timezone, canonicalizeTimezone } = useTimezone(); const ids = useMemo( - () => Array.from(new Set(idsInput.map(id => id?.trim()).filter(Boolean))), + () => Array.from(new Set(idsInput.map(id => id?.trim()).filter(Boolean))).sort(), [idsInput], ); const resolvedTimezone = canonicalizeTimezone(timezone); @@ -25,6 +25,7 @@ function useListChartsQuery(queryKey: string, path: string, idsInput: string[]) timezone: resolvedTimezone, }), enabled: ids.length > 0, + staleTime: 5 * 60 * 1000, }); } diff --git a/src/queries/sql/getWebsiteListCharts.ts b/src/queries/sql/getWebsiteListCharts.ts index e97e70778..ac7a424a6 100644 --- a/src/queries/sql/getWebsiteListCharts.ts +++ b/src/queries/sql/getWebsiteListCharts.ts @@ -11,15 +11,10 @@ const BUCKET_HOURS = 12; interface WebsiteListChartPoint { websiteId: string; - x: string; + x: string | null; y: number; } -interface WebsiteListChartTotal { - websiteId: string; - total: number; -} - export interface WebsiteListChartData { values: number[]; total: number; @@ -45,20 +40,14 @@ export async function getWebsiteListCharts( return runQuery({ [PRISMA]: async () => { - const [points, totals] = await Promise.all([ - relationalQuery(websiteIds, startDate, endDate, timezone, eventType), - relationalTotalsQuery(websiteIds, startDate, endDate, eventType), - ]); + const points = await relationalQuery(websiteIds, startDate, endDate, timezone, eventType); - return formatResults({ points, totals, websiteIds, startDate, endDate, timezone }); + return formatResults({ points, websiteIds, startDate, endDate, timezone }); }, [CLICKHOUSE]: async () => { - const [points, totals] = await Promise.all([ - clickhouseQuery(websiteIds, startDate, endDate, timezone, eventType), - clickhouseTotalsQuery(websiteIds, startDate, endDate, eventType), - ]); + const points = await clickhouseQuery(websiteIds, startDate, endDate, timezone, eventType); - return formatResults({ points, totals, websiteIds, startDate, endDate, timezone }); + return formatResults({ points, websiteIds, startDate, endDate, timezone }); }, }); } @@ -95,14 +84,20 @@ async function relationalQuery( return rawQuery( ` select - website_event.website_id as "websiteId", - ${bucketSql} as x, - count(distinct website_event.session_id) as y - from website_event - where website_event.website_id = any({{websiteIds}}::uuid[]) - ${eventTypeQuery} - and website_event.created_at between {{startDate}} and {{endDate}} - group by 1, 2 + "websiteId", + x, + count(distinct session_id) as y + from ( + select + website_event.website_id as "websiteId", + ${bucketSql} as x, + website_event.session_id + from website_event + where website_event.website_id = any({{websiteIds}}::uuid[]) + ${eventTypeQuery} + and website_event.created_at between {{startDate}} and {{endDate}} + ) as events + group by grouping sets (("websiteId", x), ("websiteId")) order by 1, 2 `, { websiteIds, startDate, endDate }, @@ -110,35 +105,6 @@ async function relationalQuery( ); } -async function relationalTotalsQuery( - websiteIds: string[], - startDate: Date, - endDate: Date, - eventType?: number, -): Promise { - const { rawQuery } = prisma; - const eventTypeQuery = - eventType != null - ? `and website_event.event_type = ${eventType}` - : `and website_event.event_type NOT IN (${EVENT_TYPE.customEvent}, ${EVENT_TYPE.performance})`; - - return rawQuery( - ` - select - website_event.website_id as "websiteId", - count(distinct website_event.session_id) as total - from website_event - where website_event.website_id = any({{websiteIds}}::uuid[]) - ${eventTypeQuery} - and website_event.created_at between {{startDate}} and {{endDate}} - group by 1 - order by 1 - `, - { websiteIds, startDate, endDate }, - FUNCTION_NAME, - ); -} - async function clickhouseQuery( websiteIds: string[], startDate: Date, @@ -169,7 +135,7 @@ async function clickhouseQuery( where website_id in {websiteIds:Array(UUID)} ${eventTypeQuery} and created_at between {startDate:DateTime64} and {endDate:DateTime64} - group by website_id, x + group by grouping sets ((website_id, x), (website_id)) order by websiteId, x `, { websiteIds, startDate, endDate }, @@ -177,46 +143,15 @@ async function clickhouseQuery( ); } -async function clickhouseTotalsQuery( - websiteIds: string[], - startDate: Date, - endDate: Date, - eventType?: number, -): Promise { - const { rawQuery } = clickhouse; - const eventTypeQuery = - eventType != null - ? `and event_type = ${eventType}` - : `and event_type NOT IN (${EVENT_TYPE.customEvent}, ${EVENT_TYPE.performance})`; - - return rawQuery( - ` - select - website_id as websiteId, - uniq(session_id) as total - from website_event_stats_hourly as website_event - where website_id in {websiteIds:Array(UUID)} - ${eventTypeQuery} - and created_at between {startDate:DateTime64} and {endDate:DateTime64} - group by website_id - order by websiteId - `, - { websiteIds, startDate, endDate }, - FUNCTION_NAME, - ); -} - function formatResults( { points, - totals, websiteIds, startDate, endDate, timezone, }: { points: WebsiteListChartPoint[]; - totals: WebsiteListChartTotal[]; websiteIds: string[]; startDate: Date; endDate: Date; @@ -243,18 +178,23 @@ function formatResults( }, {}); points.forEach(({ websiteId, x, y }) => { + if (!charts[websiteId]) { + return; + } + + // Subtotal rows from GROUPING SETS (null x on Postgres, '' on ClickHouse) + // carry the window-wide distinct total per website + if (!x) { + charts[websiteId].total = Number(y); + return; + } + const index = bucketIndex.get(String(x).slice(0, 19)); - if (index !== undefined && charts[websiteId]) { + if (index !== undefined) { charts[websiteId].values[index] = Number(y); } }); - totals.forEach(({ websiteId, total }) => { - if (charts[websiteId]) { - charts[websiteId].total = Number(total); - } - }); - return charts; }