From ba5c291f57b807c3874a0fa749cb05dec6b4078a Mon Sep 17 00:00:00 2001 From: Klaas van Schelven Date: Wed, 12 Mar 2025 14:30:02 +0100 Subject: [PATCH] Search performance: use Event.issue when searching In b0317927840b using Event.issue was made conditional (if we already filter by Tag, the tag encodes that info already, and it was assumed adding the WHERE elsewhere would confuse the query optimizer). As per that commit's message, the measurements that led me to that decision were probably wrong. I now simply think: the more places you narrow your search, the easier your DB will have it. Measuring turns out: this is indeed so, for all cases (in the order of 20-30%), for which this still matters (the present fix is on the now-less-visitied path) --- tags/search.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/tags/search.py b/tags/search.py index 37f32b1..9df9775 100644 --- a/tags/search.py +++ b/tags/search.py @@ -62,9 +62,9 @@ def parse_query(q): return ParsedQuery(tags, plain_text_q) -def _search(m2m_qs, fk_fieldname, project, obj_list_all, obj_list_filtered, q): +def _search(m2m_qs, fk_fieldname, project, obj_list, q): if not q: - return obj_list_filtered + return obj_list parsed = parse_query(q) @@ -77,15 +77,11 @@ def _search(m2m_qs, fk_fieldname, project, obj_list_all, obj_list_filtered, q): except TagValue.DoesNotExist: # if the tag doesn't exist, we can't have any issues with it; the below short-circuit is fine, I think (I # mean: we _could_ say "tag x is to blame" but that's not what one does generally in search, is it? - return obj_list_all.none() + return obj_list.none() clauses.append( Q(id__in=Subquery(m2m_qs.filter(value=tag_value_obj).values_list(fk_fieldname, flat=True)))) - # the idea is: if there are clauses from m2m tags, the filter (on Issue, for Events) is implied by those and not - # involving the on the base_qs is more efficient; if there aren't any clauses, that's necessary though. - obj_list = obj_list_all if clauses else obj_list_filtered - # this is really TSTTCPW (or more like a "fake it till you make it" thing); but I'd rather "have something" and then # have really-good-search than to have either nothing at all, or half-baked search. Note that we didn't even bother # to set indexes on the fields we search on (nor create a single searchable field for the whole of 'title'). @@ -111,18 +107,11 @@ def _search(m2m_qs, fk_fieldname, project, obj_list_all, obj_list_filtered, q): def search_issues(project, issue_list, q): - return _search(IssueTag.objects.all(), "issue_id", project, issue_list, issue_list, q) + return _search(IssueTag.objects.all(), "issue_id", project, issue_list, q) def search_events(project, issue, q): - return _search( - EventTag.objects.filter(issue=issue), - "event_id", - project, - Event.objects.all(), - Event.objects.filter(issue=issue), - q, - ) + return _search(EventTag.objects.filter(issue=issue), "event_id", project, Event.objects.filter(issue=issue), q) def search_event_tags(project, issue, parsed):