diff --git a/API.md b/API.md index ec0bb392..08e2c5c4 100644 --- a/API.md +++ b/API.md @@ -710,6 +710,7 @@ data. | `comment-count` | having given number of comments | | `fav-count` | favorited by given number of users | | `note-count` | having given number of annotations | + | `note-text` | having given note text (accepts wildcards) | | `relation-count` | having given number of relations | | `feature-count` | having been featured given number of times | | `type` | given type of posts. `` can be either `image`, `animation` (or `animated` or `anim`), `flash` (or `swf`) or `video` (or `webm`). | diff --git a/client/html/help_search_posts.tpl b/client/html/help_search_posts.tpl index f1f062c7..1c4ea86e 100644 --- a/client/html/help_search_posts.tpl +++ b/client/html/help_search_posts.tpl @@ -54,6 +54,10 @@ note-count having given number of annotations + + note-text + having given note text (accepts wildcards) + relation-count having given number of relations diff --git a/server/szurubooru/search/configs/post_search_config.py b/server/szurubooru/search/configs/post_search_config.py index cda1b1ac..003b4c25 100644 --- a/server/szurubooru/search/configs/post_search_config.py +++ b/server/szurubooru/search/configs/post_search_config.py @@ -69,25 +69,35 @@ def _create_score_filter(score: int) -> Filter: return wrapper -def _create_user_filter() -> Filter: - def wrapper( - query: SaQuery, - criterion: Optional[criteria.BaseCriterion], - negated: bool) -> SaQuery: - assert criterion - if isinstance(criterion, criteria.PlainCriterion) \ - and not criterion.value: - # pylint: disable=singleton-comparison - expr = model.Post.user_id == None - if negated: - expr = ~expr - return query.filter(expr) - return search_util.create_subquery_filter( - model.Post.user_id, - model.User.user_id, - model.User.name, - search_util.create_str_filter)(query, criterion, negated) - return wrapper +def _user_filter( + query: SaQuery, + criterion: Optional[criteria.BaseCriterion], + negated: bool) -> SaQuery: + assert criterion + if isinstance(criterion, criteria.PlainCriterion) \ + and not criterion.value: + # pylint: disable=singleton-comparison + expr = model.Post.user_id == None + if negated: + expr = ~expr + return query.filter(expr) + return search_util.create_subquery_filter( + model.Post.user_id, + model.User.user_id, + model.User.name, + search_util.create_str_filter)(query, criterion, negated) + + +def _note_filter( + query: SaQuery, + criterion: Optional[criteria.BaseCriterion], + negated: bool) -> SaQuery: + assert criterion + return search_util.create_subquery_filter( + model.Post.post_id, + model.PostNote.post_id, + model.PostNote.text, + search_util.create_str_filter)(query, criterion, negated) class PostSearchConfig(BaseSearchConfig): @@ -187,7 +197,7 @@ class PostSearchConfig(BaseSearchConfig): ( ['uploader', 'upload', 'submit'], - _create_user_filter() + _user_filter ), ( @@ -311,6 +321,11 @@ class PostSearchConfig(BaseSearchConfig): search_util.create_str_filter( model.Post.safety, _safety_transformer) ), + + ( + ['note-text'], + _note_filter + ), ]) @property diff --git a/server/szurubooru/tests/search/configs/test_post_search_config.py b/server/szurubooru/tests/search/configs/test_post_search_config.py index 945a5e4f..738ee410 100644 --- a/server/szurubooru/tests/search/configs/test_post_search_config.py +++ b/server/szurubooru/tests/search/configs/test_post_search_config.py @@ -27,8 +27,8 @@ def score_factory(user_factory): @pytest.fixture def note_factory(): - def factory(): - return model.PostNote(polygon='...', text='...') + def factory(text='...'): + return model.PostNote(polygon='...', text=text) return factory @@ -294,6 +294,25 @@ def test_filter_by_note_count( verify_unpaged(input, expected_post_ids) +@pytest.mark.parametrize('input,expected_post_ids', [ + ('note-text:*', [1, 2, 3]), + ('note-text:text2', [2]), + ('note-text:text3*', [3]), + ('note-text:text3a,text2', [2, 3]), +]) +def test_filter_by_note_count( + verify_unpaged, post_factory, note_factory, input, expected_post_ids): + post1 = post_factory(id=1) + post2 = post_factory(id=2) + post3 = post_factory(id=3) + post1.notes = [note_factory(text='text1')] + post2.notes = [note_factory(text='text2'), note_factory(text='text2')] + post3.notes = [note_factory(text='text3a'), note_factory(text='text3b')] + db.session.add_all([post1, post2, post3]) + db.session.flush() + verify_unpaged(input, expected_post_ids) + + @pytest.mark.parametrize('input,expected_post_ids', [ ('feature-count:1', [1]), ('feature-count:3', [3]),