feat: implement search equivalence of "jpg" and "jpeg" filetypes (#649)

* feat: implement search equivalence of "jpg" and "jpeg" filetypes in an extensible manner

* docs: update completion for search features on roadmap

* fix: move FILETYPE_EQUIVALENTS to media_types.py
This commit is contained in:
Jann Stute
2024-12-22 22:58:10 +01:00
committed by GitHub
parent 4c3ff42169
commit 9eed3b64d9
3 changed files with 21 additions and 10 deletions

View File

@@ -86,13 +86,13 @@ Features are broken up into the following priority levels, with nested prioritie
- [ ] GPS Location [LOW]
- [ ] Custom field names [HIGH] [#18](https://github.com/TagStudioDev/TagStudio/issues/18)
- [ ] Search engine [HIGH] [#325](https://github.com/TagStudioDev/TagStudio/issues/325)
- [ ] Boolean operators [HIGH] [#225](https://github.com/TagStudioDev/TagStudio/issues/225), [#314](https://github.com/TagStudioDev/TagStudio/issues/314)
- [x] Boolean operators [HIGH] [#225](https://github.com/TagStudioDev/TagStudio/issues/225), [#314](https://github.com/TagStudioDev/TagStudio/issues/314)
- [ ] Tag objects + autocomplete [HIGH] [#476 (Autocomplete)](https://github.com/TagStudioDev/TagStudio/issues/476)
- [ ] Filename search [HIGH]
- [ ] Filetype search [HIGH]
- [ ] Search by extension (e.g. ".jpg", ".png") [HIGH]
- [ ] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [ ] Search by media type (e.g. "image", "video", "document") [MEDIUM]
- [x] Filename search [HIGH]
- [x] Filetype search [HIGH]
- [x] Search by extension (e.g. ".jpg", ".png") [HIGH]
- [x] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [x] Search by media type (e.g. "image", "video", "document") [MEDIUM]
- [ ] Field content search [HIGH] [#272](https://github.com/TagStudioDev/TagStudio/issues/272)
- [ ] HAS operator for composition tags [HIGH]
- [ ] OCR search [LOW]
@@ -172,12 +172,12 @@ These version milestones are rough estimations for when the previous core featur
- [ ] [Tag Categories](../library/tag_categories.md) [HIGH]
- [ ] Property available for tags that allow the tag and any inheriting from it to be displayed separately in the preview panel under a title [HIGH]
- [ ] Search engine [HIGH]
- [ ] Boolean operators [HIGH]
- [x] Boolean operators [HIGH]
- [ ] Tag objects + autocomplete [HIGH]
- [x] Filename search [HIGH]
- [x] Filetype search [HIGH]
- [x] Search by extension (e.g. ".jpg", ".png") [HIGH]
- [ ] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [x] Optional consolidation of extension synonyms (i.e. ".jpg" can equal ".jpeg") [LOW]
- [x] Search by media type (e.g. "image", "video", "document") [MEDIUM]
- [ ] Field content search [HIGH]
- [ ] Sortable results [HIGH]

View File

@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
from sqlalchemy import and_, distinct, func, or_, select
from sqlalchemy.orm import Session
from sqlalchemy.sql.expression import BinaryExpression, ColumnExpressionArgument
from src.core.media_types import MediaCategories
from src.core.media_types import FILETYPE_EQUIVALENTS, MediaCategories
from src.core.query_lang import BaseVisitor
from src.core.query_lang.ast import AST, ANDList, Constraint, ConstraintType, Not, ORList, Property
@@ -17,6 +17,13 @@ else:
Library = None # don't import .library because of circular imports
def get_filetype_equivalency_list(item: str) -> list[str] | set[str]:
for s in FILETYPE_EQUIVALENTS:
if item in s:
return s
return [item]
class SQLBoolExpressionBuilder(BaseVisitor[ColumnExpressionArgument]):
def __init__(self, lib: Library) -> None:
super().__init__()
@@ -73,7 +80,9 @@ class SQLBoolExpressionBuilder(BaseVisitor[ColumnExpressionArgument]):
break
return Entry.suffix.in_(map(lambda x: x.replace(".", ""), extensions))
elif node.type == ConstraintType.FileType:
return Entry.suffix.ilike(node.value)
return or_(
*[Entry.suffix.ilike(ft) for ft in get_filetype_equivalency_list(node.value)]
)
elif node.type == ConstraintType.Special: # noqa: SIM102 unnecessary once there is a second special constraint
if node.value.lower() == "untagged":
return ~Entry.id.in_(

View File

@@ -10,6 +10,8 @@ from pathlib import Path
logging.basicConfig(format="%(message)s", level=logging.INFO)
FILETYPE_EQUIVALENTS = [set(["jpg", "jpeg"])]
class MediaType(str, Enum):
"""Names of media types."""