Skip to content

Commit 8c2ff95

Browse files
improve query performance
1 parent c0e0037 commit 8c2ff95

File tree

2 files changed

+119
-96
lines changed

2 files changed

+119
-96
lines changed

services/web/server/src/simcore_service_webserver/projects/_projects_repository_legacy.py

Lines changed: 96 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,8 @@ def _create_private_workspace_query(
354354
product_name: ProductName,
355355
user_id: UserID,
356356
workspace_query: WorkspaceQuery,
357-
project_tags_subquery: sql.Subquery,
358357
is_search_by_multi_columns: bool,
359-
user_groups: list[RowProxy],
358+
user_groups: list[int],
360359
) -> sql.Select | None:
361360
private_workspace_query = None
362361
if workspace_query.workspace_scope is not WorkspaceScope.SHARED:
@@ -378,13 +377,39 @@ def _create_private_workspace_query(
378377
"delete",
379378
project_to_groups.c.delete,
380379
),
381-
)
382-
.filter(
383-
project_to_groups.c.read # Filters out entries where "read" is False
384-
)
385-
.label("access_rights"),
386-
).group_by(project_to_groups.c.project_uuid)
387-
).subquery("access_rights_subquery")
380+
).label("access_rights"),
381+
)
382+
.where(
383+
project_to_groups.c.project_uuid == projects.c.uuid
384+
) # Correlate with main query
385+
.where(project_to_groups.c.read)
386+
.group_by(project_to_groups.c.project_uuid)
387+
.lateral() # Critical for per-row execution
388+
)
389+
390+
my_access_rights_subquery = (
391+
sa.select(
392+
project_to_groups.c.project_uuid,
393+
sa.func.jsonb_object_agg(
394+
project_to_groups.c.gid,
395+
sa.func.jsonb_build_object(
396+
"read",
397+
project_to_groups.c.read,
398+
"write",
399+
project_to_groups.c.write,
400+
"delete",
401+
project_to_groups.c.delete,
402+
),
403+
).label("access_rights"),
404+
)
405+
.where(
406+
project_to_groups.c.read, # Filters out entries where "read" is False
407+
project_to_groups.c.gid.in_(
408+
user_groups
409+
), # Filters gid to be in user_groups
410+
)
411+
.group_by(project_to_groups.c.project_uuid)
412+
).subquery("my_access_rights_subquery")
388413

389414
private_workspace_query = (
390415
sa.select(
@@ -393,13 +418,9 @@ def _create_private_workspace_query(
393418
access_rights_subquery.c.access_rights,
394419
projects_to_products.c.product_name,
395420
projects_to_folders.c.folder_id,
396-
sa.func.coalesce(
397-
project_tags_subquery.c.tags,
398-
sa.cast(sa.text("'{}'"), sa.ARRAY(sa.Integer)),
399-
).label("tags"),
400421
)
401422
.select_from(
402-
projects.join(access_rights_subquery, isouter=True)
423+
projects.join(my_access_rights_subquery)
403424
.join(projects_to_products)
404425
.join(
405426
projects_to_folders,
@@ -409,21 +430,19 @@ def _create_private_workspace_query(
409430
),
410431
isouter=True,
411432
)
412-
.join(project_tags_subquery, isouter=True)
433+
.join(
434+
access_rights_subquery,
435+
access_rights_subquery.c.project_uuid == projects.c.uuid,
436+
)
413437
)
414438
.where(
415-
(
416-
(projects.c.prj_owner == user_id)
417-
| sa.text(
418-
f"jsonb_exists_any(access_rights_subquery.access_rights, {assemble_array_groups(user_groups)})"
419-
)
420-
)
421-
& (projects.c.workspace_id.is_(None)) # <-- Private workspace
439+
(projects.c.workspace_id.is_(None)) # <-- Private workspace
422440
& (projects_to_products.c.product_name == product_name)
423441
)
424442
)
443+
425444
assert ( # nosec
426-
access_rights_subquery.description == "access_rights_subquery"
445+
my_access_rights_subquery.description == "my_access_rights_subquery"
427446
)
428447

429448
if is_search_by_multi_columns:
@@ -438,9 +457,8 @@ def _create_shared_workspace_query(
438457
*,
439458
product_name: ProductName,
440459
workspace_query: WorkspaceQuery,
441-
project_tags_subquery: sql.Subquery,
442-
user_groups: list[RowProxy],
443460
is_search_by_multi_columns: bool,
461+
user_groups: list[int],
444462
) -> sql.Select | None:
445463

446464
if workspace_query.workspace_scope is not WorkspaceScope.PRIVATE:
@@ -450,6 +468,31 @@ def _create_shared_workspace_query(
450468
)
451469

452470
workspace_access_rights_subquery = (
471+
(
472+
sa.select(
473+
workspaces_access_rights.c.workspace_id,
474+
sa.func.jsonb_object_agg(
475+
workspaces_access_rights.c.gid,
476+
sa.func.jsonb_build_object(
477+
"read",
478+
workspaces_access_rights.c.read,
479+
"write",
480+
workspaces_access_rights.c.write,
481+
"delete",
482+
workspaces_access_rights.c.delete,
483+
),
484+
).label("access_rights"),
485+
)
486+
.where(
487+
workspaces_access_rights.c.read,
488+
)
489+
.group_by(workspaces_access_rights.c.workspace_id)
490+
)
491+
.subquery("workspace_access_rights_subquery")
492+
.lateral()
493+
)
494+
495+
my_workspace_access_rights_subquery = (
453496
sa.select(
454497
workspaces_access_rights.c.workspace_id,
455498
sa.func.jsonb_object_agg(
@@ -462,11 +505,14 @@ def _create_shared_workspace_query(
462505
"delete",
463506
workspaces_access_rights.c.delete,
464507
),
465-
)
466-
.filter(workspaces_access_rights.c.read)
467-
.label("access_rights"),
468-
).group_by(workspaces_access_rights.c.workspace_id)
469-
).subquery("workspace_access_rights_subquery")
508+
).label("access_rights"),
509+
)
510+
.where(
511+
workspaces_access_rights.c.read,
512+
workspaces_access_rights.c.gid.in_(user_groups),
513+
)
514+
.group_by(workspaces_access_rights.c.workspace_id)
515+
).subquery("my_workspace_access_rights_subquery")
470516

