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

[Workflow] Add tip on how to add a WHERE constraint on a multi-state marking store #20086

Open
wants to merge 2 commits into
base: 5.4
Choose a base branch
from

Conversation

richardhj
Copy link

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
Copy link
Member

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

@richardhj
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants