mirror of
https://github.com/umami-software/umami.git
synced 2026-02-11 16:17:13 +01:00
Added session data display.
This commit is contained in:
parent
f32bf0a2e0
commit
b3e6e52473
23 changed files with 239 additions and 34 deletions
126
src/queries/analytics/events/getEventDataEvents.ts
Normal file
126
src/queries/analytics/events/getEventDataEvents.ts
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import prisma from 'lib/prisma';
|
||||
import clickhouse from 'lib/clickhouse';
|
||||
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
|
||||
import { QueryFilters, WebsiteEventData } from 'lib/types';
|
||||
|
||||
export async function getEventDataEvents(
|
||||
...args: [websiteId: string, filters: QueryFilters]
|
||||
): Promise<WebsiteEventData[]> {
|
||||
return runQuery({
|
||||
[PRISMA]: () => relationalQuery(...args),
|
||||
[CLICKHOUSE]: () => clickhouseQuery(...args),
|
||||
});
|
||||
}
|
||||
|
||||
async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
||||
const { rawQuery, parseFilters } = prisma;
|
||||
const { event } = filters;
|
||||
const { params } = await parseFilters(websiteId, filters);
|
||||
|
||||
if (event) {
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
website_event.event_name as "eventName",
|
||||
event_data.data_key as "fieldName",
|
||||
event_data.data_type as "dataType",
|
||||
event_data.string_value as "fieldValue",
|
||||
count(*) as "total"
|
||||
from event_data
|
||||
inner join website_event
|
||||
on website_event.event_id = event_data.website_event_id
|
||||
where event_data.website_id = {{websiteId::uuid}}
|
||||
and event_data.created_at between {{startDate}} and {{endDate}}
|
||||
and website_event.event_name = {{event}}
|
||||
group by website_event.event_name, event_data.data_key, event_data.data_type, event_data.string_value
|
||||
order by 1 asc, 2 asc, 3 asc, 5 desc
|
||||
`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
website_event.event_name as "eventName",
|
||||
event_data.data_key as "fieldName",
|
||||
event_data.data_type as "dataType",
|
||||
count(*) as "total"
|
||||
from event_data
|
||||
inner join website_event
|
||||
on website_event.event_id = event_data.website_event_id
|
||||
where event_data.website_id = {{websiteId::uuid}}
|
||||
and event_data.created_at between {{startDate}} and {{endDate}}
|
||||
group by website_event.event_name, event_data.data_key, event_data.data_type
|
||||
order by 1 asc, 2 asc
|
||||
limit 500
|
||||
`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
async function clickhouseQuery(
|
||||
websiteId: string,
|
||||
filters: QueryFilters,
|
||||
): Promise<{ eventName: string; fieldName: string; dataType: number; total: number }[]> {
|
||||
const { rawQuery, parseFilters } = clickhouse;
|
||||
const { event } = filters;
|
||||
const { params } = await parseFilters(websiteId, filters);
|
||||
|
||||
if (event) {
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
event_name as eventName,
|
||||
data_key as fieldName,
|
||||
data_type as dataType,
|
||||
string_value as fieldValue,
|
||||
count(*) as total
|
||||
from event_data
|
||||
where website_id = {websiteId:UUID}
|
||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
and event_name = {event:String}
|
||||
group by data_key, data_type, string_value, event_name
|
||||
order by 1 asc, 2 asc, 3 asc, 5 desc
|
||||
limit 500
|
||||
`,
|
||||
params,
|
||||
).then(result => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return {
|
||||
eventName: a.eventName,
|
||||
fieldName: a.fieldName,
|
||||
dataType: Number(a.dataType),
|
||||
fieldValue: a.fieldValue,
|
||||
total: Number(a.total),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
event_name as eventName,
|
||||
data_key as fieldName,
|
||||
data_type as dataType,
|
||||
count(*) as total
|
||||
from event_data
|
||||
where website_id = {websiteId:UUID}
|
||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
group by data_key, data_type, event_name
|
||||
order by 1 asc, 2 asc
|
||||
limit 500
|
||||
`,
|
||||
params,
|
||||
).then(result => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return {
|
||||
eventName: a.eventName,
|
||||
fieldName: a.fieldName,
|
||||
dataType: Number(a.dataType),
|
||||
total: Number(a.total),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
75
src/queries/analytics/events/getEventDataFields.ts
Normal file
75
src/queries/analytics/events/getEventDataFields.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import prisma from 'lib/prisma';
|
||||
import clickhouse from 'lib/clickhouse';
|
||||
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
|
||||
import { QueryFilters, WebsiteEventData } from 'lib/types';
|
||||
|
||||
export async function getEventDataFields(
|
||||
...args: [websiteId: string, filters: QueryFilters & { field?: string }]
|
||||
): Promise<WebsiteEventData[]> {
|
||||
return runQuery({
|
||||
[PRISMA]: () => relationalQuery(...args),
|
||||
[CLICKHOUSE]: () => clickhouseQuery(...args),
|
||||
});
|
||||
}
|
||||
|
||||
async function relationalQuery(websiteId: string, filters: QueryFilters & { field?: string }) {
|
||||
const { rawQuery, parseFilters } = prisma;
|
||||
const { filterQuery, params } = await parseFilters(websiteId, filters, {
|
||||
columns: { field: 'data_key' },
|
||||
});
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
data_key as "fieldName",
|
||||
data_type as "dataType",
|
||||
string_value as "fieldValue",
|
||||
count(*) as "total"
|
||||
from event_data
|
||||
where website_id = {{websiteId::uuid}}
|
||||
and created_at between {{startDate}} and {{endDate}}
|
||||
${filterQuery}
|
||||
group by data_key, data_type, string_value
|
||||
order by 3 desc, 2 desc, 1 asc
|
||||
limit 500
|
||||
`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
async function clickhouseQuery(
|
||||
websiteId: string,
|
||||
filters: QueryFilters & { field?: string },
|
||||
): Promise<{ fieldName: string; dataType: number; fieldValue: string; total: number }[]> {
|
||||
const { rawQuery, parseFilters } = clickhouse;
|
||||
const { filterQuery, params } = await parseFilters(websiteId, filters, {
|
||||
columns: { field: 'data_key' },
|
||||
});
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
data_key as fieldName,
|
||||
data_type as dataType,
|
||||
string_value as fieldValue,
|
||||
count(*) as total
|
||||
from event_data
|
||||
where website_id = {websiteId:UUID}
|
||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
${filterQuery}
|
||||
group by data_key, data_type, string_value
|
||||
order by 3 desc, 2 desc, 1 asc
|
||||
limit 500
|
||||
`,
|
||||
params,
|
||||
).then(result => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return {
|
||||
fieldName: a.fieldName,
|
||||
dataType: Number(a.dataType),
|
||||
fieldValue: a.fieldValue,
|
||||
total: Number(a.total),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
80
src/queries/analytics/events/getEventDataStats.ts
Normal file
80
src/queries/analytics/events/getEventDataStats.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import prisma from 'lib/prisma';
|
||||
import clickhouse from 'lib/clickhouse';
|
||||
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
|
||||
import { QueryFilters } from 'lib/types';
|
||||
|
||||
export async function getEventDataStats(
|
||||
...args: [websiteId: string, filters: QueryFilters]
|
||||
): Promise<{
|
||||
events: number;
|
||||
fields: number;
|
||||
records: number;
|
||||
}> {
|
||||
return runQuery({
|
||||
[PRISMA]: () => relationalQuery(...args),
|
||||
[CLICKHOUSE]: () => clickhouseQuery(...args),
|
||||
}).then(results => results[0]);
|
||||
}
|
||||
|
||||
async function relationalQuery(websiteId: string, filters: QueryFilters) {
|
||||
const { rawQuery, parseFilters } = prisma;
|
||||
const { filterQuery, params } = await parseFilters(websiteId, filters);
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
count(distinct t.website_event_id) as "events",
|
||||
count(distinct t.data_key) as "fields",
|
||||
sum(t.total) as "records"
|
||||
from (
|
||||
select
|
||||
website_event_id,
|
||||
data_key,
|
||||
count(*) as "total"
|
||||
from event_data
|
||||
where website_id = {{websiteId::uuid}}
|
||||
and created_at between {{startDate}} and {{endDate}}
|
||||
${filterQuery}
|
||||
group by website_event_id, data_key
|
||||
) as t
|
||||
`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
async function clickhouseQuery(
|
||||
websiteId: string,
|
||||
filters: QueryFilters,
|
||||
): Promise<{ events: number; fields: number; records: number }[]> {
|
||||
const { rawQuery, parseFilters } = clickhouse;
|
||||
const { filterQuery, params } = await parseFilters(websiteId, filters);
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
count(distinct t.event_id) as "events",
|
||||
count(distinct t.data_key) as "fields",
|
||||
sum(t.total) as "records"
|
||||
from (
|
||||
select
|
||||
event_id,
|
||||
data_key,
|
||||
count(*) as "total"
|
||||
from event_data
|
||||
where website_id = {websiteId:UUID}
|
||||
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
${filterQuery}
|
||||
group by event_id, data_key
|
||||
) as t
|
||||
`,
|
||||
params,
|
||||
).then(result => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return {
|
||||
events: Number(a.events),
|
||||
fields: Number(a.fields),
|
||||
records: Number(a.records),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
38
src/queries/analytics/events/getEventDataUsage.ts
Normal file
38
src/queries/analytics/events/getEventDataUsage.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import clickhouse from 'lib/clickhouse';
|
||||
import { CLICKHOUSE, PRISMA, runQuery, notImplemented } from 'lib/db';
|
||||
|
||||
export function getEventDataUsage(...args: [websiteIds: string[], startDate: Date, endDate: Date]) {
|
||||
return runQuery({
|
||||
[PRISMA]: notImplemented,
|
||||
[CLICKHOUSE]: () => clickhouseQuery(...args),
|
||||
});
|
||||
}
|
||||
|
||||
function clickhouseQuery(
|
||||
websiteIds: string[],
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
): Promise<{ websiteId: string; count: number }[]> {
|
||||
const { rawQuery } = clickhouse;
|
||||
|
||||
return rawQuery(
|
||||
`
|
||||
select
|
||||
website_id as websiteId,
|
||||
count(*) as count
|
||||
from event_data
|
||||
where created_at between {startDate:DateTime64} and {endDate:DateTime64}
|
||||
and website_id in {websiteIds:Array(UUID)}
|
||||
group by website_id
|
||||
`,
|
||||
{
|
||||
websiteIds,
|
||||
startDate,
|
||||
endDate,
|
||||
},
|
||||
).then(result => {
|
||||
return Object.values(result).map((a: any) => {
|
||||
return { websiteId: a.websiteId, count: Number(a.count) };
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import clickhouse from 'lib/clickhouse';
|
|||
import kafka from 'lib/kafka';
|
||||
import prisma from 'lib/prisma';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { saveEventData } from 'queries/analytics/eventData/saveEventData';
|
||||
import { saveEventData } from './saveEventData';
|
||||
|
||||
export async function saveEvent(args: {
|
||||
websiteId: string;
|
||||
|
|
|
|||
91
src/queries/analytics/events/saveEventData.ts
Normal file
91
src/queries/analytics/events/saveEventData.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { Prisma } from '@prisma/client';
|
||||
import { DATA_TYPE } from 'lib/constants';
|
||||
import { uuid } from 'lib/crypto';
|
||||
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
|
||||
import { flattenJSON, getStringValue } from 'lib/data';
|
||||
import clickhouse from 'lib/clickhouse';
|
||||
import kafka from 'lib/kafka';
|
||||
import prisma from 'lib/prisma';
|
||||
import { DynamicData } from 'lib/types';
|
||||
|
||||
export async function saveEventData(data: {
|
||||
websiteId: string;
|
||||
eventId: string;
|
||||
sessionId?: string;
|
||||
urlPath?: string;
|
||||
eventName?: string;
|
||||
eventData: DynamicData;
|
||||
createdAt?: string;
|
||||
}) {
|
||||
return runQuery({
|
||||
[PRISMA]: () => relationalQuery(data),
|
||||
[CLICKHOUSE]: () => clickhouseQuery(data),
|
||||
});
|
||||
}
|
||||
|
||||
async function relationalQuery(data: {
|
||||
websiteId: string;
|
||||
eventId: string;
|
||||
eventData: DynamicData;
|
||||
}): Promise<Prisma.BatchPayload> {
|
||||
const { websiteId, eventId, eventData } = data;
|
||||
|
||||
const jsonKeys = flattenJSON(eventData);
|
||||
|
||||
// id, websiteEventId, eventStringValue
|
||||
const flattenedData = jsonKeys.map(a => ({
|
||||
id: uuid(),
|
||||
websiteEventId: eventId,
|
||||
websiteId,
|
||||
dataKey: a.key,
|
||||
stringValue: getStringValue(a.value, a.dataType),
|
||||
numberValue: a.dataType === DATA_TYPE.number ? a.value : null,
|
||||
dateValue: a.dataType === DATA_TYPE.date ? new Date(a.value) : null,
|
||||
dataType: a.dataType,
|
||||
}));
|
||||
|
||||
return prisma.client.eventData.createMany({
|
||||
data: flattenedData,
|
||||
});
|
||||
}
|
||||
|
||||
async function clickhouseQuery(data: {
|
||||
websiteId: string;
|
||||
eventId: string;
|
||||
sessionId?: string;
|
||||
urlPath?: string;
|
||||
eventName?: string;
|
||||
eventData: DynamicData;
|
||||
createdAt?: string;
|
||||
}) {
|
||||
const { websiteId, sessionId, eventId, urlPath, eventName, eventData, createdAt } = data;
|
||||
|
||||
const { insert } = clickhouse;
|
||||
const { getDateFormat, sendMessages } = kafka;
|
||||
|
||||
const jsonKeys = flattenJSON(eventData);
|
||||
|
||||
const messages = jsonKeys.map(({ key, value, dataType }) => {
|
||||
return {
|
||||
website_id: websiteId,
|
||||
session_id: sessionId,
|
||||
event_id: eventId,
|
||||
url_path: urlPath,
|
||||
event_name: eventName,
|
||||
data_key: key,
|
||||
data_type: dataType,
|
||||
string_value: getStringValue(value, dataType),
|
||||
number_value: dataType === DATA_TYPE.number ? value : null,
|
||||
date_value: dataType === DATA_TYPE.date ? getDateFormat(value) : null,
|
||||
created_at: createdAt,
|
||||
};
|
||||
});
|
||||
|
||||
if (kafka.enabled) {
|
||||
await sendMessages('event_data', messages);
|
||||
} else {
|
||||
await insert('event_data', messages);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue