Skip to content

Commit 097f04c

Browse files
authored
Merge pull request #44858 from nextcloud/artonge/feat/support_multiple_scope_in_dav_search
Support multiple scopes in DAV search
2 parents 37c89f4 + ac0acfb commit 097f04c

1 file changed

Lines changed: 74 additions & 17 deletions

File tree

apps/dav/lib/Files/FileSearchBackend.php

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

178227
/** @var SearchResult[] $nodes */
179228
$nodes = array_map(function (Node $node) {
@@ -288,7 +337,7 @@ private function getHrefForNode(Node $node) {
288337
*
289338
* @return ISearchQuery
290339
*/
291-
private function transformQuery(Query $query): ISearchQuery {
340+
private function transformQuery(Query $query, ?SearchBinaryOperator $scopeOperators = null): ISearchQuery {
292341
$orders = array_map(function (Order $order): ISearchOrder {
293342
$direction = $order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING;
294343
if (str_starts_with($order->property->name, FilesPlugin::FILE_METADATA_PREFIX)) {
@@ -316,8 +365,16 @@ private function transformQuery(Query $query): ISearchQuery {
316365
throw new \InvalidArgumentException('Invalid search query, maximum operator limit of ' . self::OPERATOR_LIMIT . ' exceeded, got ' . $operatorCount . ' operators');
317366
}
318367

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

0 commit comments

Comments
 (0)