From 757bdb46db1ea440f6edcf796c2692311166f328 Mon Sep 17 00:00:00 2001 From: ghlen Date: Thu, 21 Apr 2022 23:13:34 +0200 Subject: [PATCH 1/8] addded Order class --- src/Order.php | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/Order.php diff --git a/src/Order.php b/src/Order.php new file mode 100644 index 00000000..2153405a --- /dev/null +++ b/src/Order.php @@ -0,0 +1,76 @@ +expression = $expression; + $this->setOrdering($ordering); + } + + /** + * Returns the expression being ordered. + * + * @return AnyType + */ + public function getExpression(): AnyType + { + return $this->expression; + } + + /** + * @return string|null + */ + public function getOrdering(): ?string + { + return $this->ordering; + } + + public function setOrdering(?string $ordering): self + { + if ($ordering !== null) { + $ordering = strtoupper($ordering); + if (!in_array($ordering, ['ASC', 'DESC', 'ASCENDING', 'DESCENDING'])) { + throw new InvalidArgumentException('Ordering must be null, "ASC", "DESC", "ASCENDING" or "DESCENDING"'); + } + + $this->ordering = $ordering; + } else { + $this->ordering = null; + } + + return $this; + } + + public function toQuery(): string + { + $cypher = $this->getExpression()->toQuery(); + if ($this->ordering) { + $cypher .= ' ' . $this->ordering; + } + + return $cypher; + } +} \ No newline at end of file From f870b253d1cf576fc4046482579edfbd238419a8 Mon Sep 17 00:00:00 2001 From: ghlen Date: Thu, 21 Apr 2022 23:24:25 +0200 Subject: [PATCH 2/8] added test for order --- src/Order.php | 6 ++++- tests/Unit/OrderTest.php | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/OrderTest.php diff --git a/src/Order.php b/src/Order.php index 2153405a..368bf421 100644 --- a/src/Order.php +++ b/src/Order.php @@ -11,6 +11,10 @@ * Defines the order of an expression. Can only be used in an ORDER BY clause. * * @see https://neo4j.com/docs/cypher-manual/current/clauses/order-by/ + * @note While the documentation online does not mention this, ORDER BY supports multiple directions in the same clause: + * - ORDER BY a ASC, b DESC + * is considered valid. + * This means it is impossible for the OrderBy clause to order all expressions individually, necessitating this class. */ class Order implements QueryConvertable { @@ -19,7 +23,7 @@ class Order implements QueryConvertable private ?string $ordering; /** - * The expression to order + * Order constructor. * * @param AnyType $expression The expression to order by. * @param string|null $ordering The order modifier. Must be null or a valid modifier ('ASC', 'ASCENDING', 'DESC', 'DESCENDING') diff --git a/tests/Unit/OrderTest.php b/tests/Unit/OrderTest.php new file mode 100644 index 00000000..a88cd848 --- /dev/null +++ b/tests/Unit/OrderTest.php @@ -0,0 +1,55 @@ +getQueryConvertableMock(RawExpression::class, 'x')); + + $this->assertEquals('x', $order->toQuery()); + $this->assertNull($order->getOrdering()); + } + + public function testBasicOrderDescending(): void + { + $order = new Order($this->getQueryConvertableMock(RawExpression::class, 'x'), 'desc'); + + $this->assertEquals('x DESC', $order->toQuery()); + $this->assertEquals('DESC', $order->getOrdering()); + } + + public function testBasicOrderAscending(): void + { + $order = new Order($this->getQueryConvertableMock(RawExpression::class, 'x'), 'asc'); + + $this->assertEquals('x ASC', $order->toQuery()); + $this->assertEquals('ASC', $order->getOrdering()); + } + + public function testBasicOrderChange(): void + { + $order = new Order($this->getQueryConvertableMock(RawExpression::class, 'x'), 'asc'); + + $order->setOrdering(null); + + $this->assertEquals('x', $order->toQuery()); + $this->assertNull($order->getOrdering()); + } + + public function testOrderFalse(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectErrorMessage('Ordering must be null, "ASC", "DESC", "ASCENDING" or "DESCENDING"'); + + new Order($this->getQueryConvertableMock(RawExpression::class, 'x'), 'ascc'); + } +} \ No newline at end of file From 3805e61b48f59677b651153fb51312165a75e0a3 Mon Sep 17 00:00:00 2001 From: ghlen Date: Thu, 21 Apr 2022 23:36:44 +0200 Subject: [PATCH 3/8] reworked order by clause to work with Order --- src/Clauses/OrderByClause.php | 44 +++++++++--------------- tests/Unit/Clauses/OrderByClauseTest.php | 26 +++++++++----- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/Clauses/OrderByClause.php b/src/Clauses/OrderByClause.php index 2e23176a..7a556c69 100644 --- a/src/Clauses/OrderByClause.php +++ b/src/Clauses/OrderByClause.php @@ -21,8 +21,10 @@ namespace WikibaseSolutions\CypherDSL\Clauses; +use WikibaseSolutions\CypherDSL\Order; use WikibaseSolutions\CypherDSL\Property; use WikibaseSolutions\CypherDSL\Traits\EscapeTrait; +use function array_map; /** * This class represents an ORDER BY clause. This clause should always be preceded by a RETURN @@ -35,9 +37,9 @@ class OrderByClause extends Clause use EscapeTrait; /** - * @var Property[] The expressions to include in the clause + * @var Order[] The expressions to include in the clause */ - private array $properties = []; + private array $orderings = []; /** * @var bool @@ -47,12 +49,14 @@ class OrderByClause extends Clause /** * Add a property to sort on. * - * @param Property $property The additional property to sort on + * @param Property $property The additional property to sort on. + * @param string|null $order The order of the property to appear. Null is equal to the default in Neo4J. + * * @return OrderByClause */ - public function addProperty(Property $property): self + public function addProperty(Property $property, ?string $order = null): self { - $this->properties[] = $property; + $this->orderings[] = new Order($property, $order); return $this; } @@ -60,34 +64,21 @@ public function addProperty(Property $property): self /** * Returns the properties to order. * - * @return Property[] + * @return Order[] */ public function getProperties(): array { - return $this->properties; - } - - /** - * Returns whether the ordering is in descending order. - * - * @return bool - */ - public function isDescending(): bool - { - return $this->descending; + return array_map(static fn (Order $o) => $o->getExpression(), $this->orderings); } /** - * Set to sort in a DESCENDING order. + * Returns the orderings. * - * @param bool $descending - * @return OrderByClause + * @return Property[] */ - public function setDescending(bool $descending = true): self + public function getOrderings(): array { - $this->descending = $descending; - - return $this; + return $this->orderings; } /** @@ -103,9 +94,8 @@ protected function getClause(): string */ protected function getSubject(): string { - $properties = array_map(fn (Property $property): string => $property->toQuery(), $this->properties); - $subject = implode(", ", $properties); + $properties = array_map(static fn ($x) => $x->toQuery(), $this->orderings); - return $this->descending ? sprintf("%s DESCENDING", $subject) : $subject; + return implode(", ", $properties); } } diff --git a/tests/Unit/Clauses/OrderByClauseTest.php b/tests/Unit/Clauses/OrderByClauseTest.php index 7d0030f7..e856c095 100644 --- a/tests/Unit/Clauses/OrderByClauseTest.php +++ b/tests/Unit/Clauses/OrderByClauseTest.php @@ -24,6 +24,7 @@ use PHPUnit\Framework\TestCase; use TypeError; use WikibaseSolutions\CypherDSL\Clauses\OrderByClause; +use WikibaseSolutions\CypherDSL\Order; use WikibaseSolutions\CypherDSL\Property; use WikibaseSolutions\CypherDSL\Tests\Unit\TestHelper; use WikibaseSolutions\CypherDSL\Types\AnyType; @@ -41,7 +42,6 @@ public function testEmptyClause(): void $this->assertSame("", $orderBy->toQuery()); $this->assertEquals([], $orderBy->getProperties()); - $this->assertFalse($orderBy->isDescending()); } public function testSingleProperty(): void @@ -52,7 +52,6 @@ public function testSingleProperty(): void $this->assertSame("ORDER BY a.a", $orderBy->toQuery()); $this->assertEquals([$property], $orderBy->getProperties()); - $this->assertFalse($orderBy->isDescending()); } public function testMultipleProperties(): void @@ -66,19 +65,16 @@ public function testMultipleProperties(): void $this->assertSame("ORDER BY a.a, a.b", $orderBy->toQuery()); $this->assertEquals([$propertyA, $propertyB], $orderBy->getProperties()); - $this->assertFalse($orderBy->isDescending()); } public function testSinglePropertyDesc(): void { $orderBy = new OrderByClause(); $property = $this->getQueryConvertableMock(Property::class, "a.a"); - $orderBy->addProperty($property); - $orderBy->setDescending(); + $orderBy->addProperty($property, 'DESCENDING'); $this->assertSame("ORDER BY a.a DESCENDING", $orderBy->toQuery()); $this->assertEquals([$property], $orderBy->getProperties()); - $this->assertTrue($orderBy->isDescending()); } public function testMultiplePropertiesDesc(): void @@ -88,12 +84,24 @@ public function testMultiplePropertiesDesc(): void $propertyB = $this->getQueryConvertableMock(Property::class, "a.b"); $orderBy->addProperty($propertyA); - $orderBy->addProperty($propertyB); - $orderBy->setDescending(); + $orderBy->addProperty($propertyB, 'DESCENDING'); $this->assertSame("ORDER BY a.a, a.b DESCENDING", $orderBy->toQuery()); $this->assertEquals([$propertyA, $propertyB], $orderBy->getProperties()); - $this->assertTrue($orderBy->isDescending()); + } + + public function testMultiplePropertiesMixed(): void + { + $orderBy = new OrderByClause(); + $propertyA = $this->getQueryConvertableMock(Property::class, "a.a"); + $propertyB = $this->getQueryConvertableMock(Property::class, "a.b"); + + $orderBy->addProperty($propertyA, 'ASC'); + $orderBy->addProperty($propertyB, 'DESCENDING'); + + $this->assertSame("ORDER BY a.a ASC, a.b DESCENDING", $orderBy->toQuery()); + $this->assertEquals([$propertyA, $propertyB], $orderBy->getProperties()); + $this->assertEquals([new Order($propertyA, 'asc'), new Order($propertyB, 'descending')], $orderBy->getOrderings()); } /** From 839f1346bb0c6f4150c4c52f39dc2407fbb0f795 Mon Sep 17 00:00:00 2001 From: ghlen Date: Mon, 25 Apr 2022 10:51:16 +0200 Subject: [PATCH 4/8] fixed regression on DESCENDING order in Query --- src/Query.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Query.php b/src/Query.php index 51fde8a3..54c845c0 100644 --- a/src/Query.php +++ b/src/Query.php @@ -472,16 +472,19 @@ public function optionalMatch($patterns): self public function orderBy($properties, bool $descending = false): self { $orderByClause = new OrderByClause(); - $orderByClause->setDescending($descending); if (!is_array($properties)) { $properties = [$properties]; } - foreach ($properties as $property) { + foreach ($properties as $i => $property) { $this->assertClass('property', Property::class, $property); - $orderByClause->addProperty($property); + if ($descending && $i === count($properties) - 1) { + $orderByClause->addProperty($property, 'DESCENDING'); + } else { + $orderByClause->addProperty($property); + } } $this->clauses[] = $orderByClause; From 9af714108e1738ff8430803f8b96e92b411ca9c0 Mon Sep 17 00:00:00 2001 From: ghlen Date: Thu, 5 May 2022 09:13:50 +0200 Subject: [PATCH 5/8] improved typings --- src/Clauses/OrderByClause.php | 8 ++++---- src/Order.php | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Clauses/OrderByClause.php b/src/Clauses/OrderByClause.php index 7a556c69..61471b63 100644 --- a/src/Clauses/OrderByClause.php +++ b/src/Clauses/OrderByClause.php @@ -22,8 +22,8 @@ namespace WikibaseSolutions\CypherDSL\Clauses; use WikibaseSolutions\CypherDSL\Order; -use WikibaseSolutions\CypherDSL\Property; use WikibaseSolutions\CypherDSL\Traits\EscapeTrait; +use WikibaseSolutions\CypherDSL\Types\PropertyTypes\PropertyType; use function array_map; /** @@ -49,12 +49,12 @@ class OrderByClause extends Clause /** * Add a property to sort on. * - * @param Property $property The additional property to sort on. + * @param PropertyType $property The additional property to sort on. * @param string|null $order The order of the property to appear. Null is equal to the default in Neo4J. * * @return OrderByClause */ - public function addProperty(Property $property, ?string $order = null): self + public function addProperty(PropertyType $property, ?string $order = null): self { $this->orderings[] = new Order($property, $order); @@ -74,7 +74,7 @@ public function getProperties(): array /** * Returns the orderings. * - * @return Property[] + * @return Order[] */ public function getOrderings(): array { diff --git a/src/Order.php b/src/Order.php index 368bf421..652cd54f 100644 --- a/src/Order.php +++ b/src/Order.php @@ -4,6 +4,7 @@ use InvalidArgumentException; use WikibaseSolutions\CypherDSL\Types\AnyType; +use WikibaseSolutions\CypherDSL\Types\PropertyTypes\PropertyType; use function in_array; use function strtoupper; @@ -18,17 +19,17 @@ */ class Order implements QueryConvertable { - private AnyType $expression; + private PropertyType $expression; /** @var string|null */ private ?string $ordering; /** * Order constructor. * - * @param AnyType $expression The expression to order by. + * @param PropertyType $expression The expression to order by. * @param string|null $ordering The order modifier. Must be null or a valid modifier ('ASC', 'ASCENDING', 'DESC', 'DESCENDING') */ - public function __construct(AnyType $expression, ?string $ordering = null) + public function __construct(PropertyType $expression, ?string $ordering = null) { $this->expression = $expression; $this->setOrdering($ordering); From 19e5d77a2a902e484d7f77ef6287fd86c1f90eb4 Mon Sep 17 00:00:00 2001 From: ghlen Date: Thu, 5 May 2022 09:14:29 +0200 Subject: [PATCH 6/8] added copyright --- src/Order.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Order.php b/src/Order.php index 652cd54f..50143b48 100644 --- a/src/Order.php +++ b/src/Order.php @@ -1,5 +1,24 @@ Date: Thu, 5 May 2022 12:30:36 +0200 Subject: [PATCH 7/8] ran php-cs-fixer --- src/Clauses/OrderByClause.php | 2 +- src/Order.php | 8 ++++---- tests/Unit/OrderTest.php | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Clauses/OrderByClause.php b/src/Clauses/OrderByClause.php index 61471b63..c50d486f 100644 --- a/src/Clauses/OrderByClause.php +++ b/src/Clauses/OrderByClause.php @@ -21,10 +21,10 @@ namespace WikibaseSolutions\CypherDSL\Clauses; +use function array_map; use WikibaseSolutions\CypherDSL\Order; use WikibaseSolutions\CypherDSL\Traits\EscapeTrait; use WikibaseSolutions\CypherDSL\Types\PropertyTypes\PropertyType; -use function array_map; /** * This class represents an ORDER BY clause. This clause should always be preceded by a RETURN diff --git a/src/Order.php b/src/Order.php index 50143b48..4c224759 100644 --- a/src/Order.php +++ b/src/Order.php @@ -21,11 +21,11 @@ namespace WikibaseSolutions\CypherDSL; +use function in_array; use InvalidArgumentException; +use function strtoupper; use WikibaseSolutions\CypherDSL\Types\AnyType; use WikibaseSolutions\CypherDSL\Types\PropertyTypes\PropertyType; -use function in_array; -use function strtoupper; /** * Defines the order of an expression. Can only be used in an ORDER BY clause. @@ -76,7 +76,7 @@ public function setOrdering(?string $ordering): self { if ($ordering !== null) { $ordering = strtoupper($ordering); - if (!in_array($ordering, ['ASC', 'DESC', 'ASCENDING', 'DESCENDING'])) { + if (!in_array($ordering, ['ASC', 'DESC', 'ASCENDING', 'DESCENDING'])) { throw new InvalidArgumentException('Ordering must be null, "ASC", "DESC", "ASCENDING" or "DESCENDING"'); } @@ -97,4 +97,4 @@ public function toQuery(): string return $cypher; } -} \ No newline at end of file +} diff --git a/tests/Unit/OrderTest.php b/tests/Unit/OrderTest.php index a88cd848..10c3e20e 100644 --- a/tests/Unit/OrderTest.php +++ b/tests/Unit/OrderTest.php @@ -52,4 +52,4 @@ public function testOrderFalse(): void new Order($this->getQueryConvertableMock(RawExpression::class, 'x'), 'ascc'); } -} \ No newline at end of file +} From 0903e8373bcbd5922c37db01ea063d0315906cea Mon Sep 17 00:00:00 2001 From: ghlen Date: Thu, 5 May 2022 12:46:07 +0200 Subject: [PATCH 8/8] fixed typing --- src/Clauses/OrderByClause.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Clauses/OrderByClause.php b/src/Clauses/OrderByClause.php index c50d486f..a651b1de 100644 --- a/src/Clauses/OrderByClause.php +++ b/src/Clauses/OrderByClause.php @@ -21,6 +21,7 @@ namespace WikibaseSolutions\CypherDSL\Clauses; +use WikibaseSolutions\CypherDSL\Types\AnyType; use function array_map; use WikibaseSolutions\CypherDSL\Order; use WikibaseSolutions\CypherDSL\Traits\EscapeTrait; @@ -64,7 +65,7 @@ public function addProperty(PropertyType $property, ?string $order = null): self /** * Returns the properties to order. * - * @return Order[] + * @return AnyType[] */ public function getProperties(): array {