mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-10-25 21:16:47 +00:00
Improved search.
This commit is contained in:
@@ -14,11 +14,15 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Support\Search;
|
||||
|
||||
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class Search
|
||||
@@ -27,20 +31,46 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class Search implements SearchInterface
|
||||
{
|
||||
/** @var int */
|
||||
private $limit = 100;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* AttachmentRepository constructor.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* The search will assume that the user does not have so many accounts
|
||||
* that this search should be paginated.
|
||||
*
|
||||
* @param array $words
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchAccounts(array $words): Collection
|
||||
{
|
||||
return auth()->user()->accounts()->with('accounttype')->where(
|
||||
function (EloquentBuilder $q) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
$q->orWhere('name', 'LIKE', '%' . e($word) . '%');
|
||||
$accounts = $this->user->accounts()->get();
|
||||
/** @var Collection $result */
|
||||
$result = $accounts->filter(
|
||||
function (Account $account) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($account->name), $words)) {
|
||||
return $account;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
)->get();
|
||||
);
|
||||
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,46 +81,46 @@ class Search implements SearchInterface
|
||||
public function searchBudgets(array $words): Collection
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = auth()->user()->budgets()->get();
|
||||
$newSet = $set->filter(
|
||||
function (Budget $b) use ($words) {
|
||||
$found = 0;
|
||||
foreach ($words as $word) {
|
||||
if (!(strpos(strtolower($b->name), strtolower($word)) === false)) {
|
||||
$found++;
|
||||
}
|
||||
$set = auth()->user()->budgets()->get();
|
||||
/** @var Collection $result */
|
||||
$result = $set->filter(
|
||||
function (Budget $budget) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($budget->name), $words)) {
|
||||
return $budget;
|
||||
}
|
||||
|
||||
return $found > 0;
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return $newSet;
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search assumes the user does not have that many categories. So no paginated search.
|
||||
*
|
||||
* @param array $words
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchCategories(array $words): Collection
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = auth()->user()->categories()->get();
|
||||
$newSet = $set->filter(
|
||||
function (Category $c) use ($words) {
|
||||
$found = 0;
|
||||
foreach ($words as $word) {
|
||||
if (!(strpos(strtolower($c->name), strtolower($word)) === false)) {
|
||||
$found++;
|
||||
}
|
||||
$categories = $this->user->categories()->get();
|
||||
/** @var Collection $result */
|
||||
$result = $categories->filter(
|
||||
function (Category $category) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($category->name), $words)) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
return $found > 0;
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $newSet;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +131,21 @@ class Search implements SearchInterface
|
||||
*/
|
||||
public function searchTags(array $words): Collection
|
||||
{
|
||||
return new Collection;
|
||||
$tags = $this->user->tags()->get();
|
||||
|
||||
/** @var Collection $result */
|
||||
$result = $tags->filter(
|
||||
function (Tag $tag) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($tag->tag), $words)) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,40 +155,86 @@ class Search implements SearchInterface
|
||||
*/
|
||||
public function searchTransactions(array $words): Collection
|
||||
{
|
||||
// decrypted transaction journals:
|
||||
$decrypted = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 0)->where(
|
||||
function (EloquentBuilder $q) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
$q->orWhere('transaction_journals.description', 'LIKE', '%' . e($word) . '%');
|
||||
}
|
||||
}
|
||||
)->get(TransactionJournal::queryFields());
|
||||
$pageSize = 100;
|
||||
$processed = 0;
|
||||
$page = 1;
|
||||
$result = new Collection();
|
||||
do {
|
||||
$collector = new JournalCollector($this->user);
|
||||
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page);
|
||||
$set = $collector->getPaginatedJournals();
|
||||
Log::debug(sprintf('Found %d journals to check. ', $set->count()));
|
||||
|
||||
// encrypted
|
||||
$all = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 1)->get(TransactionJournal::queryFields());
|
||||
$set = $all->filter(
|
||||
function (TransactionJournal $journal) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
$haystack = strtolower($journal->description);
|
||||
$word = strtolower($word);
|
||||
if (!(strpos($haystack, $word) === false)) {
|
||||
return $journal;
|
||||
// Filter transactions that match the given triggers.
|
||||
$filtered = $set->filter(
|
||||
function (Transaction $transaction) use ($words) {
|
||||
// check descr of journal:
|
||||
if ($this->strpos_arr(strtolower(strval($transaction->description)), $words)) {
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
// check descr of transaction
|
||||
if ($this->strpos_arr(strtolower(strval($transaction->transaction_description)), $words)) {
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
// return false:
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return null;
|
||||
Log::debug(sprintf('Found %d journals that match.', $filtered->count()));
|
||||
|
||||
// merge:
|
||||
/** @var Collection $result */
|
||||
$result = $result->merge($filtered);
|
||||
Log::debug(sprintf('Total count is now %d', $result->count()));
|
||||
|
||||
// Update counters
|
||||
$page++;
|
||||
$processed += count($set);
|
||||
|
||||
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
|
||||
|
||||
// Check for conditions to finish the loop
|
||||
$reachedEndOfList = $set->count() < 1;
|
||||
$foundEnough = $result->count() >= $this->limit;
|
||||
|
||||
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
|
||||
Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true)));
|
||||
|
||||
} while (!$reachedEndOfList && !$foundEnough);
|
||||
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
*/
|
||||
public function setLimit(int $limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param array $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function strpos_arr(string $haystack, array $needle)
|
||||
{
|
||||
if (strlen($haystack) === 0) {
|
||||
return false;
|
||||
}
|
||||
foreach ($needle as $what) {
|
||||
if (($pos = strpos($haystack, $what)) !== false) {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
$filtered = $set->merge($decrypted);
|
||||
$filtered = $filtered->sortBy(
|
||||
function (TransactionJournal $journal) {
|
||||
return intval($journal->date->format('U'));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$filtered = $filtered->reverse();
|
||||
|
||||
return $filtered;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user