Skip to content

Commit

Permalink
String function improvements and additions
Browse files Browse the repository at this point in the history
TRIM's second value is optional now. It also accepts a string with characters to remove at the beginnen and the end, instead of just the type integer.

Added LTRIM support

Added RTRIM support

Added FIND_FIRST support

Added FIND_LAST support

Moved CI QA scripts to separate shell script
  • Loading branch information
LaravelFreelancerNL committed Jan 27, 2022
1 parent e955b21 commit 1407247
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 38 deletions.
24 changes: 5 additions & 19 deletions .github/workflows/ci.yml
Expand Up @@ -4,7 +4,7 @@ on: [workflow_dispatch, push, pull_request]

jobs:
run:
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [8.0, 8.1]
Expand All @@ -25,22 +25,8 @@ jobs:
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest

- name: PHP Code Sniffer
- name: Run all QA tests
if: ${{ always() }}
run: vendor/bin/phpcs

- name: PHP Mess Detector
if: ${{ always() }}
run: vendor/bin/phpmd src/ text phpmd-ruleset.xml

- name: PHP Stan
if: ${{ always() }}
run: vendor/bin/phpstan analyse -c phpstan.neon

- name: Psalm
if: ${{ always() }}
run: vendor/bin/psalm --no-cache

- name: PHP Unit tests
if: ${{ always() }}
run: vendor/bin/phpunit
run: |
chmod +x "${GITHUB_WORKSPACE}/bin/qa.sh"
"${GITHUB_WORKSPACE}/bin/qa.sh"
18 changes: 18 additions & 0 deletions bin/qa.sh
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
echo "Fix coding style"
./vendor/bin/phpcbf

echo "Check for remaining coding style errors"
./vendor/bin/phpcs -p --ignore=tests

echo "Run PHPMD"
./vendor/bin/phpmd src/ text phpmd-ruleset.xml

echo "Run PHPStan"
./vendor/bin/phpstan analyse -c phpstan.neon

echo "Run Psalm"
./vendor/bin/psalm --no-cache

echo "Run PHPUnit"
./vendor/bin/phpunit
40 changes: 21 additions & 19 deletions docs/api/functions.md
Expand Up @@ -135,25 +135,27 @@ The following functions are directly supported in FluentAql.


