Skip to content
Open
5 changes: 4 additions & 1 deletion .nix/php/lib/php.ini.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@ file_uploads = On
max_file_uploads = 20
short_open_tag = off
opcache.enable=1
opcache.enable_cli=0
opcache.enable_cli=0
apc.enabled=1
apc.enable_cli=1
apc.shm_size=128M
1 change: 1 addition & 0 deletions .nix/pkgs/flow-php/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ let
with all;
enabled
++ [
apcu
bcmath
dom
mbstring
Expand Down
2 changes: 2 additions & 0 deletions src/core/etl/src/Flow/ETL/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Flow\ETL;

use Flow\ETL\Config\Aggregation\AggregationConfig;
use Flow\ETL\Config\Cache\CacheConfig;
use Flow\ETL\Config\ConfigBuilder;
use Flow\ETL\Config\Sort\SortConfig;
Expand Down Expand Up @@ -36,6 +37,7 @@ public function __construct(
public SortConfig $sort,
private ?Analyze $analyze,
public TelemetryConfig $telemetry,
public AggregationConfig $aggregation,
) {}

public static function builder(): ConfigBuilder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Flow\ETL\Config\Aggregation;

use Flow\ETL\Dataset\Memory\Unit;

final readonly class AggregationConfig
{
public const string AGGREGATION_MAX_MEMORY_ENV = 'FLOW_AGGREGATION_MAX_MEMORY';

public function __construct(
public Unit $memoryLimit,
public string $filesystemProtocol = 'file',
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Flow\ETL\Config\Aggregation;

use Flow\ETL\Dataset\Memory\Unit;

use function getenv;
use function is_string;

use const PHP_INT_MAX;

final class AggregationConfigBuilder
{
private string $filesystemProtocol = 'file';

private ?Unit $memoryLimit = null;

public function build(): AggregationConfig
{
if ($this->memoryLimit === null) {
$envLimit = getenv(AggregationConfig::AGGREGATION_MAX_MEMORY_ENV);

$this->memoryLimit = is_string($envLimit) ? Unit::fromString($envLimit) : Unit::fromBytes(PHP_INT_MAX);
}

return new AggregationConfig($this->memoryLimit, $this->filesystemProtocol);
}

public function filesystemProtocol(string $protocol): self
{
$this->filesystemProtocol = $protocol;

return $this;
}

public function memoryLimit(Unit $memoryLimit): self
{
$this->memoryLimit = $memoryLimit;

return $this;
}
}
19 changes: 19 additions & 0 deletions src/core/etl/src/Flow/ETL/Config/ConfigBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Flow\ETL\Analyze;
use Flow\ETL\Cache;
use Flow\ETL\Config;
use Flow\ETL\Config\Aggregation\AggregationConfigBuilder;
use Flow\ETL\Config\Cache\CacheConfigBuilder;
use Flow\ETL\Config\Sort\SortConfigBuilder;
use Flow\ETL\Config\Telemetry\TelemetryConfig;
Expand All @@ -34,6 +35,8 @@

final class ConfigBuilder
{
public readonly AggregationConfigBuilder $aggregation;

public readonly CacheConfigBuilder $cache;

public readonly SortConfigBuilder $sort;
Expand Down Expand Up @@ -69,6 +72,7 @@ public function __construct()
$this->putInputIntoRows = false;
$this->optimizer = null;
$this->clock = null;
$this->aggregation = new AggregationConfigBuilder();
$this->cache = new CacheConfigBuilder();
$this->sort = new SortConfigBuilder();
$this->randomValueGenerator = new NativePHPRandomValueGenerator();
Expand All @@ -79,6 +83,20 @@ public function __construct()
: PackageVersion::get('flow-php/etl');
}

public function aggregationFilesystem(string $protocol): self
{
$this->aggregation->filesystemProtocol($protocol);

return $this;
}

public function aggregationMemoryLimit(Unit $unit): self
{
$this->aggregation->memoryLimit($unit);

return $this;
}

public function analyze(Analyze $analyze): self
{
$this->analyze = $analyze;
Expand Down Expand Up @@ -111,6 +129,7 @@ public function build(EntryFactory $entryFactory = new EntryFactory()): Config
$this->sort->build(),
$this->analyze,
$this->telemetryConfig ?? TelemetryConfig::default($this->getClock()),
$this->aggregation->build(),
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/AggregatingFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface AggregatingFunction
{
public function aggregate(Row $row, FlowContext $context): void;

public function merge(self $other): void;

/**
* @return Entry<mixed>
*/
Expand Down
17 changes: 17 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Average.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,23 @@ public function apply(Row $row, Rows $partition, FlowContext $context): mixed
return (new Calculator())->divide($sum, $count, $this->scale, $this->rounding);
}

public function merge(AggregatingFunction $other): void
{
if (
!$other instanceof self
|| !$this->ref->is($other->ref)
|| $this->scale !== $other->scale
|| $this->rounding !== $other->rounding
) {
throw new InvalidArgumentException(
'Cannot merge ' . self::class . ' aggregating a different reference, scale or rounding',
);
}

$this->sum += $other->sum;
$this->count += $other->count;
}

public function over(Window $window): WindowFunction
{
$this->window = $window;
Expand Down
10 changes: 10 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Collect.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Flow\ETL\Row\EntryFactory;
use Flow\ETL\Row\Reference;

use function array_merge;
use function current;
use function Flow\ETL\DSL\to_entry;

Expand Down Expand Up @@ -41,6 +42,15 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

$this->collection = array_merge($this->collection, $other->collection);
}

/**
* @return Entry<mixed>
*/
Expand Down
14 changes: 14 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/CollectUnique.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

/** @var mixed $value */
foreach ($other->collection as $value) {
if (!in_array($value, $this->collection, true)) {
$this->collection[] = $value;
}
}
}

/**
* @return Entry<mixed>
*/
Expand Down
13 changes: 13 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Count.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,19 @@ public function apply(Row $row, Rows $partition, FlowContext $context): mixed
return $count;
}

public function merge(AggregatingFunction $other): void
{
if (
!$other instanceof self
|| ($this->ref === null) !== ($other->ref === null)
|| $this->ref !== null && $other->ref !== null && !$this->ref->is($other->ref)
) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

$this->count += $other->count;
}

public function over(Window $window): WindowFunction
{
$this->window = $window;
Expand Down
9 changes: 9 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/First.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

$this->first ??= $other->first;
}

/**
* @return Entry<mixed>
*/
Expand Down
11 changes: 11 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Last.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

if ($other->last !== null) {
$this->last = $other->last;
}
}

/**
* @return Entry<mixed>
*/
Expand Down
19 changes: 19 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Max.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

if ($other->max === null) {
return;
}

if ($this->max === null) {
$this->max = $other->max;

return;
}

$this->max = max($this->max, $other->max);
}

