Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/Database/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,13 @@ abstract public function getSupportForCastIndexArray(): bool;
*/
abstract public function getSupportForUpserts(): bool;

/**
* Are sum queries supported?
*
* @return bool
*/
abstract public function getSupportForSum(): bool;

/**
* Get current attribute count from collection document
*
Expand Down Expand Up @@ -988,6 +995,25 @@ protected function getAttributeSelections(array $queries): array
return $selections;
}

/**
* Get all sum attributes from queries
*
* @param Query[] $queries
* @return array<string|array<string>>
*/
protected function getAttributeSums(array $queries): array
{
$selections = [];

foreach ($queries as $query) {
if ($query->getMethod() === Query::TYPE_SUM) {
$selections[] = $query->getValues();
}
}

return $selections;
}

/**
* Filter Keys
*
Expand Down
43 changes: 42 additions & 1 deletion src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -2139,15 +2139,22 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
$sqlLimit .= \is_null($offset) ? '' : ' OFFSET :offset';

$selections = $this->getAttributeSelections($queries);
$sumSelections = $this->getAttributeSums($queries);

$sql = "
SELECT {$this->getAttributeProjection($selections, 'table_main')}
FROM {$this->getSQLTable($name)} AS table_main
{$sqlWhere}
{$sqlOrder}
{$sqlLimit};
{$sqlLimit}
";

if (!empty($sumSelections)) {
$sql = "SELECT {$this->getSumQueries($sumSelections)} FROM ({$sql}) table_sum;";
} else {
$sql .= ';';
}

$sql = $this->trigger(Database::EVENT_DOCUMENT_FIND, $sql);

$stmt = $this->getPDO()->prepare($sql);
Expand Down Expand Up @@ -2434,6 +2441,30 @@ protected function getAttributeProjection(array $selections, string $prefix = ''
return \implode(', ', $selections);
}

/**
* Get the SQL sum queries given the selected attributes
*
* @param array<array<string>|string> $attributeGroups
* @return string
*/
protected function getSumQueries(array $attributeGroups): string
{
$sumExpressions = [];

foreach ($attributeGroups as $attributeGroup) {
if (\is_string($attributeGroup)) {
$attributeGroup = [$attributeGroup];
}

$columnAlias = \implode('+', $attributeGroup);
$sumExpression = implode('+', array_map(fn ($attribute) => "`{$this->filter($attribute)}`", $attributeGroup));

$sumExpressions[] = "SUM({$sumExpression}) AS `{$columnAlias}`";
}

return \implode(', ', $sumExpressions);
}

/**
* Get SQL Condition
*
Expand Down Expand Up @@ -2614,6 +2645,16 @@ public function getSupportForUpserts(): bool
return true;
}

/**
* Are sum queries supported?
*
* @return bool
*/
public function getSupportForSum(): bool
{
return true;
}

/**
* Set max execution time
* @param int $milliseconds
Expand Down
5 changes: 5 additions & 0 deletions src/Database/Adapter/Mongo.php
Original file line number Diff line number Diff line change
Expand Up @@ -1820,6 +1820,11 @@ public function getSupportForUpserts(): bool
return false;
}

public function getSupportForSum(): bool
{
return false;
}

/**
* Get current attribute count from collection document
*
Expand Down
43 changes: 42 additions & 1 deletion src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -1917,15 +1917,22 @@ public function find(string $collection, array $queries = [], ?int $limit = 25,
$sqlLimit = \is_null($limit) ? '' : 'LIMIT :limit';
$sqlLimit .= \is_null($offset) ? '' : ' OFFSET :offset';
$selections = $this->getAttributeSelections($queries);
$sumSelections = $this->getAttributeSums($queries);

$sql = "
SELECT {$this->getAttributeProjection($selections, 'table_main')}
FROM {$this->getSQLTable($name)} as table_main
{$sqlWhere}
{$sqlOrder}
{$sqlLimit};
{$sqlLimit}
";

if (!empty($sumSelections)) {
$sql = "SELECT {$this->getSumQueries($sumSelections)} FROM ({$sql}) table_sum;";
} else {
$sql .= ';';
}

$sql = $this->trigger(Database::EVENT_DOCUMENT_FIND, $sql);

$stmt = $this->getPDO()->prepare($sql);
Expand Down Expand Up @@ -2201,6 +2208,30 @@ protected function getAttributeProjection(array $selections, string $prefix = ''
return \implode(', ', $selections);
}

/**
* Get the SQL sum queries given the selected attributes
*
* @param array<string|array<string>> $attributeGroups
* @return string
*/
protected function getSumQueries(array $attributeGroups): string
{
$sumExpressions = [];

foreach ($attributeGroups as $attributeGroup) {
if (\is_string($attributeGroup)) {
$attributeGroup = [$attributeGroup];
}

$columnAlias = \implode('+', $attributeGroup);
$sumExpression = implode('+', array_map(fn ($attribute) => "\"{$this->filter($attribute)}\"", $attributeGroup));

$sumExpressions[] = "SUM({$sumExpression}) AS \"{$columnAlias}\"";
}

return \implode(', ', $sumExpressions);
}


/**
* Get SQL Condition
Expand Down Expand Up @@ -2449,6 +2480,16 @@ public function getSupportForUpserts(): bool
return false;
}

/**
* Are sum queries supported?
*
* @return bool
*/
public function getSupportForSum(): bool
{
return true;
}

