Skip to content

Commit

Permalink
Structure Select function (#890)
Browse files Browse the repository at this point in the history
* Structure Select function

* Added structure DSL function allowing to easily access structure functions
  • Loading branch information
norberttech committed Dec 30, 2023
1 parent b836424 commit 5ffd260
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/core/etl/src/Flow/ETL/DSL/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
use Flow\ETL\Function\Size;
use Flow\ETL\Function\Split;
use Flow\ETL\Function\Sprintf;
use Flow\ETL\Function\StructureFunctions;
use Flow\ETL\Function\StyleConverter\StringStyles;
use Flow\ETL\Function\Sum;
use Flow\ETL\Function\ToDate;
Expand Down Expand Up @@ -484,6 +485,11 @@ function ref(string $entry) : EntryReference
return new EntryReference($entry);
}

function structure(string $entry) : StructureFunctions
{
return ref($entry)->structure();
}

function refs(string|Reference ...$entries) : References
{
return new References(...$entries);
Expand Down
18 changes: 18 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/StructureFunctions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types=1);

namespace Flow\ETL\Function;

use Flow\ETL\Row\Reference;

final class StructureFunctions
{
public function __construct(private readonly Reference $ref)
{

}

public function select(Reference|string ...$refs) : StructureSelect
{
return new StructureSelect($this->ref, ...$refs);
}
}
49 changes: 49 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/StructureSelect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php declare(strict_types=1);

namespace Flow\ETL\Function;

use Flow\ETL\Row;
use Flow\ETL\Row\Entry\StructureEntry;
use Flow\ETL\Row\EntryReference;
use Flow\ETL\Row\Reference;
use Flow\ETL\Row\References;

final class StructureSelect implements ScalarFunction
{
private readonly Reference $ref;

private readonly References $refs;

public function __construct(
Reference|string $ref,
Reference|string ...$refs,
) {
$this->ref = EntryReference::init($ref);
$this->refs = References::init(...$refs);
}

public function eval(Row $row) : array|null
{
if (!$row->has($this->ref)) {
return null;
}

$structure = $row->get($this->ref);

if (!$structure instanceof StructureEntry) {
return null;
}

$output = [];

foreach ($this->refs as $ref) {
if (\array_key_exists($ref->to(), $structure->value())) {
$output[$ref->name()] = $structure->value()[$ref->to()];
} else {
$output[$ref->name()] = null;
}
}

return $output;
}
}
6 changes: 6 additions & 0 deletions src/core/etl/src/Flow/ETL/Row/EntryReference.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Flow\ETL\Row;

use Flow\ETL\Function\ScalarFunctionChain;
use Flow\ETL\Function\StructureFunctions;
use Flow\ETL\Row;

/**
Expand Down Expand Up @@ -94,6 +95,11 @@ public function sort() : SortOrder
return $this->sort;
}

public function structure() : StructureFunctions
{
return new StructureFunctions($this);
}

public function to() : string
{
return $this->entry;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php declare(strict_types=1);

namespace Flow\ETL\Tests\Integration\Function\Structure;

use function Flow\ETL\DSL\df;
use function Flow\ETL\DSL\from_array;
use function Flow\ETL\DSL\structure;
use PHPUnit\Framework\TestCase;

final class StructureSelectTest extends TestCase
{
public function test_structure_keep() : void
{
$rows = df()
->read(
from_array(
[
[
'user' => [
'id' => 1,
'name' => 'username',
'email' => 'user_email@email.com',
'tags' => [
'tag1',
'tag2',
'tag3',
],
],
],
]
)
)
->withEntry('user', structure('user')->select('id', 'email', 'tags'))
->fetch();

$this->assertEquals(
[
[
'user' => [
'id' => 1,
'email' => 'user_email@email.com',
'tags' => [
'tag1',
'tag2',
'tag3',
],
],
],
],
$rows->toArray()
);
}
}
123 changes: 123 additions & 0 deletions src/core/etl/tests/Flow/ETL/Tests/Unit/Function/StructureKeepTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php declare(strict_types=1);

namespace Flow\ETL\Tests\Unit\Function;

use function Flow\ETL\DSL\list_entry;
use function Flow\ETL\DSL\ref;
use function Flow\ETL\DSL\row;
use function Flow\ETL\DSL\struct_element;
use function Flow\ETL\DSL\struct_entry;
use function Flow\ETL\DSL\struct_type;
use function Flow\ETL\DSL\type_int;
use function Flow\ETL\DSL\type_list;
use function Flow\ETL\DSL\type_string;
use Flow\ETL\Function\StructureSelect;
use PHPUnit\Framework\TestCase;

final class StructureKeepTest extends TestCase
{
public function test_selecting_multiple_values_from_structure() : void
{
$structure = struct_entry(
'struct',
[
'id' => 1,
'name' => 'test',
],
struct_type(
struct_element('id', type_int()),
struct_element('name', type_string()),
)
);

$this->assertEquals(
['id' => 1, 'name' => 'test'],
(new StructureSelect(ref('struct'), ref('id'), ref('name')))
->eval(row($structure))
);
}

public function test_selecting_single_value_from_structure() : void
{
$structure = struct_entry(
'struct',
[
'id' => 1,
'name' => 'test',
],
struct_type(
struct_element('id', type_int()),
struct_element('name', type_string()),
)
);

$this->assertEquals(
['id' => 1],
(new StructureSelect(ref('struct'), 'id'))
->eval(row($structure))
);
}

public function test_selecting_single_value_from_structure_with_alias() : void
{
$structure = struct_entry(
'struct',
[
'id' => 1,
'name' => 'test',
],
struct_type(
struct_element('id', type_int()),
struct_element('name', type_string()),
)
);

$this->assertEquals(
['new_id' => 1],
(new StructureSelect(ref('struct'), ref('id')->as('new_id')))
->eval(row($structure))
);
}

public function test_selecting_values_from_empty_structure() : void
{
$structure = struct_entry(
'struct',
[
'email' => 'email@email.com',
],
struct_type(
struct_element('id', type_int(true)),
struct_element('email', type_string()),
struct_element('name', type_string(true)),
)
);

$this->assertEquals(
['new_id' => null],
(new StructureSelect(ref('struct'), ref('id')->as('new_id')))
->eval(row($structure))
);
}

public function test_selecting_values_from_list() : void
{
$list = list_entry(
'list',
[
['id' => 1, 'name' => 'test'],
['id' => 2, 'name' => 'test2'],
],
type_list(
struct_type(
struct_element('id', type_int()),
struct_element('name', type_string()),
)
)
);

$this->assertNull(
(new StructureSelect(ref('list'), ref('id')))->eval(row($list))
);
}
}

0 comments on commit 5ffd260

Please sign in to comment.