diff --git a/server/szurubooru/search/configs/base_search_config.py b/server/szurubooru/search/configs/base_search_config.py index d60f3617..92ddb4c5 100644 --- a/server/szurubooru/search/configs/base_search_config.py +++ b/server/szurubooru/search/configs/base_search_config.py @@ -11,6 +11,7 @@ class BaseSearchConfig: SORT_NONE = tokens.SortToken.SORT_NONE SORT_ASC = tokens.SortToken.SORT_ASC SORT_DESC = tokens.SortToken.SORT_DESC + SORT_DESC_NULL_LAST = tokens.SortToken.SORT_DESC_NULL_LAST def on_search_query_parsed(self, search_query: SearchQuery) -> None: pass diff --git a/server/szurubooru/search/configs/post_search_config.py b/server/szurubooru/search/configs/post_search_config.py index ddc003b7..3f681b80 100644 --- a/server/szurubooru/search/configs/post_search_config.py +++ b/server/szurubooru/search/configs/post_search_config.py @@ -305,7 +305,13 @@ class PostSearchConfig(BaseSearchConfig): ), ), ( - ["creation-date", "creation-time", "date", "time"], + [ + "creation-date", + "creation-time", + "date", + "time", + "posted", + ], search_util.create_date_filter(model.Post.creation_time), ), ( @@ -314,9 +320,14 @@ class PostSearchConfig(BaseSearchConfig): "last-edit-time", "edit-date", "edit-time", + "edited", ], search_util.create_date_filter(model.Post.last_edit_time), ), + ( + ["date-taken", "time-taken", "taken"], + search_util.create_date_filter(model.Post.date_taken), + ), ( ["comment-date", "comment-time"], search_util.create_date_filter( @@ -403,6 +414,10 @@ class PostSearchConfig(BaseSearchConfig): ], (model.Post.last_edit_time, self.SORT_DESC), ), + ( + ["date-taken", "taken"], + (model.Post.date_taken, self.SORT_DESC_NULL_LAST), + ), ( ["comment-date", "comment-time"], (model.Post.last_comment_creation_time, self.SORT_DESC), diff --git a/server/szurubooru/search/configs/util.py b/server/szurubooru/search/configs/util.py index 58e6ebe5..bb851f0e 100644 --- a/server/szurubooru/search/configs/util.py +++ b/server/szurubooru/search/configs/util.py @@ -179,6 +179,8 @@ def apply_date_criterion_to_column( elif criterion.max_value: max_date = util.parse_time_range(criterion.max_value)[1] expr = column <= max_date + elif isinstance(criterion, criteria.NullCriterion): + expr = column == sa.sql.null() else: assert False return expr diff --git a/server/szurubooru/search/criteria.py b/server/szurubooru/search/criteria.py index 633c6f2e..3e052f41 100644 --- a/server/szurubooru/search/criteria.py +++ b/server/szurubooru/search/criteria.py @@ -42,3 +42,12 @@ class ArrayCriterion(BaseCriterion): def __hash__(self) -> int: return hash(tuple(["array"] + self.values)) + + +class NullCriterion(BaseCriterion): + def __init__(self, original_text) -> None: + super().__init__(original_text) + self.value = None + + def __hash__(self) -> int: + return hash(self.value) diff --git a/server/szurubooru/search/executor.py b/server/szurubooru/search/executor.py index a5ef9625..0e77465a 100644 --- a/server/szurubooru/search/executor.py +++ b/server/szurubooru/search/executor.py @@ -189,6 +189,10 @@ class Executor: db_query = db_query.order_by(column.asc()) elif order == sort_token.SORT_DESC: db_query = db_query.order_by(column.desc()) + elif order == sort_token.SORT_DESC_NULL_LAST: + db_query = db_query.order_by( + column.is_(None), column.desc() + ) db_query = self.config.finalize_query(db_query) return db_query diff --git a/server/szurubooru/search/parser.py b/server/szurubooru/search/parser.py index d5e0f2f8..3dde380f 100644 --- a/server/szurubooru/search/parser.py +++ b/server/szurubooru/search/parser.py @@ -19,6 +19,8 @@ def _create_criterion( if not low and not high: raise errors.SearchError("Empty ranged value") return criteria.RangedCriterion(original_value, low, high) + if value.lower() in ["null", "none", "unknown", "?"]: + return criteria.NullCriterion(original_value) return criteria.PlainCriterion(original_value, value) diff --git a/server/szurubooru/search/tokens.py b/server/szurubooru/search/tokens.py index 9f4eeedd..14f7800d 100644 --- a/server/szurubooru/search/tokens.py +++ b/server/szurubooru/search/tokens.py @@ -23,6 +23,7 @@ class NamedToken(AnonymousToken): class SortToken: SORT_DESC = "desc" + SORT_DESC_NULL_LAST = "desc null last" SORT_ASC = "asc" SORT_NONE = "" SORT_DEFAULT = "default"