From 91efb7f1d0962f4c4d60cda27dfd9b2db2881362 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Mon, 25 Mar 2024 22:51:10 -0700 Subject: [PATCH] Fixed search for postgresql. --- src/lib/prisma.ts | 27 +++++++++-------- src/pages/api/send.ts | 2 +- src/pages/api/websites/[websiteId]/metrics.ts | 29 +++++++------------ .../analytics/pageviews/getPageviewMetrics.ts | 12 ++------ 4 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/lib/prisma.ts b/src/lib/prisma.ts index 9352eaea..b79d4249 100644 --- a/src/lib/prisma.ts +++ b/src/lib/prisma.ts @@ -92,31 +92,31 @@ function getTimestampDiffQuery(field1: string, field2: string): string { } } -function mapFilter(column: string, filter: string, name: string, type = 'varchar') { - switch (filter) { +function mapFilter(column: string, op: string, name: string, type = 'varchar') { + switch (op) { case OPERATORS.equals: return `${column} = {{${name}::${type}}}`; case OPERATORS.notEquals: return `${column} != {{${name}::${type}}}`; case OPERATORS.contains: - return `${column} like {{${name}::${type}}}`; + return `${column} ilike {{${name}::${type}}}`; case OPERATORS.doesNotContain: - return `${column} not like {{${name}::${type}}}`; + return `${column} not ilike {{${name}::${type}}}`; default: return ''; } } function getFilterQuery(filters: QueryFilters = {}, options: QueryOptions = {}): string { - const query = Object.keys(filters).reduce((arr, name) => { - const value = filters[name]; - const filter = value?.filter ?? OPERATORS.equals; - const column = value?.column ?? FILTER_COLUMNS[name] ?? options?.columns?.[name]; + const query = Object.keys(filters).reduce((arr, key) => { + const filter = filters[key]; + const op = filter?.op ?? OPERATORS.equals; + const column = filter?.column ?? FILTER_COLUMNS[key] ?? options?.columns?.[key]; - if (value !== undefined && column !== undefined) { - arr.push(`and ${mapFilter(column, filter, name)}`); + if (filter !== undefined && column !== undefined) { + arr.push(`and ${mapFilter(column, op, key)}`); - if (name === 'referrer') { + if (key === 'referrer') { arr.push( 'and (website_event.referrer_domain != {{websiteDomain}} or website_event.referrer_domain is null)', ); @@ -171,7 +171,10 @@ async function rawQuery(sql: string, data: object): Promise { const query = sql?.replaceAll(/\{\{\s*(\w+)(::\w+)?\s*}}/g, (...args) => { const [, name, type] = args; - params.push(data[name]); + + const value = data[name]; + + params.push(value); return db === MYSQL ? '?' : `$${params.length}${type ?? ''}`; }); diff --git a/src/pages/api/send.ts b/src/pages/api/send.ts index 5aa367f0..d0d1b626 100644 --- a/src/pages/api/send.ts +++ b/src/pages/api/send.ts @@ -97,7 +97,7 @@ export default async (req: NextApiRequestCollect, res: NextApiResponse) => { // eslint-disable-next-line prefer-const let [urlPath, urlQuery] = url?.split('?') || []; let [referrerPath, referrerQuery] = referrer?.split('?') || []; - let referrerDomain; + let referrerDomain = ''; if (!urlPath) { urlPath = '/'; diff --git a/src/pages/api/websites/[websiteId]/metrics.ts b/src/pages/api/websites/[websiteId]/metrics.ts index eb636170..beb4dd50 100644 --- a/src/pages/api/websites/[websiteId]/metrics.ts +++ b/src/pages/api/websites/[websiteId]/metrics.ts @@ -3,7 +3,7 @@ import { badRequest, methodNotAllowed, ok, unauthorized } from 'next-basics'; import { WebsiteMetric, NextApiRequestQueryBody } from 'lib/types'; import { canViewWebsite } from 'lib/auth'; import { useAuth, useCors, useValidate } from 'lib/middleware'; -import { SESSION_COLUMNS, EVENT_COLUMNS, FILTER_COLUMNS } from 'lib/constants'; +import { SESSION_COLUMNS, EVENT_COLUMNS, FILTER_COLUMNS, OPERATORS } from 'lib/constants'; import { getPageviewMetrics, getSessionMetrics } from 'queries'; import { parseDateRangeQuery } from 'lib/query'; import * as yup from 'yup'; @@ -88,7 +88,7 @@ export default async ( } const { startDate, endDate } = await parseDateRangeQuery(req); - + const column = FILTER_COLUMNS[type] || type; const filters = { startDate, endDate, @@ -104,19 +104,18 @@ export default async ( city, language, event, - search, }; - const column = FILTER_COLUMNS[type] || type; + if (search) { + filters[column] = { + column, + op: OPERATORS.contains, + value: '%' + search + '%', + }; + } if (SESSION_COLUMNS.includes(type)) { - const data = await getSessionMetrics( - websiteId, - column, - { ...filters, search }, - limit, - offset, - ); + const data = await getSessionMetrics(websiteId, column, filters, limit, offset); if (type === 'language') { const combined = {}; @@ -138,13 +137,7 @@ export default async ( } if (EVENT_COLUMNS.includes(type)) { - const data = await getPageviewMetrics( - websiteId, - column, - { ...filters, search }, - limit, - offset, - ); + const data = await getPageviewMetrics(websiteId, column, filters, limit, offset); return ok(res, data); } diff --git a/src/queries/analytics/pageviews/getPageviewMetrics.ts b/src/queries/analytics/pageviews/getPageviewMetrics.ts index 8673dbe6..9b6a494b 100644 --- a/src/queries/analytics/pageviews/getPageviewMetrics.ts +++ b/src/queries/analytics/pageviews/getPageviewMetrics.ts @@ -1,7 +1,7 @@ import prisma from 'lib/prisma'; import clickhouse from 'lib/clickhouse'; import { runQuery, CLICKHOUSE, PRISMA } from 'lib/db'; -import { EVENT_TYPE, SESSION_COLUMNS, OPERATORS } from 'lib/constants'; +import { EVENT_TYPE, SESSION_COLUMNS } from 'lib/constants'; import { QueryFilters } from 'lib/types'; export async function getPageviewMetrics( @@ -27,6 +27,7 @@ async function relationalQuery( offset: number = 0, ) { const { rawQuery, parseFilters } = prisma; + const { filterQuery, joinSession, params } = await parseFilters( websiteId, { @@ -69,16 +70,9 @@ async function clickhouseQuery( offset: number = 0, ): Promise<{ x: string; y: number }[]> { const { rawQuery, parseFilters } = clickhouse; + const { filterQuery, params } = await parseFilters(websiteId, { ...filters, - ...(filters.search && { - [column]: { - value: filters.search, - filter: OPERATORS.contains, - column, - name: column, - }, - }), eventType: column === 'event_name' ? EVENT_TYPE.customEvent : EVENT_TYPE.pageView, });