perf(prompts): make /tags fetch only the tags column with SQL access filter (#24287)

Non-admin GET /api/v1/prompts/tags went through get_prompts_by_user_id,
which loaded every active prompt with its full content/data/meta plus
owner records and all access grants, then ran one has_access query per
prompt that wasn't owned by the caller - all so the endpoint could
collapse the result to a sorted tag list. With 600 prompts this took
several seconds while the admin path (a single SELECT) returned in <1s.

Add Prompts.get_tags_by_user_id which selects only the tags column and
applies the same EXISTS-based access filter used by /list. Also tighten
the admin get_tags to project just the tags column instead of full rows.
The endpoint is now one DB query (plus one for groups), no row hydration,
no N+1.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Classic298
2026-05-08 22:20:13 +02:00
committed by GitHub
parent 26b1a3d7dc
commit 1a3e5ef4c1
2 changed files with 32 additions and 12 deletions
+31 -5
View File
@@ -663,12 +663,38 @@ class PromptsTable:
async def get_tags(self, db: Optional[AsyncSession] = None) -> list[str]:
try:
async with get_async_db_context(db) as db:
result = await db.execute(select(Prompt).filter_by(is_active=True))
prompts = result.scalars().all()
result = await db.execute(select(Prompt.tags).filter(Prompt.is_active == True))
tags = set()
for prompt in prompts:
if prompt.tags:
for tag in prompt.tags:
for (tag_list,) in result.all():
if tag_list:
for tag in tag_list:
if tag:
tags.add(tag)
return sorted(list(tags))
except Exception:
return []
async def get_tags_by_user_id(self, user_id: str, db: Optional[AsyncSession] = None) -> list[str]:
try:
async with get_async_db_context(db) as db:
user_groups = await Groups.get_groups_by_member_id(user_id, db=db)
user_group_ids = [group.id for group in user_groups]
query = select(Prompt.tags).filter(Prompt.is_active == True)
query = AccessGrants.has_permission_filter(
db=db,
query=query,
DocumentModel=Prompt,
filter={'user_id': user_id, 'group_ids': user_group_ids},
resource_type='prompt',
permission='read',
)
result = await db.execute(query)
tags = set()
for (tag_list,) in result.all():
if tag_list:
for tag in tag_list:
if tag:
tags.add(tag)
return sorted(list(tags))
+1 -7
View File
@@ -61,13 +61,7 @@ async def get_prompts(user=Depends(get_verified_user), db: AsyncSession = Depend
async def get_prompt_tags(user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
if user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL:
return await Prompts.get_tags(db=db)
else:
prompts = await Prompts.get_prompts_by_user_id(user.id, 'read', db=db)
tags = set()
for prompt in prompts:
if prompt.tags:
tags.update(prompt.tags)
return sorted(list(tags))
return await Prompts.get_tags_by_user_id(user.id, db=db)
@router.get('/list', response_model=PromptAccessListResponse)