enhance sub-issue query performance with optimized annotations and subqueries (#8889)
This commit is contained in:
committed by
GitHub
parent
bbf14fba31
commit
13db2f883f
@@ -7,7 +7,7 @@ import json
|
||||
|
||||
# Django imports
|
||||
from django.utils import timezone
|
||||
from django.db.models import OuterRef, Func, F, Q, Value, UUIDField, Subquery
|
||||
from django.db.models import OuterRef, Func, F, Q, Value, UUIDField, Subquery, Count, IntegerField
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.gzip import gzip_page
|
||||
from django.contrib.postgres.aggregates import ArrayAgg
|
||||
@@ -22,7 +22,7 @@ from rest_framework import status
|
||||
from .. import BaseAPIView
|
||||
from plane.app.serializers import IssueSerializer
|
||||
from plane.app.permissions import ProjectEntityPermission
|
||||
from plane.db.models import Issue, IssueLink, FileAsset, CycleIssue
|
||||
from plane.db.models import Issue, IssueLink, FileAsset, CycleIssue, IssueLabel, IssueAssignee, ModuleIssue
|
||||
from plane.bgtasks.issue_activities_task import issue_activity
|
||||
from plane.utils.timezone_converter import user_timezone_converter
|
||||
from collections import defaultdict
|
||||
@@ -37,70 +37,97 @@ class SubIssuesEndpoint(BaseAPIView):
|
||||
def get(self, request, slug, project_id, issue_id):
|
||||
sub_issues = (
|
||||
Issue.issue_objects.filter(parent_id=issue_id, workspace__slug=slug)
|
||||
.select_related("workspace", "project", "state", "parent")
|
||||
.prefetch_related("assignees", "labels", "issue_module__module")
|
||||
.annotate(
|
||||
cycle_id=Subquery(
|
||||
CycleIssue.objects.filter(issue=OuterRef("id"), deleted_at__isnull=True).values("cycle_id")[:1]
|
||||
)
|
||||
)
|
||||
.annotate(
|
||||
link_count=IssueLink.objects.filter(issue=OuterRef("id"))
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
attachment_count=FileAsset.objects.filter(
|
||||
issue_id=OuterRef("id"),
|
||||
entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT,
|
||||
link_count=Coalesce(
|
||||
Subquery(
|
||||
IssueLink.objects.filter(issue=OuterRef("id"))
|
||||
.order_by()
|
||||
.values("issue")
|
||||
.annotate(count=Count("id"))
|
||||
.values("count"),
|
||||
output_field=IntegerField(),
|
||||
),
|
||||
0,
|
||||
)
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
.values("count")
|
||||
)
|
||||
.annotate(
|
||||
sub_issues_count=Issue.issue_objects.filter(parent=OuterRef("id"))
|
||||
.order_by()
|
||||
.annotate(count=Func(F("id"), function="Count"))
|
||||
.values("count")
|
||||
attachment_count=Coalesce(
|
||||
Subquery(
|
||||
FileAsset.objects.filter(
|
||||
issue_id=OuterRef("id"),
|
||||
entity_type=FileAsset.EntityTypeContext.ISSUE_ATTACHMENT,
|
||||
)
|
||||
.order_by()
|
||||
.values("issue_id")
|
||||
.annotate(count=Count("id"))
|
||||
.values("count"),
|
||||
output_field=IntegerField(),
|
||||
),
|
||||
0,
|
||||
)
|
||||
)
|
||||
.annotate(
|
||||
sub_issues_count=Coalesce(
|
||||
Subquery(
|
||||
Issue.issue_objects.filter(parent=OuterRef("id"))
|
||||
.order_by()
|
||||
.values("parent")
|
||||
.annotate(count=Count("id"))
|
||||
.values("count"),
|
||||
output_field=IntegerField(),
|
||||
),
|
||||
0,
|
||||
)
|
||||
)
|
||||
.annotate(
|
||||
label_ids=Coalesce(
|
||||
ArrayAgg(
|
||||
"labels__id",
|
||||
distinct=True,
|
||||
filter=Q(~Q(labels__id__isnull=True) & Q(label_issue__deleted_at__isnull=True)),
|
||||
Subquery(
|
||||
IssueLabel.objects.filter(issue_id=OuterRef("id"), deleted_at__isnull=True)
|
||||
.order_by()
|
||||
.values("issue_id")
|
||||
.annotate(arr=ArrayAgg("label_id", distinct=True))
|
||||
.values("arr"),
|
||||
output_field=ArrayField(UUIDField()),
|
||||
),
|
||||
Value([], output_field=ArrayField(UUIDField())),
|
||||
),
|
||||
assignee_ids=Coalesce(
|
||||
ArrayAgg(
|
||||
"assignees__id",
|
||||
distinct=True,
|
||||
filter=Q(
|
||||
~Q(assignees__id__isnull=True)
|
||||
& Q(assignees__member_project__is_active=True)
|
||||
& Q(issue_assignee__deleted_at__isnull=True)
|
||||
),
|
||||
Subquery(
|
||||
IssueAssignee.objects.filter(
|
||||
issue_id=OuterRef("id"),
|
||||
assignee__member_project__is_active=True,
|
||||
deleted_at__isnull=True,
|
||||
)
|
||||
.order_by()
|
||||
.values("issue_id")
|
||||
.annotate(arr=ArrayAgg("assignee_id", distinct=True))
|
||||
.values("arr"),
|
||||
output_field=ArrayField(UUIDField()),
|
||||
),
|
||||
Value([], output_field=ArrayField(UUIDField())),
|
||||
),
|
||||
module_ids=Coalesce(
|
||||
ArrayAgg(
|
||||
"issue_module__module_id",
|
||||
distinct=True,
|
||||
filter=Q(
|
||||
~Q(issue_module__module_id__isnull=True)
|
||||
& Q(issue_module__module__archived_at__isnull=True)
|
||||
& Q(issue_module__deleted_at__isnull=True)
|
||||
),
|
||||
Subquery(
|
||||
ModuleIssue.objects.filter(
|
||||
issue_id=OuterRef("id"),
|
||||
module__archived_at__isnull=True,
|
||||
deleted_at__isnull=True,
|
||||
)
|
||||
.order_by()
|
||||
.values("issue_id")
|
||||
.annotate(arr=ArrayAgg("module_id", distinct=True))
|
||||
.values("arr"),
|
||||
output_field=ArrayField(UUIDField()),
|
||||
),
|
||||
Value([], output_field=ArrayField(UUIDField())),
|
||||
),
|
||||
)
|
||||
.annotate(state_group=F("state__group"))
|
||||
.order_by("-created_at")
|
||||
)
|
||||
|
||||
# Ordering
|
||||
@@ -110,38 +137,42 @@ class SubIssuesEndpoint(BaseAPIView):
|
||||
if order_by_param:
|
||||
sub_issues, order_by_param = order_issue_queryset(sub_issues, order_by_param)
|
||||
|
||||
sub_issues = list(
|
||||
sub_issues.values(
|
||||
"id",
|
||||
"name",
|
||||
"state_id",
|
||||
"sort_order",
|
||||
"completed_at",
|
||||
"estimate_point",
|
||||
"priority",
|
||||
"start_date",
|
||||
"target_date",
|
||||
"sequence_id",
|
||||
"project_id",
|
||||
"parent_id",
|
||||
"cycle_id",
|
||||
"module_ids",
|
||||
"label_ids",
|
||||
"assignee_ids",
|
||||
"sub_issues_count",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"attachment_count",
|
||||
"link_count",
|
||||
"is_draft",
|
||||
"archived_at",
|
||||
"state_group",
|
||||
)
|
||||
)
|
||||
|
||||
# create's a dict with state group name with their respective issue id's
|
||||
result = defaultdict(list)
|
||||
for sub_issue in sub_issues:
|
||||
result[sub_issue.state_group].append(str(sub_issue.id))
|
||||
result[sub_issue["state_group"]].append(str(sub_issue["id"]))
|
||||
|
||||
sub_issues = sub_issues.values(
|
||||
"id",
|
||||
"name",
|
||||
"state_id",
|
||||
"sort_order",
|
||||
"completed_at",
|
||||
"estimate_point",
|
||||
"priority",
|
||||
"start_date",
|
||||
"target_date",
|
||||
"sequence_id",
|
||||
"project_id",
|
||||
"parent_id",
|
||||
"cycle_id",
|
||||
"module_ids",
|
||||
"label_ids",
|
||||
"assignee_ids",
|
||||
"sub_issues_count",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"attachment_count",
|
||||
"link_count",
|
||||
"is_draft",
|
||||
"archived_at",
|
||||
)
|
||||
datetime_fields = ["created_at", "updated_at"]
|
||||
sub_issues = user_timezone_converter(sub_issues, datetime_fields, request.user.user_timezone)
|
||||
# Grouping
|
||||
|
||||
Reference in New Issue
Block a user