/**
* @return string
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Database/Adapter/SQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ public function getSupportForRelationships(): bool
*/
protected function bindConditionValue(mixed $stmt, Query $query): void
{
if ($query->getMethod() == Query::TYPE_SELECT) {
if ($query->getMethod() == Query::TYPE_SELECT || $query->getMethod() == Query::TYPE_SUM) {
return;
}

Expand Down Expand Up @@ -1134,7 +1134,7 @@ public function getSQLConditions(array $queries = [], string $separator = 'AND')
$conditions = [];
foreach ($queries as $query) {

if ($query->getMethod() === Query::TYPE_SELECT) {
if ($query->getMethod() === Query::TYPE_SELECT || $query->getMethod() === Query::TYPE_SUM) {
continue;
}

Expand Down
11 changes: 10 additions & 1 deletion src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -5533,6 +5533,7 @@ public function find(string $collection, array $queries = [], string $forPermiss

$selections = $this->validateSelections($collection, $selects);
$nestedSelections = [];
$skipDecode = false;

foreach ($queries as $index => &$query) {
switch ($query->getMethod()) {
Expand Down Expand Up @@ -5567,6 +5568,12 @@ public function find(string $collection, array $queries = [], string $forPermiss
}
$query->setValues(\array_values($values));
break;
case Query::TYPE_SUM:
$skipDecode = true;
if (\str_contains($query->getAttribute(), '.')) {
unset($queries[$index]);
}
break;
default:
if (\str_contains($query->getAttribute(), '.')) {
unset($queries[$index]);
Expand Down Expand Up @@ -5596,7 +5603,9 @@ public function find(string $collection, array $queries = [], string $forPermiss
$node = $this->silent(fn () => $this->populateDocumentRelationships($collection, $node, $nestedSelections));
}
$node = $this->casting($collection, $node);
$node = $this->decode($collection, $node, $selections);
if (!$skipDecode) {
$node = $this->decode($collection, $node, $selections);
}

if (!$node->isEmpty()) {
$node->setAttribute('$collection', $collection->getId());
Expand Down
14 changes: 14 additions & 0 deletions src/Database/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Query
public const TYPE_ENDS_WITH = 'endsWith';

public const TYPE_SELECT = 'select';
public const TYPE_SUM = 'sum';

// Order methods
public const TYPE_ORDER_DESC = 'orderDesc';
Expand Down Expand Up @@ -53,6 +54,7 @@ class Query
self::TYPE_STARTS_WITH,
self::TYPE_ENDS_WITH,
self::TYPE_SELECT,
self::TYPE_SUM,
self::TYPE_ORDER_DESC,
self::TYPE_ORDER_ASC,
self::TYPE_LIMIT,
Expand Down Expand Up @@ -214,6 +216,7 @@ public static function isMethod(string $value): bool
self::TYPE_ENDS_WITH,
self::TYPE_OR,
self::TYPE_AND,
self::TYPE_SUM,
self::TYPE_SELECT => true,
default => false,
};
Expand Down Expand Up @@ -459,6 +462,17 @@ public static function select(array $attributes): self
return new self(self::TYPE_SELECT, values: $attributes);
}

/**
* Helper method to create Query with sum method
*
* @param array<string> $attributes
* @return Query
*/
public static function sum(array $attributes): self
{
return new self(self::TYPE_SUM, values: $attributes);
}

/**
* Helper method to create Query with orderDesc method
*
Expand Down
13 changes: 13 additions & 0 deletions src/Database/Validator/Queries.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ public function isValid($value): bool
return false;
}

$methodsUsed = array_map(
fn (Query|string $query) => $query instanceof Query ? $query->getMethod() : $query,
$value
);

$methodsUsed = array_unique($methodsUsed);

if (in_array(Query::TYPE_SELECT, $methodsUsed) && in_array(Query::TYPE_SUM, $methodsUsed)) {
$this->message = 'Invalid query: Cannot mix select and sum queries';
return false;
}

foreach ($value as $query) {
if (!$query instanceof Query) {
try {
Expand All @@ -80,6 +92,7 @@ public function isValid($value): bool
$method = $query->getMethod();
$methodType = match ($method) {
Query::TYPE_SELECT => Base::METHOD_TYPE_SELECT,
Query::TYPE_SUM => Base::METHOD_TYPE_SUM,
Query::TYPE_LIMIT => Base::METHOD_TYPE_LIMIT,
Query::TYPE_OFFSET => Base::METHOD_TYPE_OFFSET,
Query::TYPE_CURSOR_AFTER,
Expand Down
2 changes: 1 addition & 1 deletion src/Database/Validator/Queries/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(array $attributes)
]);

$validators = [
new Select($attributes),
new Select($attributes)
];

parent::__construct($validators);
Expand Down
2 changes: 2 additions & 0 deletions src/Database/Validator/Queries/Documents.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\Query\Order;
use Utopia\Database\Validator\Query\Select;
use Utopia\Database\Validator\Query\Sum;

class Documents extends IndexedQueries
{
Expand Down Expand Up @@ -66,6 +67,7 @@ public function __construct(
),
new Order($attributes),
new Select($attributes),
new Sum($attributes),
];

parent::__construct($attributes, $indexes, $validators);
Expand Down
1 change: 1 addition & 0 deletions src/Database/Validator/Query/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ abstract class Base extends Validator
public const METHOD_TYPE_ORDER = 'order';
public const METHOD_TYPE_FILTER = 'filter';
public const METHOD_TYPE_SELECT = 'select';
public const METHOD_TYPE_SUM = 'sum';

protected string $message = 'Invalid query';

Expand Down
Loading
Loading