Skip to content

Commit 16abc6a

Browse files
authored
Merge pull request #60 from lohanidamodar/feat-get-sum
get sum and related tests
2 parents 2645c15 + 58df2f0 commit 16abc6a

5 files changed

Lines changed: 163 additions & 1 deletion

File tree

src/Database/Adapter.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,18 @@ abstract public function deleteDocument(string $collection, string $id): bool;
239239
*/
240240
abstract public function find(string $collection, array $queries = [], int $limit = 25, int $offset = 0, array $orderAttributes = [], array $orderTypes = [], array $orderAfter = []): array;
241241

242+
/**
243+
* Sum an attribute
244+
*
245+
* @param string $collection
246+
* @param string $attribute
247+
* @param Query[] $queries
248+
* @param int $max
249+
*
250+
* @return int|float
251+
*/
252+
abstract public function sum(string $collection, string $attribute, array $queries = [], int $max = 0);
253+
242254
/**
243255
* Count Documents
244256
*

src/Database/Adapter/MariaDB.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,60 @@ public function count(string $collection, array $queries = [], int $max = 0): in
643643
return $result['sum'] ?? 0;
644644
}
645645

646+
/**
647+
* Sum an Attribute
648+
*
649+
* Sum an attribute using chosen queries
650+
*
651+
* @param string $collection
652+
* @param string $attribute
653+
* @param Query[] $queries
654+
* @param int $max
655+
*
656+
* @return int|float
657+
*/
658+
public function sum(string $collection, string $attribute, array $queries = [], int $max = 0)
659+
{
660+
$name = $this->filter($collection);
661+
$roles = Authorization::getRoles();
662+
$where = ['1=1'];
663+
$limit = ($max === 0) ? '' : 'LIMIT :max';
664+
665+
$permissions = (Authorization::$status) ? $this->getSQLPermissions($roles) : '1=1'; // Disable join when no authorization required
666+
667+
foreach($queries as $i => $query) {
668+
$conditions = [];
669+
foreach ($query->getValues() as $key => $value) {
670+
$conditions[] = $this->getSQLCondition('table_main.'.$query->getAttribute(), $query->getOperator(), ':attribute_'.$i.'_'.$key.'_'.$query->getAttribute(), $value);
671+
}
672+
673+
$where[] = implode(' OR ', $conditions);
674+
}
675+
676+
$stmt = $this->getPDO()->prepare("SELECT SUM({$attribute}) as sum FROM (SELECT {$attribute} FROM {$this->getNamespace()}.{$name} table_main
677+
WHERE {$permissions} AND ".implode(' AND ', $where)."
678+
{$limit}) table_count
679+
");
680+
681+
foreach($queries as $i => $query) {
682+
if($query->getOperator() === Query::TYPE_SEARCH) continue;
683+
foreach($query->getValues() as $key => $value) {
684+
$stmt->bindValue(':attribute_'.$i.'_'.$key.'_'.$query->getAttribute(), $value, $this->getPDOType($value));
685+
}
686+
}
687+
688+
if($max !== 0) {
689+
$stmt->bindValue(':max', $max, PDO::PARAM_INT);
690+
}
691+
692+
$stmt->execute();
693+
694+
/** @var array $result */
695+
$result = $stmt->fetch(PDO::FETCH_ASSOC);
696+
697+
return $result['sum'] ?? 0;
698+
}
699+
646700
/**
647701
* Get max STRING limit
648702
*

src/Database/Adapter/MongoDB.php

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,6 @@ public function count(string $collection, array $queries = [], int $max = 0): in
518518
{
519519
$name = $this->filter($collection);
520520
$collection = $this->getDatabase()->$name;
521-
$roles = Authorization::getRoles();
522521

523522
$filters = [];
524523

@@ -538,6 +537,55 @@ public function count(string $collection, array $queries = [], int $max = 0): in
538537
return $collection->countDocuments($filters, $options);
539538
}
540539

540+
/**
541+
* Sum an attribute
542+
*
543+
* @param string $collection
544+
* @param string $attribute
545+
* @param Query[] $queries
546+
* @param int $max
547+
*
548+
* @return int|float
549+
*/
550+
public function sum(string $collection, string $attribute, array $queries = [], int $max = 0)
551+
{
552+
$name = $this->filter($collection);
553+
$collection = $this->getDatabase()->$name;
554+
555+
$filters = [];
556+
557+
// queries
558+
$filters = $this->buildFilters($queries);
559+
560+
// permissions
561+
if (Authorization::$status) { // skip if authorization is disabled
562+
$filters['_read']['$in'] = Authorization::getRoles();
563+
}
564+
565+
// using aggregation to get sum an attribute as described in
566+
// https://docs.mongodb.com/manual/reference/method/db.collection.aggregate/
567+
// Pipeline consists of stages to aggregation, so first we set $match
568+
// that will load only documents that matches the filters provided and passes to the next stage
569+
// then we set $limit (if $max is provided) so that only $max documents will be passed to the next stage
570+
// finally we use $group stage to sum the provided attribute that matches the given filters and max
571+
// We pass the $pipeline to the aggregate method, which returns a cursor, then we get
572+
// the array of results from the cursor and we return the total sum of the attribute
573+
$pipeline = [];
574+
if(!empty($filters)) {
575+
$pipeline[] = ['$match' => $filters];
576+
}
577+
if(!empty($max)) {
578+
$pipeline[] = ['$limit' => $max];
579+
}
580+
$pipeline[] = [
581+
'$group' => [
582+
'_id' => null,
583+
'total' => ['$sum' => '$' . $attribute],
584+
],
585+
];
586+
return ($collection->aggregate($pipeline)->toArray()[0] ?? [])['total'] ?? 0;
587+
}
588+
541589
/**
542590
* @return MongoDatabase
543591
*

src/Database/Database.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,25 @@ public function count(string $collection, array $queries = [], int $max = 0): in
10491049
return $count;
10501050
}
10511051

1052+
/**
1053+
* Sum an attribute
1054+
*
1055+
* Sum an attribute for all the documents. Pass $max=0 for unlimited count
1056+
*
1057+
* @param string $collection
1058+
* @param string $attribute
1059+
* @param Query[] $queries
1060+
* @param int $max
1061+
*
1062+
* @return int|float
1063+
*/
1064+
public function sum(string $collection, string $attribute, array $queries = [], int $max = 0)
1065+
{
1066+
$count = $this->adapter->sum($collection, $attribute, $queries, $max);
1067+
1068+
return $count;
1069+
}
1070+
10521071
// /**
10531072
// * @param array $data
10541073
// *

