Merge branch 'dev' of https://github.com/umami-software/umami into feat/um-376-retention-report

This commit is contained in:
Francis Cao 2023-08-04 14:38:44 -07:00
commit cad3dd8847
7 changed files with 56 additions and 40 deletions

View File

@ -4,7 +4,7 @@ import { MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db';
import { FILTER_COLUMNS, IGNORED_FILTERS, SESSION_COLUMNS } from './constants'; import { FILTER_COLUMNS, IGNORED_FILTERS, SESSION_COLUMNS } from './constants';
import { loadWebsite } from './load'; import { loadWebsite } from './load';
import { maxDate } from './date'; import { maxDate } from './date';
import { QueryFilters } from './types'; import { QueryFilters, QueryOptions } from './types';
const MYSQL_DATE_FORMATS = { const MYSQL_DATE_FORMATS = {
minute: '%Y-%m-%d %H:%i:00', minute: '%Y-%m-%d %H:%i:00',
@ -74,12 +74,12 @@ function getFilterQuery(filters = {}): string {
if (filter !== undefined && !IGNORED_FILTERS.includes(key)) { if (filter !== undefined && !IGNORED_FILTERS.includes(key)) {
const column = FILTER_COLUMNS[key] || key; const column = FILTER_COLUMNS[key] || key;
arr.push(`and ${column}={{${key}}}`); arr.push(`and ${column}={{${key}}}`);
}
if (key === 'referrer') { if (key === 'referrer') {
arr.push( arr.push(
'and (website_event.referrer_domain != {{websiteDomain}} or website_event.referrer_domain is null)', 'and (website_event.referrer_domain != {{websiteDomain}} or website_event.referrer_domain is null)',
); );
}
} }
return arr; return arr;
@ -88,13 +88,18 @@ function getFilterQuery(filters = {}): string {
return query.join('\n'); return query.join('\n');
} }
async function parseFilters(websiteId, filters: QueryFilters & { [key: string]: any } = {}) { async function parseFilters(
websiteId,
filters: QueryFilters & { [key: string]: any } = {},
options: QueryOptions = {},
) {
const website = await loadWebsite(websiteId); const website = await loadWebsite(websiteId);
return { return {
joinSession: Object.keys(filters).find(key => SESSION_COLUMNS[key]) joinSession:
? `inner join session on website_event.session_id = session.session_id` options?.joinSession || Object.keys(filters).find(key => SESSION_COLUMNS.includes(key))
: '', ? `inner join session on website_event.session_id = session.session_id`
: '',
filterQuery: getFilterQuery(filters), filterQuery: getFilterQuery(filters),
params: { params: {
...filters, ...filters,

View File

@ -137,11 +137,11 @@ export interface QueryFilters {
unit?: string; unit?: string;
domain?: string; domain?: string;
eventType?: number; eventType?: number;
eventName?: string;
url?: string; url?: string;
referrer?: string; referrer?: string;
title?: string; title?: string;
query?: string; query?: string;
event?: string;
os?: string; os?: string;
browser?: string; browser?: string;
device?: string; device?: string;
@ -150,3 +150,7 @@ export interface QueryFilters {
city?: string; city?: string;
language?: string; language?: string;
} }
export interface QueryOptions {
joinSession?: boolean;
}

View File

@ -43,10 +43,8 @@ export default async (
endDate, endDate,
timezone, timezone,
unit, unit,
filters: { url,
url, eventName,
eventName,
},
}); });
return ok(res, events); return ok(res, events);

View File

@ -5,7 +5,7 @@ import { WebsiteEventMetric, QueryFilters } from 'lib/types';
import { EVENT_TYPE } from 'lib/constants'; import { EVENT_TYPE } from 'lib/constants';
export async function getEventMetrics( export async function getEventMetrics(
...args: [websiteId: string, criteria: QueryFilters] ...args: [websiteId: string, filters: QueryFilters]
): Promise<WebsiteEventMetric[]> { ): Promise<WebsiteEventMetric[]> {
return runQuery({ return runQuery({
[PRISMA]: () => relationalQuery(...args), [PRISMA]: () => relationalQuery(...args),
@ -16,7 +16,7 @@ export async function getEventMetrics(
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 { rawQuery, getDateQuery, parseFilters } = prisma; const { rawQuery, getDateQuery, parseFilters } = prisma;
const { filterQuery, params } = await parseFilters(websiteId, { const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters, ...filters,
eventType: EVENT_TYPE.customEvent, eventType: EVENT_TYPE.customEvent,
}); });
@ -28,6 +28,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
${getDateQuery('created_at', unit, timezone)} t, ${getDateQuery('created_at', unit, timezone)} t,
count(*) y count(*) y
from website_event from website_event
${joinSession}
where website_id = {{websiteId::uuid}} where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}} and created_at between {{startDate}} and {{endDate}}
and event_type = {{eventType}} and event_type = {{eventType}}

View File

@ -1,7 +1,7 @@
import prisma from 'lib/prisma'; import prisma from 'lib/prisma';
import clickhouse from 'lib/clickhouse'; import clickhouse from 'lib/clickhouse';
import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db';
import { EVENT_TYPE } from 'lib/constants'; import { EVENT_TYPE, SESSION_COLUMNS } from 'lib/constants';
import { QueryFilters } from 'lib/types'; import { QueryFilters } from 'lib/types';
export async function getPageviewMetrics( export async function getPageviewMetrics(
@ -15,16 +15,20 @@ export async function getPageviewMetrics(
async function relationalQuery(websiteId: string, column: string, filters: QueryFilters) { async function relationalQuery(websiteId: string, column: string, filters: QueryFilters) {
const { rawQuery, parseFilters } = prisma; const { rawQuery, parseFilters } = prisma;
const { filterQuery, joinSession, params } = await parseFilters(websiteId, { const { filterQuery, joinSession, params } = await parseFilters(
...filters, websiteId,
eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, {
}); ...filters,
eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView,
},
{ joinSession: SESSION_COLUMNS.includes(column) },
);
return rawQuery( return rawQuery(
` `
select ${column} x, count(*) y select ${column} x, count(*) y
from website_event from website_event
${joinSession} ${joinSession}
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 event_type = {{eventType}} and event_type = {{eventType}}

View File

@ -1,7 +1,7 @@
import prisma from 'lib/prisma'; import prisma from 'lib/prisma';
import clickhouse from 'lib/clickhouse'; import clickhouse from 'lib/clickhouse';
import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db';
import { EVENT_TYPE } from 'lib/constants'; import { EVENT_TYPE, SESSION_COLUMNS } from 'lib/constants';
import { QueryFilters } from 'lib/types'; import { QueryFilters } from 'lib/types';
export async function getSessionMetrics( export async function getSessionMetrics(
@ -15,21 +15,26 @@ export async function getSessionMetrics(
async function relationalQuery(websiteId: string, column: string, filters: QueryFilters) { async function relationalQuery(websiteId: string, column: string, filters: QueryFilters) {
const { parseFilters, rawQuery } = prisma; const { parseFilters, rawQuery } = prisma;
const { filterQuery, params } = await parseFilters(websiteId, { const { filterQuery, joinSession, params } = await parseFilters(
...filters, websiteId,
eventType: EVENT_TYPE.pageView, {
}); ...filters,
eventType: EVENT_TYPE.pageView,
},
{
joinSession: SESSION_COLUMNS.includes(column),
},
);
return rawQuery( return rawQuery(
`select ${column} x, count(*) y `
from website_event select ${column} x, count(*) y
inner join session from website_event
on session.session_id = website_event.session_id ${joinSession}
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 = {{eventType}} and website_event.event_type = {{eventType}}
${filterQuery} ${filterQuery}
) as t
group by 1 group by 1
order by 2 desc order by 2 desc
limit 100`, limit 100`,

View File

@ -14,7 +14,7 @@ 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 { getDateQuery, parseFilters, rawQuery } = prisma; const { getDateQuery, parseFilters, rawQuery } = prisma;
const { filterQuery, params } = await parseFilters(websiteId, { const { filterQuery, joinSession, params } = await parseFilters(websiteId, {
...filters, ...filters,
eventType: EVENT_TYPE.pageView, eventType: EVENT_TYPE.pageView,
}); });
@ -25,8 +25,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
${getDateQuery('website_event.created_at', unit, timezone)} x, ${getDateQuery('website_event.created_at', unit, timezone)} x,
count(distinct website_event.session_id) y count(distinct website_event.session_id) y
from website_event from website_event
inner join session ${joinSession}
on session.session_id = website_event.session_id
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 event_type = {{eventType}} and event_type = {{eventType}}