diff --git a/packages/db/src/services/chart.service.ts b/packages/db/src/services/chart.service.ts index f3eef631..9deca5d3 100644 --- a/packages/db/src/services/chart.service.ts +++ b/packages/db/src/services/chart.service.ts @@ -11,21 +11,6 @@ import type { import { TABLE_NAMES, formatClickhouseDate } from '../clickhouse/client'; import { createSqlBuilder } from '../sql-builder'; -/** - * Helper function to check if endDate is after startDate - * This prevents ClickHouse errors when WITH FILL TO value is less than FROM value - */ -function isValidDateRange(startDate: string, endDate: string): boolean { - try { - const start = DateTime.fromFormat(startDate, 'yyyy-MM-dd HH:mm:ss'); - const end = DateTime.fromFormat(endDate, 'yyyy-MM-dd HH:mm:ss'); - return end > start; - } catch { - // If date parsing fails, assume invalid range - return false; - } -} - export function transformPropertyKey(property: string) { const propertyPatterns = ['properties', 'profile.properties']; const match = propertyPatterns.find((pattern) => @@ -118,43 +103,29 @@ export function getChartSql({ } sb.select.count = 'count(*) as count'; - - // Only apply WITH FILL if the date range is valid (endDate > startDate) - const hasValidDateRange = isValidDateRange(startDate, endDate); - switch (interval) { case 'minute': { - if (hasValidDateRange) { - sb.fill = `FROM toStartOfMinute(toDateTime('${startDate}')) TO toStartOfMinute(toDateTime('${endDate}')) STEP toIntervalMinute(1)`; - } + sb.fill = `FROM toStartOfMinute(toDateTime('${startDate}')) TO toStartOfMinute(toDateTime('${endDate}')) STEP toIntervalMinute(1)`; sb.select.date = 'toStartOfMinute(created_at) as date'; break; } case 'hour': { - if (hasValidDateRange) { - sb.fill = `FROM toStartOfHour(toDateTime('${startDate}')) TO toStartOfHour(toDateTime('${endDate}')) STEP toIntervalHour(1)`; - } + sb.fill = `FROM toStartOfHour(toDateTime('${startDate}')) TO toStartOfHour(toDateTime('${endDate}')) STEP toIntervalHour(1)`; sb.select.date = 'toStartOfHour(created_at) as date'; break; } case 'day': { - if (hasValidDateRange) { - sb.fill = `FROM toStartOfDay(toDateTime('${startDate}')) TO toStartOfDay(toDateTime('${endDate}')) STEP toIntervalDay(1)`; - } + sb.fill = `FROM toStartOfDay(toDateTime('${startDate}')) TO toStartOfDay(toDateTime('${endDate}')) STEP toIntervalDay(1)`; sb.select.date = 'toStartOfDay(created_at) as date'; break; } case 'week': { - if (hasValidDateRange) { - sb.fill = `FROM toStartOfWeek(toDateTime('${startDate}'), 1, '${timezone}') TO toStartOfWeek(toDateTime('${endDate}'), 1, '${timezone}') STEP toIntervalWeek(1)`; - } + sb.fill = `FROM toStartOfWeek(toDateTime('${startDate}'), 1, '${timezone}') TO toStartOfWeek(toDateTime('${endDate}'), 1, '${timezone}') STEP toIntervalWeek(1)`; sb.select.date = `toStartOfWeek(created_at, 1, '${timezone}') as date`; break; } case 'month': { - if (hasValidDateRange) { - sb.fill = `FROM toStartOfMonth(toDateTime('${startDate}'), '${timezone}') TO toStartOfMonth(toDateTime('${endDate}'), '${timezone}') STEP toIntervalMonth(1)`; - } + sb.fill = `FROM toStartOfMonth(toDateTime('${startDate}'), '${timezone}') TO toStartOfMonth(toDateTime('${endDate}'), '${timezone}') STEP toIntervalMonth(1)`; sb.select.date = `toStartOfMonth(created_at, '${timezone}') as date`; break; } diff --git a/packages/db/src/services/organization.service.ts b/packages/db/src/services/organization.service.ts index de81bd13..e96a9003 100644 --- a/packages/db/src/services/organization.service.ts +++ b/packages/db/src/services/organization.service.ts @@ -7,27 +7,6 @@ import { db } from '../prisma-client'; import { createSqlBuilder } from '../sql-builder'; import { getOrganizationAccess, getProjectAccess } from './access.service'; import { type IServiceProject, getProjectById } from './project.service'; - -/** - * Helper function to check if endDate is after startDate - * This prevents ClickHouse errors when WITH FILL TO value is less than FROM value - */ -function isValidDateRange(startDate: string, endDate: string): boolean { - try { - const start = DateTime.fromFormat(startDate, 'yyyy-MM-dd HH:mm:ss'); - const end = DateTime.fromFormat(endDate, 'yyyy-MM-dd HH:mm:ss'); - return end > start; - } catch { - // Try alternative format - try { - const start = new Date(startDate); - const end = new Date(endDate); - return !isNaN(start.getTime()) && !isNaN(end.getTime()) && end > start; - } catch { - return false; - } - } -} export type IServiceOrganization = Awaited< ReturnType >; @@ -260,19 +239,7 @@ export async function getOrganizationBillingEventsCountSerie( sb.select.count = 'COUNT(*) AS count'; sb.select.day = `toDate(toStartOf${interval.slice(0, 1).toUpperCase() + interval.slice(1)}(created_at)) AS ${interval}`; sb.groupBy.day = interval; - - // Only apply WITH FILL if the date range is valid (endDate > startDate) - const hasValidDateRange = isValidDateRange( - formatClickhouseDate(startDate, true), - formatClickhouseDate(endDate, true) - ); - - if (hasValidDateRange) { - sb.orderBy.day = `${interval} WITH FILL FROM toDate(${sqlstring.escape(formatClickhouseDate(startDate, true))}) TO toDate(${sqlstring.escape(formatClickhouseDate(endDate, true))}) STEP INTERVAL 1 ${interval.toUpperCase()}`; - } else { - sb.orderBy.day = `${interval} ASC`; - } - + sb.orderBy.day = `${interval} WITH FILL FROM toDate(${sqlstring.escape(formatClickhouseDate(startDate, true))}) TO toDate(${sqlstring.escape(formatClickhouseDate(endDate, true))}) STEP INTERVAL 1 ${interval.toUpperCase()}`; sb.where.projectIds = `project_id IN (${organization.projects.map((project) => sqlstring.escape(project.id)).join(',')})`; sb.where.createdAt = `${interval} BETWEEN ${sqlstring.escape(formatClickhouseDate(startDate, true))} AND ${sqlstring.escape(formatClickhouseDate(endDate, true))}`; diff --git a/packages/db/src/services/overview.service.ts b/packages/db/src/services/overview.service.ts index abb7c822..38ef8cc7 100644 --- a/packages/db/src/services/overview.service.ts +++ b/packages/db/src/services/overview.service.ts @@ -261,7 +261,7 @@ export class OverviewService { .rawWhere(this.getRawWhereClause('events', filters)) .groupBy(['date', 'ds.bounce_rate']) .orderBy('date', 'ASC') - .safeFill( + .fill( clix.toStartOf( clix.datetime( startDate, @@ -342,7 +342,7 @@ export class OverviewService { .having('sum(sign)', '>', 0) .rollup() .orderBy('date', 'ASC') - .safeFill( + .fill( clix.toStartOf( clix.datetime( startDate,