diff --git a/app/Helpers/Collector/Extensions/MetaCollection.php b/app/Helpers/Collector/Extensions/MetaCollection.php index dadb4067f7..61cc757453 100644 --- a/app/Helpers/Collector/Extensions/MetaCollection.php +++ b/app/Helpers/Collector/Extensions/MetaCollection.php @@ -408,6 +408,23 @@ trait MetaCollection return $this; } + /** + * Limit the search to a specific bunch of categories. + * + * @param Collection $categories + * + * @return GroupCollectorInterface + */ + public function setNotCategories(Collection $categories): GroupCollectorInterface + { + if ($categories->count() > 0) { + $this->withCategoryInformation(); + $this->query->whereNotIn('categories.id', $categories->pluck('id')->toArray()); + } + + return $this; + } + /** * Will include category ID + name, if any. * diff --git a/app/Helpers/Collector/GroupCollectorInterface.php b/app/Helpers/Collector/GroupCollectorInterface.php index b24aa90703..3fcec55ce7 100644 --- a/app/Helpers/Collector/GroupCollectorInterface.php +++ b/app/Helpers/Collector/GroupCollectorInterface.php @@ -571,6 +571,15 @@ interface GroupCollectorInterface */ public function setCategories(Collection $categories): GroupCollectorInterface; + /** + * Limit the search not to have a specific bunch of categories. + * + * @param Collection $categories + * + * @return GroupCollectorInterface + */ + public function setNotCategories(Collection $categories): GroupCollectorInterface; + /** * Limit the search to a specific category. * diff --git a/app/Support/Search/OperatorQuerySearch.php b/app/Support/Search/OperatorQuerySearch.php index 3ddf291adb..a0075bf504 100644 --- a/app/Support/Search/OperatorQuerySearch.php +++ b/app/Support/Search/OperatorQuerySearch.php @@ -38,6 +38,7 @@ use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\ParseDateString; use FireflyIII\User; +use Gdbots\QueryParser\Enum\BoolOperator; use Gdbots\QueryParser\Node\Date; use Gdbots\QueryParser\Node\Emoji; use Gdbots\QueryParser\Node\Emoticon; @@ -206,14 +207,18 @@ class OperatorQuerySearch implements SearchInterface Log::debug(sprintf('Now handle Node class %s', $class)); /** @var Field $searchNode */ // used to search for x:y - $operator = strtolower($searchNode->getValue()); - $value = $searchNode->getNode()->getValue(); + $operator = strtolower($searchNode->getValue()); + $value = $searchNode->getNode()->getValue(); + $prohibited = $searchNode->getBoolOperator()->equals(BoolOperator::PROHIBITED()->getValue()); // must be valid operator: - if (in_array($operator, $this->validOperators, true) && $this->updateCollector($operator, (string) $value)) { + if ( + in_array($operator, $this->validOperators, true) && + $this->updateCollector($operator, (string) $value, $prohibited)) { $this->operators->push( [ - 'type' => self::getRootOperator($operator), - 'value' => (string) $value, + 'type' => self::getRootOperator($operator), + 'value' => (string) $value, + 'prohibited' => $prohibited, ] ); Log::debug(sprintf('Added operator type "%s"', $operator)); @@ -236,8 +241,12 @@ class OperatorQuerySearch implements SearchInterface * @return bool * @throws FireflyException */ - private function updateCollector(string $operator, string $value): bool + private function updateCollector(string $operator, string $value, bool $prohibited): bool { + if ($prohibited) { + $operator = sprintf('!%s', $operator); + } + Log::debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value)); // check if alias, replace if necessary: @@ -476,6 +485,15 @@ class OperatorQuerySearch implements SearchInterface $this->collector->findNothing(); } break; + case '!category_contains': + $result = $this->categoryRepository->searchCategory($value, 1337); + if ($result->count() > 0) { + $this->collector->setNotCategories($result); + } + if (0 === $result->count()) { + $this->collector->findNothing(); + } + break; // // budgets // diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 6c2bd336fd..7e94c61f64 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -404,6 +404,7 @@ return [ 'search_modifier_account_nr_ends' => 'Either account number / IBAN ends with ":value"', 'search_modifier_account_nr_starts' => 'Either account number / IBAN starts with ":value"', 'search_modifier_category_contains' => 'Category contains ":value"', + 'search_modifier_not_category_contains' => 'Category does not contain ":value"', 'search_modifier_category_ends' => 'Category ends with ":value"', 'search_modifier_category_starts' => 'Category starts with ":value"', 'search_modifier_budget_contains' => 'Budget contains ":value"', diff --git a/resources/views/search/index.twig b/resources/views/search/index.twig index a053d03b20..6531669958 100644 --- a/resources/views/search/index.twig +++ b/resources/views/search/index.twig @@ -57,7 +57,12 @@
{{ trans('firefly.modifiers_applies_are') }}