|
|
|
@ -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,
|
|
|
|
|