From f4ae51ae7eee52aa09e94b3e5c0d3f248d6a2347 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Mon, 7 Apr 2025 15:24:44 +0200 Subject: [PATCH] fix: give priority to small usernames when query is small Basically, when the search is "joe" we first try to search for an exact account named "joe", and fallback to normal flow when nothing matches. fix #2591 --- umap/autocomplete.py | 22 ++++++++++++++++++++++ umap/tests/test_views.py | 15 +++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/umap/autocomplete.py b/umap/autocomplete.py index c2bd02c0..537fa634 100644 --- a/umap/autocomplete.py +++ b/umap/autocomplete.py @@ -2,6 +2,7 @@ from agnocomplete.core import AgnocompleteModel from agnocomplete.register import register from django.conf import settings from django.contrib.auth import get_user_model +from django.db.models import Q @register @@ -13,3 +14,24 @@ class AutocompleteUser(AgnocompleteModel): data = super().item(current_item) data["url"] = current_item.get_url() return data + + def get_queryset_filters(self, query): + if len(query) <= 3 and self._force_exact: + conditions = Q() + for field_name in self.fields: + if not field_name[0].isalnum(): + field_name = field_name[1:] + # Force exact match + conditions |= Q(**{self._construct_qs_filter(f"={field_name}"): query}) + return conditions + + return super().get_queryset_filters(query) + + def build_filtered_queryset(self, query, **kwargs): + self._force_exact = len(query) <= 3 + qs = super().build_filtered_queryset(query, **kwargs) + if len(query) <= 3 and not len(qs): + # Not exact match, let's fallback to normal "startswith" query + self._force_exact = False + qs = super().build_filtered_queryset(query, **kwargs) + return qs diff --git a/umap/tests/test_views.py b/umap/tests/test_views.py index e13a9c9a..9a963c61 100644 --- a/umap/tests/test_views.py +++ b/umap/tests/test_views.py @@ -510,3 +510,18 @@ def test_can_combine_search_and_filter(client, map): url = reverse("search") response = client.get(url + "?q=dur&tags=bike") assert "Blé dur" in response.content.decode() + + +@pytest.mark.django_db +def test_can_find_small_usernames(client): + UserFactory(username="Joe") + UserFactory(username="JoeJoe") + url = "/agnocomplete/AutocompleteUser/" + response = client.get(url + "?q=joe") + data = json.loads(response.content)["data"] + assert len(data) == 1 + assert data[0]["label"] == "Joe" + response = client.get(url + "?q=joej") + data = json.loads(response.content)["data"] + assert len(data) == 1 + assert data[0]["label"] == "JoeJoe"