aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2024-04-16 16:18:14 +0200
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>2024-04-22 15:28:29 +0000
commita97c2131d20e601eefc9586be42319fe87c0dca0 (patch)
tree0a864ab3ca112efcf69426858308264c44678d0c
parentb86679cdb26d48ea7988695b4be6473bfbae961b (diff)
downloadnextcloud-server-a97c2131d20e601eefc9586be42319fe87c0dca0.tar.gz
nextcloud-server-a97c2131d20e601eefc9586be42319fe87c0dca0.zip
feat(dav): Support multiple scopes in DAV search
Signed-off-by: Louis Chemineau <louis@chmn.me>
-rw-r--r--apps/dav/lib/Files/FileSearchBackend.php91
1 files changed, 74 insertions, 17 deletions
diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php
index fd45491da7e..8b87ae624ba 100644
--- a/apps/dav/lib/Files/FileSearchBackend.php
+++ b/apps/dav/lib/Files/FileSearchBackend.php
@@ -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,