mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Add new search options for https://github.com/firefly-iii/firefly-iii/issues/7571
This commit is contained in:
parent
a5c370c70e
commit
3136bccb24
@ -917,6 +917,7 @@ trait MetaCollection
|
|||||||
$list = $tags->pluck('tag')->toArray();
|
$list = $tags->pluck('tag')->toArray();
|
||||||
$filter = static function (array $object) use ($list): bool {
|
$filter = static function (array $object) use ($list): bool {
|
||||||
foreach ($object['transactions'] as $transaction) {
|
foreach ($object['transactions'] as $transaction) {
|
||||||
|
app('log')->debug(sprintf('Transaction has %d tag(s)', count($transaction['tags'])));
|
||||||
foreach ($transaction['tags'] as $tag) {
|
foreach ($transaction['tags'] as $tag) {
|
||||||
if (in_array($tag['name'], $list, true)) {
|
if (in_array($tag['name'], $list, true)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -515,7 +515,6 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
// add to query:
|
// add to query:
|
||||||
$this->query->orWhereIn('transaction_journals.transaction_group_id', $groupIds);
|
$this->query->orWhereIn('transaction_journals.transaction_group_id', $groupIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->query->get($this->fields);
|
$result = $this->query->get($this->fields);
|
||||||
|
|
||||||
// now to parse this into an array.
|
// now to parse this into an array.
|
||||||
@ -823,10 +822,15 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
private function postFilterCollection(Collection $collection): Collection
|
private function postFilterCollection(Collection $collection): Collection
|
||||||
{
|
{
|
||||||
$currentCollection = $collection;
|
$currentCollection = $collection;
|
||||||
|
|
||||||
|
app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d filter(s) and %d transaction(s).', count($this->postFilters), count($currentCollection)));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Closure $function
|
* @var Closure $function
|
||||||
*/
|
*/
|
||||||
foreach ($this->postFilters as $function) {
|
foreach ($this->postFilters as $function) {
|
||||||
|
app('log')->debug('Applying filter...');
|
||||||
$nextCollection = new Collection();
|
$nextCollection = new Collection();
|
||||||
// loop everything in the current collection
|
// loop everything in the current collection
|
||||||
// and save it (or not) in the new collection.
|
// and save it (or not) in the new collection.
|
||||||
@ -843,6 +847,7 @@ class GroupCollector implements GroupCollectorInterface
|
|||||||
$nextCollection->push($item);
|
$nextCollection->push($item);
|
||||||
}
|
}
|
||||||
$currentCollection = $nextCollection;
|
$currentCollection = $nextCollection;
|
||||||
|
app('log')->debug(sprintf('GroupCollector: postFilterCollection has %d transaction(s) left.', count($currentCollection)));
|
||||||
}
|
}
|
||||||
return $currentCollection;
|
return $currentCollection;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function get(): Collection
|
public function get(): Collection
|
||||||
{
|
{
|
||||||
return $this->user->tags()->orderBy('tag', 'ASC')->get();
|
return $this->user->tags()->orderBy('tag', 'ASC')->get(['tags.*']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -454,4 +454,24 @@ class TagRepository implements TagRepositoryInterface
|
|||||||
/** @var Location|null */
|
/** @var Location|null */
|
||||||
return $tag->locations()->first();
|
return $tag->locations()->first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function tagStartsWith(string $query): Collection
|
||||||
|
{
|
||||||
|
$search = sprintf('%s%%', $query);
|
||||||
|
|
||||||
|
return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function tagEndsWith(string $query): Collection
|
||||||
|
{
|
||||||
|
$search = sprintf('%%%s', $query);
|
||||||
|
|
||||||
|
return $this->user->tags()->where('tag', 'LIKE', $search)->get(['tags.*']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,24 @@ interface TagRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function searchTag(string $query): Collection;
|
public function searchTag(string $query): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find one or more tags that start with the string in the query
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function tagStartsWith(string $query): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find one or more tags that start with the string in the query
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
*
|
||||||
|
* @return Collection
|
||||||
|
*/
|
||||||
|
public function tagEndsWith(string $query): Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search the users tags.
|
* Search the users tags.
|
||||||
*
|
*
|
||||||
|
@ -57,7 +57,6 @@ use Gdbots\QueryParser\QueryParser;
|
|||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use PragmaRX\Random\Generators\StringGenerator;
|
|
||||||
use TypeError;
|
use TypeError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,6 +81,9 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
private array $validOperators;
|
private array $validOperators;
|
||||||
private array $words;
|
private array $words;
|
||||||
|
|
||||||
|
private array $excludeTags;
|
||||||
|
private array $includeTags;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OperatorQuerySearch constructor.
|
* OperatorQuerySearch constructor.
|
||||||
*
|
*
|
||||||
@ -93,6 +95,8 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
$this->operators = new Collection();
|
$this->operators = new Collection();
|
||||||
$this->page = 1;
|
$this->page = 1;
|
||||||
$this->words = [];
|
$this->words = [];
|
||||||
|
$this->excludeTags = [];
|
||||||
|
$this->includeTags = [];
|
||||||
$this->prohibitedWords = [];
|
$this->prohibitedWords = [];
|
||||||
$this->invalidOperators = [];
|
$this->invalidOperators = [];
|
||||||
$this->limit = 25;
|
$this->limit = 25;
|
||||||
@ -167,6 +171,8 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
foreach ($query1->getNodes() as $searchNode) {
|
foreach ($query1->getNodes() as $searchNode) {
|
||||||
$this->handleSearchNode($searchNode);
|
$this->handleSearchNode($searchNode);
|
||||||
}
|
}
|
||||||
|
$this->parseTagInstructions();
|
||||||
|
|
||||||
|
|
||||||
$this->collector->setSearchWords($this->words);
|
$this->collector->setSearchWords($this->words);
|
||||||
$this->collector->excludeSearchWords($this->prohibitedWords);
|
$this->collector->excludeSearchWords($this->prohibitedWords);
|
||||||
@ -868,7 +874,8 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
case 'tag_is':
|
case 'tag_is':
|
||||||
$result = $this->tagRepository->findByTag($value);
|
$result = $this->tagRepository->findByTag($value);
|
||||||
if (null !== $result) {
|
if (null !== $result) {
|
||||||
$this->collector->setTags(new Collection([$result]));
|
$this->includeTags[] = $result->id;
|
||||||
|
$this->includeTags = array_unique($this->includeTags);
|
||||||
}
|
}
|
||||||
// no tags found means search must result in nothing.
|
// no tags found means search must result in nothing.
|
||||||
if (null === $result) {
|
if (null === $result) {
|
||||||
@ -876,11 +883,79 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
$this->collector->findNothing();
|
$this->collector->findNothing();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'tag_contains':
|
||||||
|
$tags = $this->tagRepository->searchTag($value);
|
||||||
|
if (0 === $tags->count()) {
|
||||||
|
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
|
||||||
|
$this->collector->findNothing();
|
||||||
|
}
|
||||||
|
if ($tags->count() > 0) {
|
||||||
|
$ids = array_values($tags->pluck('id')->toArray());
|
||||||
|
$this->includeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'tag_starts':
|
||||||
|
$tags = $this->tagRepository->tagStartsWith($value);
|
||||||
|
if (0 === $tags->count()) {
|
||||||
|
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
|
||||||
|
$this->collector->findNothing();
|
||||||
|
}
|
||||||
|
if ($tags->count() > 0) {
|
||||||
|
$ids = array_values($tags->pluck('id')->toArray());
|
||||||
|
$this->includeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '-tag_starts':
|
||||||
|
$tags = $this->tagRepository->tagStartsWith($value);
|
||||||
|
if (0 === $tags->count()) {
|
||||||
|
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
|
||||||
|
$this->collector->findNothing();
|
||||||
|
}
|
||||||
|
if ($tags->count() > 0) {
|
||||||
|
$ids = array_values($tags->pluck('id')->toArray());
|
||||||
|
$this->excludeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'tag_ends':
|
||||||
|
$tags = $this->tagRepository->tagEndsWith($value);
|
||||||
|
if (0 === $tags->count()) {
|
||||||
|
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
|
||||||
|
$this->collector->findNothing();
|
||||||
|
}
|
||||||
|
if ($tags->count() > 0) {
|
||||||
|
$ids = array_values($tags->pluck('id')->toArray());
|
||||||
|
$this->includeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '-tag_ends':
|
||||||
|
$tags = $this->tagRepository->tagEndsWith($value);
|
||||||
|
if (0 === $tags->count()) {
|
||||||
|
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
|
||||||
|
$this->collector->findNothing();
|
||||||
|
}
|
||||||
|
if ($tags->count() > 0) {
|
||||||
|
$ids = array_values($tags->pluck('id')->toArray());
|
||||||
|
$this->excludeTags = array_unique(array_merge($this->includeTags, $ids));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '-tag_contains':
|
||||||
|
$tags = $this->tagRepository->searchTag($value)->keyBy('id');
|
||||||
|
|
||||||
|
if (0 === $tags->count()) {
|
||||||
|
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
|
||||||
|
$this->collector->findNothing();
|
||||||
|
}
|
||||||
|
if ($tags->count() > 0) {
|
||||||
|
$ids = array_values($tags->pluck('id')->toArray());
|
||||||
|
$this->excludeTags = array_unique(array_merge($this->excludeTags, $ids));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case '-tag_is':
|
case '-tag_is':
|
||||||
case 'tag_is_not':
|
case 'tag_is_not':
|
||||||
$result = $this->tagRepository->searchTag($value);
|
$result = $this->tagRepository->searchTag($value);
|
||||||
if ($result->count() > 0) {
|
if ($result->count() > 0) {
|
||||||
$this->collector->setWithoutSpecificTags($result);
|
$this->excludeTags[] = $result->id;
|
||||||
|
$this->excludeTags = array_unique($this->excludeTags);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
//
|
//
|
||||||
@ -1443,7 +1518,7 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
*
|
*
|
||||||
* @param string $value
|
* @param string $value
|
||||||
* @param SearchDirection $searchDirection
|
* @param SearchDirection $searchDirection
|
||||||
* @param StringPosition $stringPosition
|
* @param StringPosition $stringPosition
|
||||||
* @param bool $prohibited
|
* @param bool $prohibited
|
||||||
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
|
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
|
||||||
*/
|
*/
|
||||||
@ -2160,4 +2235,42 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
$this->limit = $limit;
|
$this->limit = $limit;
|
||||||
$this->collector->setLimit($this->limit);
|
$this->collector->setLimit($this->limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return void
|
||||||
|
* @throws \Psr\Container\ContainerExceptionInterface
|
||||||
|
* @throws \Psr\Container\NotFoundExceptionInterface
|
||||||
|
*/
|
||||||
|
private function parseTagInstructions(): void
|
||||||
|
{
|
||||||
|
app('log')->debug('Now in parseTagInstructions()');
|
||||||
|
// if exclude tags, remove excluded tags.
|
||||||
|
if (count($this->excludeTags) > 0) {
|
||||||
|
app('log')->debug(sprintf('%d exclude tag(s)', count($this->excludeTags)));
|
||||||
|
$collection = new Collection;
|
||||||
|
foreach ($this->excludeTags as $tagId) {
|
||||||
|
$tag = $this->tagRepository->find($tagId);
|
||||||
|
if (null !== $tag) {
|
||||||
|
app('log')->debug(sprintf('Exclude tag "%s"', $tag->tag));
|
||||||
|
$collection->push($tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app('log')->debug(sprintf('Selecting all tags except %d excluded tag(s).', $collection->count()));
|
||||||
|
$this->collector->setWithoutSpecificTags($collection);
|
||||||
|
}
|
||||||
|
// if include tags, include them:
|
||||||
|
if (count($this->includeTags) > 0) {
|
||||||
|
app('log')->debug(sprintf('%d include tag(s)', count($this->includeTags)));
|
||||||
|
$collection = new Collection;
|
||||||
|
foreach ($this->includeTags as $tagId) {
|
||||||
|
$tag = $this->tagRepository->find($tagId);
|
||||||
|
if (null !== $tag) {
|
||||||
|
app('log')->debug(sprintf('Include tag "%s"', $tag->tag));
|
||||||
|
$collection->push($tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->collector->setTags($collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,9 @@ return [
|
|||||||
'tag_is' => ['alias' => false, 'needs_context' => true,],
|
'tag_is' => ['alias' => false, 'needs_context' => true,],
|
||||||
'tag_is_not' => ['alias' => false, 'needs_context' => true,],
|
'tag_is_not' => ['alias' => false, 'needs_context' => true,],
|
||||||
'tag' => ['alias' => true, 'alias_for' => 'tag_is', 'needs_context' => true,],
|
'tag' => ['alias' => true, 'alias_for' => 'tag_is', 'needs_context' => true,],
|
||||||
|
'tag_contains' => ['alias' => false, 'needs_context' => true,],
|
||||||
|
'tag_ends' => ['alias' => false, 'needs_context' => true,],
|
||||||
|
'tag_starts' => ['alias' => false, 'needs_context' => true,],
|
||||||
'description_is' => ['alias' => false, 'needs_context' => true,],
|
'description_is' => ['alias' => false, 'needs_context' => true,],
|
||||||
'description' => ['alias' => true, 'alias_for' => 'description_is', 'needs_context' => true,],
|
'description' => ['alias' => true, 'alias_for' => 'description_is', 'needs_context' => true,],
|
||||||
'description_contains' => ['alias' => false, 'needs_context' => true,],
|
'description_contains' => ['alias' => false, 'needs_context' => true,],
|
||||||
|
@ -449,6 +449,10 @@ return [
|
|||||||
'search_modifier_transaction_type' => 'Transaction type is ":value"',
|
'search_modifier_transaction_type' => 'Transaction type is ":value"',
|
||||||
'search_modifier_not_transaction_type' => 'Transaction type is not ":value"',
|
'search_modifier_not_transaction_type' => 'Transaction type is not ":value"',
|
||||||
'search_modifier_tag_is' => 'Tag is ":value"',
|
'search_modifier_tag_is' => 'Tag is ":value"',
|
||||||
|
'search_modifier_tag_contains' => 'Tag contains ":value"',
|
||||||
|
'search_modifier_not_tag_contains' => 'Tag does not contain ":value"',
|
||||||
|
'search_modifier_tag_ends' => 'Tag ends with ":value"',
|
||||||
|
'search_modifier_tag_starts' => 'Tag starts with ":value"',
|
||||||
'search_modifier_not_tag_is' => 'No tag is ":value"',
|
'search_modifier_not_tag_is' => 'No tag is ":value"',
|
||||||
'search_modifier_date_on_year' => 'Transaction is in year ":value"',
|
'search_modifier_date_on_year' => 'Transaction is in year ":value"',
|
||||||
'search_modifier_not_date_on_year' => 'Transaction is not in year ":value"',
|
'search_modifier_not_date_on_year' => 'Transaction is not in year ":value"',
|
||||||
|
Loading…
Reference in New Issue
Block a user