diff --git a/django/thunderstore/api/cyberstorm/tests/test_community_list.py b/django/thunderstore/api/cyberstorm/tests/test_community_list.py index 0b9d9055b..cfa3cf3eb 100644 --- a/django/thunderstore/api/cyberstorm/tests/test_community_list.py +++ b/django/thunderstore/api/cyberstorm/tests/test_community_list.py @@ -164,3 +164,39 @@ def __query_api(client: APIClient, query: str = "", response_status_code=200) -> response = client.get(f"{url}?{query}") assert response.status_code == response_status_code return response.json() + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "query, search_keywords, name, should_match", + [ + ("repo", ["repo"], "R.E.P.O", True), + ("repo", None, "R.E.P.O", False), + ("ror2", ["ror2"], "Risk of Rain 2", True), + ("ror2", None, "Risk of Rain 2", False), + ("ror", ["ror"], "Risk of Rain 2", True), + ("ror", None, "Risk of Rain 2", False), + ("lethal", ["lethal", "lc", "lethalcompany"], "Lethal Company", True), + ("lc", ["lethal", "lc", "lethalcompany"], "Lethal Company", True), + ("lethalcompany", ["lethal", "lc", "lethalcompany"], "Lethal Company", True), + ("hello", ["lethal", "lc", "lethalcompany"], "Lethal Company", False), + ("LETHAL", ["lethal", "lc"], "Lethal Company", True), + ("Lc", ["lethal", "LC"], "Lethal Company", True), + ], +) +def test_api_cyberstorm_community_search_with_keywords( + api_client: APIClient, + query: str, + search_keywords: List[str], + name: str, + should_match: bool, +) -> None: + CommunityFactory(name=name, search_keywords=search_keywords) + data = __query_api(api_client, query=f"search={query}") + + if should_match: + assert data["count"] == 1 + assert data["results"][0]["name"] == name + else: + assert data["count"] == 0 + assert data["results"] == [] diff --git a/django/thunderstore/api/cyberstorm/views/community_list.py b/django/thunderstore/api/cyberstorm/views/community_list.py index 8fcaee457..5af66fe7a 100644 --- a/django/thunderstore/api/cyberstorm/views/community_list.py +++ b/django/thunderstore/api/cyberstorm/views/community_list.py @@ -18,7 +18,7 @@ class CommunityListAPIView(CyberstormAutoSchemaMixin, ListAPIView): pagination_class = CommunityPaginator queryset = Community.objects.listed() filter_backends = [SearchFilter, StrictOrderingFilter] - search_fields = ["name"] + search_fields = ["name", "search_keywords"] ordering_fields = [ "aggregated_fields__download_count", "aggregated_fields__package_count", diff --git a/django/thunderstore/community/migrations/0034_add_search_keywords.py b/django/thunderstore/community/migrations/0034_add_search_keywords.py new file mode 100644 index 000000000..c5ae9839d --- /dev/null +++ b/django/thunderstore/community/migrations/0034_add_search_keywords.py @@ -0,0 +1,25 @@ +# Generated by Django 3.1.7 on 2025-03-05 08:35 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("community", "0033_add_mod_manager_support_field"), + ] + + operations = [ + migrations.AddField( + model_name="community", + name="search_keywords", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=512), + blank=True, + default=list, + null=True, + size=None, + ), + ), + ] diff --git a/django/thunderstore/community/models/community.py b/django/thunderstore/community/models/community.py index 558004b7c..df613c30a 100644 --- a/django/thunderstore/community/models/community.py +++ b/django/thunderstore/community/models/community.py @@ -2,6 +2,7 @@ from functools import lru_cache from typing import TYPE_CHECKING, Optional +from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError from django.db import models, transaction from django.db.models import Manager, QuerySet @@ -117,6 +118,13 @@ class Community(TimestampMixin, models.Model): # Will hide/show "Install with Mod Manager" button on package pages has_mod_manager_support = models.BooleanField(default=True) + search_keywords = ArrayField( + models.CharField(max_length=512), + blank=True, + null=True, + default=list, + ) + @property def aggregated(self) -> "AggregatedFields": return (