Skip to content

Commit c2b8311

Browse files
juliusknorrblizzz
authored andcommitted
perf(dav): Preload dav search with tags/favorites
Signed-off-by: Julius Knorr <[email protected]>
1 parent e130cc9 commit c2b8311

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed

apps/dav/lib/Connector/Sabre/TagsPlugin.php

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public function initialize(\Sabre\DAV\Server $server) {
120120
$this->server = $server;
121121
$this->server->on('propFind', [$this, 'handleGetProperties']);
122122
$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
123+
$this->server->on('preloadProperties', [$this, 'handlePreloadProperties']);
123124
}
124125

125126
/**
@@ -175,6 +176,24 @@ private function getTags($fileId) {
175176
return null;
176177
}
177178

179+
/**
180+
* Prefetches tags for a list of file IDs and caches the results
181+
*
182+
* @param array $fileIds List of file IDs to prefetch tags for
183+
* @return void
184+
*/
185+
private function prefetchTagsForFileIds(array $fileIds) {
186+
$tags = $this->getTagger()->getTagsForObjects($fileIds);
187+
if ($tags === false) {
188+
// the tags API returns false on error...
189+
$tags = [];
190+
}
191+
192+
foreach ($fileIds as $fileId) {
193+
$this->cachedTags[$fileId] = $tags[$fileId] ?? [];
194+
}
195+
}
196+
178197
/**
179198
* Updates the tags of the given file id
180199
*
@@ -225,22 +244,11 @@ public function handleGetProperties(
225244
)) {
226245
// note: pre-fetching only supported for depth <= 1
227246
$folderContent = $node->getChildren();
228-
$fileIds[] = (int)$node->getId();
247+
$fileIds = [(int) $node->getId()];
229248
foreach ($folderContent as $info) {
230249
$fileIds[] = (int)$info->getId();
231250
}
232-
$tags = $this->getTagger()->getTagsForObjects($fileIds);
233-
if ($tags === false) {
234-
// the tags API returns false on error...
235-
$tags = [];
236-
}
237-
238-
$this->cachedTags = $this->cachedTags + $tags;
239-
$emptyFileIds = array_diff($fileIds, array_keys($tags));
240-
// also cache the ones that were not found
241-
foreach ($emptyFileIds as $fileId) {
242-
$this->cachedTags[$fileId] = [];
243-
}
251+
$this->prefetchTagsForFileIds($fileIds);
244252
}
245253

246254
$isFav = null;
@@ -296,4 +304,14 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
296304
return 200;
297305
});
298306
}
307+
308+
public function handlePreloadProperties(array $nodes, array $requestProperties): void {
309+
if (
310+
!in_array(self::FAVORITE_PROPERTYNAME, $requestProperties, true) &&
311+
!in_array(self::TAGS_PROPERTYNAME, $requestProperties, true)
312+
) {
313+
return;
314+
}
315+
$this->prefetchTagsForFileIds(array_map(fn ($node) => $node->getId(), $nodes));
316+
}
299317
}

apps/dav/lib/Files/FileSearchBackend.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use OCA\DAV\Connector\Sabre\CachingTree;
3636
use OCA\DAV\Connector\Sabre\Directory;
3737
use OCA\DAV\Connector\Sabre\FilesPlugin;
38+
use OCA\DAV\Connector\Sabre\Server;
3839
use OCA\DAV\Connector\Sabre\TagsPlugin;
3940
use OCP\Files\Cache\ICacheEntry;
4041
use OCP\Files\Folder;
@@ -64,6 +65,7 @@ class FileSearchBackend implements ISearchBackend {
6465
public const OPERATOR_LIMIT = 100;
6566

6667
public function __construct(
68+
private Server $server,
6769
private CachingTree $tree,
6870
private IUser $user,
6971
private IRootFolder $rootFolder,
@@ -153,6 +155,7 @@ private function getPropertyDefinitionsForMetadata(): array {
153155
* @param string[] $requestProperties
154156
*/
155157
public function preloadPropertyFor(array $nodes, array $requestProperties): void {
158+
$this->server->emit('preloadProperties', [$nodes, $requestProperties]);
156159
}
157160

158161
private function getFolderForPath(?string $path = null): Folder {

apps/dav/lib/Server.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ public function __construct(IRequest $request, string $baseUri) {
341341
\OC::$server->getAppManager()
342342
));
343343
$lazySearchBackend->setBackend(new \OCA\DAV\Files\FileSearchBackend(
344+
$this->server,
344345
$this->server->tree,
345346
$user,
346347
\OC::$server->getRootFolder(),

apps/dav/tests/unit/Files/FileSearchBackendTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use OCA\DAV\Connector\Sabre\File;
3535
use OCA\DAV\Connector\Sabre\FilesPlugin;
3636
use OCA\DAV\Connector\Sabre\ObjectTree;
37+
use OCA\DAV\Connector\Sabre\Server;
3738
use OCA\DAV\Files\FileSearchBackend;
3839
use OCP\Files\FileInfo;
3940
use OCP\Files\Folder;
@@ -53,6 +54,7 @@
5354
class FileSearchBackendTest extends TestCase {
5455
/** @var ObjectTree|\PHPUnit\Framework\MockObject\MockObject */
5556
private $tree;
57+
private Server|\PHPUnit\Framework\MockObject\MockObject $server;
5658

5759
/** @var IUser */
5860
private $user;
@@ -87,6 +89,8 @@ protected function setUp(): void {
8789
->disableOriginalConstructor()
8890
->getMock();
8991

92+
$this->server = $this->createMock(Server::class);
93+
9094
$this->view = $this->createMock(View::class);
9195

9296
$this->view->expects($this->any())
@@ -117,7 +121,7 @@ protected function setUp(): void {
117121

118122
$filesMetadataManager = $this->createMock(IFilesMetadataManager::class);
119123

120-
$this->search = new FileSearchBackend($this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager);
124+
$this->search = new FileSearchBackend($this->server, $this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager);
121125
}
122126

123127
public function testSearchFilename(): void {
@@ -441,4 +445,17 @@ public function testSearchOperatorLimit(): void {
441445
$this->expectException(\InvalidArgumentException::class);
442446
$this->search->search($query);
443447
}
448+
449+
public function testPreloadPropertyFor(): void {
450+
$node1 = $this->createMock(File::class);
451+
$node2 = $this->createMock(Directory::class);
452+
$nodes = [$node1, $node2];
453+
$requestProperties = ['{DAV:}getcontenttype', '{DAV:}getlastmodified'];
454+
455+
$this->server->expects($this->once())
456+
->method('emit')
457+
->with('preloadProperties', [$nodes, $requestProperties]);
458+
459+
$this->search->preloadPropertyFor($nodes, $requestProperties);
460+
}
444461
}

0 commit comments

Comments
 (0)