471517
shared_workspace_query = (
472518
sa.select(
@@ -475,16 +521,12 @@ def _create_shared_workspace_query(
475521
workspace_access_rights_subquery.c.access_rights,
476522
projects_to_products.c.product_name,
477523
projects_to_folders.c.folder_id,
478-
sa.func.coalesce(
479-
project_tags_subquery.c.tags,
480-
sa.cast(sa.text("'{}'"), sa.ARRAY(sa.Integer)),
481-
).label("tags"),
482524
)
483525
.select_from(
484526
projects.join(
485-
workspace_access_rights_subquery,
527+
my_workspace_access_rights_subquery,
486528
projects.c.workspace_id
487-
== workspace_access_rights_subquery.c.workspace_id,
529+
== my_workspace_access_rights_subquery.c.workspace_id,
488530
)
489531
.join(projects_to_products)
490532
.join(
@@ -495,20 +537,17 @@ def _create_shared_workspace_query(
495537
),
496538
isouter=True,
497539
)
498-
.join(project_tags_subquery, isouter=True)
499-
)
500-
.where(
501-
(
502-
sa.text(
503-
f"jsonb_exists_any(workspace_access_rights_subquery.access_rights, {assemble_array_groups(user_groups)})"
504-
)
540+
.join(
541+
workspace_access_rights_subquery,
542+
projects.c.workspace_id
543+
== workspace_access_rights_subquery.c.workspace_id,
505544
)
506-
& (projects_to_products.c.product_name == product_name)
507545
)
546+
.where(projects_to_products.c.product_name == product_name)
508547
)
509548
assert ( # nosec
510-
workspace_access_rights_subquery.description
511-
== "workspace_access_rights_subquery"
549+
my_workspace_access_rights_subquery.description
550+
== "my_workspace_access_rights_subquery"
512551
)
513552

514553
if workspace_query.workspace_scope == WorkspaceScope.ALL:
@@ -541,9 +580,7 @@ def _create_attributes_filters(
541580
filter_trashed: bool | None,
542581
search_by_multi_columns: str | None,
543582
search_by_project_name: str | None,
544-
filter_tag_ids_list: list[int] | None,
545583
folder_query: FolderQuery,
546-
project_tags_subquery: sql.Subquery,
547584
) -> list[ColumnElement]:
548585
attributes_filters: list[ColumnElement] = []
549586

@@ -581,14 +618,6 @@ def _create_attributes_filters(
581618
projects.c.name.like(f"%{search_by_project_name}%")
582619
)
583620

584-
if filter_tag_ids_list:
585-
attributes_filters.append(
586-
sa.func.coalesce(
587-
project_tags_subquery.c.tags,
588-
sa.cast(sa.text("'{}'"), sa.ARRAY(sa.Integer)),
589-
).op("@>")(filter_tag_ids_list)
590-
)
591-
592621
if folder_query.folder_scope is not FolderScope.ALL:
593622
if folder_query.folder_scope == FolderScope.SPECIFIC:
594623
attributes_filters.append(
@@ -614,7 +643,6 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
614643
filter_published: bool | None = None,
615644
filter_hidden: bool | None = False,
616645
filter_trashed: bool | None = False,
617-
filter_tag_ids_list: list[int] | None = None,
618646
# search
619647
search_by_multi_columns: str | None = None,
620648
search_by_project_name: str | None = None,
@@ -624,20 +652,12 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
624652
# order
625653
order_by: OrderBy = DEFAULT_ORDER_BY,
626654
) -> tuple[list[ProjectDict], list[ProjectType], int]:
627-
628-
if filter_tag_ids_list is None:
629-
filter_tag_ids_list = []
630-
631655
async with self.engine.acquire() as conn:
632-
user_groups: list[RowProxy] = await self._list_user_groups(conn, user_id)
633-
project_tags_subquery = (
634-
sa.select(
635-
projects_tags.c.project_id,
636-
sa.func.array_agg(projects_tags.c.tag_id).label("tags"),
637-
)
638-
.where(projects_tags.c.project_id.is_not(None))
639-
.group_by(projects_tags.c.project_id)
640-
).subquery("project_tags_subquery")
656+
user_groups_proxy: list[RowProxy] = await self._list_user_groups(
657+
conn, user_id
658+
)
659+
user_groups_array_str = assemble_array_groups(user_groups_proxy)
660+
user_groups: list[int] = [group.gid for group in user_groups_proxy]
641661

642662
###
643663
# Private workspace query
@@ -647,8 +667,8 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
647667
product_name=product_name,
648668
user_id=user_id,
649669
workspace_query=workspace_query,
650-
project_tags_subquery=project_tags_subquery,
651670
is_search_by_multi_columns=search_by_multi_columns is not None,
671+
user_groups_array_str=user_groups_array_str,
652672
user_groups=user_groups,
653673
)
654674

@@ -658,9 +678,8 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
658678
shared_workspace_query = self._create_shared_workspace_query(
659679
product_name=product_name,
660680
workspace_query=workspace_query,
661-
project_tags_subquery=project_tags_subquery,
662-
user_groups=user_groups,
663681
is_search_by_multi_columns=search_by_multi_columns is not None,
682+
user_groups=user_groups,
664683
)
665684

666685
###
@@ -674,9 +693,7 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
674693
filter_trashed=filter_trashed,
675694
search_by_multi_columns=search_by_multi_columns,
676695
search_by_project_name=search_by_project_name,
677-
filter_tag_ids_list=filter_tag_ids_list,
678696
folder_query=folder_query,
679-
project_tags_subquery=project_tags_subquery,
680697
)
681698

