Skip to content

Commit 9f14ef9

Browse files
committed
perf(dav): Preload dav search with tags/favorites
Signed-off-by: Julius Knorr <[email protected]>
1 parent 565d524 commit 9f14ef9

File tree

4 files changed

+60
-14
lines changed

4 files changed

+60
-14
lines changed

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

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public function initialize(\Sabre\DAV\Server $server) {
9494
$this->server = $server;
9595
$this->server->on('propFind', [$this, 'handleGetProperties']);
9696
$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
97+
$this->server->on('preloadProperties', [$this, 'handlePreloadProperties']);
9798
}
9899

99100
/**
@@ -149,6 +150,27 @@ private function getTags($fileId) {
149150
return null;
150151
}
151152

153+
/**
154+
* Prefetches tags for a list of file IDs and caches the results
155+
*
156+
* @param array $fileIds List of file IDs to prefetch tags for
157+
* @return void
158+
*/
159+
private function prefetchTagsForFileIds(array $fileIds) {
160+
$tags = $this->getTagger()->getTagsForObjects($fileIds);
161+
if ($tags === false) {
162+
// the tags API returns false on error...
163+
$tags = [];
164+
}
165+
166+
$this->cachedTags = $this->cachedTags + $tags;
167+
$emptyFileIds = array_diff($fileIds, array_keys($tags));
168+
// also cache the ones that were not found
169+
foreach ($emptyFileIds as $fileId) {
170+
$this->cachedTags[$fileId] = [];
171+
}
172+
}
173+
152174
/**
153175
* Updates the tags of the given file id
154176
*
@@ -199,22 +221,11 @@ public function handleGetProperties(
199221
)) {
200222
// note: pre-fetching only supported for depth <= 1
201223
$folderContent = $node->getChildren();
202-
$fileIds[] = (int)$node->getId();
224+
$fileIds = [(int)$node->getId()];
203225
foreach ($folderContent as $info) {
204226
$fileIds[] = (int)$info->getId();
205227
}
206-
$tags = $this->getTagger()->getTagsForObjects($fileIds);
207-
if ($tags === false) {
208-
// the tags API returns false on error...
209-
$tags = [];
210-
}
211-
212-
$this->cachedTags = $this->cachedTags + $tags;
213-
$emptyFileIds = array_diff($fileIds, array_keys($tags));
214-
// also cache the ones that were not found
215-
foreach ($emptyFileIds as $fileId) {
216-
$this->cachedTags[$fileId] = [];
217-
}
228+
$this->prefetchTagsForFileIds($fileIds);
218229
}
219230

220231
$isFav = null;
@@ -270,4 +281,16 @@ public function handleUpdateProperties($path, PropPatch $propPatch) {
270281
return 200;
271282
});
272283
}
284+
285+
public function handlePreloadProperties(array $nodes, array $requestProperties): void {
286+
if (
287+
!in_array(self::FAVORITE_PROPERTYNAME, $requestProperties, true) &&
288+
!in_array(self::TAGS_PROPERTYNAME, $requestProperties, true)
289+
) {
290+
return;
291+
}
292+
$this->prefetchTagsForFileIds(array_map(function ($node) {
293+
return $node->getId();
294+
}, $nodes));
295+
}
273296
}

apps/dav/lib/Files/FileSearchBackend.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use OCA\DAV\Connector\Sabre\Directory;
1616
use OCA\DAV\Connector\Sabre\File;
1717
use OCA\DAV\Connector\Sabre\FilesPlugin;
18+
use OCA\DAV\Connector\Sabre\Server;
1819
use OCA\DAV\Connector\Sabre\TagsPlugin;
1920
use OCP\Files\Cache\ICacheEntry;
2021
use OCP\Files\Folder;
@@ -44,6 +45,7 @@ class FileSearchBackend implements ISearchBackend {
4445
public const OPERATOR_LIMIT = 100;
4546

4647
public function __construct(
48+
private Server $server,
4749
private CachingTree $tree,
4850
private IUser $user,
4951
private IRootFolder $rootFolder,
@@ -133,6 +135,7 @@ private function getPropertyDefinitionsForMetadata(): array {
133135
* @param string[] $requestProperties
134136
*/
135137
public function preloadPropertyFor(array $nodes, array $requestProperties): void {
138+
$this->server->emit('preloadProperties', [$nodes, $requestProperties]);
136139
}
137140

138141
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
@@ -352,6 +352,7 @@ public function __construct(
352352
\OCP\Server::get(IAppManager::class)
353353
));
354354
$lazySearchBackend->setBackend(new FileSearchBackend(
355+
$this->server,
355356
$this->server->tree,
356357
$user,
357358
\OCP\Server::get(IRootFolder::class),

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use OCA\DAV\Connector\Sabre\File;
1414
use OCA\DAV\Connector\Sabre\FilesPlugin;
1515
use OCA\DAV\Connector\Sabre\ObjectTree;
16+
use OCA\DAV\Connector\Sabre\Server;
1617
use OCA\DAV\Files\FileSearchBackend;
1718
use OCP\Files\FileInfo;
1819
use OCP\Files\Folder;
@@ -30,6 +31,9 @@
3031
use Test\TestCase;
3132

3233
class FileSearchBackendTest extends TestCase {
34+
/** @var Server|\PHPUnit\Framework\MockObject\MockObject */
35+
private $server;
36+
3337
/** @var ObjectTree|\PHPUnit\Framework\MockObject\MockObject */
3438
private $tree;
3539

@@ -66,6 +70,8 @@ protected function setUp(): void {
6670
->method('getUID')
6771
->willReturn('test');
6872

73+
$this->server = $this->createMock(Server::class);
74+
6975
$this->tree = $this->getMockBuilder(ObjectTree::class)
7076
->disableOriginalConstructor()
7177
->getMock();
@@ -100,7 +106,7 @@ protected function setUp(): void {
100106

101107
$filesMetadataManager = $this->createMock(IFilesMetadataManager::class);
102108

103-
$this->search = new FileSearchBackend($this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager);
109+
$this->search = new FileSearchBackend($this->server, $this->tree, $this->user, $this->rootFolder, $this->shareManager, $this->view, $filesMetadataManager);
104110
}
105111

106112
public function testSearchFilename(): void {
@@ -424,4 +430,17 @@ public function testSearchOperatorLimit(): void {
424430
$this->expectException(\InvalidArgumentException::class);
425431
$this->search->search($query);
426432
}
433+
434+
public function testPreloadPropertyFor(): void {
435+
$node1 = $this->createMock(File::class);
436+
$node2 = $this->createMock(Directory::class);
437+
$nodes = [$node1, $node2];
438+
$requestProperties = ['{DAV:}getcontenttype', '{DAV:}getlastmodified'];
439+
440+
$this->server->expects($this->once())
441+
->method('emit')
442+
->with('preloadProperties', [$nodes, $requestProperties]);
443+
444+
$this->search->preloadPropertyFor($nodes, $requestProperties);
445+
}
427446
}

0 commit comments

Comments
 (0)