Refactor part 2: Electric Boogaloo. Standardize way of passing filter parameters.

This commit is contained in:
Mike Cao 2025-07-04 01:23:11 -07:00
parent f26f1b0581
commit cdf391d5c2
90 changed files with 867 additions and 709 deletions

View file

@ -23,7 +23,7 @@ export async function getEventDataEvents(
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { rawQuery, parseFilters } = prisma;
const { event } = filters;
const { queryParams } = await parseFilters(filters);
const { queryParams } = parseFilters(filters);
if (event) {
return rawQuery(
@ -73,7 +73,7 @@ async function clickhouseQuery(
): Promise<{ eventName: string; propertyName: string; dataType: number; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { event } = filters;
const { queryParams } = await parseFilters(filters);
const { queryParams } = parseFilters(filters);
if (event) {
return rawQuery(

View file

@ -12,7 +12,7 @@ export async function getEventDataFields(...args: [websiteId: string, filters: Q
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { rawQuery, parseFilters, getDateSQL } = prisma;
const { filterQuery, queryParams } = await parseFilters(filters);
const { filterQuery, queryParams } = parseFilters(filters);
return rawQuery(
`
@ -43,7 +43,7 @@ async function clickhouseQuery(
filters: QueryFilters,
): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters(filters);
const { filterQuery, queryParams } = parseFilters(filters);
return rawQuery(
`

View file

@ -17,7 +17,7 @@ async function relationalQuery(
filters: QueryFilters & { propertyName?: string },
) {
const { rawQuery, parseFilters } = prisma;
const { filterQuery, queryParams } = await parseFilters(filters, {
const { filterQuery, queryParams } = parseFilters(filters, {
columns: { propertyName: 'data_key' },
});
@ -45,7 +45,7 @@ async function clickhouseQuery(
filters: QueryFilters & { propertyName?: string },
): Promise<{ eventName: string; propertyName: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters(filters, {
const { filterQuery, queryParams } = parseFilters(filters, {
columns: { propertyName: 'data_key' },
});

View file

@ -18,7 +18,7 @@ export async function getEventDataStats(
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { rawQuery, parseFilters } = prisma;
const { filterQuery, queryParams } = await parseFilters({ ...filters, websiteId });
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`
@ -47,7 +47,7 @@ async function clickhouseQuery(
filters: QueryFilters,
): Promise<{ events: number; properties: number; records: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({ ...filters, websiteId });
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`

View file

@ -25,7 +25,7 @@ async function relationalQuery(
filters: QueryFilters & { eventName?: string; propertyName?: string },
) {
const { rawQuery, parseFilters, getDateSQL } = prisma;
const { filterQuery, queryParams } = await parseFilters({ ...filters, websiteId });
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`
@ -56,7 +56,7 @@ async function clickhouseQuery(
filters: QueryFilters & { eventName?: string; propertyName?: string },
): Promise<{ value: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({ ...filters, websiteId });
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`

View file

@ -22,8 +22,9 @@ export async function getEventMetrics(
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc', unit = 'day' } = filters;
const { rawQuery, getDateSQL, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters({
const { filterQuery, joinSessionQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.customEvent,
});
@ -49,11 +50,12 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
async function clickhouseQuery(
websiteId: string,
filters: QueryFilters,
): Promise<{ x: string; t: string; y: number }[]> {
): Promise<WebsiteEventMetricData[]> {
const { timezone = 'UTC', unit = 'day' } = filters;
const { rawQuery, getDateSQL, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.customEvent,
});

View file

@ -13,7 +13,7 @@ export function getWebsiteEvents(...args: [websiteId: string, filters: QueryFilt
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { pagedRawQuery, parseFilters } = prisma;
const { search } = filters;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, dateQuery, queryParams } = parseFilters({
...filters,
websiteId,
});
@ -40,7 +40,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
event_name as "eventName"
from website_event
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
${dateQuery}
${filterQuery}
${searchQuery}
order by created_at desc
@ -52,7 +52,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
const { pagedRawQuery, parseFilters } = clickhouse;
const { queryParams, dateQuery, filterQuery } = await parseFilters({
const { queryParams, dateQuery, filterQuery } = parseFilters({
...filters,
websiteId,
});
@ -74,6 +74,10 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
referrer_path as referrerPath,
referrer_query as referrerQuery,
referrer_domain as referrerDomain,
country as country,
device as device,
os as os,
browser as browser,
page_title as pageTitle,
event_type as eventType,
event_name as eventName

View file

@ -12,7 +12,7 @@ export async function getChannelMetrics(...args: [websiteId: string, filters?: Q
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { rawQuery, parseFilters } = prisma;
const { queryParams, filterQuery, dateQuery } = await parseFilters(filters);
const { queryParams, filterQuery, dateQuery } = parseFilters(filters);
return rawQuery(
`
@ -36,7 +36,7 @@ async function clickhouseQuery(
filters: QueryFilters,
): Promise<{ x: string; y: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { queryParams, filterQuery, dateQuery } = await parseFilters(filters);
const { queryParams, filterQuery, dateQuery } = parseFilters(filters);
const sql = `
select

View file

@ -12,7 +12,7 @@ export async function getRealtimeActivity(...args: [websiteId: string, filters:
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { rawQuery, parseFilters } = prisma;
const { queryParams, filterQuery, dateQuery } = await parseFilters(filters);
const { queryParams, filterQuery, dateQuery } = parseFilters(filters);
return rawQuery(
`
@ -41,7 +41,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
async function clickhouseQuery(websiteId: string, filters: QueryFilters): Promise<{ x: number }> {
const { rawQuery, parseFilters } = clickhouse;
const { queryParams, filterQuery, dateQuery } = await parseFilters(filters);
const { queryParams, filterQuery, dateQuery } = parseFilters(filters);
return rawQuery(
`

View file

@ -12,7 +12,7 @@ export async function getWebsiteDateRange(...args: [websiteId: string]) {
async function relationalQuery(websiteId: string) {
const { rawQuery, parseFilters } = prisma;
const { queryParams } = await parseFilters({
const { queryParams } = parseFilters({
startDate: new Date(DEFAULT_RESET_DATE),
websiteId,
});
@ -34,7 +34,7 @@ async function relationalQuery(websiteId: string) {
async function clickhouseQuery(websiteId: string) {
const { rawQuery, parseFilters } = clickhouse;
const { queryParams } = await parseFilters({
const { queryParams } = parseFilters({
startDate: new Date(DEFAULT_RESET_DATE),
websiteId,
});

View file

@ -27,7 +27,7 @@ async function relationalQuery(
filters: QueryFilters,
): Promise<WebsiteStatsData[]> {
const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters({
const { filterQuery, joinSessionQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,
@ -66,7 +66,7 @@ async function clickhouseQuery(
filters: QueryFilters,
): Promise<WebsiteStatsData[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,

View file

@ -4,14 +4,19 @@ import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface PageviewMetricsParameters {
type: string;
limit?: number | string;
offset?: number | string;
}
export interface PageviewMetricsData {
x: string;
y: number;
}
export async function getPageviewMetrics(
...args: [
websiteId: string,
type: string,
filters: QueryFilters,
limit?: number | string,
offset?: number | string,
]
...args: [websiteId: string, parameters: PageviewMetricsParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
@ -21,16 +26,16 @@ export async function getPageviewMetrics(
async function relationalQuery(
websiteId: string,
type: string,
parameters: PageviewMetricsParameters,
filters: QueryFilters,
limit: number | string = 500,
offset: number | string = 0,
) {
): Promise<PageviewMetricsData[]> {
const { type, limit = 500, offset = 0 } = parameters;
const column = FILTER_COLUMNS[type] || type;
const { rawQuery, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters(
const { filterQuery, joinSessionQuery, queryParams } = parseFilters(
{
...filters,
websiteId,
eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView,
},
{
@ -87,15 +92,15 @@ async function relationalQuery(
async function clickhouseQuery(
websiteId: string,
type: string,
parameters: PageviewMetricsParameters,
filters: QueryFilters,
limit: number | string = 500,
offset: number | string = 0,
): Promise<{ x: string; y: number }[]> {
): Promise<PageviewMetricsData[]> {
const { type, limit = 500, offset = 0 } = parameters;
const column = FILTER_COLUMNS[type] || type;
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView,
});

View file

@ -14,7 +14,7 @@ export async function getPageviewStats(...args: [websiteId: string, filters: Que
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc', unit = 'day' } = filters;
const { getDateSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters({
const { filterQuery, joinSessionQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,
@ -44,7 +44,7 @@ async function clickhouseQuery(
): Promise<{ x: string; y: number }[]> {
const { timezone = 'utc', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,

View file

@ -2,8 +2,9 @@ import clickhouse from '@/lib/clickhouse';
import { EVENT_TYPE } from '@/lib/constants';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface AttributionCriteria {
export interface AttributionParameters {
startDate: Date;
endDate: Date;
model: string;
@ -23,7 +24,9 @@ export interface AttributionResult {
total: { pageviews: number; visitors: number; visits: number };
}
export async function getAttribution(...args: [websiteId: string, criteria: AttributionCriteria]) {
export async function getAttribution(
...args: [websiteId: string, parameters: AttributionParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
@ -32,12 +35,18 @@ export async function getAttribution(...args: [websiteId: string, criteria: Attr
async function relationalQuery(
websiteId: string,
criteria: AttributionCriteria,
parameters: AttributionParameters,
filters: QueryFilters,
): Promise<AttributionResult> {
const { startDate, endDate, model, type, step, currency } = criteria;
const { rawQuery } = prisma;
const { model, type, currency } = parameters;
const { rawQuery, parseFilters } = prisma;
const eventType = type === 'page' ? EVENT_TYPE.pageView : EVENT_TYPE.customEvent;
const column = type === 'page' ? 'url_path' : 'event_name';
const { filterQuery, queryParams } = parseFilters({
...filters,
...parameters,
eventType,
});
function getUTMQuery(utmColumn: string) {
return `
@ -64,8 +73,9 @@ async function relationalQuery(
from website_event
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
and ${column} = {{conversionStep}}
and ${column} = {{step}}
and event_type = {{eventType}}
${filterQuery}
group by 1),`;
const revenueEventQuery = `WITH events AS (
@ -76,8 +86,9 @@ async function relationalQuery(
from revenue
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
and ${column} = {{conversionStep}}
and ${column} = {{step}}
and currency = {{currency}}
${filterQuery}
group by 1),`;
function getModelQuery(model: string) {
@ -128,7 +139,7 @@ async function relationalQuery(
order by 2 desc
limit 20
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const paidAdsres = await rawQuery(
@ -161,7 +172,7 @@ async function relationalQuery(
FROM results
${currency ? '' : `WHERE name != ''`}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const sourceRes = await rawQuery(
@ -170,7 +181,7 @@ async function relationalQuery(
${getModelQuery(model)}
${getUTMQuery('utm_source')}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const mediumRes = await rawQuery(
@ -179,7 +190,7 @@ async function relationalQuery(
${getModelQuery(model)}
${getUTMQuery('utm_medium')}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const campaignRes = await rawQuery(
@ -188,7 +199,7 @@ async function relationalQuery(
${getModelQuery(model)}
${getUTMQuery('utm_campaign')}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const contentRes = await rawQuery(
@ -197,7 +208,7 @@ async function relationalQuery(
${getModelQuery(model)}
${getUTMQuery('utm_content')}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const termRes = await rawQuery(
@ -206,7 +217,7 @@ async function relationalQuery(
${getModelQuery(model)}
${getUTMQuery('utm_term')}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const totalRes = await rawQuery(
@ -218,10 +229,11 @@ async function relationalQuery(
from website_event
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
and ${column} = {{conversionStep}}
and ${column} = {{step}}
and event_type = {{eventType}}
${filterQuery}
`,
{ websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
).then(result => result?.[0]);
return {
@ -238,13 +250,19 @@ async function relationalQuery(
async function clickhouseQuery(
websiteId: string,
criteria: AttributionCriteria,
parameters: AttributionParameters,
filters: QueryFilters,
): Promise<AttributionResult> {
const { startDate, endDate, model, type, step, currency } = criteria;
const { model, type, currency } = parameters;
const { rawQuery, parseFilters } = clickhouse;
const eventType = type === 'page' ? EVENT_TYPE.pageView : EVENT_TYPE.customEvent;
const column = type === 'page' ? 'url_path' : 'event_name';
const { filterQuery, queryParams } = await parseFilters(criteria);
const { filterQuery, queryParams } = parseFilters({
...filters,
...parameters,
websiteId,
eventType,
});
function getUTMQuery(utmColumn: string) {
return `
@ -301,7 +319,7 @@ async function clickhouseQuery(
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and ${column} = {conversionStep:String}
and ${column} = {step:String}
and event_type = {eventType:UInt32}
group by 1),`;
@ -313,7 +331,7 @@ async function clickhouseQuery(
from website_revenue
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and ${column} = {conversionStep:String}
and ${column} = {step:String}
and currency = {currency:String}
group by 1),`;
@ -345,7 +363,7 @@ async function clickhouseQuery(
order by 2 desc
limit 20
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const paidAdsres = await rawQuery<
@ -376,7 +394,7 @@ async function clickhouseQuery(
order by 2 desc
limit 20
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const sourceRes = await rawQuery<
@ -390,7 +408,7 @@ async function clickhouseQuery(
${getModelQuery(model)}
${getUTMQuery('utm_source')}
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const mediumRes = await rawQuery<
@ -404,7 +422,7 @@ async function clickhouseQuery(
${getModelQuery(model)}
${getUTMQuery('utm_medium')}
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const campaignRes = await rawQuery<
@ -418,7 +436,7 @@ async function clickhouseQuery(
${getModelQuery(model)}
${getUTMQuery('utm_campaign')}
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const contentRes = await rawQuery<
@ -432,7 +450,7 @@ async function clickhouseQuery(
${getModelQuery(model)}
${getUTMQuery('utm_content')}
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const termRes = await rawQuery<
@ -446,7 +464,7 @@ async function clickhouseQuery(
${getModelQuery(model)}
${getUTMQuery('utm_term')}
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
);
const totalRes = await rawQuery<{ pageviews: number; visitors: number; visits: number }>(
@ -458,11 +476,11 @@ async function clickhouseQuery(
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and ${column} = {conversionStep:String}
and ${column} = {step:String}
and event_type = {eventType:UInt32}
${filterQuery}
`,
{ ...queryParams, websiteId, startDate, endDate, conversionStep: step, eventType, currency },
queryParams,
).then(result => result?.[0]);
return {

View file

@ -4,8 +4,19 @@ import clickhouse from '@/lib/clickhouse';
import { EVENT_TYPE, FILTER_COLUMNS, SESSION_COLUMNS } from '@/lib/constants';
import { QueryFilters } from '@/lib/types';
export interface BreakdownParameters {
startDate: Date;
endDate: Date;
fields: string[];
}
export interface BreakdownData {
x: string;
y: number;
}
export async function getBreakdown(
...args: [websiteId: string, fields: string[], filters: QueryFilters]
...args: [websiteId: string, parameters: BreakdownParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
@ -15,22 +26,21 @@ export async function getBreakdown(
async function relationalQuery(
websiteId: string,
fields: string[],
parameters: BreakdownParameters,
filters: QueryFilters,
): Promise<
{
x: string;
y: number;
}[]
> {
): Promise<BreakdownData[]> {
const { getTimestampDiffSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters(
const { startDate, endDate, fields } = parameters;
const { filterQuery, joinSessionQuery, queryParams } = parseFilters(
{
...filters,
websiteId,
startDate,
endDate,
eventType: EVENT_TYPE.pageView,
},
{
joinSession: !!fields.find(name => SESSION_COLUMNS.includes(name)),
joinSession: !!fields.find((name: string) => SESSION_COLUMNS.includes(name)),
},
);
@ -70,17 +80,16 @@ async function relationalQuery(
async function clickhouseQuery(
websiteId: string,
fields: string[],
parameters: BreakdownParameters,
filters: QueryFilters,
): Promise<
{
x: string;
y: number;
}[]
> {
): Promise<BreakdownData[]> {
const { parseFilters, rawQuery } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { startDate, endDate, fields } = parameters;
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
startDate,
endDate,
eventType: EVENT_TYPE.pageView,
});

View file

@ -1,15 +1,24 @@
import clickhouse from '@/lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface FunnelCriteria {
windowMinutes: number;
export interface FunnelParameters {
startDate: Date;
endDate: Date;
window: number;
steps: { type: string; value: string }[];
}
export async function getFunnel(...args: [websiteId: string, criteria: FunnelCriteria]) {
export interface FunnelResult {
value: string;
visitors: number;
dropoff: number;
}
export async function getFunnel(
...args: [websiteId: string, parameters: FunnelParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
@ -18,21 +27,18 @@ export async function getFunnel(...args: [websiteId: string, criteria: FunnelCri
async function relationalQuery(
websiteId: string,
criteria: FunnelCriteria,
): Promise<
{
value: string;
visitors: number;
dropoff: number;
}[]
> {
const { windowMinutes, startDate, endDate, steps } = criteria;
const { rawQuery, getAddIntervalQuery } = prisma;
const { levelOneQuery, levelQuery, sumQuery, params } = getFunnelQuery(steps, windowMinutes);
parameters: FunnelParameters,
filters: QueryFilters,
): Promise<FunnelResult[]> {
const { startDate, endDate, window, steps } = parameters;
const { rawQuery, getAddIntervalQuery, parseFilters } = prisma;
const { levelOneQuery, levelQuery, sumQuery, params } = getFunnelQuery(steps, window);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate });
function getFunnelQuery(
steps: { type: string; value: string }[],
windowMinutes: number,
window: number,
): {
levelOneQuery: string;
levelQuery: string;
@ -62,6 +68,7 @@ async function relationalQuery(
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
and ${column} ${operator} {{${i}}}
${filterQuery}
)`;
} else {
pv.levelQuery += `
@ -73,7 +80,7 @@ async function relationalQuery(
where we.website_id = {{websiteId::uuid}}
and we.created_at between l.created_at and ${getAddIntervalQuery(
`l.created_at `,
`${windowMinutes} minute`,
`${window} minute`,
)}
and we.${column} ${operator} {{${i}}}
and we.created_at <= {{endDate}}
@ -102,17 +109,16 @@ async function relationalQuery(
ORDER BY level;
`,
{
websiteId,
startDate,
endDate,
...params,
...queryParams,
},
).then(formatResults(steps));
}
async function clickhouseQuery(
websiteId: string,
criteria: FunnelCriteria,
parameters: FunnelParameters,
filters: QueryFilters,
): Promise<
{
value: string;
@ -120,17 +126,17 @@ async function clickhouseQuery(
dropoff: number;
}[]
> {
const { windowMinutes, startDate, endDate, steps } = criteria;
const { startDate, endDate, window, steps } = parameters;
const { rawQuery, parseFilters } = clickhouse;
const { levelOneQuery, levelQuery, sumQuery, stepFilterQuery, params } = getFunnelQuery(
steps,
windowMinutes,
window,
);
const { filterQuery, queryParams } = await parseFilters(criteria);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate });
function getFunnelQuery(
steps: { type: string; value: string }[],
windowMinutes: number,
window: number,
): {
levelOneQuery: string;
levelQuery: string;
@ -172,7 +178,7 @@ async function clickhouseQuery(
from level${i} x
join level0 y
on x.session_id = y.session_id
where y.created_at between x.created_at and x.created_at + interval ${windowMinutes} minute
where y.created_at between x.created_at and x.created_at + interval ${window} minute
and y.${column} ${operator} {param${i}:String}
)`;
}
@ -211,9 +217,6 @@ async function clickhouseQuery(
) ORDER BY level;
`,
{
websiteId,
startDate,
endDate,
...params,
...queryParams,
},

View file

@ -3,30 +3,37 @@ import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface GoalCriteria {
export interface GoalParameters {
startDate: Date;
endDate: Date;
type: string;
value: string;
operator?: string;
property?: string;
filters: QueryFilters;
}
export async function getGoal(...args: [websiteId: string, criteria: GoalCriteria]) {
export async function getGoal(
...args: [websiteId: string, params: GoalParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
});
}
async function relationalQuery(websiteId: string, criteria: GoalCriteria) {
const { type, value, filters } = criteria;
async function relationalQuery(
websiteId: string,
parameters: GoalParameters,
filters: QueryFilters,
) {
const { startDate, endDate, type, value } = parameters;
const { rawQuery, parseFilters } = prisma;
const { filterQuery, dateQuery, queryParams } = await parseFilters({
const { filterQuery, dateQuery, queryParams } = parseFilters({
...filters,
websiteId,
value,
startDate,
endDate,
});
const isPage = type === 'page';
const column = isPage ? 'url_path' : 'event_name';
@ -53,13 +60,19 @@ async function relationalQuery(websiteId: string, criteria: GoalCriteria) {
);
}
async function clickhouseQuery(websiteId: string, criteria: GoalCriteria) {
const { type, value, filters } = criteria;
async function clickhouseQuery(
websiteId: string,
parameters: GoalParameters,
filters: QueryFilters,
) {
const { startDate, endDate, type, value } = parameters;
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, dateQuery, queryParams } = await parseFilters({
const { filterQuery, dateQuery, queryParams } = parseFilters({
...filters,
websiteId,
value,
startDate,
endDate,
});
const isPage = type === 'page';
const column = isPage ? 'url_path' : 'event_name';

View file

@ -1,8 +1,17 @@
import clickhouse from '@/lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
interface JourneyResult {
export interface JourneyParameters {
startDate: Date;
endDate: Date;
steps: number;
startStep?: string;
endStep?: string;
}
export interface JourneyResult {
e1: string;
e2: string;
e3: string;
@ -14,16 +23,7 @@ interface JourneyResult {
}
export async function getJourney(
...args: [
websiteId: string,
filters: {
startDate: Date;
endDate: Date;
steps: number;
startStep?: string;
endStep?: string;
},
]
...args: [websiteId: string, parameters: JourneyParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
@ -33,21 +33,17 @@ export async function getJourney(
async function relationalQuery(
websiteId: string,
filters: {
startDate: Date;
endDate: Date;
steps: number;
startStep?: string;
endStep?: string;
},
parameters: JourneyParameters,
filters: QueryFilters,
): Promise<JourneyResult[]> {
const { startDate, endDate, steps, startStep, endStep } = filters;
const { rawQuery } = prisma;
const { startDate, endDate, steps, startStep, endStep } = parameters;
const { rawQuery, parseFilters } = prisma;
const { sequenceQuery, startStepQuery, endStepQuery, params } = getJourneyQuery(
steps,
startStep,
endStep,
);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate });
function getJourneyQuery(
steps: number,
@ -123,6 +119,7 @@ async function relationalQuery(
from website_event
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}),
${filterQuery}
${sequenceQuery}
select *
from sequences
@ -133,31 +130,25 @@ async function relationalQuery(
limit 100
`,
{
websiteId,
startDate,
endDate,
...params,
...queryParams,
},
).then(parseResult);
}
async function clickhouseQuery(
websiteId: string,
filters: {
startDate: Date;
endDate: Date;
steps: number;
startStep?: string;
endStep?: string;
},
parameters: JourneyParameters,
filters: QueryFilters,
): Promise<JourneyResult[]> {
const { startDate, endDate, steps, startStep, endStep } = filters;
const { startDate, endDate, steps, startStep, endStep } = parameters;
const { rawQuery, parseFilters } = clickhouse;
const { sequenceQuery, startStepQuery, endStepQuery, params } = getJourneyQuery(
steps,
startStep,
endStep,
);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate });
function getJourneyQuery(
steps: number,
@ -222,8 +213,6 @@ async function clickhouseQuery(
};
}
const { filterQuery, queryParams } = await parseFilters(filters);
return rawQuery(
`
WITH events AS (
@ -245,9 +234,6 @@ async function clickhouseQuery(
limit 100
`,
{
websiteId,
startDate,
endDate,
...params,
...queryParams,
},

View file

@ -1,8 +1,9 @@
import clickhouse from '@/lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface RetentionCriteria {
export interface RetentionParameters {
startDate: Date;
endDate: Date;
timezone?: string;
@ -16,7 +17,9 @@ export interface RetentionResult {
percentage: number;
}
export async function getRetention(...args: [websiteId: string, criteria: RetentionCriteria]) {
export async function getRetention(
...args: [websiteId: string, parameters: RetentionParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
@ -25,13 +28,20 @@ export async function getRetention(...args: [websiteId: string, criteria: Retent
async function relationalQuery(
websiteId: string,
criteria: RetentionCriteria,
parameters: RetentionParameters,
filters: QueryFilters,
): Promise<RetentionResult[]> {
const { startDate, endDate, timezone } = criteria;
const { startDate, endDate, timezone } = parameters;
const { getDateSQL, getDayDiffQuery, getCastColumnQuery, rawQuery, parseFilters } = prisma;
const unit = 'day';
const { filterQuery, queryParams } = await parseFilters(criteria);
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
startDate,
endDate,
timezone,
});
return rawQuery(
`
@ -81,24 +91,26 @@ async function relationalQuery(
on c.cohort_date = s.cohort_date
where c.day_number <= 31
order by 1, 2`,
{
websiteId,
startDate,
endDate,
...queryParams,
},
queryParams,
);
}
async function clickhouseQuery(
websiteId: string,
criteria: RetentionCriteria,
parameters: RetentionParameters,
filters: QueryFilters,
): Promise<RetentionResult[]> {
const { startDate, endDate, timezone } = criteria;
const { startDate, endDate, timezone } = parameters;
const { getDateSQL, rawQuery, parseFilters } = clickhouse;
const unit = 'day';
const { filterQuery, queryParams } = await parseFilters(criteria);
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
startDate,
endDate,
timezone,
});
return rawQuery(
`
@ -150,11 +162,6 @@ async function clickhouseQuery(
on c.cohort_date = s.cohort_date
where c.day_number <= 31
order by 1, 2`,
{
websiteId,
startDate,
endDate,
...queryParams,
},
queryParams,
);
}

View file

@ -1,8 +1,9 @@
import clickhouse from '@/lib/clickhouse';
import { CLICKHOUSE, getDatabaseType, POSTGRESQL, PRISMA, runQuery } from '@/lib/db';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface RevenueCriteria {
export interface RevenuParameters {
startDate: Date;
endDate: Date;
unit: string;
@ -21,7 +22,9 @@ export interface RevenueResult {
}[];
}
export async function getRevenue(...args: [websiteId: string, criteria: RevenueCriteria]) {
export async function getRevenue(
...args: [websiteId: string, parameters: RevenuParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
@ -30,12 +33,12 @@ export async function getRevenue(...args: [websiteId: string, criteria: RevenueC
async function relationalQuery(
websiteId: string,
criteria: RevenueCriteria,
parameters: RevenuParameters,
filters: QueryFilters,
): Promise<RevenueResult> {
const { startDate, endDate, unit = 'day', currency } = criteria;
const { getDateSQL, rawQuery } = prisma;
const db = getDatabaseType();
const like = db === POSTGRESQL ? 'ilike' : 'like';
const { startDate, endDate, currency, unit = 'day' } = parameters;
const { getDateSQL, rawQuery, parseFilters } = prisma;
const { queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate, currency });
const chart = await rawQuery(
`
@ -50,7 +53,7 @@ async function relationalQuery(
group by x, t
order by t
`,
{ websiteId, startDate, endDate, unit, currency },
queryParams,
);
const country = await rawQuery(
@ -63,10 +66,10 @@ async function relationalQuery(
on s.session_id = r.session_id
where r.website_id = {{websiteId::uuid}}
and r.created_at between {{startDate}} and {{endDate}}
and r.currency ${like} {{currency}}
and r.currency = {{currency}}
group by s.country
`,
{ websiteId, startDate, endDate, currency },
queryParams,
);
const total = await rawQuery(
@ -78,9 +81,9 @@ async function relationalQuery(
from revenue r
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
and currency ${like} {{currency}}
and currency = {{currency}}
`,
{ websiteId, startDate, endDate, currency },
queryParams,
).then(result => result?.[0]);
total.average = total.count > 0 ? total.sum / total.count : 0;
@ -98,7 +101,7 @@ async function relationalQuery(
group by currency
order by sum desc
`,
{ websiteId, startDate, endDate, unit, currency },
queryParams,
);
return { chart, country, table, total };
@ -106,10 +109,18 @@ async function relationalQuery(
async function clickhouseQuery(
websiteId: string,
criteria: RevenueCriteria,
parameters: RevenuParameters,
filters: QueryFilters,
): Promise<RevenueResult> {
const { startDate, endDate, unit = 'day', currency } = criteria;
const { getDateSQL, rawQuery } = clickhouse;
const { startDate, endDate, unit = 'day', currency } = parameters;
const { getDateSQL, rawQuery, parseFilters } = clickhouse;
const { queryParams } = parseFilters({
...filters,
websiteId,
startDate,
endDate,
currency,
});
const chart = await rawQuery<
{
@ -130,7 +141,7 @@ async function clickhouseQuery(
group by x, t
order by t
`,
{ websiteId, startDate, endDate, unit, currency },
queryParams,
);
const country = await rawQuery<
@ -144,9 +155,11 @@ async function clickhouseQuery(
s.country as name,
sum(w.revenue) as value
from website_revenue w
join (select distinct website_id, session_id, country
from website_event
where website_id = {websiteId:UUID}) s
join (
select distinct website_id, session_id, country
from website_event
where website_id = {websiteId:UUID}
) s
on w.website_id = s.website_id
and w.session_id = s.session_id
where w.website_id = {websiteId:UUID}
@ -154,7 +167,7 @@ async function clickhouseQuery(
and w.currency = {currency:String}
group by s.country
`,
{ websiteId, startDate, endDate, currency },
queryParams,
);
const total = await rawQuery<{
@ -172,7 +185,7 @@ async function clickhouseQuery(
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and currency = {currency:String}
`,
{ websiteId, startDate, endDate, currency },
queryParams,
).then(result => result?.[0]);
total.average = total.count > 0 ? total.sum / total.count : 0;
@ -197,7 +210,7 @@ async function clickhouseQuery(
group by currency
order by sum desc
`,
{ websiteId, startDate, endDate, unit, currency },
queryParams,
);
return { chart, country, table, total };

View file

@ -1,23 +1,30 @@
import clickhouse from '@/lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface UTMCriteria {
export interface UTMParameters {
startDate: Date;
endDate: Date;
}
export async function getUTM(...args: [websiteId: string, criteria: UTMCriteria]) {
export async function getUTM(
...args: [websiteId: string, parameters: UTMParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
});
}
async function relationalQuery(websiteId: string, criteria: UTMCriteria) {
const { startDate, endDate } = criteria;
async function relationalQuery(
websiteId: string,
parameters: UTMParameters,
filters: QueryFilters,
) {
const { startDate, endDate } = parameters;
const { rawQuery, parseFilters } = prisma;
const { filterQuery, queryParams } = await parseFilters(criteria);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate });
return rawQuery(
`
@ -30,19 +37,18 @@ async function relationalQuery(websiteId: string, criteria: UTMCriteria) {
${filterQuery}
group by 1
`,
{
...queryParams,
websiteId,
startDate,
endDate,
},
queryParams,
).then(result => parseParameters(result as any[]));
}
async function clickhouseQuery(websiteId: string, criteria: UTMCriteria) {
const { startDate, endDate } = criteria;
async function clickhouseQuery(
websiteId: string,
parameters: UTMParameters,
filters: QueryFilters,
) {
const { startDate, endDate } = parameters;
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters(criteria);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId, startDate, endDate });
return rawQuery(
`
@ -55,12 +61,7 @@ async function clickhouseQuery(websiteId: string, criteria: UTMCriteria) {
${filterQuery}
group by 1
`,
{
...queryParams,
websiteId,
startDate,
endDate,
},
queryParams,
).then(result => parseParameters(result as any[]));
}

View file

@ -17,7 +17,7 @@ async function relationalQuery(
filters: QueryFilters & { propertyName?: string },
) {
const { rawQuery, parseFilters } = prisma;
const { filterQuery, queryParams } = await parseFilters(filters, {
const { filterQuery, queryParams } = parseFilters(filters, {
columns: { propertyName: 'data_key' },
});
@ -45,7 +45,7 @@ async function clickhouseQuery(
filters: QueryFilters & { propertyName?: string },
): Promise<{ propertyName: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters(filters, {
const { filterQuery, queryParams } = parseFilters(filters, {
columns: { propertyName: 'data_key' },
});

View file

@ -17,7 +17,7 @@ async function relationalQuery(
filters: QueryFilters & { propertyName?: string },
) {
const { rawQuery, parseFilters, getDateSQL } = prisma;
const { filterQuery, queryParams } = await parseFilters(filters);
const { filterQuery, queryParams } = parseFilters(filters);
return rawQuery(
`
@ -48,7 +48,7 @@ async function clickhouseQuery(
filters: QueryFilters & { propertyName?: string },
): Promise<{ propertyName: string; dataType: number; propertyValue: string; total: number }[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters(filters);
const { filterQuery, queryParams } = parseFilters(filters);
return rawQuery(
`

View file

@ -4,14 +4,14 @@ import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import { QueryFilters } from '@/lib/types';
export interface SessionMetricsParameters {
type: string;
limit: number | string;
offset: number | string;
}
export async function getSessionMetrics(
...args: [
websiteId: string,
type: string,
filters: QueryFilters,
limit?: number | string,
offset?: number | string,
]
...args: [websiteId: string, parameters: SessionMetricsParameters, filters: QueryFilters]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
@ -21,16 +21,16 @@ export async function getSessionMetrics(
async function relationalQuery(
websiteId: string,
type: string,
parameters: SessionMetricsParameters,
filters: QueryFilters,
limit: number | string = 500,
offset: number | string = 0,
) {
const { type, limit = 500, offset = 0 } = parameters;
const column = FILTER_COLUMNS[type] || type;
const { parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters(
const { filterQuery, joinSessionQuery, queryParams } = parseFilters(
{
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,
},
{
@ -57,21 +57,21 @@ async function relationalQuery(
limit ${limit}
offset ${offset}
`,
queryParams,
{ ...queryParams, ...parameters },
);
}
async function clickhouseQuery(
websiteId: string,
type: string,
parameters: SessionMetricsParameters,
filters: QueryFilters,
limit: number | string = 500,
offset: number | string = 0,
): Promise<{ x: string; y: number }[]> {
const { type, limit = 500, offset = 0 } = parameters;
const column = FILTER_COLUMNS[type] || type;
const { parseFilters, rawQuery } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,
});
const includeCountry = column === 'city' || column === 'region';
@ -114,5 +114,5 @@ async function clickhouseQuery(
`;
}
return rawQuery(sql, queryParams);
return rawQuery(sql, { ...queryParams, ...parameters });
}

View file

@ -14,8 +14,9 @@ export async function getSessionStats(...args: [websiteId: string, filters: Quer
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc', unit = 'day' } = filters;
const { getDateSQL, parseFilters, rawQuery } = prisma;
const { filterQuery, joinSessionQuery, queryParams } = await parseFilters({
const { filterQuery, joinSessionQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,
});
@ -43,8 +44,9 @@ async function clickhouseQuery(
): Promise<{ x: string; y: number }[]> {
const { timezone = 'utc', unit = 'day' } = filters;
const { parseFilters, rawQuery, getDateSQL } = clickhouse;
const { filterQuery, queryParams } = await parseFilters({
const { filterQuery, queryParams } = parseFilters({
...filters,
websiteId,
eventType: EVENT_TYPE.pageView,
});

View file

@ -25,7 +25,7 @@ async function relationalQuery(
filters: QueryFilters,
): Promise<WebsiteSessionStatsData[]> {
const { parseFilters, rawQuery } = prisma;
const { filterQuery, queryParams } = await parseFilters(filters);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`
@ -50,7 +50,7 @@ async function clickhouseQuery(
filters: QueryFilters,
): Promise<WebsiteSessionStatsData[]> {
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, queryParams } = await parseFilters(filters);
const { filterQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery(
`

View file

@ -12,7 +12,14 @@ export async function getWebsiteSessions(...args: [websiteId: string, filters: Q
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { pagedRawQuery, parseFilters } = prisma;
const { filterQuery, dateQuery, queryParams } = await parseFilters(filters);
const { search } = filters;
const { filterQuery, dateQuery, queryParams } = parseFilters({
...filters,
websiteId,
search: search ? `%${search}%` : undefined,
});
const searchQuery = search ? `and session.distinct_id ilike {{search}}` : '';
return pagedRawQuery(
`
@ -38,6 +45,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
where website_event.website_id = {{websiteId::uuid}}
${dateQuery}
${filterQuery}
${searchQuery}
group by session.session_id,
session.website_id,
website_event.hostname,
@ -58,7 +66,13 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
const { pagedRawQuery, parseFilters, getDateStringSQL } = clickhouse;
const { filterQuery, dateQuery, queryParams } = await parseFilters(filters);
const { search } = filters;
const { filterQuery, dateQuery, queryParams } = parseFilters({
...filters,
websiteId,
});
const searchQuery = search ? `and positionCaseInsensitive(distinct_id, {search:String}) > 0` : '';
return pagedRawQuery(
`
@ -83,6 +97,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
where website_id = {websiteId:UUID}
${dateQuery}
${filterQuery}
${searchQuery}
group by session_id, website_id, hostname, browser, os, device, screen, language, country, region, city
order by lastAt desc
`,

View file

@ -15,7 +15,7 @@ export async function getWebsiteSessionsWeekly(
async function relationalQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc' } = filters;
const { rawQuery, getDateWeeklySQL, parseFilters } = prisma;
const { queryParams } = await parseFilters(filters);
const { queryParams } = parseFilters(filters);
return rawQuery(
`
@ -35,7 +35,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
const { timezone = 'utc' } = filters;
const { rawQuery, parseFilters } = clickhouse;
const { queryParams } = await parseFilters(filters);
const { queryParams } = parseFilters(filters);
return rawQuery(
`