Skip to content

Commit

Permalink
Added concat_ws (#1385)
Browse files Browse the repository at this point in the history
  • Loading branch information
norberttech authored Jan 17, 2025
1 parent 9f4bb17 commit 386ed87
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 7 deletions.
13 changes: 13 additions & 0 deletions src/core/etl/src/Flow/ETL/DSL/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
CollectUnique,
Combine,
Concat,
ConcatWithSeparator,
Count,
DateTimeFormat,
DenseRank,
Expand Down Expand Up @@ -832,12 +833,24 @@ function combine(ScalarFunction|array $keys, ScalarFunction|array $values) : Com
return new Combine($keys, $values);
}

/**
* Concat all values. If you want to concatenate values with separator use concat_ws function.
*/
#[DocumentationDSL(module: Module::CORE, type: DSLType::SCALAR_FUNCTION)]
function concat(ScalarFunction|string ...$functions) : Concat
{
return new Concat(...$functions);
}

/**
* Concat all values with separator.
*/
#[DocumentationDSL(module: Module::CORE, type: DSLType::SCALAR_FUNCTION)]
function concat_ws(ScalarFunction|string ...$functions) : ConcatWithSeparator
{
return new ConcatWithSeparator(...$functions);
}

#[DocumentationDSL(module: Module::CORE, type: DSLType::SCALAR_FUNCTION)]
function hash(mixed $value, Algorithm $algorithm = new NativePHPHash()) : Hash
{
Expand Down
12 changes: 9 additions & 3 deletions src/core/etl/src/Flow/ETL/Function/Concat.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,19 @@ public function eval(Row $row) : mixed
{
$values = \array_map(fn (ScalarFunction|string $string) : mixed => \is_string($string) ? $string : Caster::default()->to(type_string(true))->value($string->eval($row)), $this->refs);

$concatValues = [];

foreach ($values as $value) {
if (!\is_string($value)) {
return null;
if (\is_string($value)) {
$concatValues[] = $value;
}
}

if (\count($concatValues) === 0) {
return '';
}

/** @var array<string> $values */
return \implode('', $values);
return \implode('', $concatValues);
}
}
50 changes: 50 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/ConcatWithSeparator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Flow\ETL\Function;

use function Flow\ETL\DSL\type_string;
use Flow\ETL\PHP\Type\Caster;
use Flow\ETL\Row;

final class ConcatWithSeparator extends ScalarFunctionChain
{
/**
* @var array<ScalarFunction|string>
*/
private readonly array $refs;

public function __construct(
private readonly ScalarFunction|string $separator,
ScalarFunction|string ...$refs,
) {
$this->refs = $refs;
}

public function eval(Row $row) : mixed
{
$separator = (new Parameter($this->separator))->asString($row);

if (!\is_string($separator)) {
return '';
}

$values = \array_map(fn (ScalarFunction|string $string) : mixed => \is_string($string) ? $string : Caster::default()->to(type_string(true))->value($string->eval($row)), $this->refs);

$concatValues = [];

foreach ($values as $value) {
if (\is_string($value)) {
$concatValues[] = $value;
}
}

if (\count($concatValues) === 0) {
return '';
}

/** @var array<string> $values */
return \implode($separator, $concatValues);
}
}
5 changes: 5 additions & 0 deletions src/core/etl/src/Flow/ETL/Function/ScalarFunctionChain.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ public function concat(ScalarFunction|string ...$params) : self
return new Concat($this, ...$params);
}

public function concatWithSeparator(ScalarFunction|string $separator, ScalarFunction|string ...$params) : self
{
return new ConcatWithSeparator($separator, ...$params);
}

public function contains(ScalarFunction|string $needle) : self
{
return new Contains($this, $needle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,33 @@ public function test_concat_on_non_string_value() : void

self::assertSame(
[
['id' => 1, 'concat' => null],
['id' => 2, 'concat' => null],
['id' => 1, 'concat' => '1'],
['id' => 2, 'concat' => '2'],
],
$memory->dump()
);
}

public function test_concat_on_nulls() : void
{
(data_frame())
->read(
from_array(
[
['id' => 1, 'array' => ['field' => 'value']],
['id' => 2],
]
)
)
->withEntry('concat', concat(lit(null), lit(null)))
->drop('array')
->write(to_memory($memory = new ArrayMemory()))
->run();

self::assertSame(
[
['id' => 1, 'concat' => ''],
['id' => 2, 'concat' => ''],
],
$memory->dump()
);
Expand All @@ -54,7 +79,7 @@ public function test_concat_on_stringable_value() : void
self::assertSame(
[
['id' => 1, 'concat' => '1-value'],
['id' => 2, 'concat' => null],
['id' => 2, 'concat' => '2-'],
],
$memory->dump()
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php

declare(strict_types=1);

namespace Flow\ETL\Tests\Integration\Function;

use function Flow\ETL\DSL\data_frame;
use function Flow\ETL\DSL\{concat_ws, from_array, lit, ref, to_memory};
use Flow\ETL\Memory\ArrayMemory;
use Flow\ETL\Tests\FlowTestCase;

final class ConcatWithSeparatorTest extends FlowTestCase
{
public function test_concat_on_non_string_value() : void
{
(data_frame())
->read(
from_array(
[
['id' => 1],
['id' => 2],
]
)
)
->withEntry('concat', concat_ws(lit(','), ref('id'), lit(null)))
->write(to_memory($memory = new ArrayMemory()))
->run();

self::assertSame(
[
['id' => 1, 'concat' => '1'],
['id' => 2, 'concat' => '2'],
],
$memory->dump()
);
}

public function test_concat_on_nulls() : void
{
(data_frame())
->read(
from_array(
[
['id' => 1, 'array' => ['field' => 'value']],
['id' => 2],
]
)
)
->withEntry('concat', concat_ws(lit(null), lit(null)))
->drop('array')
->write(to_memory($memory = new ArrayMemory()))
->run();

self::assertSame(
[
['id' => 1, 'concat' => ''],
['id' => 2, 'concat' => ''],
],
$memory->dump()
);
}

public function test_concat_with_separator() : void
{
(data_frame())
->read(
from_array(
[
['id' => 1],
['id' => 2],
]
)
)
->withEntry('concat', concat_ws(lit('->'), lit('id'), ref('id')))
->drop('array')
->write(to_memory($memory = new ArrayMemory()))
->run();

self::assertSame(
[
['id' => 1, 'concat' => 'id->1'],
['id' => 2, 'concat' => 'id->2'],
],
$memory->dump()
);
}
}
2 changes: 1 addition & 1 deletion web/landing/resources/dsl.json

Large diffs are not rendered by default.

0 comments on commit 386ed87

Please sign in to comment.