diff options
| author | 2025-06-29 11:09:08 +0200 | |
|---|---|---|
| committer | 2025-06-29 11:09:08 +0200 | |
| commit | c8bbf355342985c83054c6c36c6538a780ab509e (patch) | |
| tree | 87286cac4e98769b3a14308e042abdd4d83153e4 /app/Models | |
| parent | 7c57f38008136202ba7b38e3154ac87be4eefb68 (diff) | |
Add search operator `c:` for categories (#7696)
* Add search operator `c:` for categories
fix https://github.com/FreshRSS/FreshRSS/discussions/7692
Allow searching for e.g. `c:23,34`
Diffstat (limited to 'app/Models')
| -rw-r--r-- | app/Models/Entry.php | 6 | ||||
| -rw-r--r-- | app/Models/EntryDAO.php | 19 | ||||
| -rw-r--r-- | app/Models/Search.php | 51 |
3 files changed, 76 insertions, 0 deletions
diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 66c05a830..190b435c8 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -642,6 +642,12 @@ HTML; if ($ok && $filter->getNotFeedIds() !== null) { $ok &= !in_array($this->feedId, $filter->getNotFeedIds(), true); } + if ($ok && $filter->getCategoryIds() !== null) { + $ok &= in_array($this->feed()?->categoryId(), $filter->getCategoryIds(), true); + } + if ($ok && $filter->getNotCategoryIds() !== null) { + $ok &= !in_array($this->feed()?->categoryId(), $filter->getNotCategoryIds(), true); + } if ($ok && $filter->getAuthor() !== null) { foreach ($filter->getAuthor() as $author) { $ok &= stripos(implode(';', $this->authors), $author) !== false; diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 68746c380..fecf02cf8 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -917,6 +917,25 @@ SQL; $sub_search .= ') '; } + if ($filter->getCategoryIds() !== null) { + $sub_search .= 'AND ' . $alias . 'id_feed IN (SELECT f.id FROM `_feed` f WHERE f.category IN ('; + foreach ($filter->getCategoryIds() as $category_id) { + $sub_search .= '?,'; + $values[] = $category_id; + } + $sub_search = rtrim($sub_search, ','); + $sub_search .= ')) '; + } + if ($filter->getNotCategoryIds() !== null) { + $sub_search .= 'AND ' . $alias . 'id_feed NOT IN (SELECT f.id FROM `_feed` f WHERE f.category IN ('; + foreach ($filter->getNotCategoryIds() as $category_id) { + $sub_search .= '?,'; + $values[] = $category_id; + } + $sub_search = rtrim($sub_search, ','); + $sub_search .= ')) '; + } + if ($filter->getLabelIds() !== null) { if ($filter->getLabelIds() === '*') { $sub_search .= 'AND EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) '; diff --git a/app/Models/Search.php b/app/Models/Search.php index d425fcee8..68f0a6bce 100644 --- a/app/Models/Search.php +++ b/app/Models/Search.php @@ -21,6 +21,8 @@ class FreshRSS_Search implements \Stringable { private ?array $entry_ids = null; /** @var list<int>|null */ private ?array $feed_ids = null; + /** @var list<int>|null */ + private ?array $category_ids = null; /** @var list<int>|'*'|null */ private $label_ids = null; /** @var list<string>|null */ @@ -62,6 +64,8 @@ class FreshRSS_Search implements \Stringable { private ?array $not_entry_ids = null; /** @var list<int>|null */ private ?array $not_feed_ids = null; + /** @var list<int>|null */ + private ?array $not_category_ids = null; /** @var list<int>|'*'|null */ private $not_label_ids = null; /** @var list<string>|null */ @@ -107,6 +111,7 @@ class FreshRSS_Search implements \Stringable { $input = $this->parseNotEntryIds($input); $input = $this->parseNotFeedIds($input); + $input = $this->parseNotCategoryIds($input); $input = $this->parseNotLabelIds($input); $input = $this->parseNotLabelNames($input); @@ -121,6 +126,7 @@ class FreshRSS_Search implements \Stringable { $input = $this->parseEntryIds($input); $input = $this->parseFeedIds($input); + $input = $this->parseCategoryIds($input); $input = $this->parseLabelIds($input); $input = $this->parseLabelNames($input); @@ -165,6 +171,15 @@ class FreshRSS_Search implements \Stringable { return $this->not_feed_ids; } + /** @return list<int>|null */ + public function getCategoryIds(): ?array { + return $this->category_ids; + } + /** @return list<int>|null */ + public function getNotCategoryIds(): ?array { + return $this->not_category_ids; + } + /** @return list<int>|'*'|null */ public function getLabelIds(): array|string|null { return $this->label_ids; @@ -420,6 +435,42 @@ class FreshRSS_Search implements \Stringable { return $input; } + private function parseCategoryIds(string $input): string { + if (preg_match_all('/\\bc:(?P<search>[0-9,]*)/', $input, $matches)) { + $input = str_replace($matches[0], '', $input); + $ids_lists = $matches['search']; + $this->category_ids = []; + foreach ($ids_lists as $ids_list) { + $category_ids = explode(',', $ids_list); + $category_ids = self::removeEmptyValues($category_ids); + /** @var list<int> $category_ids */ + $category_ids = array_map('intval', $category_ids); + if (!empty($category_ids)) { + $this->category_ids = array_merge($this->category_ids, $category_ids); + } + } + } + return $input; + } + + private function parseNotCategoryIds(string $input): string { + if (preg_match_all('/(?<=[\\s(]|^)[!-]c:(?P<search>[0-9,]*)/', $input, $matches)) { + $input = str_replace($matches[0], '', $input); + $ids_lists = $matches['search']; + $this->not_category_ids = []; + foreach ($ids_lists as $ids_list) { + $category_ids = explode(',', $ids_list); + $category_ids = self::removeEmptyValues($category_ids); + /** @var list<int> $category_ids */ + $category_ids = array_map('intval', $category_ids); + if (!empty($category_ids)) { + $this->not_category_ids = array_merge($this->not_category_ids, $category_ids); + } + } + } + return $input; + } + /** * Parse the search string to find tags (labels) IDs. */ |
