]> source.dussan.org Git - nextcloud-server.git/commitdiff
feat(dav): Support multiple scopes in DAV search 44969/head
authorLouis Chemineau <louis@chmn.me>
Tue, 16 Apr 2024 14:18:14 +0000 (16:18 +0200)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Mon, 22 Apr 2024 15:28:29 +0000 (15:28 +0000)
Signed-off-by: Louis Chemineau <louis@chmn.me>
apps/dav/lib/Files/FileSearchBackend.php

index fd45491da7e0e3c55f15d9fad25872944901a0dc..8b87ae624ba2574e7eec0ef838ff24e0bb4e5f80 100644 (file)
@@ -30,6 +30,7 @@ use OC\Files\Search\SearchBinaryOperator;
 use OC\Files\Search\SearchComparison;
 use OC\Files\Search\SearchOrder;
 use OC\Files\Search\SearchQuery;
+use OC\Files\Storage\Wrapper\Jail;
 use OC\Files\View;
 use OCA\DAV\Connector\Sabre\CachingTree;
 use OCA\DAV\Connector\Sabre\Directory;
@@ -39,6 +40,8 @@ use OCP\Files\Cache\ICacheEntry;
 use OCP\Files\Folder;
 use OCP\Files\IRootFolder;
 use OCP\Files\Node;
+use OCP\Files\Search\ISearchBinaryOperator;
+use OCP\Files\Search\ISearchComparison;
 use OCP\Files\Search\ISearchOperator;
 use OCP\Files\Search\ISearchOrder;
 use OCP\Files\Search\ISearchQuery;
@@ -152,28 +155,74 @@ class FileSearchBackend implements ISearchBackend {
        public function preloadPropertyFor(array $nodes, array $requestProperties): void {
        }
 
-       /**
-        * @param Query $search
-        * @return SearchResult[]
-        */
-       public function search(Query $search): array {
-               if (count($search->from) !== 1) {
-                       throw new \InvalidArgumentException('Searching more than one folder is not supported');
-               }
-               $query = $this->transformQuery($search);
-               $scope = $search->from[0];
-               if ($scope->path === null) {
+       private function getFolderForPath(?string $path = null): Folder {
+               if ($path === null) {
                        throw new \InvalidArgumentException('Using uri\'s as scope is not supported, please use a path relative to the search arbiter instead');
                }
-               $node = $this->tree->getNodeForPath($scope->path);
+
+               $node = $this->tree->getNodeForPath($path);
+
                if (!$node instanceof Directory) {
                        throw new \InvalidArgumentException('Search is only supported on directories');
                }
 
                $fileInfo = $node->getFileInfo();
-               $folder = $this->rootFolder->get($fileInfo->getPath());
-               /** @var Folder $folder $results */
-               $results = $folder->search($query);
+
+               /** @var Folder */
+               return $this->rootFolder->get($fileInfo->getPath());
+       }
+
+       /**
+        * @param Query $search
+        * @return SearchResult[]
+        */
+       public function search(Query $search): array {
+               switch (count($search->from)) {
+                       case 0:
+                               throw new \InvalidArgumentException('You need to specify a scope for the search.');
+                               break;
+                       case 1:
+                               $scope = $search->from[0];
+                               $folder = $this->getFolderForPath($scope->path);
+                               $query = $this->transformQuery($search);
+                               $results = $folder->search($query);
+                               break;
+                       default:
+                               $scopes = [];
+                               foreach ($search->from as $scope) {
+                                       $folder = $this->getFolderForPath($scope->path);
+                                       $folderStorage = $folder->getStorage();
+                                       if ($folderStorage->instanceOfStorage(Jail::class)) {
+                                               /** @var Jail $folderStorage */
+                                               $internalPath = $folderStorage->getUnjailedPath($folder->getInternalPath());
+                                       } else {
+                                               $internalPath = $folder->getInternalPath();
+                                       }
+
+                                       $scopes[] = new SearchBinaryOperator(
+                                               ISearchBinaryOperator::OPERATOR_AND,
+                                               [
+                                                       new SearchComparison(
+                                                               ISearchComparison::COMPARE_EQUAL,
+                                                               'storage',
+                                                               $folderStorage->getCache()->getNumericStorageId(),
+                                                               ''
+                                                       ),
+                                                       new SearchComparison(
+                                                               ISearchComparison::COMPARE_LIKE,
+                                                               'path',
+                                                               $internalPath . '/%',
+                                                               ''
+                                                       ),
+                                               ]
+                                       );
+                               }
+
+                               $scopeOperators = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $scopes);
+                               $query = $this->transformQuery($search, $scopeOperators);
+                               $userFolder = $this->rootFolder->getUserFolder($this->user->getUID());
+                               $results = $userFolder->search($query);
+               }
 
                /** @var SearchResult[] $nodes */
                $nodes = array_map(function (Node $node) {
@@ -288,7 +337,7 @@ class FileSearchBackend implements ISearchBackend {
         *
         * @return ISearchQuery
         */
-       private function transformQuery(Query $query): ISearchQuery {
+       private function transformQuery(Query $query, ?SearchBinaryOperator $scopeOperators = null): ISearchQuery {
                $orders = array_map(function (Order $order): ISearchOrder {
                        $direction = $order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING;
                        if (str_starts_with($order->property->name, FilesPlugin::FILE_METADATA_PREFIX)) {
@@ -316,8 +365,16 @@ class FileSearchBackend implements ISearchBackend {
                        throw new \InvalidArgumentException('Invalid search query, maximum operator limit of ' . self::OPERATOR_LIMIT . ' exceeded, got ' . $operatorCount . ' operators');
                }
 
+               /** @var SearchBinaryOperator|SearchComparison */
+               $queryOperators = $this->transformSearchOperation($query->where);
+               if ($scopeOperators === null) {
+                       $operators = $queryOperators;
+               } else {
+                       $operators = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [$queryOperators, $scopeOperators]);
+               }
+
                return new SearchQuery(
-                       $this->transformSearchOperation($query->where),
+                       $operators,
                        (int)$limit->maxResults,
                        $offset,
                        $orders,