diff --git a/lib/cartodb/models/aggregation/aggregation-query.js b/lib/cartodb/models/aggregation/aggregation-query.js index 3e6ba873..0e01b5af 100644 --- a/lib/cartodb/models/aggregation/aggregation-query.js +++ b/lib/cartodb/models/aggregation/aggregation-query.js @@ -119,83 +119,13 @@ const aggregateColumnDefs = ctx => { const aggregateDimensions = ctx => ctx.dimensions || {}; const timeDimensionParameters = definition => { - let group_by_count = definition.step || 1; - let group_by_units; - let group_by_cycle; - switch (definition.group_by) { - case 'second': - case 'minute': - case 'hour': - case 'day': - case 'week': - case 'month': - case 'quarter': - case 'year': - case 'century': - case 'millenium': - group_by_units = definition.group_by; - break; - case 'semester': - group_by_units = 'month'; - group_by_count *= 6; - break; - case 'trimester': - group_by_units = 'month'; - group_by_count *= 4; - break; - case 'minuteOfHour': - group_by_units = 'minute'; - group_by_cycle = 'hour'; - break; - case 'hourOfDay': - group_by_units = 'hour'; - group_by_cycle = 'day'; - break; - case 'dayOfWeek': - group_by_units = 'day'; - group_by_cycle = 'week'; - break; - case 'dayOfMonth': - group_by_units = 'day'; - group_by_cycle = 'month'; - break; - case 'dayOfYear': - group_by_units = 'day'; - group_by_cycle = 'year'; - break; - case 'weekOfYear': - group_by_units = 'week'; - group_by_cycle = 'year'; - break; - case 'monthOfYear': - group_by_units = 'month'; - group_by_cycle = 'year'; - break; - case 'quarterOfYear': - group_by_units = 'quarter'; - group_by_cycle = 'year'; - break; - case 'trimesterOfYear': - group_by_units = 'month'; - group_by_count *= 4; - group_by_cycle = 'year'; - break; - case 'semesterOfYear': - group_by_units = 'month'; - group_by_count *= 6; - group_by_cycle = 'year'; - break; - default: - throw new Error(`Invalid time grouping ${definition.group_by}`); - } // definition.column should correspond to a wrapped date column return { time: `to_timestamp("${definition.column}")`, timezone: definition.timezone || 'utc', - group_by_units, - group_by_count, - group_by_cycle, - epoch: definition.starting + group_by: definition.group_by, + group_by_count: definition.group_by_count || 1, + starting: definition.starting }; }; diff --git a/lib/cartodb/models/aggregation/time-dimension.js b/lib/cartodb/models/aggregation/time-dimension.js index 8d39b671..63073c2f 100644 --- a/lib/cartodb/models/aggregation/time-dimension.js +++ b/lib/cartodb/models/aggregation/time-dimension.js @@ -1,24 +1,3 @@ -const MONTH_SECONDS = 365.2425 / 12 * 24 * 3600; // PG intervals use 30 * 24 * 3600 -const YEAR_SECONDS = 12 * MONTH_SECONDS; - -// time unit durations -const usecs = { - second: 1, - minute: 60, - hour: 3600, - day: 24 * 3600, - week: 7 * 24 * 3600, - month: MONTH_SECONDS, - year: YEAR_SECONDS, - - quarter: 3 * MONTH_SECONDS, - semester: 6 * MONTH_SECONDS, - trimester: 4 * MONTH_SECONDS, - decade: 12 * YEAR_SECONDS, - century: 100 * YEAR_SECONDS, - millennium: 1000 * YEAR_SECONDS -}; - const YEARSPAN = "(date_part('year', $t)-date_part('year', $epoch))"; // Note that SECONDSPAN is not a UTC epoch, but an epoch in the specified TZ, // so we can use it to compute any multiple of seconds with it without using date_part or date_trunc @@ -125,14 +104,14 @@ function serialNormalize(u, m) { } function cyclicNormalize(u, m, c) { - if (u === 'month' && m === 3) { - u = 'quarter'; + if (u === 'monthOfYear' && m === 3) { + u = 'quarterOfYear'; m = 1; - } else if (u === 'month' && m === 6) { - u = 'semester'; + } else if (u === 'monthOfYear' && m === 6) { + u = 'semesterOfYear'; m = 1; - } else if (u === 'month' && m === 4) { - u = 'trimester'; + } else if (u === 'monthOfYear' && m === 4) { + u = 'trimesterOfYear'; m = 1; } if (m !== 1) { @@ -185,96 +164,89 @@ function epochExpression(epoch) { return `TIMESTAMP '${epoch}'`; } -function cyclicSqlExpr(t, tz, u, c, m = 1) { +function cyclicSqlExpr(t, tz, u, m = 1) { [u, m, c] = cyclicNormalize(u, m, c); - const comb = `${u}/${c}`; const column = timeExpression(t, tz); if (m === 1) { - switch (comb) { - case 'day/week': + switch (u) { + case 'dayOfWeek': // 1 = monday; 7 = sunday; return `date_part('isodow', ${column})`; - case 'day/month': + case 'dayOfMonth': // result: 1-31 return `date_part('day', ${column})`; - case 'day/year': + case 'dayOfYear': // result: 1-366 return `date_part('doy', ${column})`; - case 'hour/day': + case 'hourOfDay': // result: 0-23 return `date_part('hour', ${column})`; - case 'month/year': + case 'monthOfYear': // result 1-12 return `date_part('month', ${column})`; - case 'quarter/year': + case 'quarterOfYear': // result 1-4 return `date_part('quarter', ${column})`; - case 'semester/year': + case 'semesterOfYear': // result 1-2 return `FLOOR((date_part('month', ${column})-1)/6.0) + 1`; - case 'trimester/year': + case 'trimesterOfYear': // result 1-3 return `FLOOR((date_part('month', ${column})-1)/4.0) + 1`; - case 'week/year': + case 'weekOfYear': // result 1-53 return `date_part('week', ${column})`; - case 'minute/hour': + case 'minuteOfHour': // result 0-59 return `date_part('minute', ${column})`; } } - // TODO: remove generic expression, reaching here should error - return genericCyclicSqlExpr(t, u, c, 0, m); -} - -function genericCyclicSqlExpr(t, tz, u, c, c_offset = 0, m = 1) { - const usec = usecs[u]; - const csec = usecs[c]; - const column = timeExpression(t, tz); - return `((FLOOR(date_part('epoch', ${column})/(${usec*m}))*(${usec*m})+${c_offset}) % ${csec})/${usec*m}`; + throw new Error(`Invalid cyclic time grouping ${u}`) } function validateParameters(_params) { return true; } +function isCyclic(groupBy) { + return groupBy.match(/.+By.+/); +} + function classificationSql(params) { validateParameters(params); - if (params.group_by_cycle) { + if (isCyclic(params.group_by)) { // TODO: validate group_by_count === 1, No epoch return cyclicSqlExpr( params.time, - params.timezone || 'utc', - params.group_by_units, - params.group_by_cycle, - 0, - params.group_by_count || 1 + params.timezone, + params.group_by, + params.group_by_count ); } else if (params.format === 'iso') { // TODO: validate group_by_count === 1, No epoch return isoSqlExpr( params.time, - params.timezone || 'utc', - params.group_by_units, - params.group_by_count || 1 + params.timezone, + params.group_by, + params.group_by_count ); } else { return serialSqlExpr( params.time, - params.timezone || 'utc', - params.group_by_units, - params.group_by_count || 1, - params.epoch + params.timezone, + params.group_by, + params.group_by_count, + params.starting ); }