From 1a749d746631fed0b137223ab47f6d5b9fdd5ba7 Mon Sep 17 00:00:00 2001 From: Pierre Boissinot Date: Wed, 4 Nov 2020 18:11:43 +0100 Subject: [PATCH 1/2] jsonb_exists_any (#1) * chore: update requirements * feat: add jsonb_exists_any --- Query/JsonbExistenceAny.php | 48 +++++++++++++++++++++++++++ README.md | 19 ++++++----- Tests/BaseTest.php | 3 +- Tests/Query/JsonbExistenceAnyTest.php | 35 +++++++++++++++++++ Types/JsonbArrayType.php | 2 +- composer.json | 13 ++++++-- 6 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 Query/JsonbExistenceAny.php create mode 100644 Tests/Query/JsonbExistenceAnyTest.php diff --git a/Query/JsonbExistenceAny.php b/Query/JsonbExistenceAny.php new file mode 100644 index 0000000..ebbaa47 --- /dev/null +++ b/Query/JsonbExistenceAny.php @@ -0,0 +1,48 @@ + + * @author Pierre Boissinot + */ +class JsonbExistenceAny extends FunctionNode +{ + public $leftHandSide; + public $rightHandSide; + + public function parse(Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + $this->leftHandSide = $parser->StringPrimary(); + $parser->match(Lexer::T_COMMA); + $this->rightHandSide = $parser->StringPrimary(); + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $sqlWalker) + { + // We use a workaround to allow this statement in a WHERE. Doctrine relies on the existence of an ComparisonOperator + return 'jsonb_exists_any(' . + $this->leftHandSide->dispatch($sqlWalker) .', '. + 'ARRAY['.$this->rightHandSide->dispatch($sqlWalker) . ']'. + ')'; + } +} diff --git a/README.md b/README.md index 07431b3..37701ab 100644 --- a/README.md +++ b/README.md @@ -46,12 +46,13 @@ doctrine: JSONB_AG: Boldtrn\JsonbBundle\Query\JsonbAtGreater JSONB_HGG: Boldtrn\JsonbBundle\Query\JsonbHashGreaterGreater JSONB_EX: Boldtrn\JsonbBundle\Query\JsonbExistence + JSONB_EX_ANY: Boldtrn\JsonbBundle\Query\JsonbExistenceAny ``` -Note: There were people having issues with the above configuration. They had the following exception: +Note: There were people having issues with the above configuration. They had the following exception: ``` -[Symfony\Component\Config\Definition\Exception\InvalidConfigurationException] - Unrecognized options "dql" under "doctrine.orm" +[Symfony\Component\Config\Definition\Exception\InvalidConfigurationException] + Unrecognized options "dql" under "doctrine.orm" ``` This was fixed by changing the dql part in the following (add the `entity_managers` between `orm` and `dql`): @@ -89,7 +90,7 @@ class Test } ``` -Step 4.1: Write a Repository Method using a NativeQuery +Step 4.1: Write a Repository Method using a NativeQuery ------------------------- ```php @@ -102,14 +103,14 @@ $q = $this WHERE t.attrs @> 'value' " , $rsm); -``` +``` You only need to setup the `$rsm` ResultSetMapping according to the Doctrine documentation. -Step 4.2: Write a Repository Method that queries for the jsonb using the custom JSONB_FUNCTIONS +Step 4.2: Write a Repository Method that queries for the jsonb using the custom JSONB_FUNCTIONS ------------------------- -This example shows how to use the contains statement in a WHERE clause. +This example shows how to use the contains statement in a WHERE clause. The `= TRUE` is a workaround for Doctrine that needs an comparison operator in the WHERE clause. ```php @@ -122,7 +123,7 @@ $q = $this WHERE JSONB_AG(t.attrs, 'value') = TRUE " ); -``` +``` This produces the following Query: ```SQL @@ -132,7 +133,7 @@ SELECT t0_.id AS id0, t0_.attrs AS attrs1 FROM Test t0_ WHERE (t0_.attrs @> 'val This example shows how to query for a value that is LIKE `%d%` The result could be data like: ``` - id | attrs + id | attrs ----+-------------------------------------- 4 | {"a": 1, "b": {"c": "abcdefg", "e": true}} ``` diff --git a/Tests/BaseTest.php b/Tests/BaseTest.php index 7be1144..b8d030f 100644 --- a/Tests/BaseTest.php +++ b/Tests/BaseTest.php @@ -56,6 +56,7 @@ protected function setUp() 'JSONB_AG' => 'Boldtrn\JsonbBundle\Query\JsonbAtGreater', 'JSONB_HGG' => 'Boldtrn\JsonbBundle\Query\JsonbHashGreaterGreater', 'JSONB_EX' => 'Boldtrn\JsonbBundle\Query\JsonbExistence', + 'JSONB_EX_ANY' => 'Boldtrn\JsonbBundle\Query\JsonbExistenceAny', ) ); @@ -94,4 +95,4 @@ protected function setUpDBALTypes() $this->connection->getDatabasePlatform()->registerDoctrineTypeMapping('JSONB', 'jsonb'); } -} \ No newline at end of file +} diff --git a/Tests/Query/JsonbExistenceAnyTest.php b/Tests/Query/JsonbExistenceAnyTest.php new file mode 100644 index 0000000..5a5c5a7 --- /dev/null +++ b/Tests/Query/JsonbExistenceAnyTest.php @@ -0,0 +1,35 @@ +entityManager + ->createQuery( + " + SELECT t + FROM E:Test t + WHERE JSONB_EX_ANY(t.attrs, 'value') = TRUE + " + ); + + $expectedSQL = "SELECT t0.id AS id0, t0.attrs AS attrs1 FROM Test t0 WHERE jsonb_exists_any(t0.attrs, ARRAY['value']) = true"; + + $expectedSQL = str_replace("_", "", $expectedSQL); + + $actualSQL = str_replace("_", "", $q->getSql()); + + $this->assertEquals( + $expectedSQL, + $actualSQL + ); + } + +} diff --git a/Types/JsonbArrayType.php b/Types/JsonbArrayType.php index 6ef0137..f2178fb 100644 --- a/Types/JsonbArrayType.php +++ b/Types/JsonbArrayType.php @@ -26,7 +26,7 @@ public function getName() { return 'jsonb'; } - + /** * {@inheritdoc} */ diff --git a/composer.json b/composer.json index 7f5079d..dcc72dd 100644 --- a/composer.json +++ b/composer.json @@ -23,14 +23,21 @@ } ], "require": { - "php": ">=5.3.3", - "doctrine/dbal": "~2.4" + "php": ">=7.1", + "doctrine/dbal": "~2.6" + }, + "scripts": { + "test": "./vendor/bin/phpunit", + "martin-georgiev/postgresql-for-doctrine": "^0.12.0" + }, + "scripts": { + "test": "./vendor/bin/phpunit" }, "suggest": { "doctrine/orm": "To use DQL functions" }, "require-dev": { - "doctrine/orm": ">2.4,<2.6", + "doctrine/orm": ">2.6", "phpunit/phpunit": "~4.2" } } From 8feeb3664dcfa8ae782aa5640808e0164b800ca9 Mon Sep 17 00:00:00 2001 From: Pierre Boissinot Date: Wed, 4 Nov 2020 18:28:11 +0100 Subject: [PATCH 2/2] doc(readme): add ?| operator --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37701ab..8124473 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ JsonbBundle This bundle extends Doctrine to use the `jsonb` datatype that ships with Postgresql 9.4. This bundle is fully compatible with Symfony, but you do not have to use Symfony (see the `composer.json` for dependencies). Please make sure you have Postgresql with a version of at least 9.4 installed before using this bundle. -The Bundle allows to create Jsonb fields and use the `@>`,`?` and the `#>>` operator on the Jsonb field. +The Bundle allows to create Jsonb fields and use the `@>`,`?`, `#>>` and the `?|` operator on the Jsonb field. Other Operations can be easily added. I recently discovered the power of NativeQueries (http://doctrine-orm.readthedocs.org/en/latest/reference/native-sql.html).