diff --git a/db/postgresql/schema.prisma b/db/postgresql/schema.prisma index b2cb4662..b6a7610a 100644 --- a/db/postgresql/schema.prisma +++ b/db/postgresql/schema.prisma @@ -11,7 +11,6 @@ model User { id String @id @unique @map("user_id") @db.Uuid username String @unique @db.VarChar(255) password String @db.VarChar(60) - isAdmin Boolean @default(false) @map("is_admin") createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) groupRole GroupRole[] @@ -20,7 +19,6 @@ model User { teamWebsite TeamWebsite[] teamUser TeamUser[] userWebsite UserWebsite[] - website Website[] @@map("user") } @@ -44,7 +42,6 @@ model Session { model Website { id String @id @unique @map("website_id") @db.Uuid - userId String @map("user_id") @db.Uuid name String @db.VarChar(100) domain String? @db.VarChar(500) shareId String? @unique @map("share_id") @db.VarChar(64) @@ -52,11 +49,9 @@ model Website { createdAt DateTime? @default(now()) @map("created_at") @db.Timestamptz(6) isDeleted Boolean @default(false) @map("is_deleted") - user User @relation(fields: [userId], references: [id]) teamWebsite TeamWebsite[] userWebsite UserWebsite[] - @@index([userId]) @@index([createdAt]) @@index([shareId]) @@map("website") @@ -117,7 +112,7 @@ model GroupUser { isDeleted Boolean @default(false) @map("is_deleted") group Group @relation(fields: [groupId], references: [id]) - User User @relation(fields: [userId], references: [id]) + user User @relation(fields: [userId], references: [id]) @@map("group_user") } diff --git a/queries/analytics/event/getEvents.js b/queries/analytics/event/getEvents.js new file mode 100644 index 00000000..81a187ce --- /dev/null +++ b/queries/analytics/event/getEvents.js @@ -0,0 +1,45 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; + +export function getEvents(...args) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +function relationalQuery(websites, start_at) { + return prisma.client.event.findMany({ + where: { + websiteId: { + in: websites, + }, + createdAt: { + gte: start_at, + }, + }, + }); +} + +function clickhouseQuery(websites, start_at) { + const { rawQuery, getDateFormat, getCommaSeparatedStringFormat } = clickhouse; + + return rawQuery( + `select + event_id, + website_id, + session_id, + created_at, + url, + event_name + from event + where event_name != '' + and ${ + websites && websites.length > 0 + ? `website_id in (${getCommaSeparatedStringFormat(websites)})` + : '0 = 0' + } + and created_at >= ${getDateFormat(start_at)}`, + ); +} diff --git a/queries/analytics/pageview/getPageviews.js b/queries/analytics/pageview/getPageviews.js new file mode 100644 index 00000000..2bf41b0b --- /dev/null +++ b/queries/analytics/pageview/getPageviews.js @@ -0,0 +1,43 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; + +export async function getPageviews(...args) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websites, start_at) { + return prisma.client.pageview.findMany({ + where: { + websiteId: { + in: websites, + }, + createdAt: { + gte: start_at, + }, + }, + }); +} + +async function clickhouseQuery(websites, start_at) { + const { rawQuery, getCommaSeparatedStringFormat } = clickhouse; + + return rawQuery( + `select + website_id, + session_id, + created_at, + url + from event + where event_name = '' + and ${ + websites && websites.length > 0 + ? `website_id in (${getCommaSeparatedStringFormat(websites)})` + : '0 = 0' + } + and created_at >= ${clickhouse.getDateFormat(start_at)}`, + ); +} diff --git a/queries/analytics/session/getSessions.js b/queries/analytics/session/getSessions.js new file mode 100644 index 00000000..c5fed485 --- /dev/null +++ b/queries/analytics/session/getSessions.js @@ -0,0 +1,52 @@ +import prisma from 'lib/prisma'; +import clickhouse from 'lib/clickhouse'; +import { runQuery, PRISMA, CLICKHOUSE } from 'lib/db'; + +export async function getSessions(...args) { + return runQuery({ + [PRISMA]: () => relationalQuery(...args), + [CLICKHOUSE]: () => clickhouseQuery(...args), + }); +} + +async function relationalQuery(websites, start_at) { + return prisma.client.session.findMany({ + where: { + ...(websites && websites.length > 0 + ? { + websiteId: { + in: websites, + }, + } + : {}), + createdAt: { + gte: start_at, + }, + }, + }); +} + +async function clickhouseQuery(websites, start_at) { + const { rawQuery, getDateFormat, getCommaSeparatedStringFormat } = clickhouse; + + return rawQuery( + `select distinct + session_id, + website_id, + created_at, + hostname, + browser, + os, + device, + screen, + language, + country + from event + where ${ + websites && websites.length > 0 + ? `website_id in (${getCommaSeparatedStringFormat(websites)})` + : '0 = 0' + } + and created_at >= ${getDateFormat(start_at)}`, + ); +} diff --git a/queries/analytics/stats/getRealtimeData.ts b/queries/analytics/stats/getRealtimeData.ts index 042bcc46..659f6145 100644 --- a/queries/analytics/stats/getRealtimeData.ts +++ b/queries/analytics/stats/getRealtimeData.ts @@ -1,3 +1,30 @@ -export async function getRealtimeData() { - throw new Error('Not Implemented'); +import { getPageviews } from '../pageview/getPageviews'; +import { getSessions } from '../session/getSessions'; +import { getEvents } from '../event/getEvents'; + +export async function getRealtimeData(websites, time) { + const [pageviews, sessions, events] = await Promise.all([ + getPageviews(websites, time), + getSessions(websites, time), + getEvents(websites, time), + ]); + + return { + pageviews: pageviews.map(({ id, ...props }) => ({ + __id: `p${id}`, + pageviewId: id, + ...props, + })), + sessions: sessions.map(({ id, ...props }) => ({ + __id: `s${id}`, + sessionId: id, + ...props, + })), + events: events.map(({ id, ...props }) => ({ + __id: `e${id}`, + eventId: id, + ...props, + })), + timestamp: Date.now(), + }; }