diff --git a/docs/updates/roadmap.md b/docs/updates/roadmap.md index e204bc3b..7a8e2557 100644 --- a/docs/updates/roadmap.md +++ b/docs/updates/roadmap.md @@ -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] diff --git a/tagstudio/src/core/library/alchemy/visitors.py b/tagstudio/src/core/library/alchemy/visitors.py index 6f73a45a..1756bb08 100644 --- a/tagstudio/src/core/library/alchemy/visitors.py +++ b/tagstudio/src/core/library/alchemy/visitors.py @@ -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_( diff --git a/tagstudio/src/core/media_types.py b/tagstudio/src/core/media_types.py index 78755aac..be9ebdbd 100644 --- a/tagstudio/src/core/media_types.py +++ b/tagstudio/src/core/media_types.py @@ -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."""