/**
* @return Entry<?\DateTimeInterface>|Entry<?float>|Entry<?int>
*/
Expand Down
19 changes: 19 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Min.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

if ($other->min === null) {
return;
}

if ($this->min === null) {
$this->min = $other->min;

return;
}

$this->min = min($this->min, $other->min);
}

/**
* @return Entry<?\DateTimeInterface>|Entry<?float>|Entry<?int>
*/
Expand Down
18 changes: 18 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/StringAggregate.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@

namespace Flow\ETL\Function;

use Flow\ETL\Exception\InvalidArgumentException;
use Flow\ETL\FlowContext;
use Flow\ETL\Row;
use Flow\ETL\Row\Entry;
use Flow\ETL\Row\EntryFactory;
use Flow\ETL\Row\Reference;
use Flow\ETL\Row\SortOrder;

use function array_merge;
use function count;
use function Flow\ETL\DSL\str_entry;
use function implode;
Expand Down Expand Up @@ -40,6 +42,22 @@ public function aggregate(Row $row, FlowContext $context): void
}
}

public function merge(AggregatingFunction $other): void
{
if (
!$other instanceof self
|| !$this->ref->is($other->ref)
|| $this->separator !== $other->separator
|| $this->sort !== $other->sort
) {
throw new InvalidArgumentException(
'Cannot merge ' . self::class . ' aggregating a different reference, separator or sort',
);
}

$this->values = array_merge($this->values, $other->values);
}

/**
* @return Row\Entry<?string>
*/
Expand Down
9 changes: 9 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/Sum.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ public function apply(Row $row, Rows $partition, FlowContext $context): mixed
return $sum;
}

public function merge(AggregatingFunction $other): void
{
if (!$other instanceof self || !$this->ref->is($other->ref)) {
throw new InvalidArgumentException('Cannot merge ' . self::class . ' aggregating a different reference');
}

$this->sum = (new Calculator())->add($this->sum, $other->sum);
}

public function over(Window $window): WindowFunction
{
$this->window = $window;
Expand Down
Loading
Loading