Implement unified search for Files
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
This commit is contained in:
parent
4488e846a5
commit
f8e08a74ba
|
@ -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
|
||||||
'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;
|
||||||
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 {
|
||||||
|
|
||||||
$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);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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 {
|
||||||
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"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?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);
|
||||||
|
|
||||||
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;
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
$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 {
|
||||||
$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 {
|
||||||
}, $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;
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue