From 05e71e033fb6ced46ca4f828bc87684730ba6db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20Poyatos?= Date: Fri, 7 Feb 2025 14:51:01 +0100 Subject: [PATCH] feat(findings): Use ArrayAgg and subqueries on metadata endpoint (#6863) Co-authored-by: Pepe Fagoaga --- api/src/backend/api/CHANGELOG.md | 1 + api/src/backend/api/v1/views.py | 54 +++++++++----------------------- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/api/src/backend/api/CHANGELOG.md b/api/src/backend/api/CHANGELOG.md index 6f8971f40d..07b5e3f8ff 100644 --- a/api/src/backend/api/CHANGELOG.md +++ b/api/src/backend/api/CHANGELOG.md @@ -10,5 +10,6 @@ All notable changes to the **Prowler API** are documented in this file. ### Changed - Daily scheduled scan instances are now created beforehand with `SCHEDULED` state [(#6700)](https://github.com/prowler-cloud/prowler/pull/6700). - Findings endpoints now require at least one date filter [(#6800)](https://github.com/prowler-cloud/prowler/pull/6800). +- Findings metadata endpoint received a performance improvement [(#6863)](https://github.com/prowler-cloud/prowler/pull/6863). --- diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index 13c1d43f23..722ecbfd7d 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -1418,50 +1418,26 @@ class FindingViewSet(BaseRLSViewSet): queryset = self.get_queryset() filtered_queryset = self.filter_queryset(queryset) - relevant_resources = Resource.objects.filter( - tenant_id=tenant_id, findings__in=filtered_queryset - ).distinct() + filtered_ids = filtered_queryset.order_by().values("id") - services = ( - relevant_resources.values_list("service", flat=True) - .distinct() - .order_by("service") + relevant_resources = Resource.all_objects.filter( + tenant_id=tenant_id, findings__id__in=Subquery(filtered_ids) + ).only("service", "region", "type") + + aggregation = relevant_resources.aggregate( + services=ArrayAgg("service", flat=True), + regions=ArrayAgg("region", flat=True), + resource_types=ArrayAgg("type", flat=True), ) - regions = ( - relevant_resources.exclude(region="") - .values_list("region", flat=True) - .distinct() - .order_by("region") - ) - - resource_types = ( - relevant_resources.values_list("type", flat=True) - .distinct() - .order_by("type") - ) - - # Temporarily disabled until we implement tag filtering in the UI - # tag_data = ( - # relevant_resources - # .filter(tags__key__isnull=False, tags__value__isnull=False) - # .exclude(tags__key="") - # .exclude(tags__value="") - # .values("tags__key", "tags__value") - # .distinct() - # .order_by("tags__key", "tags__value") - # ) - # - # tags_dict = {} - # for row in tag_data: - # k, v = row["tags__key"], row["tags__value"] - # tags_dict.setdefault(k, []).append(v) + services = sorted(set(aggregation["services"] or [])) + regions = sorted({region for region in aggregation["regions"] or [] if region}) + resource_types = sorted(set(aggregation["resource_types"] or [])) result = { - "services": list(services), - "regions": list(regions), - "resource_types": list(resource_types), - # "tags": tags_dict + "services": services, + "regions": regions, + "resource_types": resource_types, } serializer = self.get_serializer(data=result)