Skip to content

Commit 9f418b6

Browse files
authored
Add greatest and least scalar functions (#1320)
* Added greatest and least scalar functions * Updated dsl json * Fixed failing tests
1 parent 4ed793f commit 9f418b6

File tree

7 files changed

+211
-1
lines changed

7 files changed

+211
-1
lines changed

src/core/etl/src/Flow/ETL/DSL/functions.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@
4040
DenseRank,
4141
Exists,
4242
First,
43+
Greatest,
4344
Hash,
4445
Last,
46+
Least,
4547
ListFunctions,
4648
Literal,
4749
Max,
@@ -1165,6 +1167,18 @@ function average(EntryReference|string $ref) : Average
11651167
return new Average(is_string($ref) ? ref($ref) : $ref);
11661168
}
11671169

1170+
#[DocumentationDSL(module: Module::CORE, type: DSLType::SCALAR_FUNCTION)]
1171+
function greatest(mixed ...$values) : Greatest
1172+
{
1173+
return new Greatest($values);
1174+
}
1175+
1176+
#[DocumentationDSL(module: Module::CORE, type: DSLType::SCALAR_FUNCTION)]
1177+
function least(mixed ...$values) : Least
1178+
{
1179+
return new Least($values);
1180+
}
1181+
11681182
#[DocumentationDSL(module: Module::CORE, type: DSLType::AGGREGATING_FUNCTION)]
11691183
function collect(EntryReference|string $ref) : Collect
11701184
{

src/core/etl/src/Flow/ETL/Function/Comparison/Comparable.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,31 @@
99

1010
trait Comparable
1111
{
12+
public function assertAllComparable(array $values, string $symbol) : void
13+
{
14+
$detector = new TypeDetector();
15+
16+
$types = [];
17+
18+
foreach ($values as $value) {
19+
$type = $detector->detectType($value);
20+
21+
if (!in_array($type, $types, true)) {
22+
$types[] = $type;
23+
}
24+
}
25+
26+
if (count($types) > 1) {
27+
foreach ($types as $nextType) {
28+
foreach ($types as $baseType) {
29+
if (!$baseType->isComparableWith($nextType)) {
30+
throw new InvalidArgumentException(\sprintf("Can't compare '(%s %s %s)' due to data type mismatch.", $baseType->toString(), $symbol, $nextType->toString()));
31+
}
32+
}
33+
}
34+
}
35+
}
36+
1237
public function assertComparable(mixed $left, mixed $right, string $symbol) : void
1338
{
1439
$detector = new TypeDetector();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\ETL\Function;
6+
7+
use Flow\ETL\Function\Comparison\Comparable;
8+
use Flow\ETL\Row;
9+
10+
final class Greatest extends ScalarFunctionChain
11+
{
12+
use Comparable;
13+
14+
public function __construct(
15+
private readonly array $values,
16+
) {
17+
}
18+
19+
public function eval(Row $row) : mixed
20+
{
21+
$extractedValues = [];
22+
23+
foreach ($this->values as $value) {
24+
$extractedValues[] = (new Parameter($value))->eval($row);
25+
}
26+
27+
$this->assertAllComparable($extractedValues, '>');
28+
29+
return max($extractedValues);
30+
}
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\ETL\Function;
6+
7+
use Flow\ETL\Function\Comparison\Comparable;
8+
use Flow\ETL\Row;
9+
10+
final class Least extends ScalarFunctionChain
11+
{
12+
use Comparable;
13+
14+
public function __construct(
15+
private readonly array $values,
16+
) {
17+
}
18+
19+
public function eval(Row $row) : mixed
20+
{
21+
$extractedValues = [];
22+
23+
foreach ($this->values as $value) {
24+
$extractedValues[] = (new Parameter($value))->eval($row);
25+
}
26+
27+
$this->assertAllComparable($extractedValues, '<');
28+
29+
return \min($extractedValues);
30+
}
31+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\ETL\Tests\Unit\Function;
6+
7+
use function Flow\ETL\DSL\{greatest, int_entry, ref, row};
8+
use Flow\ETL\Tests\FlowTestCase;
9+
10+
final class GreatestTest extends FlowTestCase
11+
{
12+
public function test_greatest_value() : void
13+
{
14+
$greatest = greatest(
15+
10,
16+
20,
17+
ref('int'),
18+
40
19+
);
20+
21+
self::assertSame(
22+
55,
23+
$greatest->eval(row(int_entry('int', 55)))
24+
);
25+
}
26+
27+
public function test_greatest_with_non_comparable_values() : void
28+
{
29+
$greatest = greatest(
30+
null,
31+
20,
32+
ref('int'),
33+
new \DateTimeImmutable('now')
34+
);
35+
36+
$this->expectExceptionMessage("Can't compare '(datetime > integer)' due to data type mismatch.");
37+
38+
$greatest->eval(row(int_entry('int', 55)));
39+
}
40+
41+
public function test_greatest_with_null() : void
42+
{
43+
$greatest = greatest(
44+
null,
45+
20,
46+
ref('int'),
47+
1257
48+
);
49+
50+
self::assertSame(
51+
1257,
52+
$greatest->eval(row(int_entry('int', 55)))
53+
);
54+
}
55+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\ETL\Tests\Unit\Function;
6+
7+
use function Flow\ETL\DSL\{int_entry, least, ref, row};
8+
use Flow\ETL\Tests\FlowTestCase;
9+
10+
final class LeastTest extends FlowTestCase
11+
{
12+
public function test_greatest_with_non_comparable_values() : void
13+
{
14+
$lest = least(
15+
null,
16+
20,
17+
ref('int'),
18+
new \DateTimeImmutable('now'),
19+
);
20+
21+
$this->expectExceptionMessage("Can't compare '(datetime < integer)' due to data type mismatch.");
22+
23+
$lest->eval(row(int_entry('int', 55)));
24+
}
25+
26+
public function test_least_value() : void
27+
{
28+
$lest = least(
29+
10,
30+
20,
31+
ref('int'),
32+
40,
33+
);
34+
35+
self::assertSame(
36+
10,
37+
$lest->eval(row(int_entry('int', 55)))
38+
);
39+
}
40+
41+
public function test_least_with_null() : void
42+
{
43+
$ltest = least(
44+
null,
45+
20,
46+
ref('int'),
47+
1257
48+
);
49+
50+
self::assertNull(
51+
$ltest->eval(row(int_entry('int', 4)))
52+
);
53+
}
54+
}

web/landing/resources/dsl.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)