Skip to content

Commit 5908afb

Browse files
authored
⚡️(backend) improve trashbin endpoint performance (#1495)
The trashbin endpoint is slow. To filter documents the user has owner access, we use a subquery to compute the roles and then filter on this subquery. This is very slow. To improve it, we use the same way to filter children used in the tree endpoint. First we look for all highest ancestors the user has access on with the owner role. Then we create one queryset filtering on all the docs starting by the given path and are deleted.
1 parent e2298a3 commit 5908afb

File tree

3 files changed

+26
-5
lines changed

3 files changed

+26
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ and this project adheres to
1212
- ♿ add missing aria-label to add sub-doc button for accessib… #1480
1313
- ♿ add missing aria-label to more options button on sub-docs #1481
1414

15+
### Fixed
16+
17+
- ⚡️(backend) improve trashbin endpoint performance
18+
1519
## [3.8.0] - 2025-10-14
1620

1721
### Added

src/backend/core/api/viewsets.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,12 +623,29 @@ def trashbin(self, request, *args, **kwargs):
623623
The selected documents are those deleted within the cutoff period defined in the
624624
settings (see TRASHBIN_CUTOFF_DAYS), before they are considered permanently deleted.
625625
"""
626+
627+
if not request.user.is_authenticated:
628+
return self.get_response_for_queryset(self.queryset.none())
629+
630+
access_documents_paths = (
631+
models.DocumentAccess.objects.select_related("document")
632+
.filter(
633+
db.Q(user=self.request.user) | db.Q(team__in=self.request.user.teams),
634+
role=models.RoleChoices.OWNER,
635+
)
636+
.values_list("document__path", flat=True)
637+
)
638+
639+
children_clause = db.Q()
640+
for path in access_documents_paths:
641+
children_clause |= db.Q(path__startswith=path)
642+
626643
queryset = self.queryset.filter(
644+
children_clause,
627645
deleted_at__isnull=False,
628646
deleted_at__gte=models.get_trashbin_cutoff(),
629647
)
630648
queryset = queryset.annotate_user_roles(self.request.user)
631-
queryset = queryset.filter(user_roles__contains=[models.RoleChoices.OWNER])
632649

633650
return self.get_response_for_queryset(queryset)
634651

src/backend/core/tests/documents/test_api_documents_trashbin.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,10 @@ def test_api_documents_trashbin_authenticated_direct(django_assert_num_queries):
166166

167167
expected_ids = {str(document1.id), str(document2.id), str(document3.id)}
168168

169-
with django_assert_num_queries(10):
169+
with django_assert_num_queries(11):
170170
response = client.get("/api/v1.0/documents/trashbin/")
171171

172-
with django_assert_num_queries(4):
172+
with django_assert_num_queries(5):
173173
response = client.get("/api/v1.0/documents/trashbin/")
174174

175175
assert response.status_code == 200
@@ -208,10 +208,10 @@ def test_api_documents_trashbin_authenticated_via_team(
208208

209209
expected_ids = {str(deleted_document_team1.id), str(deleted_document_team2.id)}
210210

211-
with django_assert_num_queries(7):
211+
with django_assert_num_queries(8):
212212
response = client.get("/api/v1.0/documents/trashbin/")
213213

214-
with django_assert_num_queries(3):
214+
with django_assert_num_queries(4):
215215
response = client.get("/api/v1.0/documents/trashbin/")
216216

217217
assert response.status_code == 200

0 commit comments

Comments
 (0)