From 78d404e403f9c9e0f80b9d82c6f65abde4ef9007 Mon Sep 17 00:00:00 2001 From: abaisero Date: Tue, 5 Apr 2022 01:21:16 -0400 Subject: [PATCH] fix: generalizes player search query format Prior to this commit, the search query was used monolithically as a single string to match the members' member id or their (comma-separated) full name; this had many shortcomings, e.g., a search query " 12345 " would fail to match a member with id "12345", and a search query "surname name" would fail to match a member with full name "surname, name". Also see #164 and #248. In this commit, the search is first stripped of outer whitespace, which helps search for member ids. If the search query is not an id, then the search query is split into tokens, and a match with a member is found if all of the respective query tokens match the member's full name. This allows users to run queries such as "name", "surname", "name surname" or "surname name". --- agagd/agagd_core/views/search.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/agagd/agagd_core/views/search.py b/agagd/agagd_core/views/search.py index 4099f9e2..e3302330 100644 --- a/agagd/agagd_core/views/search.py +++ b/agagd/agagd_core/views/search.py @@ -1,3 +1,6 @@ +import functools +import operator + from agagd_core.models import Member from agagd_core.tables.search import SearchResultsTable from django.db.models import F, Q @@ -13,7 +16,7 @@ class SearchView(DetailView): search_results_template_name = "search_results.html" def get(self, request): - query = request.GET.get("q", "") + query = request.GET.get("q", "").strip() if not query: return TemplateResponse(request, self.template_name) @@ -22,9 +25,14 @@ def get(self, request): member_id = [int(query)] return HttpResponseRedirect(reverse("players_profile", args=member_id)) + # constructing intersection queryset from query tokens + tokens = query.split() + querysets = (Q(full_name__icontains=token) for token in tokens) + queryset = functools.reduce(operator.and_, querysets) + member_table_data = ( Member.objects.filter(Q(member_id=F("players__pin_player"))) - .filter(full_name__icontains=query) + .filter(queryset) .values( "member_id", "chapter_id",