682699
###
@@ -709,11 +726,13 @@ async def list_projects_dicts( # pylint: disable=too-many-arguments,too-many-st
709726

710727
if order_by.direction == OrderDirection.ASC:
711728
combined_query = combined_query.order_by(
712-
sa.asc(getattr(projects.c, order_by.field))
729+
sa.asc(getattr(projects.c, order_by.field)),
730+
projects.c.id,
713731
)
714732
else:
715733
combined_query = combined_query.order_by(
716-
sa.desc(getattr(projects.c, order_by.field))
734+
sa.desc(getattr(projects.c, order_by.field)),
735+
projects.c.id,
717736
)
718737

719738
prjs, prj_types = await self._execute_without_permission_check(

services/web/server/src/simcore_service_webserver/workspaces/_workspaces_repository.py

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,25 +81,29 @@ def _create_base_select_query(
8181
) -> Select:
8282
# any other access
8383
access_rights_subquery = (
84-
select(
85-
workspaces_access_rights.c.workspace_id,
86-
func.jsonb_object_agg(
87-
workspaces_access_rights.c.gid,
88-
func.jsonb_build_object(
89-
"read",
90-
workspaces_access_rights.c.read,
91-
"write",
92-
workspaces_access_rights.c.write,
93-
"delete",
94-
workspaces_access_rights.c.delete,
95-
),
96-
)
97-
.filter(
98-
workspaces_access_rights.c.read # Filters out entries where "read" is False
99-
)
100-
.label("access_rights"),
101-
).group_by(workspaces_access_rights.c.workspace_id)
102-
).subquery("access_rights_subquery")
84+
(
85+
select(
86+
workspaces_access_rights.c.workspace_id,
87+
func.jsonb_object_agg(
88+
workspaces_access_rights.c.gid,
89+
func.jsonb_build_object(
90+
"read",
91+
workspaces_access_rights.c.read,
92+
"write",
93+
workspaces_access_rights.c.write,
94+
"delete",
95+
workspaces_access_rights.c.delete,
96+
),
97+
)
98+
.filter(
99+
workspaces_access_rights.c.read # Filters out entries where "read" is False
100+
)
101+
.label("access_rights"),
102+
).group_by(workspaces_access_rights.c.workspace_id)
103+
)
104+
.subquery("access_rights_subquery")
105+
.lateral()
106+
)
103107

104108
# caller's access rights
105109
my_access_rights_subquery = create_my_workspace_access_rights_subquery(

0 commit comments

Comments
 (0)