diff options
Diffstat (limited to 'lib/private/Search')
-rw-r--r-- | lib/private/Search/Provider/File.php | 2 | ||||
-rw-r--r-- | lib/private/Search/Result/Audio.php | 4 | ||||
-rw-r--r-- | lib/private/Search/Result/File.php | 10 | ||||
-rw-r--r-- | lib/private/Search/Result/Folder.php | 2 | ||||
-rw-r--r-- | lib/private/Search/Result/Image.php | 4 | ||||
-rw-r--r-- | lib/private/Search/SearchComposer.php | 154 | ||||
-rw-r--r-- | lib/private/Search/SearchQuery.php | 88 |
7 files changed, 262 insertions, 2 deletions
diff --git a/lib/private/Search/Provider/File.php b/lib/private/Search/Provider/File.php index 02521460d8c..9a41a46bd35 100644 --- a/lib/private/Search/Provider/File.php +++ b/lib/private/Search/Provider/File.php @@ -32,6 +32,7 @@ use OC\Files\Filesystem; /** * Provide search results from the 'files' app + * @deprecated 20.0.0 */ class File extends \OCP\Search\Provider { @@ -39,6 +40,7 @@ class File extends \OCP\Search\Provider { * Search for files and folders matching the given query * @param string $query * @return \OCP\Search\Result + * @deprecated 20.0.0 */ public function search($query) { $files = Filesystem::search($query); diff --git a/lib/private/Search/Result/Audio.php b/lib/private/Search/Result/Audio.php index ef0d3bf9d20..e3917b7e4b3 100644 --- a/lib/private/Search/Result/Audio.php +++ b/lib/private/Search/Result/Audio.php @@ -27,15 +27,17 @@ namespace OC\Search\Result; /** * A found audio file + * @deprecated 20.0.0 */ class Audio extends File { /** * Type name; translated in templates * @var string + * @deprecated 20.0.0 */ public $type = 'audio'; - + /** * @TODO add ID3 information */ diff --git a/lib/private/Search/Result/File.php b/lib/private/Search/Result/File.php index cfff54e0692..f93b033c07f 100644 --- a/lib/private/Search/Result/File.php +++ b/lib/private/Search/Result/File.php @@ -31,36 +31,42 @@ use OCP\Files\Folder; /** * A found file + * @deprecated 20.0.0 */ class File extends \OCP\Search\Result { /** * Type name; translated in templates * @var string + * @deprecated 20.0.0 */ public $type = 'file'; /** * Path to file * @var string + * @deprecated 20.0.0 */ public $path; /** * Size, in bytes * @var int + * @deprecated 20.0.0 */ public $size; /** * Date modified, in human readable form * @var string + * @deprecated 20.0.0 */ public $modified; /** * File mime type * @var string + * @deprecated 20.0.0 */ public $mime_type; @@ -68,12 +74,14 @@ class File extends \OCP\Search\Result { * File permissions: * * @var string + * @deprecated 20.0.0 */ public $permissions; /** * Create a new file search result * @param FileInfo $data file data given by provider + * @deprecated 20.0.0 */ public function __construct(FileInfo $data) { $path = $this->getRelativePath($data->getPath()); @@ -97,6 +105,7 @@ class File extends \OCP\Search\Result { /** * @var Folder $userFolderCache + * @deprecated 20.0.0 */ protected static $userFolderCache = null; @@ -105,6 +114,7 @@ class File extends \OCP\Search\Result { * eg /user/files/foo.txt -> /foo.txt * @param string $path * @return string relative path + * @deprecated 20.0.0 */ protected function getRelativePath($path) { if (!isset(self::$userFolderCache)) { diff --git a/lib/private/Search/Result/Folder.php b/lib/private/Search/Result/Folder.php index 8110d61bead..1268b1379b2 100644 --- a/lib/private/Search/Result/Folder.php +++ b/lib/private/Search/Result/Folder.php @@ -27,12 +27,14 @@ namespace OC\Search\Result; /** * A found folder + * @deprecated 20.0.0 */ class Folder extends File { /** * Type name; translated in templates * @var string + * @deprecated 20.0.0 */ public $type = 'folder'; } diff --git a/lib/private/Search/Result/Image.php b/lib/private/Search/Result/Image.php index e569c91ea02..5a46138f594 100644 --- a/lib/private/Search/Result/Image.php +++ b/lib/private/Search/Result/Image.php @@ -27,15 +27,17 @@ namespace OC\Search\Result; /** * A found image file + * @deprecated 20.0.0 */ class Image extends File { /** * Type name; translated in templates * @var string + * @deprecated 20.0.0 */ public $type = 'image'; - + /** * @TODO add EXIF information */ diff --git a/lib/private/Search/SearchComposer.php b/lib/private/Search/SearchComposer.php new file mode 100644 index 00000000000..ae4350ca5cc --- /dev/null +++ b/lib/private/Search/SearchComposer.php @@ -0,0 +1,154 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Search; + +use InvalidArgumentException; +use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\AppFramework\QueryException; +use OCP\ILogger; +use OCP\IServerContainer; +use OCP\IUser; +use OCP\Search\IProvider; +use OCP\Search\ISearchQuery; +use OCP\Search\SearchResult; +use function array_map; + +/** + * Queries individual \OCP\Search\IProvider implementations and composes a + * unified search result for the user's search term + * + * The search process is generally split into two steps + * + * 1. Get a list of provider (`getProviders`) + * 2. Get search results of each provider (`search`) + * + * The reasoning behind this is that the runtime complexity of a combined search + * result would be O(n) and linearly grow with each provider added. This comes + * from the nature of php where we can't concurrently fetch the search results. + * So we offload the concurrency the client application (e.g. JavaScript in the + * browser) and let it first get the list of providers to then fetch all results + * concurrently. The client is free to decide whether all concurrent search + * results are awaited or shown as they come in. + * + * @see IProvider::search() for the arguments of the individual search requests + */ +class SearchComposer { + + /** @var string[] */ + private $lazyProviders = []; + + /** @var IProvider[] */ + private $providers = []; + + /** @var IServerContainer */ + private $container; + + /** @var ILogger */ + private $logger; + + public function __construct(IServerContainer $container, + ILogger $logger) { + $this->container = $container; + $this->logger = $logger; + } + + /** + * Register a search provider lazily + * + * Registers the fully-qualified class name of an implementation of an + * IProvider. The service will only be queried on demand. Apps will register + * the providers through the registration context object. + * + * @see IRegistrationContext::registerSearchProvider() + * + * @param string $class + */ + public function registerProvider(string $class): void { + $this->lazyProviders[] = $class; + } + + /** + * Load all providers dynamically that were registered through `registerProvider` + * + * If a provider can't be loaded we log it but the operation continues nevertheless + */ + private function loadLazyProviders(): void { + $classes = $this->lazyProviders; + foreach ($classes as $class) { + try { + /** @var IProvider $provider */ + $provider = $this->container->query($class); + $this->providers[$provider->getId()] = $provider; + } catch (QueryException $e) { + // Log an continue. We can be fault tolerant here. + $this->logger->logException($e, [ + 'message' => 'Could not load search provider dynamically: ' . $e->getMessage(), + 'level' => ILogger::ERROR, + ]); + } + } + $this->lazyProviders = []; + } + + /** + * Get a list of all provider IDs for the consecutive calls to `search` + * + * @return string[] + */ + public function getProviders(): array { + $this->loadLazyProviders(); + + /** + * Return an array with the IDs, but strip the associative keys + */ + return array_values( + array_map(function (IProvider $provider) { + return $provider->getId(); + }, $this->providers)); + } + + /** + * Query an individual search provider for results + * + * @param IUser $user + * @param string $providerId one of the IDs received by `getProviders` + * @param ISearchQuery $query + * + * @return SearchResult + * @throws InvalidArgumentException when the $providerId does not correspond to a registered provider + */ + public function search(IUser $user, + string $providerId, + ISearchQuery $query): SearchResult { + $this->loadLazyProviders(); + + $provider = $this->providers[$providerId] ?? null; + if ($provider === null) { + throw new InvalidArgumentException("Provider $providerId is unknown"); + } + return $provider->search($user, $query); + } +} diff --git a/lib/private/Search/SearchQuery.php b/lib/private/Search/SearchQuery.php new file mode 100644 index 00000000000..2ed31fed441 --- /dev/null +++ b/lib/private/Search/SearchQuery.php @@ -0,0 +1,88 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +namespace OC\Search; + +use OCP\Search\ISearchQuery; + +class SearchQuery implements ISearchQuery { + public const LIMIT_DEFAULT = 20; + + /** @var string */ + private $term; + + /** @var int */ + private $sortOrder; + + /** @var int */ + private $limit; + + /** @var int|string|null */ + private $cursor; + + /** + * @param string $term + * @param int $sortOrder + * @param int $limit + * @param int|string|null $cursor + */ + public function __construct(string $term, + int $sortOrder = ISearchQuery::SORT_DATE_DESC, + int $limit = self::LIMIT_DEFAULT, + $cursor = null) { + $this->term = $term; + $this->sortOrder = $sortOrder; + $this->limit = $limit; + $this->cursor = $cursor; + } + + /** + * @inheritDoc + */ + public function getTerm(): string { + return $this->term; + } + + /** + * @inheritDoc + */ + public function getSortOrder(): int { + return $this->sortOrder; + } + + /** + * @inheritDoc + */ + public function getLimit(): int { + return $this->limit; + } + + /** + * @inheritDoc + */ + public function getCursor() { + return $this->cursor; + } +} |