Skip to content
Snippets Groups Projects
Unverified Commit f8e08a74 authored by Christoph Wurst's avatar Christoph Wurst
Browse files

Implement unified search for Files

parent 4488e846
No related branches found
No related tags found
No related merge requests found
...@@ -47,6 +47,8 @@ return array( ...@@ -47,6 +47,8 @@ return array(
'OCA\\Files\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php', 'OCA\\Files\\Listener\\LoadSidebarListener' => $baseDir . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php', 'OCA\\Files\\Migration\\Version11301Date20191205150729' => $baseDir . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php', 'OCA\\Files\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Search\\FilesSearchProvider' => $baseDir . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Search\\FilesSearchResultEntry' => $baseDir . '/../lib/Search/FilesSearchResultEntry.php',
'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php', 'OCA\\Files\\Service\\DirectEditingService' => $baseDir . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php', 'OCA\\Files\\Service\\OwnershipTransferService' => $baseDir . '/../lib/Service/OwnershipTransferService.php',
'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php', 'OCA\\Files\\Service\\TagService' => $baseDir . '/../lib/Service/TagService.php',
......
...@@ -62,6 +62,8 @@ class ComposerStaticInitFiles ...@@ -62,6 +62,8 @@ class ComposerStaticInitFiles
'OCA\\Files\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php', 'OCA\\Files\\Listener\\LoadSidebarListener' => __DIR__ . '/..' . '/../lib/Listener/LoadSidebarListener.php',
'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php', 'OCA\\Files\\Migration\\Version11301Date20191205150729' => __DIR__ . '/..' . '/../lib/Migration/Version11301Date20191205150729.php',
'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php', 'OCA\\Files\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
'OCA\\Files\\Search\\FilesSearchProvider' => __DIR__ . '/..' . '/../lib/Search/FilesSearchProvider.php',
'OCA\\Files\\Search\\FilesSearchResultEntry' => __DIR__ . '/..' . '/../lib/Search/FilesSearchResultEntry.php',
'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php', 'OCA\\Files\\Service\\DirectEditingService' => __DIR__ . '/..' . '/../lib/Service/DirectEditingService.php',
'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php', 'OCA\\Files\\Service\\OwnershipTransferService' => __DIR__ . '/..' . '/../lib/Service/OwnershipTransferService.php',
'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php', 'OCA\\Files\\Service\\TagService' => __DIR__ . '/..' . '/../lib/Service/TagService.php',
......
...@@ -44,6 +44,7 @@ use OCA\Files\Event\LoadSidebar; ...@@ -44,6 +44,7 @@ use OCA\Files\Event\LoadSidebar;
use OCA\Files\Listener\LegacyLoadAdditionalScriptsAdapter; use OCA\Files\Listener\LegacyLoadAdditionalScriptsAdapter;
use OCA\Files\Listener\LoadSidebarListener; use OCA\Files\Listener\LoadSidebarListener;
use OCA\Files\Notification\Notifier; use OCA\Files\Notification\Notifier;
use OCA\Files\Search\FilesSearchProvider;
use OCA\Files\Service\TagService; use OCA\Files\Service\TagService;
use OCP\AppFramework\App; use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootContext;
...@@ -106,6 +107,8 @@ class Application extends App implements IBootstrap { ...@@ -106,6 +107,8 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(LoadAdditionalScriptsEvent::class, LegacyLoadAdditionalScriptsAdapter::class); $context->registerEventListener(LoadAdditionalScriptsEvent::class, LegacyLoadAdditionalScriptsAdapter::class);
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class); $context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
$context->registerSearchProvider(FilesSearchProvider::class);
} }
public function boot(IBootContext $context): void { public function boot(IBootContext $context): void {
......
...@@ -25,7 +25,10 @@ declare(strict_types=1); ...@@ -25,7 +25,10 @@ declare(strict_types=1);
namespace OCA\Files\Search; namespace OCA\Files\Search;
use OC\Search\Provider\File;
use OC\Search\Result\File as FileResult;
use OCP\IL10N; use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\IUser; use OCP\IUser;
use OCP\Search\IProvider; use OCP\Search\IProvider;
use OCP\Search\ISearchQuery; use OCP\Search\ISearchQuery;
...@@ -33,11 +36,21 @@ use OCP\Search\SearchResult; ...@@ -33,11 +36,21 @@ use OCP\Search\SearchResult;
class FilesSearchProvider implements IProvider { class FilesSearchProvider implements IProvider {
/** @var File */
private $fileSearch;
/** @var IL10N */ /** @var IL10N */
private $l10n; private $l10n;
public function __construct(IL10N $l10n) { /** @var IURLGenerator */
private $urlGenerator;
public function __construct(File $fileSearch,
IL10N $l10n,
IURLGenerator $urlGenerator) {
$this->l10n = $l10n; $this->l10n = $l10n;
$this->fileSearch = $fileSearch;
$this->urlGenerator = $urlGenerator;
} }
public function getId(): string { public function getId(): string {
...@@ -47,26 +60,14 @@ class FilesSearchProvider implements IProvider { ...@@ -47,26 +60,14 @@ class FilesSearchProvider implements IProvider {
public function search(IUser $user, ISearchQuery $query): SearchResult { public function search(IUser $user, ISearchQuery $query): SearchResult {
return SearchResult::complete( return SearchResult::complete(
$this->l10n->t('Files'), $this->l10n->t('Files'),
[ array_map(function (FileResult $result) {
new FilesSearchResultEntry( return new FilesSearchResultEntry(
"path/to/icon.png", $this->urlGenerator->linkToRoute('core.Preview.getPreviewByFileId', ['x' => 32, 'y' => 32, 'fileId' => $result->id]),
"cute cats.jpg", $result->name,
"/Cats", $result->path,
"/f/21156" $result->link
), );
new FilesSearchResultEntry( }, $this->fileSearch->search($query->getTerm()))
"path/to/icon.png",
"cat 1.jpg",
"/Cats",
"/f/21192"
),
new FilesSearchResultEntry(
"path/to/icon.png",
"cat 2.jpg",
"/Cats",
"/f/25942"
),
]
); );
} }
} }
<?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 OCA\Files\Search;
use OCP\Search\ASearchResultEntry;
class FilesSearchResultEntry extends ASearchResultEntry {
public function __construct(string $thumbnailUrl,
string $filename,
string $path,
string $url) {
parent::__construct($thumbnailUrl, $filename, $path, $url);
}
}
...@@ -25,6 +25,8 @@ declare(strict_types=1); ...@@ -25,6 +25,8 @@ declare(strict_types=1);
namespace OC\Search; namespace OC\Search;
use InvalidArgumentException;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\QueryException; use OCP\AppFramework\QueryException;
use OCP\ILogger; use OCP\ILogger;
use OCP\IServerContainer; use OCP\IServerContainer;
...@@ -37,6 +39,21 @@ use function array_map; ...@@ -37,6 +39,21 @@ use function array_map;
/** /**
* Queries individual \OCP\Search\IProvider implementations and composes a * Queries individual \OCP\Search\IProvider implementations and composes a
* unified search result for the user's search term * 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 { class SearchComposer {
...@@ -58,6 +75,17 @@ class SearchComposer { ...@@ -58,6 +75,17 @@ class SearchComposer {
$this->logger = $logger; $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 { public function registerProvider(string $class): void {
$this->lazyProviders[] = $class; $this->lazyProviders[] = $class;
} }
...@@ -85,6 +113,11 @@ class SearchComposer { ...@@ -85,6 +113,11 @@ class SearchComposer {
$this->lazyProviders = []; $this->lazyProviders = [];
} }
/**
* Get a list of all provider IDs for the consecutive calls to `search`
*
* @return string[]
*/
public function getProviders(): array { public function getProviders(): array {
$this->loadLazyProviders(); $this->loadLazyProviders();
...@@ -97,11 +130,25 @@ class SearchComposer { ...@@ -97,11 +130,25 @@ class SearchComposer {
}, $this->providers)); }, $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, public function search(IUser $user,
string $providerId, string $providerId,
ISearchQuery $query): SearchResult { ISearchQuery $query): SearchResult {
$this->loadLazyProviders(); $this->loadLazyProviders();
return $this->providers[$providerId]->search($user, $query); $provider = $this->providers[$providerId] ?? null;
if ($provider === null) {
throw new InvalidArgumentException("Provider $providerId is unknown");
}
return $provider->search($user, $query);
} }
} }
...@@ -28,7 +28,7 @@ namespace OCP\Search; ...@@ -28,7 +28,7 @@ namespace OCP\Search;
use OCP\IUser; use OCP\IUser;
/** /**
* Interface for an app search providers * Interface for search providers
* *
* These providers will be implemented in apps, so they can participate in the * These providers will be implemented in apps, so they can participate in the
* global search results of Nextcloud. If an app provides more than one type of * global search results of Nextcloud. If an app provides more than one type of
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment