From 5c9ff367e35e9ef3cdb670631f7411962dae1c69 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 25 Jan 2026 16:56:51 -0800 Subject: [PATCH 1/4] Fixhancement: change date calculation for 'this year' to include future documents (#11884) --- src/documents/index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/documents/index.py b/src/documents/index.py index ea26ea926..8afc31fe9 100644 --- a/src/documents/index.py +++ b/src/documents/index.py @@ -602,7 +602,7 @@ def rewrite_natural_date_keywords(query_string: str) -> str: case "this year": start = datetime(local_now.year, 1, 1, 0, 0, 0, tzinfo=tz) - end = datetime.combine(today, time.max, tzinfo=tz) + end = datetime(local_now.year, 12, 31, 23, 59, 59, tzinfo=tz) case "previous week": days_since_monday = local_now.weekday() From 72e8b73108dcdd5a586e6f03014405d4b4629d17 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Sun, 25 Jan 2026 17:08:15 -0800 Subject: [PATCH 2/4] Fix test --- src/documents/tests/test_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/documents/tests/test_index.py b/src/documents/tests/test_index.py index 3167bb762..ef6b535f7 100644 --- a/src/documents/tests/test_index.py +++ b/src/documents/tests/test_index.py @@ -180,7 +180,7 @@ class TestRewriteNaturalDateKeywords(SimpleTestCase): ( "added:this year", datetime(2025, 7, 15, 12, 0, 0, tzinfo=timezone.utc), - ("added:[20250101", "TO 20250715"), + ("added:[20250101", "TO 20251231"), ), ( "added:previous year", From 2312314aa739a3c12dd83ccf42dec22f06db2dd0 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 12 Jan 2026 13:04:03 -0800 Subject: [PATCH 3/4] Performance: improve treenode inefficiencies (#11606) --- src/documents/serialisers.py | 44 ++++++++++++++++++++---------------- src/documents/views.py | 35 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index 0648aa0b3..e96400eff 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -580,30 +580,34 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer): ), ) def get_children(self, obj): - filter_q = self.context.get("document_count_filter") - request = self.context.get("request") - if filter_q is None: - user = getattr(request, "user", None) if request else None - filter_q = get_document_count_filter_for_user(user) - self.context["document_count_filter"] = filter_q + children_map = self.context.get("children_map") + if children_map is not None: + children = children_map.get(obj.pk, []) + else: + filter_q = self.context.get("document_count_filter") + request = self.context.get("request") + if filter_q is None: + user = getattr(request, "user", None) if request else None + filter_q = get_document_count_filter_for_user(user) + self.context["document_count_filter"] = filter_q - children_queryset = ( - obj.get_children_queryset() - .select_related("owner") - .annotate(document_count=Count("documents", filter=filter_q)) - ) + children = ( + obj.get_children_queryset() + .select_related("owner") + .annotate(document_count=Count("documents", filter=filter_q)) + ) - view = self.context.get("view") - ordering = ( - OrderingFilter().get_ordering(request, children_queryset, view) - if request and view - else None - ) - ordering = ordering or (Lower("name"),) - children_queryset = children_queryset.order_by(*ordering) + view = self.context.get("view") + ordering = ( + OrderingFilter().get_ordering(request, children, view) + if request and view + else None + ) + ordering = ordering or (Lower("name"),) + children = children.order_by(*ordering) serializer = TagSerializer( - children_queryset, + children, many=True, user=self.user, full_perms=self.full_perms, diff --git a/src/documents/views.py b/src/documents/views.py index d5910497f..4e0460dc9 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -448,8 +448,43 @@ class TagViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin): def get_serializer_context(self): context = super().get_serializer_context() context["document_count_filter"] = self.get_document_count_filter() + if hasattr(self, "_children_map"): + context["children_map"] = self._children_map return context + def list(self, request, *args, **kwargs): + """ + Build a children map once to avoid per-parent queries in the serializer. + """ + queryset = self.filter_queryset(self.get_queryset()) + ordering = OrderingFilter().get_ordering(request, queryset, self) or ( + Lower("name"), + ) + queryset = queryset.order_by(*ordering) + + all_tags = list(queryset) + descendant_pks = {pk for tag in all_tags for pk in tag.get_descendants_pks()} + + if descendant_pks: + filter_q = self.get_document_count_filter() + children_source = ( + Tag.objects.filter(pk__in=descendant_pks | {t.pk for t in all_tags}) + .select_related("owner") + .annotate(document_count=Count("documents", filter=filter_q)) + .order_by(*ordering) + ) + else: + children_source = all_tags + + children_map = {} + for tag in children_source: + children_map.setdefault(tag.tn_parent_id, []).append(tag) + self._children_map = children_map + + page = self.paginate_queryset(queryset) + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + def perform_update(self, serializer): old_parent = self.get_object().get_parent() tag = serializer.save() From 891f4a2faf3b317cdf48a259376d1d31a0765a44 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:12:03 -0800 Subject: [PATCH 4/4] Fix: correctly extract all ids for nested tags (#11888) --- .../management-list.component.spec.ts | 15 +++++++++++++++ .../management-list/management-list.component.ts | 2 +- src/documents/views.py | 10 +++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts index 813c81148..86f0f0469 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.spec.ts @@ -229,6 +229,21 @@ describe('ManagementListComponent', () => { expect(reloadSpy).toHaveBeenCalled() }) + it('should use the all list length for collection size when provided', fakeAsync(() => { + jest.spyOn(tagService, 'listFiltered').mockReturnValueOnce( + of({ + count: 1, + all: [1, 2, 3], + results: tags.slice(0, 1), + }) + ) + + component.reloadData() + tick(100) + + expect(component.collectionSize).toBe(3) + })) + it('should support quick filter for objects', () => { const qfSpy = jest.spyOn(documentListViewService, 'quickFilter') const filterButton = fixture.debugElement.queryAll(By.css('button'))[9] diff --git a/src-ui/src/app/components/manage/management-list/management-list.component.ts b/src-ui/src/app/components/manage/management-list/management-list.component.ts index b1af1f1d1..44160fcdf 100644 --- a/src-ui/src/app/components/manage/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/management-list/management-list.component.ts @@ -171,7 +171,7 @@ export abstract class ManagementListComponent tap((c) => { this.unfilteredData = c.results this.data = this.filterData(c.results) - this.collectionSize = c.count + this.collectionSize = c.all?.length ?? c.count }), delay(100) ) diff --git a/src/documents/views.py b/src/documents/views.py index 4e0460dc9..a91ad8594 100644 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -467,11 +467,11 @@ class TagViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin): if descendant_pks: filter_q = self.get_document_count_filter() - children_source = ( + children_source = list( Tag.objects.filter(pk__in=descendant_pks | {t.pk for t in all_tags}) .select_related("owner") .annotate(document_count=Count("documents", filter=filter_q)) - .order_by(*ordering) + .order_by(*ordering), ) else: children_source = all_tags @@ -483,7 +483,11 @@ class TagViewSet(ModelViewSet, PermissionsAwareDocumentCountMixin): page = self.paginate_queryset(queryset) serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(serializer.data) + response = self.get_paginated_response(serializer.data) + if descendant_pks: + # Include children in the "all" field, if needed + response.data["all"] = [tag.pk for tag in children_source] + return response def perform_update(self, serializer): old_parent = self.get_object().get_parent()