Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add jsonb_exists_any function #16

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Query/JsonbExistenceAny.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace Boldtrn\JsonbBundle\Query;


use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

/**
* Class JsonbExistenceAny
*
* JsonbExistenceAny ::= "JSONB_EX_ANY" "(" LeftHandSide "," RightHandSide ")"
*
* This will be converted to: "jsonb_exists_any( LeftHandSide, RightHandSide )"
*
* RightHandSide is a PHP array
* @example in WHERE clause: JSONB_EX_ANY(blogPost.tags, :tags) = TRUE
*
* @package Boldtrn\JsonbBundle\Query
* @author Robin Boldt <[email protected]>
* @author Pierre Boissinot <[email protected]>
*/
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) . ']'.
')';
}
}
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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`):
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -122,7 +123,7 @@ $q = $this
WHERE JSONB_AG(t.attrs, 'value') = TRUE
"
);
```
```

This produces the following Query:
```SQL
Expand All @@ -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}}
```
Expand Down
3 changes: 2 additions & 1 deletion Tests/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
)
);

Expand Down Expand Up @@ -94,4 +95,4 @@ protected function setUpDBALTypes()
$this->connection->getDatabasePlatform()->registerDoctrineTypeMapping('JSONB', 'jsonb');
}

}
}
35 changes: 35 additions & 0 deletions Tests/Query/JsonbExistenceAnyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Boldtrn\JsonbBundle\Tests\Query;


use Boldtrn\JsonbBundle\Tests\BaseTest;

class JsonbExistenceAnyTest extends BaseTest
{

public function testExistence()
{
$q = $this
->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
);
}

}
2 changes: 1 addition & 1 deletion Types/JsonbArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function getName()
{
return 'jsonb';
}

/**
* {@inheritdoc}
*/
Expand Down
13 changes: 10 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}