diff --git a/app/Providers/SearchServiceProvider.php b/app/Providers/SearchServiceProvider.php index 944348218d..98b5a0f850 100644 --- a/app/Providers/SearchServiceProvider.php +++ b/app/Providers/SearchServiceProvider.php @@ -23,10 +23,10 @@ declare(strict_types=1); namespace FireflyIII\Providers; -use FireflyIII\Support\Search\GdbotsQueryParser; +use FireflyIII\Support\Search\QueryParser\GdbotsQueryParser; use FireflyIII\Support\Search\OperatorQuerySearch; -use FireflyIII\Support\Search\QueryParser; -use FireflyIII\Support\Search\QueryParserInterface; +use FireflyIII\Support\Search\QueryParser\QueryParser; +use FireflyIII\Support\Search\QueryParser\QueryParserInterface; use FireflyIII\Support\Search\SearchInterface; use Illuminate\Foundation\Application; use Illuminate\Support\ServiceProvider; diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 74e518fd5c..e6c445b231 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -150,7 +150,7 @@ class OperatorQuerySearch implements SearchInterface } app('log')->debug(sprintf('Found %d node(s) at top-level', count($parsedQuery->getNodes()))); - $this->handleSearchNode($parsedQuery); + $this->handleSearchNode($parsedQuery, $parsedQuery->isProhibited(false)); // add missing information $this->collector->withBillInformation(); @@ -164,21 +164,21 @@ class OperatorQuerySearch implements SearchInterface * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - private function handleSearchNode(Node $node): void + private function handleSearchNode(Node $node, $flipProhibitedFlag): void { app('log')->debug(sprintf('Now in handleSearchNode(%s)', get_class($node))); switch (true) { case $node instanceof StringNode: - $this->handleStringNode($node); + $this->handleStringNode($node, $flipProhibitedFlag); break; case $node instanceof FieldNode: - $this->handleFieldNode($node); + $this->handleFieldNode($node, $flipProhibitedFlag); break; case $node instanceof NodeGroup: - $this->handleNodeGroup($node); + $this->handleNodeGroup($node, $flipProhibitedFlag); break; default: @@ -187,20 +187,24 @@ class OperatorQuerySearch implements SearchInterface } } - private function handleNodeGroup(NodeGroup $node): void + private function handleNodeGroup(NodeGroup $node, $flipProhibitedFlag): void { - //TODO: Handle Subquery prohibition, i.e. flip all prohibition flags inside the subquery + $prohibited = $node->isProhibited($flipProhibitedFlag); + foreach ($node->getNodes() as $subNode) { - $this->handleSearchNode($subNode); + $this->handleSearchNode($subNode, $prohibited); } } - private function handleStringNode(StringNode $node): void + private function handleStringNode(StringNode $node, $flipProhibitedFlag): void { $string = (string) $node->getValue(); - if($node->isProhibited()) { + + $prohibited = $node->isProhibited($flipProhibitedFlag); + + if($prohibited) { app('log')->debug(sprintf('Exclude string "%s" from search string', $string)); $this->prohibitedWords[] = $string; } else { @@ -212,11 +216,11 @@ class OperatorQuerySearch implements SearchInterface /** * @throws FireflyException */ - private function handleFieldNode(FieldNode $node): void + private function handleFieldNode(FieldNode $node, $flipProhibitedFlag): void { $operator = strtolower($node->getOperator()); $value = $node->getValue(); - $prohibited = $node->isProhibited(); + $prohibited = $node->isProhibited($flipProhibitedFlag); $context = config(sprintf('search.operators.%s.needs_context', $operator)); diff --git a/app/Support/Search/QueryParser/Node.php b/app/Support/Search/QueryParser/Node.php index 3050bd82c7..e7ae29611b 100644 --- a/app/Support/Search/QueryParser/Node.php +++ b/app/Support/Search/QueryParser/Node.php @@ -11,8 +11,21 @@ abstract class Node { protected bool $prohibited; - public function isProhibited(): bool + /** + * Returns the prohibited status of the node, optionally inverted based on flipFlag + * + * Flipping is used when a node is inside a NodeGroup that has a prohibited status itself, causing inversion of the query parts inside + * + * @param bool $flipFlag When true, inverts the prohibited status + * @return bool The (potentially inverted) prohibited status + */ + public function isProhibited(bool $flipFlag): bool { - return $this->prohibited; + if ($flipFlag === true) { + return !$this->prohibited; + } else { + return $this->prohibited; + } + } }