diff --git a/amy/dashboard/views.py b/amy/dashboard/views.py index 28f03c53e..2b8d50920 100644 --- a/amy/dashboard/views.py +++ b/amy/dashboard/views.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -import re from urllib.parse import unquote from django.conf import settings from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.postgres.search import SearchVector from django.db.models import ( Case, Count, @@ -585,6 +585,12 @@ def get_redirect_url(self) -> str: def search(request): """Search the database by term.""" + def multiple_Q_icontains(term: str, *args: str) -> Q: + q = Q() + for arg in args: + q |= Q(**{f"{arg}__icontains": term}) + return q + term = "" organizations = None memberships = None @@ -598,7 +604,6 @@ def search(request): form = SearchForm(request.GET) if form.is_valid(): term = form.cleaned_data.get("term", "").strip() - tokens = re.split(r"\s+", term) results_combined = [] organizations = list( @@ -628,79 +633,58 @@ def search(request): ) results_combined += events - # if user searches for two words, assume they mean a person - # name - if len(tokens) == 2: - name1, name2 = tokens - complex_q = ( - (Q(personal__icontains=name1) & Q(family__icontains=name2)) - | (Q(personal__icontains=name2) & Q(family__icontains=name1)) - | Q(email__icontains=term) - | Q(secondary_email__icontains=term) - | Q(github__icontains=term) + persons = ( + Person.objects.annotate( + search=SearchVector("personal", "middle", "family") ) - persons = list(Person.objects.filter(complex_q).order_by("family")) - else: - persons = list( - Person.objects.filter( - Q(personal__icontains=term) - | Q(family__icontains=term) - | Q(email__icontains=term) - | Q(secondary_email__icontains=term) - | Q(github__icontains=term) - ).order_by("family") + .filter( + Q(search=term) + | multiple_Q_icontains(term, "email", "secondary_email", "github") ) - - results_combined += persons + .order_by("family") + ) + results_combined += list(persons) airports = list( Airport.objects.filter( - Q(iata__icontains=term) | Q(fullname__icontains=term) + multiple_Q_icontains(term, "iata", "fullname") ).order_by("iata") ) results_combined += airports - if len(tokens) == 2: - name1, name2 = tokens - complex_q = ( - Q(member_code__icontains=term) - | (Q(personal__icontains=name1) & Q(family__icontains=name2)) - | (Q(personal__icontains=name2) & Q(family__icontains=name1)) - | Q(email__icontains=term) - | Q(secondary_email__icontains=term) - | Q(github__icontains=term) - | Q(affiliation__icontains=term) - | Q(location__icontains=term) - | Q(user_notes__icontains=term) + training_requests = ( + TrainingRequest.objects.annotate( + search=SearchVector("personal", "middle", "family") ) - training_requests = list( - TrainingRequest.objects.filter(complex_q).order_by("family") - ) - - else: - training_requests = list( - TrainingRequest.objects.filter( - Q(member_code__icontains=term) - | Q(family__icontains=term) - | Q(email__icontains=term) - | Q(github__icontains=term) - | Q(affiliation__icontains=term) - | Q(location__icontains=term) - | Q(user_notes__icontains=term) - ).order_by("family") + .filter( + Q(search=term) + | multiple_Q_icontains( + term, + "member_code", + "email", + "secondary_email", + "github", + "affiliation", + "location", + "user_notes", + ) ) - - results_combined += training_requests + .order_by("family") + ) + results_combined += list(training_requests) comments = list( Comment.objects.filter( - Q(comment__icontains=term) - | Q(user_name__icontains=term) - | Q(user_email__icontains=term) - | Q(user__personal__icontains=term) - | Q(user__family__icontains=term) - | Q(user__email__icontains=term) - | Q(user__github__icontains=term) + multiple_Q_icontains( + term, + "comment", + "user_name", + "user_email", + "user__personal", + "user__family", + "user__email", + "user__github", + ) ).prefetch_related("content_object") ) results_combined += comments @@ -709,7 +693,7 @@ def search(request): if len(results_combined) == 1 and not form.cleaned_data["no_redirect"]: result = results_combined[0] msg = format_html( - "You were moved to this page, because your search {} " + "You were moved to this page, because your search {} " "yields only this result.", term, )