diff options
author | Arthur Schiwon <blizzz@arthur-schiwon.de> | 2025-07-03 10:41:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-07-03 10:41:23 +0200 |
commit | 33805b8d6965d44653e5cc37a597dd7e8ef78dd1 (patch) | |
tree | 84517d883aa5ffe11cc0ba07aa6017f5e15ddcd6 | |
parent | 5bcd26985b0b5f9a6dc99e83bd5edea80b48746e (diff) | |
parent | 782f42c6784d5de3e831be9927844b957cb36d17 (diff) | |
download | nextcloud-server-stable30.tar.gz nextcloud-server-stable30.zip |
Merge pull request #53773 from nextcloud/backport/53730/stable30stable30
[stable30] perf(dav): Preload dav search with tags/favorites
-rw-r--r-- | apps/dav/lib/Connector/Sabre/TagsPlugin.php | 44 | ||||
-rw-r--r-- | apps/dav/lib/Files/FileSearchBackend.php | 3 | ||||
-rw-r--r-- | apps/dav/lib/Server.php | 1 | ||||
-rw-r--r-- | apps/dav/tests/unit/Files/FileSearchBackendTest.php | 19 |
4 files changed, 53 insertions, 14 deletions
diff --git a/apps/dav/lib/Connector/Sabre/TagsPlugin.php b/apps/dav/lib/Connector/Sabre/TagsPlugin.php index ee219aff18f..0e37920a32d 100644 --- a/apps/dav/lib/Connector/Sabre/TagsPlugin.php +++ b/apps/dav/lib/Connector/Sabre/TagsPlugin.php @@ -98,6 +98,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin { $this->server = $server; $this->server->on('propFind', [$this, 'handleGetProperties']); $this->server->on('propPatch', [$this, 'handleUpdateProperties']); + $this->server->on('preloadProperties', [$this, 'handlePreloadProperties']); } /** @@ -154,6 +155,24 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin { } /** + * Prefetches tags for a list of file IDs and caches the results + * + * @param array $fileIds List of file IDs to prefetch tags for + * @return void + */ + private function prefetchTagsForFileIds(array $fileIds) { + $tags = $this->getTagger()->getTagsForObjects($fileIds); + if ($tags === false) { + // the tags API returns false on error... + $tags = []; + } + + foreach ($fileIds as $fileId) { + $this->cachedTags[$fileId] = $tags[$fileId] ?? []; + } + } + + /** * Updates the tags of the given file id * * @param int $fileId @@ -203,22 +222,11 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin { )) { // note: pre-fetching only supported for depth <= 1 $folderContent = $node->getChildren(); - $fileIds[] = (int) $node->getId(); + $fileIds = [(int) $node->getId()]; foreach ($folderContent as $info) { $fileIds[] = (int) $info->getId(); } - $tags = $this->getTagger()->getTagsForObjects($fileIds); - if ($tags === false) { - // the tags API returns false on error... - $tags = []; - } - - $this->cachedTags = $this->cachedTags + $tags; - $emptyFileIds = array_diff($fileIds, array_keys($tags)); - // also cache the ones that were not found - foreach ($emptyFileIds as $fileId) { - $this->cachedTags[$fileId] = []; - } + $this->prefetchTagsForFileIds($fileIds); } $isFav = null; @@ -274,4 +282,14 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin { return 200; }); } + + public function handlePreloadProperties(array $nodes, array $requestProperties): void { + if ( + !in_array(self::FAVORITE_PROPERTYNAME, $requestProperties, true) && + !in_array(self::TAGS_PROPERTYNAME, $requestProperties, true) + ) { + return; + } + $this->prefetchTagsForFileIds(array_map(fn ($node) => $node->getId(), $nodes)); + } } diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 049b8facf8e..713a44a9e87 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -15,6 +15,7 @@ use OC\Files\View; use OCA\DAV\Connector\Sabre\CachingTree; use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\FilesPlugin; +use OCA\DAV\Connector\Sabre\Server; use OCA\DAV\Connector\Sabre\TagsPlugin; use OCP\Files\Cache\ICacheEntry; use OCP\Files\Folder; @@ -44,6 +45,7 @@ class FileSearchBackend implements ISearchBackend { public const OPERATOR_LIMIT = 100; public function __construct( + private Server $server, private CachingTree $tree, private IUser $user, private IRootFolder $rootFolder, @@ -133,6 +135,7 @@ class FileSearchBackend implements ISearchBackend { * @param string[] $requestProperties */ public function preloadPropertyFor(array $nodes, array $requestProperties): void { + $this->server->emit('preloadProperties', [$nodes, $requestProperties]); } private function getFolderForPath(?string $path = null): Folder { diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 8f4a6bc726f..39f75ac2d5b 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -322,6 +322,7 @@ class Server { \OC::$server->getAppManager() )); $lazySearchBackend->setBackend(new \OCA\DAV\Files\FileSearchBackend( + $this->server, $this->server->tree, $user, \OC::$server->getRootFolder(), diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php index 1ac605eeaef..9b7859c4697 100644 --- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -14,6 +14,7 @@ use OCA\DAV\Connector\Sabre\Directory; use OCA\DAV\Connector\Sabre\File; use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\ObjectTree; +use OCA\DAV\Connector\Sabre\Server; use OCA\DAV\Files\FileSearchBackend; use OCP\Files\FileInfo; use OCP\Files\Folder; @@ -33,6 +34,7 @@ use Test\TestCase; class FileSearchBackendTest extends TestCase { /** @var ObjectTree|\PHPUnit\Framework\MockObject\MockObject */ private $tree; + private Server&\PHPUnit\Framework\MockObject\MockObject $server; /** @var IUser */ private $user; @@ -67,6 +69,8 @@ class FileSearchBackendTest extends TestCase { ->disableOriginalConstructor() ->getMock(); + $this->server = $this->createMock(Server::class); + $this->view = $this->createMock(View::class); $this->view->expects($this->any()) @@ -97,7 +101,7 @@ class FileSearchBackendTest extends TestCase { $filesMetadataManager = $this->createMock(IFilesMetadataManager::class); - $this->search = new FileSearchBackend($this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager); + $this->search = new FileSearchBackend($this->server, $this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager); } public function testSearchFilename(): void { @@ -421,4 +425,17 @@ class FileSearchBackendTest extends TestCase { $this->expectException(\InvalidArgumentException::class); $this->search->search($query); } + + public function testPreloadPropertyFor(): void { + $node1 = $this->createMock(File::class); + $node2 = $this->createMock(Directory::class); + $nodes = [$node1, $node2]; + $requestProperties = ['{DAV:}getcontenttype', '{DAV:}getlastmodified']; + + $this->server->expects($this->once()) + ->method('emit') + ->with('preloadProperties', [$nodes, $requestProperties]); + + $this->search->preloadPropertyFor($nodes, $requestProperties); + } } |