### String functions
| Description | AQL Function |
|:---------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------|
| concat(...$arguments) | [CONCAT(value1, value2, ... valueN)](https://www.arangodb.com/docs/stable/aql/functions-string.html#concat) |
| concatSeparator($separator, $values) | [CONCAT_SEPARATOR(separator, value1, value2, ... valueN)](https://www.arangodb.com/docs/stable/aql/functions-string.html#concat_separator) |
| levenshteinDistance($value1, $value2) | [LEVENSHTEIN_DISTANCE(value1, value2)](https://www.arangodb.com/docs/stable/aql/functions-string.html#levenshtein_distance) |
| lower($value) | [LOWER(value)](https://www.arangodb.com/docs/stable/aql/functions-string.html#lower) |
| ltrim($value, $char) | [LTRIM(value, char)](https://www.arangodb.com/docs/stable/aql/functions-string.html#ltrim) |
| regexMatches($text, $regex, $caseInsensitive) | [REGEX_MATCHES(text, regex, caseInsensitive)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_matches) |
| regexReplace($text, $regex, $replacement, $caseInsensitive) | [REGEX_REPLACE(text, search, replacement, caseInsensitive)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_replace) |
| regexSplit($text, $splitExpression, $caseInsensitive, $limit) | [REGEX_SPLIT(text, splitExpression, caseInsensitive, limit)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_split) |
| regexTest($text, $search, $caseInsensitive) | [REGEX_TEST(text, search, caseInsensitive)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_test) |
| rtrim($value, $char) | [RTRIM(value, char)](https://www.arangodb.com/docs/stable/aql/functions-string.html#rtrim) |
| split($value, $separator, $limit) | [SPLIT(value, separator, limit)](https://www.arangodb.com/docs/stable/aql/functions-string.html#split) |
| substitute($text, $search, $replace, $limit) | [SUBSTITUTE(value, search, replace, limit)](https://www.arangodb.com/docs/stable/aql/functions-string.html#substitute) |
| substring($value, $offset, $length) | [SUBSTRING(value, offset, length)](https://www.arangodb.com/docs/stable/aql/functions-string.html#substitute) |
| tokens($input, $analyzer) | [TOKENS(input, analyzer)](https://www.arangodb.com/docs/stable/aql/functions-string.html#tokens) |
| trim($value, $type) | [TRIM(value, type)](https://www.arangodb.com/docs/stable/aql/functions-string.html#trim) |
| upper($value) | [UPPER(value)](https://www.arangodb.com/docs/stable/aql/functions-string.html#upper) |
| uuid() | [UUID()](https://www.arangodb.com/docs/stable/aql/functions-string.html#uuid) |
| Description | AQL Function |
|:--------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------|
| concat(...$arguments) | [CONCAT(value1, value2, ... valueN)](https://www.arangodb.com/docs/stable/aql/functions-string.html#concat) |
| concatSeparator($separator, $values) | [CONCAT_SEPARATOR(separator, value1, value2, ... valueN)](https://www.arangodb.com/docs/stable/aql/functions-string.html#concat_separator) |
| findFirst($text, $search, $start, $end) | [FIND_FIRST(text, search, start, end)](https://www.arangodb.com/docs/stable/aql/functions-string.html#find_first) |
| findLast($text, $search, $start, $end) | [FIND_Last(text, search, start, end)](https://www.arangodb.com/docs/stable/aql/functions-string.html#find_Last) |
| levenshteinDistance($value1, $value2) | [LEVENSHTEIN_DISTANCE(value1, value2)](https://www.arangodb.com/docs/stable/aql/functions-string.html#levenshtein_distance) |
| lower($value) | [LOWER(value)](https://www.arangodb.com/docs/stable/aql/functions-string.html#lower) |
| ltrim($value, $char) | [LTRIM(value, char)](https://www.arangodb.com/docs/stable/aql/functions-string.html#ltrim) |
| regexMatches($text, $regex, $caseInsensitive) | [REGEX_MATCHES(text, regex, caseInsensitive)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_matches) |
| regexReplace($text, $regex, $replacement, $caseInsensitive) | [REGEX_REPLACE(text, search, replacement, caseInsensitive)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_replace) |
| regexSplit($text, $splitExpression, $caseInsensitive, $limit) | [REGEX_SPLIT(text, splitExpression, caseInsensitive, limit)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_split) |
| regexTest($text, $search, $caseInsensitive) | [REGEX_TEST(text, search, caseInsensitive)](https://www.arangodb.com/docs/stable/aql/functions-string.html#regex_test) |
| rtrim($value, $char) | [RTRIM(value, char)](https://www.arangodb.com/docs/stable/aql/functions-string.html#rtrim) |
| split($value, $separator, $limit) | [SPLIT(value, separator, limit)](https://www.arangodb.com/docs/stable/aql/functions-string.html#split) |
| substitute($text, $search, $replace, $limit) | [SUBSTITUTE(value, search, replace, limit)](https://www.arangodb.com/docs/stable/aql/functions-string.html#substitute) |
| substring($value, $offset, $length) | [SUBSTRING(value, offset, length)](https://www.arangodb.com/docs/stable/aql/functions-string.html#substitute) |
| tokens($input, $analyzer) | [TOKENS(input, analyzer)](https://www.arangodb.com/docs/stable/aql/functions-string.html#tokens) |
| trim($value, $type) | [TRIM(value, type)](https://www.arangodb.com/docs/stable/aql/functions-string.html#trim) |
| upper($value) | [UPPER(value)](https://www.arangodb.com/docs/stable/aql/functions-string.html#upper) |
| uuid() | [UUID()](https://www.arangodb.com/docs/stable/aql/functions-string.html#uuid) |


### Type functions
Expand Down
62 changes: 62 additions & 0 deletions src/AQL/HasStringFunctions.php
Expand Up @@ -59,6 +59,57 @@ public function contains(
return new FunctionExpression('CONTAINS', [$text, $search, $returnIndex]);
}

/**
* Return the position of the first occurrence of the string search inside the string text. Positions start at 0.
*
* @link https://www.arangodb.com/docs/stable/aql/functions-string.html#find_first
*/
public function findFirst(
string|object $text,
string|object $search,
int|object $start = null,
int|object $end = null
): FunctionExpression {
$arguments = [
'text' => $text,
'search' => $search,
];
if (isset($start)) {
$arguments['start'] = $start;
}
if (isset($end)) {
$arguments['end'] = $end;
}

return new FunctionExpression('FIND_FIRST', $arguments);
}


/**
* Return the position of the last occurrence of the string search inside the string text. Positions start at 0.
*
* @link https://www.arangodb.com/docs/stable/aql/functions-string.html#find_last
*/
public function findLast(
string|object $text,
string|object $search,
int|object $start = null,
int|object $end = null
): FunctionExpression {
$arguments = [
'text' => $text,
'search' => $search,
];
if (isset($start)) {
$arguments['start'] = $start;
}
if (isset($end)) {
$arguments['end'] = $end;
}

return new FunctionExpression('FIND_LAST', $arguments);
}

/**
* Calculate the Damerau-Levenshtein distance between two strings.
*
Expand Down Expand Up @@ -187,6 +238,17 @@ public function rtrim(
return new FunctionExpression('RTRIM', $arguments);
}

/**
* Return the soundex fingerprint of value.
*
* @link https://www.arangodb.com/docs/stable/aql/functions-string.html#soundex
*/
public function soundex(
string|object $value,
): FunctionExpression {
return new FunctionExpression('SOUNDEX', [$value]);
}

/**
* Split the given string text into a list of strings, using the separator.
*
Expand Down
59 changes: 59 additions & 0 deletions src/Traits/NormalizesStringFunctions.php
Expand Up @@ -43,6 +43,60 @@ protected function normalizeContains(QueryBuilder $queryBuilder): void
);
}

protected function normalizeFindFirst(QueryBuilder $queryBuilder): void
{
$this->parameters['text'] = $queryBuilder->normalizeArgument(
$this->parameters['text'],
['Query', 'Reference', 'Bind']
);

$this->parameters['search'] = $queryBuilder->normalizeArgument(
$this->parameters['search'],
['Query', 'Reference', 'Bind']
);

if (isset($this->parameters['start'])) {
$this->parameters['start'] = $queryBuilder->normalizeArgument(
$this->parameters['start'],
['Number', 'Query', 'Reference', 'Bind']
);
}

if (isset($this->parameters['end'])) {
$this->parameters['end'] = $queryBuilder->normalizeArgument(
$this->parameters['end'],
['Number', 'Query', 'Reference', 'Bind']
);
}
}

protected function normalizeFindLast(QueryBuilder $queryBuilder): void
{
$this->parameters['text'] = $queryBuilder->normalizeArgument(
$this->parameters['text'],
['Query', 'Reference', 'Bind']
);

$this->parameters['search'] = $queryBuilder->normalizeArgument(
$this->parameters['search'],
['Query', 'Reference', 'Bind']
);

if (isset($this->parameters['start'])) {
$this->parameters['start'] = $queryBuilder->normalizeArgument(
$this->parameters['start'],
['Number', 'Query', 'Reference', 'Bind']
);
}

if (isset($this->parameters['end'])) {
$this->parameters['end'] = $queryBuilder->normalizeArgument(
$this->parameters['end'],
['Number', 'Query', 'Reference', 'Bind']
);
}
}

protected function normalizeLevenshteinDistance(QueryBuilder $queryBuilder): void
{
$this->normalizeStrings($queryBuilder);
Expand Down Expand Up @@ -163,6 +217,11 @@ protected function normalizeRtrim(QueryBuilder $queryBuilder): void
);
}

protected function normalizeSoundex(QueryBuilder $queryBuilder): void
{
$this->normalizeStrings($queryBuilder);
}

protected function normalizeSplit(QueryBuilder $queryBuilder): void
{
$this->parameters['value'] = $queryBuilder->normalizeArgument(
Expand Down
95 changes: 95 additions & 0 deletions tests/Unit/AQL/StringFunctionsTest.php
Expand Up @@ -51,6 +51,90 @@ public function testContains()
);
}

public function testContainsReturnsIndex()
{
$qb = new QueryBuilder();
$qb->return($qb->contains('foobarbaz', 'bar', true));
self::assertEquals(
'RETURN CONTAINS(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2, true)',
$qb->get()->query
);
}

public function testFindFirst()
{
$qb = new QueryBuilder();
$qb->return($qb->findFirst('foobarbaz', 'bar'));
self::assertEquals(
'RETURN FIND_FIRST(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2)',
$qb->get()->query
);
}

public function testFindFirstWithStart()
{
$qb = new QueryBuilder();
$qb->return($qb->findFirst('foobarbaz', 'bar', 3));
self::assertEquals(
'RETURN FIND_FIRST(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2, 3)',
$qb->get()->query
);
}

public function testFindFirstWithStartAndEnd()
{
$qb = new QueryBuilder();
$qb->return($qb->findFirst('foobarbaz', 'bar', 3, 12));
self::assertEquals(
'RETURN FIND_FIRST(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2, 3, 12)',
$qb->get()->query
);
}

public function testFindLast()
{
$qb = new QueryBuilder();
$qb->return($qb->findLast('foobarbaz', 'bar'));
self::assertEquals(
'RETURN FIND_LAST(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2)',
$qb->get()->query
);
}

public function testFindLastWithStart()
{
$qb = new QueryBuilder();
$qb->return($qb->findLast('foobarbaz', 'bar', 3));
self::assertEquals(
'RETURN FIND_LAST(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2, 3)',
$qb->get()->query
);
}

public function testFindLastWithStartAndEnd()
{
$qb = new QueryBuilder();
$qb->return($qb->findLast('foobarbaz', 'bar', 3, 12));
self::assertEquals(
'RETURN FIND_LAST(@'
. $qb->getQueryId() . '_1, @'
. $qb->getQueryId() . '_2, 3, 12)',
$qb->get()->query
);
}

public function testContainsReturnIndex()
{
$qb = new QueryBuilder();
Expand Down Expand Up @@ -170,6 +254,17 @@ public function testRightTrimWithChar()
);
}

public function testSoundex()
{
$qb = new QueryBuilder();
$qb->return($qb->soundex('foobarbaz'));
self::assertEquals(
'RETURN SOUNDEX(@'
. $qb->getQueryId() . '_1)',
$qb->get()->query
);
}

public function testSplit()
{
$qb = new QueryBuilder();
Expand Down

0 comments on commit 1407247

Please sign in to comment.