Skip to content

Commit 08ca4e5

Browse files
lohanidamodarclaude
andcommitted
feat: add sumDailyBatch() for batch billing queries
Single query with GROUP BY metric for summing multiple metrics from the daily table. Returns array<string, int>. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 84c4332 commit 08ca4e5

4 files changed

Lines changed: 102 additions & 0 deletions

File tree

src/Usage/Adapter.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ abstract public function findDaily(array $queries = []): array;
133133
*/
134134
abstract public function sumDaily(array $queries = [], string $attribute = 'value'): int;
135135

136+
/**
137+
* Sum multiple event metrics from the pre-aggregated daily table in one query.
138+
*
139+
* @param array<string> $metrics List of metric names
140+
* @param array<\Utopia\Query\Query> $queries Additional filters (e.g. date range)
141+
* @return array<string, int> Metric name => sum value
142+
*/
143+
abstract public function sumDailyBatch(array $metrics, array $queries = []): array;
144+
136145
/**
137146
* Set the namespace prefix for table names.
138147
*

src/Usage/Adapter/ClickHouse.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1648,6 +1648,70 @@ public function sumDaily(array $queries = [], string $attribute = 'value'): int
16481648
return (is_array($json) && isset($json['data'][0]['total'])) ? (int) $json['data'][0]['total'] : 0;
16491649
}
16501650

1651+
/**
1652+
* Sum multiple event metrics from the pre-aggregated daily table in one query.
1653+
*
1654+
* @param array<string> $metrics
1655+
* @param array<Query> $queries
1656+
* @return array<string, int>
1657+
* @throws Exception
1658+
*/
1659+
public function sumDailyBatch(array $metrics, array $queries = []): array
1660+
{
1661+
if (empty($metrics)) {
1662+
return [];
1663+
}
1664+
1665+
$this->setOperationContext('sumDailyBatch()');
1666+
1667+
$totals = \array_fill_keys($metrics, 0);
1668+
1669+
$fromTable = $this->buildTableReference($this->getEventsDailyTableName());
1670+
1671+
// Build metric IN params
1672+
$metricParams = [];
1673+
$metricPlaceholders = [];
1674+
foreach ($metrics as $i => $metric) {
1675+
$paramName = 'metric_' . $i;
1676+
$metricParams[$paramName] = $metric;
1677+
$metricPlaceholders[] = "{{$paramName}:String}";
1678+
}
1679+
$metricInClause = implode(', ', $metricPlaceholders);
1680+
1681+
$parsed = $this->parseQueries($queries, Usage::TYPE_EVENT);
1682+
$params = array_merge($metricParams, $parsed['params']);
1683+
1684+
$whereData = $this->buildWhereClause($parsed['filters'], $params);
1685+
$whereClause = $whereData['clause'];
1686+
$params = $whereData['params'];
1687+
1688+
$metricFilter = $this->escapeIdentifier('metric') . " IN ({$metricInClause})";
1689+
$whereClause = !empty($whereClause)
1690+
? $whereClause . ' AND ' . $metricFilter
1691+
: ' WHERE ' . $metricFilter;
1692+
1693+
$sql = "
1694+
SELECT metric, SUM(value) as total
1695+
FROM {$fromTable}{$whereClause}
1696+
GROUP BY metric
1697+
FORMAT JSON
1698+
";
1699+
1700+
$result = $this->query($sql, $params);
1701+
$json = json_decode($result, true);
1702+
1703+
if (is_array($json) && isset($json['data']) && is_array($json['data'])) {
1704+
foreach ($json['data'] as $row) {
1705+
$metricName = $row['metric'] ?? '';
1706+
if (isset($totals[$metricName])) {
1707+
$totals[$metricName] = (int) ($row['total'] ?? 0);
1708+
}
1709+
}
1710+
}
1711+
1712+
return $totals;
1713+
}
1714+
16511715
/**
16521716
* Get time series data for metrics with query-time aggregation.
16531717
*

src/Usage/Adapter/Database.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,22 @@ public function findDaily(array $queries = []): array
284284
return $this->find($queries, Usage::TYPE_EVENT);
285285
}
286286

287+
/**
288+
* Sum multiple metrics from daily table — falls back to individual sumDaily calls.
289+
*
290+
* @param array<\Utopia\Query\Query> $queries
291+
* @return array<string, int>
292+
*/
293+
public function sumDailyBatch(array $metrics, array $queries = []): array
294+
{
295+
$totals = \array_fill_keys($metrics, 0);
296+
foreach ($metrics as $metric) {
297+
$metricQueries = array_merge($queries, [Query::equal('metric', [$metric])]);
298+
$totals[$metric] = $this->sumDaily($metricQueries);
299+
}
300+
return $totals;
301+
}
302+
287303
/**
288304
* Sum from daily table — Database adapter falls back to regular sum for events.
289305
*

src/Usage/Usage.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,19 @@ public function sumDaily(array $queries = [], string $attribute = 'value'): int
224224
return $this->adapter->sumDaily($queries, $attribute);
225225
}
226226

227+
/**
228+
* Sum multiple event metrics from the pre-aggregated daily table in one query.
229+
*
230+
* @param array<string> $metrics List of metric names
231+
* @param array<\Utopia\Query\Query> $queries Additional filters (e.g. date range)
232+
* @return array<string, int> Metric name => sum value
233+
* @throws \Exception
234+
*/
235+
public function sumDailyBatch(array $metrics, array $queries = []): array
236+
{
237+
return $this->adapter->sumDailyBatch($metrics, $queries);
238+
}
239+
227240
/**
228241
* Set the namespace prefix for table names.
229242
*

0 commit comments

Comments
 (0)