From d8d88724145f25cf0281a33e4880f5cc6b180e27 Mon Sep 17 00:00:00 2001 From: Trenton H <797416+stumpylog@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:59:44 -0700 Subject: [PATCH] Fix (beta): compute num_notes via a subquery instead of Count()+distinct() (#13182) DocumentViewSet.get_queryset() annotated num_notes via a LEFT JOIN to documents_note plus Count(), which requires the database to aggregate every matching document's note count before it can even sort or paginate the result -- on every list request, not just filtered ones. Switched to the same correlated-subquery pattern already used two lines above for effective_content (Subquery + OuterRef), which Django compiles to portable SQL across sqlite/postgresql/mariadb rather than a join-based aggregate. Benchmarked against a 100k synthetic document corpus (Postgres): the plain document list request dropped from ~2.2-2.4s to ~0.9-1.0s. Note that a separate, larger cost remains in the same queryset's plain .distinct() call, which still forces a full sort over every matching row regardless of this fix -- tracked separately, not addressed here. Co-authored-by: Claude Sonnet 5 --- src/documents/views.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/documents/views.py b/src/documents/views.py index 20dbe9247..3c49d6c76 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -1040,12 +1040,23 @@ class DocumentViewSet( .order_by("-id") .values("content")[:1], ) + # A correlated subquery avoids the LEFT JOIN + Count() this used to + # be, which forced a GROUP BY aggregate over every matching document + # before the query could even be sorted or limited. + note_count = Subquery( + Note.objects.filter(document=OuterRef("pk")) + .order_by() + .values("document") + .annotate(count=Count("pk")) + .values("count"), + output_field=IntegerField(), + ) return ( Document.objects.filter(root_document__isnull=True) .distinct() .order_by("-created", "-id") .annotate(effective_content=Coalesce(latest_version_content, F("content"))) - .annotate(num_notes=Count("notes")) + .annotate(num_notes=Coalesce(note_count, 0)) .select_related("correspondent", "storage_path", "document_type", "owner") .prefetch_related( Prefetch(