tests/Database/Base.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,35 @@ public function testCount()
982982
Authorization::reset();
983983
}
984984

985+
/**
986+
* @depends testFind
987+
*/
988+
public function testSum()
989+
{
990+
Authorization::setRole('userx');
991+
$sum = static::getDatabase()->sum('movies', 'year', [new Query('year', Query::TYPE_EQUAL, [2019]),]);
992+
$this->assertEquals(2019+2019, $sum);
993+
$sum = static::getDatabase()->sum('movies', 'year');
994+
$this->assertEquals(2013+2019+2011+2019+2025+2026, $sum);
995+
$sum = static::getDatabase()->sum('movies', 'price', [new Query('year', Query::TYPE_EQUAL, [2019]),]);
996+
$this->assertEquals(round(39.50+25.99, 2), round($sum, 2));
997+
$sum = static::getDatabase()->sum('movies', 'price', [new Query('year', Query::TYPE_EQUAL, [2019]),]);
998+
$this->assertEquals(round(39.50+25.99, 2), round($sum, 2));
999+
1000+
$sum = static::getDatabase()->sum('movies', 'year', [new Query('year', Query::TYPE_EQUAL, [2019])], 1);
1001+
$this->assertEquals(2019, $sum);
1002+
1003+
Authorization::unsetRole('userx');
1004+
$sum = static::getDatabase()->sum('movies', 'year', [new Query('year', Query::TYPE_EQUAL, [2019]),]);
1005+
$this->assertEquals(2019+2019, $sum);
1006+
$sum = static::getDatabase()->sum('movies', 'year');
1007+
$this->assertEquals(2013+2019+2011+2019+2025, $sum);
1008+
$sum = static::getDatabase()->sum('movies', 'price', [new Query('year', Query::TYPE_EQUAL, [2019]),]);
1009+
$this->assertEquals(round(39.50+25.99, 2), round($sum, 2));
1010+
$sum = static::getDatabase()->sum('movies', 'price', [new Query('year', Query::TYPE_EQUAL, [2019]),]);
1011+
$this->assertEquals(round(39.50+25.99, 2), round($sum, 2));
1012+
}
1013+
9851014
public function testEncodeDecode()
9861015
{
9871016
$collection = new Document([

0 commit comments

Comments
 (0)