From 3d63b0fffabb133a6a79b9da9915253cedf7ebb5 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Wed, 8 Jul 2026 11:35:10 +0200 Subject: [PATCH] perf: Don't fetch all the trash items when deleting a single item Signed-off-by: Carl Schwan --- apps/files_trashbin/lib/Helper.php | 24 +++++++++++++++++++ apps/files_trashbin/lib/Sabre/TrashRoot.php | 24 +++++++------------ .../lib/Trash/ITrashManager.php | 9 +++++++ .../lib/Trash/LegacyTrashBackend.php | 8 +++++++ .../files_trashbin/lib/Trash/TrashManager.php | 20 ++++++++++++++++ 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/apps/files_trashbin/lib/Helper.php b/apps/files_trashbin/lib/Helper.php index 177b77e49aa..2a4ebb5ad6c 100644 --- a/apps/files_trashbin/lib/Helper.php +++ b/apps/files_trashbin/lib/Helper.php @@ -53,6 +53,30 @@ class Helper { return $result; } + public static function getTrashFile(string $dir, string $user, string $name): ?FileInfo { + $timestamp = null; + + $view = new View('/' . $user . '/files_trashbin/files'); + + if (ltrim($dir, '/') !== '' && !$view->is_dir($dir)) { + throw new \Exception('Directory does not exists'); + } + + $mount = $view->getMount($dir); + $storage = $mount->getStorage(); + $absoluteDir = $view->getAbsolutePath($dir); + $internalPath = $mount->getInternalPath($absoluteDir); + + $extraData = Trashbin::getExtraData($user); + $entry = $storage->getCache()->get($mount->getInternalPath($view->getAbsolutePath($dir . '/' . $name))); + if ($entry === false) { + return null; + } + + $timestamp = null; + return self::buildFileInfo($entry, $dir, $timestamp, $extraData, $storage, $absoluteDir, $internalPath, $mount); + } + private static function buildFileInfo(ICacheEntry $entry, string $dir, ?string &$timestamp, array $extraData, $storage, string $absoluteDir, string $internalPath, IMountPoint $mount): FileInfo { $entryName = $entry->getName(); $name = $entryName; diff --git a/apps/files_trashbin/lib/Sabre/TrashRoot.php b/apps/files_trashbin/lib/Sabre/TrashRoot.php index 9c730c69d87..7da8573ab7d 100644 --- a/apps/files_trashbin/lib/Sabre/TrashRoot.php +++ b/apps/files_trashbin/lib/Sabre/TrashRoot.php @@ -63,37 +63,31 @@ class TrashRoot implements ICollection { public function getChildren(): array { $entries = $this->trashManager->listTrashRoot($this->user); - $children = array_map(function (ITrashItem $entry) { + return array_map(function (ITrashItem $entry): TrashFile|TrashFolder { if ($entry->getType() === FileInfo::TYPE_FOLDER) { return new TrashFolder($this->trashManager, $entry); } return new TrashFile($this->trashManager, $entry); }, $entries); - - return $children; } #[\Override] public function getChild($name): ITrash { - $entries = $this->getChildren(); + $entry = $this->trashManager->getTrashRootItem($this->user, $name); - foreach ($entries as $entry) { - if ($entry->getName() === $name) { - return $entry; - } + if ($entry === null) { + throw new NotFound(); } - throw new NotFound(); + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolder($this->trashManager, $entry); + } + return new TrashFile($this->trashManager, $entry); } #[\Override] public function childExists($name): bool { - try { - $this->getChild($name); - return true; - } catch (NotFound $e) { - return false; - } + return $this->trashManager->getTrashRootItem($this->user, $name) !== null; } #[\Override] diff --git a/apps/files_trashbin/lib/Trash/ITrashManager.php b/apps/files_trashbin/lib/Trash/ITrashManager.php index 25a07d66c8b..f5188dc19de 100644 --- a/apps/files_trashbin/lib/Trash/ITrashManager.php +++ b/apps/files_trashbin/lib/Trash/ITrashManager.php @@ -31,6 +31,15 @@ interface ITrashManager extends ITrashBackend { #[\Override] public function listTrashRoot(IUser $user): array; + /** + * Get a specific item in the root of the trashbin + * + * @param IUser $user + * @return ?ITrashItem + * @since 35.0.0 + */ + public function getTrashRootItem(IUser $user, string $name): ?ITrashItem; + /** * Temporally prevent files from being moved to the trash * diff --git a/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php index 54ffd6f4cab..8bd64d83011 100644 --- a/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php +++ b/apps/files_trashbin/lib/Trash/LegacyTrashBackend.php @@ -65,6 +65,14 @@ class LegacyTrashBackend implements ITrashBackend { return $this->mapTrashItems($entries, $user); } + public function getTrashRootItem(IUser $user, string $name): ?ITrashItem { + $entry = Helper::getTrashFile('/', $user->getUID(), $name); + if ($entry === null) { + return null; + } + return $this->mapTrashItems([$entry], $user)[0]; + } + #[\Override] public function listTrashFolder(ITrashItem $folder): array { $user = $folder->getUser(); diff --git a/apps/files_trashbin/lib/Trash/TrashManager.php b/apps/files_trashbin/lib/Trash/TrashManager.php index 3706a6a2e80..cf0f34cff6d 100644 --- a/apps/files_trashbin/lib/Trash/TrashManager.php +++ b/apps/files_trashbin/lib/Trash/TrashManager.php @@ -39,6 +39,26 @@ class TrashManager implements ITrashManager { return $items; } + #[\Override] + public function getTrashRootItem(IUser $user, string $name): ?ITrashItem { + foreach ($this->getBackends() as $backend) { + if (method_exists($backend, 'getTrashRootItem')) { + $item = $backend->getTrashRootItem($user, $name); + if ($item !== null) { + return $item; + } + } else { + $items = $backend->listTrashRoot($user); + foreach ($items as $item) { + if ($item->getName() === $name) { + return $item; + } + } + } + } + return null; + } + private function getBackendForItem(ITrashItem $item) { return $item->getTrashBackend(); }