python 41 lines · 9 steps

Ranked full-text search in Django

A Django view that debounces requests per client and ranks Postgres full-text matches by weighted relevance.

Explained by highlit
1from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
2from django.core.cache import cache
3from django.db.models import F
4from django.http import JsonResponse
5from django.utils.timezone import now
6from django.views import View
7 
8from .models import Article
9 
10 
11class ArticleSearchView(View):
12 DEBOUNCE_SECONDS = 0.4
13 MIN_QUERY_LENGTH = 3
14 
15 def get(self, request):
16 term = request.GET.get("q", "").strip()
17 if len(term) < self.MIN_QUERY_LENGTH:
18 return JsonResponse({"results": []})
19 
20 client_key = f"search:debounce:{request.session.session_key or request.META['REMOTE_ADDR']}"
21 last_hit = cache.get(client_key)
22 if last_hit and (now().timestamp() - last_hit) < self.DEBOUNCE_SECONDS:
23 return JsonResponse({"results": [], "throttled": True}, status=429)
24 cache.set(client_key, now().timestamp(), timeout=5)
25 
26 query = SearchQuery(term, config="english", search_type="websearch")
27 vector = (
28 SearchVector("title", weight="A", config="english")
29 + SearchVector("summary", weight="B", config="english")
30 + SearchVector("body", weight="C", config="english")
31 )
32 
33 results = (
34 Article.objects.filter(is_published=True)
35 .annotate(rank=SearchRank(vector, query))
36 .filter(rank__gte=0.05)
37 .order_by("-rank", "-published_at")
38 .values("id", "title", slug=F("url_slug"), score=F("rank"))[:20]
39 )
40 
41 return JsonResponse({"results": list(results)})
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Weighted SearchVectors let you rank matches by which field they hit, so title matches outrank body matches.
  2. 2Per-client cache keys with short timeouts implement server-side debouncing to throttle rapid-fire requests.
  3. 3SearchRank annotation plus a threshold filter turns full-text relevance into a sortable, prunable score.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Ranked full-text search in Django — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code