aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@arthur-schiwon.de>2025-07-03 10:41:23 +0200
committerGitHub <noreply@github.com>2025-07-03 10:41:23 +0200
commit33805b8d6965d44653e5cc37a597dd7e8ef78dd1 (patch)
tree84517d883aa5ffe11cc0ba07aa6017f5e15ddcd6
parent5bcd26985b0b5f9a6dc99e83bd5edea80b48746e (diff)
parent782f42c6784d5de3e831be9927844b957cb36d17 (diff)
downloadnextcloud-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.php44
-rw-r--r--apps/dav/lib/Files/FileSearchBackend.php3
-rw-r--r--apps/dav/lib/Server.php1
-rw-r--r--apps/dav/tests/unit/Files/FileSearchBackendTest.php19
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);
+ }
}