Conversation

richardhj

Filtering a single state marking store might be straightforward (i.e., WHERE currentPlaces = :place).

I found filtering on a multiple state marking store with JSON schema more challenging (because of the key-value structure as well).
Also, JSON functions are not default to Doctrine and not standardized among databases.

@javiereguiluz

Ping @lyrixx in case you can review this proposal. Thanks!

`scienta/doctrine-json-functions` and enable the `JSON_CONTAINS_PATH` doctrine
function. Then you can filter for a current place as follows:
``$qb->andWhere("JSON_CONTAINS_PATH(item.currentPlaces, 'one', '$.draft') <> 0")``
(where `draft` is the place to be checked)
Copy link
Member

@lyrixx lyrixx Aug 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it works.

I personally use:

    private function filterByMarking(QueryBuilder $queryBuilder, array $markings, bool $and = false): void
    {
        $entityAlias = $queryBuilder->getRootAliases()[0];

        $contains = [];
        foreach ($markings as $marking) {
            $contains[] = $queryBuilder->expr()->eq(sprintf(
                'JSON_CONTAINS(%s.marking, %s)',
                $entityAlias,
                $queryBuilder->expr()->literal(json_encode([$marking => 1], \JSON_THROW_ON_ERROR)),
            ), 1);
        }

        if ($and) {
            $queryBuilder->andWhere($queryBuilder->expr()->andX(...$contains));
        } else {
            $queryBuilder->andWhere($queryBuilder->expr()->orX(...$contains));
        }

        $queryBuilder->andWhere($queryBuilder->expr()->isNotNull("{$entityAlias}.marking"));
    }

With this class

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

class JsonContains extends FunctionNode
{
    private const string FUNCTION_NAME = 'JSON_CONTAINS';

    public ?Node $jsonDocExpr = null;
    public ?Node $jsonValExpr = null;
    public ?Node $jsonPathExpr = null;

    public function getSql(SqlWalker $sqlWalker): string
    {
        $jsonDoc = $sqlWalker->walkStringPrimary($this->jsonDocExpr);
        $jsonVal = $sqlWalker->walkStringPrimary($this->jsonValExpr);
        $jsonPath = '';
        if ($this->jsonPathExpr) {
            $jsonPath = ', ' . $sqlWalker->walkStringPrimary($this->jsonPathExpr);
        }

        return sprintf('%s(%s, %s)', self::FUNCTION_NAME, $jsonDoc, "{$jsonVal}{$jsonPath}");
    }

    public function parse(Parser $parser): void
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->jsonDocExpr = $parser->StringPrimary();
        $parser->match(Lexer::T_COMMA);
        $this->jsonValExpr = $parser->StringPrimary();

        if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
            $parser->match(Lexer::T_COMMA);
            $this->jsonPathExpr = $parser->StringPrimary();
        }
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

Anyway, before merging this PR, item.currentPlaces, one, and $.draft must be explained

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, PostgreSQL and mariadb have different option to deal with JSON

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should document this in detail. It looks too convoluted. If this is truly useful/common, could Symfony provide something to ease this? Otherwise, maybe we can just mention that you have to install some package or create your own JSON function to query this.

Not sure what to do here 😐

@richardhj

I think JSON CONTAINS and JSON_CONTAINS PATH in these usages are functional equivalent in these cases but I wonder what is more performant :-)

@xabbuhxabbuh modified the milestones: 5.4, 6.4 Dec 16, 2024
Sign up for free to join this conversation on . Already have an account? Sign in to comment
None yet

Successfully merging this pull request may close these issues.