diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fd31b832..58162daed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,4 +18,7 @@ Master branch - fix error when interval is hour only - layout of page "list exports" +- create function "SIMILARITY" (see [posgtgresql documentation](https://www.postgresql.org/docs/9.6/static/pgtrgm.html)) +- create function "OVERLAPSI", which will detect period of date overlapping, replacing NULL date by infinity or -infinity (see [postgresql page for date time function and operators](https://www.postgresql.org/docs/9.6/static/functions-datetime.html)) + diff --git a/DependencyInjection/ChillMainExtension.php b/DependencyInjection/ChillMainExtension.php index 27449454b..8d7e1e202 100644 --- a/DependencyInjection/ChillMainExtension.php +++ b/DependencyInjection/ChillMainExtension.php @@ -30,6 +30,8 @@ use Chill\MainBundle\Doctrine\DQL\GetJsonFieldByKey; use Chill\MainBundle\Doctrine\DQL\Unaccent; use Chill\MainBundle\Doctrine\DQL\JsonAggregate; use Chill\MainBundle\Doctrine\DQL\JsonbExistsInArray; +use Chill\MainBundle\Doctrine\DQL\Similarity; +use Chill\MainBundle\Doctrine\DQL\OverlapsI; /** * This class load config for chillMainExtension. @@ -159,7 +161,9 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, 'AGGREGATE' => JsonAggregate::class ), 'numeric_functions' => [ - 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class + 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class, + 'SIMILARITY' => Similarity::class, + 'OVERLAPSI' => OverlapsI::class ] ) ) diff --git a/Doctrine/DQL/OverlapsI.php b/Doctrine/DQL/OverlapsI.php new file mode 100644 index 000000000..ae9df72f4 --- /dev/null +++ b/Doctrine/DQL/OverlapsI.php @@ -0,0 +1,110 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\MainBundle\Doctrine\DQL; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\Lexer; + +/** + * DQL function for OVERLAPS function in postgresql + * + * If a value is null in period start, it will be replaced by -infinity. + * If a value is null in period end, it will be replaced by infinity + * + */ +class OverlapsI extends FunctionNode +{ + private $firstPeriodStart; + + private $firstPeriodEnd; + + private $secondPeriodStart; + + private $secondPeriodEnd; + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return '(' + .$this->makeCase($sqlWalker, $this->firstPeriodStart, 'start').', ' + .$this->makeCase($sqlWalker, $this->firstPeriodEnd, 'end'). + ') OVERLAPS (' + .$this->makeCase($sqlWalker, $this->secondPeriodStart, 'start').', ' + .$this->makeCase($sqlWalker, $this->secondPeriodEnd, 'end').')' + ; + } + + protected function makeCase($sqlWalker, $part, $position) + { + //return $part->dispatch($sqlWalker); + + switch ($position) { + case 'start' : + $p = '-infinity'; + break; + case 'end': + $p = 'infinity'; + break; + } + + if ($part instanceof \Doctrine\ORM\Query\AST\PathExpression) { + return 'CASE WHEN ' + .' '.$part->dispatch($sqlWalker).' IS NOT NULL ' + . 'THEN '. + $part->dispatch($sqlWalker) + . ' ELSE '. + "'".$p."'::date " + . 'END'; + } else { + return 'CASE WHEN ' + .' '.$part->dispatch($sqlWalker).'::date IS NOT NULL ' + . 'THEN '. + $part->dispatch($sqlWalker) + . '::date ELSE '. + "'".$p."'::date " + . 'END'; + } + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstPeriodStart = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->firstPeriodEnd = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + + $parser->match(Lexer::T_COMMA); + + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->secondPeriodStart = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondPeriodEnd = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/Doctrine/DQL/Similarity.php b/Doctrine/DQL/Similarity.php new file mode 100644 index 000000000..988e55f5f --- /dev/null +++ b/Doctrine/DQL/Similarity.php @@ -0,0 +1,54 @@ + + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +namespace Chill\MainBundle\Doctrine\DQL; + +use Doctrine\ORM\Query\AST\Functions\FunctionNode; +use Doctrine\ORM\Query\Lexer; + +/** + * + * + * + */ +class Similarity extends FunctionNode +{ + private $firstPart; + + private $secondPart; + + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) + { + return 'SIMILARITY('.$this->firstPart->dispatch($sqlWalker). + ', ' . $this->secondPart->dispatch($sqlWalker) .")"; + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstPart = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondPart = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +}