From ad7f2d53889081e388841f45a23807263fdd1004 Mon Sep 17 00:00:00 2001 From: seb-jean Date: Thu, 24 Apr 2025 08:19:15 +0200 Subject: [PATCH 1/2] feat: add support for distance operator <@> --- docs/INTEGRATING-WITH-DOCTRINE.md | 1 + docs/INTEGRATING-WITH-LARAVEL.md | 1 + docs/INTEGRATING-WITH-SYMFONY.md | 1 + .../Doctrine/Entity/ContainsPoints.php | 18 ++++++++++ .../ORM/Query/AST/Functions/Distance.php | 23 ++++++++++++ .../ORM/Query/AST/Functions/DistanceTest.php | 36 +++++++++++++++++++ 6 files changed, 80 insertions(+) create mode 100644 fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php create mode 100644 src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Distance.php create mode 100644 tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php diff --git a/docs/INTEGRATING-WITH-DOCTRINE.md b/docs/INTEGRATING-WITH-DOCTRINE.md index 3edbb93e..6a61e120 100644 --- a/docs/INTEGRATING-WITH-DOCTRINE.md +++ b/docs/INTEGRATING-WITH-DOCTRINE.md @@ -163,6 +163,7 @@ $configuration->addCustomStringFunction('REGEXP_MATCH', MartinGeorgiev\Doctrine\ $configuration->addCustomStringFunction('STRCONCAT', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\StrConcat::class); $configuration->addCustomStringFunction('REGEXP_REPLACE', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RegexpReplace::class); $configuration->addCustomStringFunction('ROW', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Row::class); +$configuration->addCustomStringFunction('DISTANCE', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Distance::class); # aggregation functions $configuration->addCustomStringFunction('ARRAY_AGG', MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayAgg::class); diff --git a/docs/INTEGRATING-WITH-LARAVEL.md b/docs/INTEGRATING-WITH-LARAVEL.md index ba511120..e9b3a796 100644 --- a/docs/INTEGRATING-WITH-LARAVEL.md +++ b/docs/INTEGRATING-WITH-LARAVEL.md @@ -218,6 +218,7 @@ return [ 'REGEXP_SUBSTR' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RegexpSubstr::class, 'STRCONCAT' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\StrConcat::class, // the `||` operator 'ROW' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Row::class, + 'DISTANCE' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Distance::class, # aggregation functions 'ARRAY_AGG' => MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayAgg::class, diff --git a/docs/INTEGRATING-WITH-SYMFONY.md b/docs/INTEGRATING-WITH-SYMFONY.md index 32083e96..479cde4f 100644 --- a/docs/INTEGRATING-WITH-SYMFONY.md +++ b/docs/INTEGRATING-WITH-SYMFONY.md @@ -212,6 +212,7 @@ doctrine: REGEXP_SUBSTR: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\RegexpSubstr ROW: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Row STRCONCAT: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\StrConcat + DISTANCE: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Distance # aggregation functions ARRAY_AGG: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayAgg diff --git a/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php b/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php new file mode 100644 index 00000000..825ef932 --- /dev/null +++ b/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php @@ -0,0 +1,18 @@ +`). + * + * @see https://www.postgresql.org/docs/17/earthdistance.html#EARTHDISTANCE-POINT-BASED + * @since 3.1 + * + * @author Sébastien Jean + */ +class Distance extends BaseFunction +{ + protected function customizeFunction(): void + { + $this->setFunctionPrototype('(%s <@> %s)'); + $this->addNodeMapping('StringPrimary'); + $this->addNodeMapping('StringPrimary'); + } +} diff --git a/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php b/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php new file mode 100644 index 00000000..232cc0f3 --- /dev/null +++ b/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php @@ -0,0 +1,36 @@ + Distance::class, + ]; + } + + protected function getExpectedSqlStatements(): array + { + return [ + "SELECT (c0_.point <@> '(2.320041, 48.858889)') AS sclr_0 FROM ContainsPoints c0_", + 'SELECT (c0_.point <@> c0_.point2) AS sclr_0 FROM ContainsPoints c0_', + "SELECT ('(1.0, 1.0)' <@> '(2.0, 2.0)') AS sclr_0 FROM ContainsPoints c0_", + ]; + } + + protected function getDqlStatements(): array + { + return [ + \sprintf("SELECT DISTANCE(e.point, '(2.320041, 48.858889)') FROM %s e", ContainsPoints::class), + \sprintf('SELECT DISTANCE(e.point, e.point2) FROM %s e', ContainsPoints::class), + \sprintf("SELECT DISTANCE('(1.0, 1.0)', '(2.0, 2.0)') FROM %s e", ContainsPoints::class), + ]; + } +} From 8a2c478dd34f497472dd3be341931598380973e6 Mon Sep 17 00:00:00 2001 From: Martin Georgiev Date: Thu, 24 Apr 2025 12:07:02 +0100 Subject: [PATCH 2/2] Apply suggestions from code review --- .../MartinGeorgiev/Doctrine/Entity/ContainsPoints.php | 2 +- .../Doctrine/ORM/Query/AST/Functions/DistanceTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php b/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php index fe6fec7c..e59447b1 100644 --- a/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php +++ b/fixtures/MartinGeorgiev/Doctrine/Entity/ContainsPoints.php @@ -11,7 +11,7 @@ class ContainsPoints extends Entity { #[ORM\Column(type: 'point')] - public Point $point; + public Point $point1; #[ORM\Column(type: 'point')] public Point $point2; diff --git a/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php b/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php index 232cc0f3..1d56d2d6 100644 --- a/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php +++ b/tests/Unit/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/DistanceTest.php @@ -19,8 +19,8 @@ protected function getStringFunctions(): array protected function getExpectedSqlStatements(): array { return [ - "SELECT (c0_.point <@> '(2.320041, 48.858889)') AS sclr_0 FROM ContainsPoints c0_", - 'SELECT (c0_.point <@> c0_.point2) AS sclr_0 FROM ContainsPoints c0_', + "SELECT (c0_.point1 <@> '(2.320041, 48.858889)') AS sclr_0 FROM ContainsPoints c0_", + 'SELECT (c0_.point1 <@> c0_.point2) AS sclr_0 FROM ContainsPoints c0_', "SELECT ('(1.0, 1.0)' <@> '(2.0, 2.0)') AS sclr_0 FROM ContainsPoints c0_", ]; } @@ -28,8 +28,8 @@ protected function getExpectedSqlStatements(): array protected function getDqlStatements(): array { return [ - \sprintf("SELECT DISTANCE(e.point, '(2.320041, 48.858889)') FROM %s e", ContainsPoints::class), - \sprintf('SELECT DISTANCE(e.point, e.point2) FROM %s e', ContainsPoints::class), + \sprintf("SELECT DISTANCE(e.point1, '(2.320041, 48.858889)') FROM %s e", ContainsPoints::class), + \sprintf('SELECT DISTANCE(e.point1, e.point2) FROM %s e', ContainsPoints::class), \sprintf("SELECT DISTANCE('(1.0, 1.0)', '(2.0, 2.0)') FROM %s e", ContainsPoints::class), ]; }