Skip to content

Commit 784e43b

Browse files
committed
feat(dav): Support multiple scopes in DAV search
Signed-off-by: Louis Chemineau <louis@chmn.me>
1 parent 33c4ddd commit 784e43b

1 file changed

Lines changed: 73 additions & 17 deletions

File tree

apps/dav/lib/Files/FileSearchBackend.php

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use OC\Files\Search\SearchComparison;
3131
use OC\Files\Search\SearchOrder;
3232
use OC\Files\Search\SearchQuery;
33+
use OC\Files\Storage\Wrapper\Jail;
3334
use OC\Files\View;
3435
use OCA\DAV\Connector\Sabre\CachingTree;
3536
use OCA\DAV\Connector\Sabre\Directory;
@@ -39,6 +40,8 @@
3940
use OCP\Files\Folder;
4041
use OCP\Files\IRootFolder;
4142
use OCP\Files\Node;
43+
use OCP\Files\Search\ISearchBinaryOperator;
44+
use OCP\Files\Search\ISearchComparison;
4245
use OCP\Files\Search\ISearchOperator;
4346
use OCP\Files\Search\ISearchOrder;
4447
use OCP\Files\Search\ISearchQuery;
@@ -152,28 +155,73 @@ private function getPropertyDefinitionsForMetadata(): array {
152155
public function preloadPropertyFor(array $nodes, array $requestProperties): void {
153156
}
154157

155-
/**
156-
* @param Query $search
157-
* @return SearchResult[]
158-
*/
159-
public function search(Query $search): array {
160-
if (count($search->from) !== 1) {
161-
throw new \InvalidArgumentException('Searching more than one folder is not supported');
162-
}
163-
$query = $this->transformQuery($search);
164-
$scope = $search->from[0];
165-
if ($scope->path === null) {
158+
private function getFolderForPath(string $path): Folder {
159+
if ($path === null) {
166160
throw new \InvalidArgumentException('Using uri\'s as scope is not supported, please use a path relative to the search arbiter instead');
167161
}
168-
$node = $this->tree->getNodeForPath($scope->path);
162+
163+
$node = $this->tree->getNodeForPath($path);
164+
169165
if (!$node instanceof Directory) {
170166
throw new \InvalidArgumentException('Search is only supported on directories');
171167
}
172168

173169
$fileInfo = $node->getFileInfo();
174-
$folder = $this->rootFolder->get($fileInfo->getPath());
175-
/** @var Folder $folder $results */
176-
$results = $folder->search($query);
170+
171+
return $this->rootFolder->get($fileInfo->getPath());
172+
}
173+
174+
/**
175+
* @param Query $search
176+
* @return SearchResult[]
177+
*/
178+
public function search(Query $search): array {
179+
switch (count($search->from)) {
180+
case 0:
181+
throw new \InvalidArgumentException('You need to specify a scope for the search.');
182+
break;
183+
case 1:
184+
$scope = $search->from[0];
185+
$folder = $this->getFolderForPath($scope->path);
186+
$query = $this->transformQuery($search);
187+
$results = $folder->search($query);
188+
break;
189+
default:
190+
$scopes = [];
191+
foreach ($search->from as $scope) {
192+
$folder = $this->getFolderForPath($scope->path);
193+
$folderStorage = $folder->getStorage();
194+
if ($folderStorage->instanceOfStorage(Jail::class)) {
195+
/** @var Jail $folderStorage */
196+
$internalPath = $folderStorage->getUnjailedPath($folder->getInternalPath());
197+
} else {
198+
$internalPath = $folder->getInternalPath();
199+
}
200+
201+
$scopes[] = new SearchBinaryOperator(
202+
ISearchBinaryOperator::OPERATOR_AND,
203+
[
204+
new SearchComparison(
205+
ISearchComparison::COMPARE_EQUAL,
206+
'storage',
207+
$folderStorage->getCache()->getNumericStorageId(),
208+
''
209+
),
210+
new SearchComparison(
211+
ISearchComparison::COMPARE_LIKE,
212+
'path',
213+
$internalPath . '/%',
214+
''
215+
),
216+
]
217+
);
218+
}
219+
220+
$scopeOperators = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $scopes);
221+
$query = $this->transformQuery($search, $scopeOperators);
222+
$userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
223+
$results = $userFolder->search($query);
224+
}
177225

178226
/** @var SearchResult[] $nodes */
179227
$nodes = array_map(function (Node $node) {
@@ -288,7 +336,7 @@ private function getHrefForNode(Node $node) {
288336
*
289337
* @return ISearchQuery
290338
*/
291-
private function transformQuery(Query $query): ISearchQuery {
339+
private function transformQuery(Query $query, ?SearchBinaryOperator $scopeOperators = null): ISearchQuery {
292340
$orders = array_map(function (Order $order): ISearchOrder {
293341
$direction = $order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING;
294342
if (str_starts_with($order->property->name, FilesPlugin::FILE_METADATA_PREFIX)) {
@@ -316,8 +364,16 @@ private function transformQuery(Query $query): ISearchQuery {
316364
throw new \InvalidArgumentException('Invalid search query, maximum operator limit of ' . self::OPERATOR_LIMIT . ' exceeded, got ' . $operatorCount . ' operators');
317365
}
318366

367+
/** @var SearchBinaryOperator|SearchComparison */
368+
$queryOperators = $this->transformSearchOperation($query->where);
369+
if ($scopeOperators === null) {
370+
$operators = $queryOperators;
371+
} else {
372+
$operators = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [$queryOperators, $scopeOperators]);
373+
}
374+
319375
return new SearchQuery(
320-
$this->transformSearchOperation($query->where),
376+
$operators,
321377
(int)$limit->maxResults,
322378
$offset,
323379
$orders,

0 commit comments

Comments
 (0)