Merge branch 'dev' into bug/um-362-relational-funnels-query

This commit is contained in:
Francis Cao 2023-07-27 13:53:49 -07:00
commit a03574e8d4
78 changed files with 1110 additions and 852 deletions

View file

@ -1,7 +1,6 @@
import prisma from '@umami/prisma-client';
import moment from 'moment-timezone';
import { MYSQL, POSTGRESQL, getDatabaseType } from 'lib/db';
import { getDynamicDataType } from './dynamicData';
import { FILTER_COLUMNS } from './constants';
const MYSQL_DATE_FORMATS = {
@ -45,7 +44,7 @@ function getAddMinutesQuery(field: string, minutes: number): string {
}
function getDateQuery(field: string, unit: string, timezone?: string): string {
const db = getDatabaseType(process.env.DATABASE_URL);
const db = getDatabaseType();
if (db === POSTGRESQL) {
if (timezone) {
@ -65,8 +64,8 @@ function getDateQuery(field: string, unit: string, timezone?: string): string {
}
}
function getTimestampInterval(field: string): string {
const db = getDatabaseType(process.env.DATABASE_URL);
function getTimestampIntervalQuery(field: string): string {
const db = getDatabaseType();
if (db === POSTGRESQL) {
return `floor(extract(epoch from max(${field}) - min(${field})))`;
@ -77,47 +76,6 @@ function getTimestampInterval(field: string): string {
}
}
function getEventDataFilterQuery(
filters: {
eventKey?: string;
eventValue?: string | number | boolean | Date;
}[],
params: any[],
) {
const query = filters.reduce((ac, cv) => {
const type = getDynamicDataType(cv.eventValue);
let value = cv.eventValue;
ac.push(`and (event_key = $${params.length + 1}`);
params.push(cv.eventKey);
switch (type) {
case 'number':
ac.push(`and number_value = $${params.length + 1})`);
params.push(value);
break;
case 'string':
ac.push(`and string_value = $${params.length + 1})`);
params.push(decodeURIComponent(cv.eventValue as string));
break;
case 'boolean':
ac.push(`and string_value = $${params.length + 1})`);
params.push(decodeURIComponent(cv.eventValue as string));
value = cv ? 'true' : 'false';
break;
case 'date':
ac.push(`and date_value = $${params.length + 1})`);
params.push(cv.eventValue);
break;
}
return ac;
}, []);
return query.join('\n');
}
function getFilterQuery(filters = {}, params = []): string {
const query = Object.keys(filters).reduce((arr, key) => {
const filter = filters[key];
@ -196,8 +154,8 @@ function getFunnelQuery(
and ${getAddMinutesQuery(`l.created_at `, windowMinutes)}
and we.referrer_path = $${i + initParamLength}
and we.url_path = $${levelNumber + initParamLength}
and we.created_at <= $3
and we.website_id = $1${toUuid()}
and we.created_at <= {{endDate}}
and we.website_id = {{websiteId}}${toUuid()}
)`;
}
@ -214,25 +172,31 @@ function getFunnelQuery(
);
}
async function rawQuery(query: string, params: never[] = []): Promise<any> {
const db = getDatabaseType(process.env.DATABASE_URL);
async function rawQuery(sql: string, data: object): Promise<any> {
const db = getDatabaseType();
const params = [];
if (db !== POSTGRESQL && db !== MYSQL) {
return Promise.reject(new Error('Unknown database.'));
}
const sql = db === MYSQL ? query.replace(/\$[0-9]+/g, '?') : query;
const query = sql?.replaceAll(/\{\{\s*(\w+)(::\w+)?\s*}}/g, (...args) => {
const [, name, type] = args;
return prisma.rawQuery(sql, params);
params.push(data[name]);
return db === MYSQL ? '?' : `$${params.length}${type ?? ''}`;
});
return prisma.rawQuery(query, params);
}
export default {
...prisma,
getAddMinutesQuery,
getDateQuery,
getTimestampInterval,
getTimestampIntervalQuery,
getFilterQuery,
getEventDataFilterQuery,
toUuid,
parseFilters,
getFunnelParams,