mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-09 03:18:18 +00:00
perf: batch the shared folder listing instead of fetching one folder at a time
Opening the shared folder list fetched every shared folder in its own query, fetched a chunk of them a second time to walk their children, and looked up each distinct owner separately. With forty folders shared with a user that is over a hundred queries before any subtree work starts. The folders and their owners now come back in one query each, and the inheritance pass reuses the rows already in hand. Both folder listings also gained an explicit order: the sidebar merges shared subfolders in response order without sorting them, and neither query had an ORDER BY, so on Postgres a folder rename could reshuffle its siblings. Measured with forty shared folders and no subtrees: 181 queries and ~105 ms before, 92 and ~66 ms after. With subtrees attached, 203 folders in total, it is 341 queries before against 252 after; the remainder is the recursive child walk, which this change deliberately leaves alone. The returned set, permissions and owner names are unchanged, including for a grant pointing at a deleted folder row, a folder the caller owns that is also shared with them, a folder whose owner record is gone, and a child folder that is itself directly shared.
This commit is contained in:
parent
5ea9ff3ed9
commit
47b391f64c
2 changed files with 33 additions and 34 deletions
|
|
@ -157,6 +157,11 @@ class FolderTable:
|
|||
except Exception:
|
||||
return None
|
||||
|
||||
async def get_folders_by_ids(self, ids: list[str], db: AsyncSession | None = None) -> list[FolderModel]:
|
||||
async with get_async_db_context(db) as db:
|
||||
result = await db.execute(select(Folder).filter(Folder.id.in_(ids)).order_by(Folder.updated_at.desc()))
|
||||
return [FolderModel.model_validate(folder) for folder in result.scalars().all()]
|
||||
|
||||
async def get_shared_folder_ids_for_user(
|
||||
self, user_id: str, user_group_ids: set[str], db: Optional[AsyncSession] = None
|
||||
) -> dict[str, str]:
|
||||
|
|
@ -248,7 +253,9 @@ class FolderTable:
|
|||
self, parent_id: Optional[str], user_id: str, db: Optional[AsyncSession] = None
|
||||
) -> list[FolderModel]:
|
||||
async with get_async_db_context(db) as db:
|
||||
result = await db.execute(select(Folder).filter_by(parent_id=parent_id, user_id=user_id))
|
||||
result = await db.execute(
|
||||
select(Folder).filter_by(parent_id=parent_id, user_id=user_id).order_by(Folder.updated_at.desc())
|
||||
)
|
||||
return [FolderModel.model_validate(folder) for folder in result.scalars().all()]
|
||||
|
||||
async def get_folder_ids_by_id_and_user_id_in_subtree(
|
||||
|
|
|
|||
|
|
@ -233,43 +233,35 @@ async def get_shared_folders(
|
|||
|
||||
folder_perms = await Folders.get_shared_folder_ids_for_user(user.id, group_ids, db=db)
|
||||
|
||||
# Filter out folders owned by the user
|
||||
results = []
|
||||
owner_cache = {}
|
||||
for folder_id, permission in folder_perms.items():
|
||||
folder = await Folders.get_folder_by_id(folder_id, db=db)
|
||||
if not folder or folder.user_id == user.id:
|
||||
continue
|
||||
folders = await Folders.get_folders_by_ids(list(folder_perms.keys()), db=db)
|
||||
shared_folders = [folder for folder in folders if folder.user_id != user.id]
|
||||
|
||||
# Get owner name (cached)
|
||||
if folder.user_id not in owner_cache:
|
||||
owner = await Users.get_user_by_id(folder.user_id, db=db)
|
||||
owner_cache[folder.user_id] = owner.name if owner else 'Unknown'
|
||||
owners = await Users.get_users_by_user_ids([folder.user_id for folder in shared_folders], db=db)
|
||||
owner_names = {owner.id: owner.name for owner in owners}
|
||||
|
||||
results.append(
|
||||
{
|
||||
**folder.model_dump(),
|
||||
'owner_name': owner_cache[folder.user_id],
|
||||
'permission': permission,
|
||||
}
|
||||
)
|
||||
results = [
|
||||
{
|
||||
**folder.model_dump(),
|
||||
'owner_name': owner_names.get(folder.user_id, 'Unknown'),
|
||||
'permission': folder_perms[folder.id],
|
||||
}
|
||||
for folder in shared_folders
|
||||
]
|
||||
|
||||
# Also include child folders of shared folders (inheritance)
|
||||
shared_root_ids = {r['id'] for r in results}
|
||||
for root_id in list(shared_root_ids):
|
||||
root_folder = await Folders.get_folder_by_id(root_id, db=db)
|
||||
if root_folder:
|
||||
children = await Folders.get_children_folders_by_id_and_user_id(root_id, root_folder.user_id, db=db)
|
||||
if children:
|
||||
for child in children:
|
||||
if child.id not in {r['id'] for r in results}:
|
||||
results.append(
|
||||
{
|
||||
**child.model_dump(),
|
||||
'owner_name': owner_cache.get(child.user_id, 'Unknown'),
|
||||
'permission': folder_perms.get(root_id, 'read'),
|
||||
}
|
||||
)
|
||||
seen_ids = {folder.id for folder in shared_folders}
|
||||
for folder in shared_folders:
|
||||
children = await Folders.get_children_folders_by_id_and_user_id(folder.id, folder.user_id, db=db)
|
||||
for child in children or []:
|
||||
if child.id not in seen_ids:
|
||||
seen_ids.add(child.id)
|
||||
results.append(
|
||||
{
|
||||
**child.model_dump(),
|
||||
'owner_name': owner_names.get(child.user_id, 'Unknown'),
|
||||
'permission': folder_perms[folder.id],
|
||||
}
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue