Skip to content

Commit

Permalink
feat(Firestore): SUM / AVG aggregation feature (#6557)
Browse files Browse the repository at this point in the history
  • Loading branch information
yash30201 committed Nov 15, 2023
1 parent abbfb83 commit 53a2795
Show file tree
Hide file tree
Showing 9 changed files with 430 additions and 57 deletions.
71 changes: 68 additions & 3 deletions Firestore/src/Aggregate.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ class Aggregate
*/
private const TYPE_COUNT = 'count';

/**
* Default placeholder for all sum aggregate props.
*/
private const TYPE_SUM = 'sum';

/**
* Default placeholder for all average aggregate props.
*/
private const TYPE_AVG = 'avg';

/**
* @var array Aggregation properties for an AggregateQuery.
*/
Expand All @@ -63,9 +73,64 @@ private function __construct($aggregationType)
*/
public static function count()
{
$count = new Aggregate(self::TYPE_COUNT);
$count->props[$count->aggregationType] = [];
return $count;
return self::createAggregate(self::TYPE_COUNT);
}

/**
* Creates sum aggregation properties.
*
* Example:
* ```
* $sum = Aggregate::sum('field_to_aggregate_upon');
* ```
* Result of SUM aggregation can be a integer or a float.
* Sum of integers which exceed maxinum integer value returns a float.
* Sum of numbers exceeding max float value returns `INF`.
* Sum of data which contains `NaN` returns `NaN`.
* Non numeric values are ignored.
*
* @param string $field The relative path of the field to aggregate upon.
* @return Aggregate
*/
public static function sum($field)
{
return self::createAggregate(self::TYPE_SUM, $field);
}

/**
* Creates average aggregation properties.
*
* Example:
* ```
* $avg = Aggregate::avg('field_to_aggregate_upon');
* ```
*
* Result of AVG aggregation can be a float or a null.
* Average of empty valid data set return `null`.
* Average of numbers exceeding max float value returns `INF`.
* Average of data which contains `NaN` returns `NaN`.
* Non numeric values are ignored.
*
* @param string|null $field The relative path of the field to aggregate upon.
* @return Aggregate
*/
public static function avg($field)
{
return self::createAggregate(self::TYPE_AVG, $field);
}

private static function createAggregate(string $type, $field = null)
{
$aggregate = new Aggregate($type);
$aggregate->props[$aggregate->aggregationType] = [];
if (!is_null($field)) {
$aggregate->props[$aggregate->aggregationType] = [
'field' => [
'fieldPath' => $field
]
];
}
return $aggregate;
}

/**
Expand Down
10 changes: 8 additions & 2 deletions Firestore/src/AggregateQuerySnapshot.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public function getReadTime()
* Get the Query Aggregation value.
*
* @param string $alias The aggregation alias.
* @return int
* @return mixed
* @throws \InvalidArgumentException If provided alias does not exist in result.
*/
public function get($alias)
Expand All @@ -106,7 +106,13 @@ public function get($alias)
}
$result = $this->aggregateFields[$alias];
if (is_array($result)) {
return $result['integerValue'];
$key = array_key_first($result);
if ($key == 'nullValue') {
return null;
}
// `$result` would contain only one of
// (@see https://cloud.google.com/firestore/docs/reference/rest/v1/Value)
return $result[$key];
}
return $result;
}
Expand Down
58 changes: 58 additions & 0 deletions Firestore/src/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,64 @@ public function count(array $options = [])
return $aggregationResult->get('count');
}

/**
* Gets the sum of all documents matching the provided query filters.
*
* Example:
* ```
* $sum = $query->sum();
* ```
*
* Sum of integers which exceed maximum integer value returns a float.
* Sum of numbers exceeding max float value returns `INF`.
* Sum of data which contains `NaN` returns `NaN`.
* Non numeric values are ignored.
*
* @param string $field The relative path of the field to aggregate upon.
* @param array $options [optional] {
* Configuration options is an array.
*
* @type Timestamp $readTime Reads entities as they were at the given timestamp.
* }
* @return int|float
*/
public function sum(string $field, array $options = [])
{
$aggregateQuery = $this->addAggregation(Aggregate::sum($field)->alias('sum'));

$aggregationResult = $aggregateQuery->getSnapshot($options);
return $aggregationResult->get('sum');
}

/**
* Gets the average of all documents matching the provided query filters.
*
* Example:
* ```
* $avg = $query->avg();
* ```
*
* Average of empty valid data set return `null`.
* Average of numbers exceeding max float value returns `INF`.
* Average of data which contains `NaN` returns `NaN`.
* Non numeric values are ignored.
*
* @param string $field The relative path of the field to aggregate upon.
* @param array $options [optional] {
* Configuration options is an array.
*
* @type Timestamp $readTime Reads entities as they were at the given timestamp.
* }
* @return float|null
*/
public function avg(string $field, array $options = [])
{
$aggregateQuery = $this->addAggregation(Aggregate::avg($field)->alias('avg'));

$aggregationResult = $aggregateQuery->getSnapshot($options);
return $aggregationResult->get('avg');
}

/**
* Returns an aggregate query provided an aggregation with existing query filters.
*
Expand Down
Loading

0 comments on commit 53a2795

Please sign in to comment.