diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index 709a4cd68ed8f..396901d0c7657 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -84,6 +84,7 @@ class FilesPlugin extends ServerPlugin { public const SHARE_NOTE = '{http://nextcloud.org/ns}note'; public const SUBFOLDER_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-folder-count'; public const SUBFILE_COUNT_PROPERTYNAME = '{http://nextcloud.org/ns}contained-file-count'; + public const FILE_METADATA_PREFIX = '{http://nextcloud.org/ns}metadata-'; public const FILE_METADATA_SIZE = '{http://nextcloud.org/ns}file-metadata-size'; public const FILE_METADATA_GPS = '{http://nextcloud.org/ns}file-metadata-gps'; diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 524f90e6623d1..07745677bdfd8 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -42,6 +42,8 @@ use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; +use OCP\FilesMetadata\IFilesMetadataManager; +use OCP\FilesMetadata\Model\IMetadataValueWrapper; use OCP\IUser; use OCP\Share\IManager; use Sabre\DAV\Exception\NotFound; @@ -57,37 +59,14 @@ class FileSearchBackend implements ISearchBackend { public const OPERATOR_LIMIT = 100; - /** @var CachingTree */ - private $tree; - - /** @var IUser */ - private $user; - - /** @var IRootFolder */ - private $rootFolder; - - /** @var IManager */ - private $shareManager; - - /** @var View */ - private $view; - - /** - * FileSearchBackend constructor. - * - * @param CachingTree $tree - * @param IUser $user - * @param IRootFolder $rootFolder - * @param IManager $shareManager - * @param View $view - * @internal param IRootFolder $rootFolder - */ - public function __construct(CachingTree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) { - $this->tree = $tree; - $this->user = $user; - $this->rootFolder = $rootFolder; - $this->shareManager = $shareManager; - $this->view = $view; + public function __construct( + private CachingTree $tree, + private IUser $user, + private IRootFolder $rootFolder, + private IManager $shareManager, + private View $view, + private IFilesMetadataManager $filesMetadataManager, + ) { } /** @@ -115,7 +94,7 @@ public function getPropertyDefinitionsForScope(string $href, ?string $path): arr // all valid scopes support the same schema //todo dynamically load all propfind properties that are supported - return [ + $props = [ // queryable properties new SearchPropertyDefinition('{DAV:}displayname', true, true, true), new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), @@ -137,6 +116,33 @@ public function getPropertyDefinitionsForScope(string $href, ?string $path): arr new SearchPropertyDefinition(FilesPlugin::FILE_METADATA_SIZE, true, false, false, SearchPropertyDefinition::DATATYPE_STRING), new SearchPropertyDefinition(FilesPlugin::FILEID_PROPERTYNAME, true, false, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), ]; + + return array_merge($props, $this->getPropertyDefinitionsForMetadata()); + } + + + private function getPropertyDefinitionsForMetadata(): array { + $metadataProps = []; + $metadata = $this->filesMetadataManager->getAllMetadata(); + $indexes = $metadata->getIndexes(); + foreach ($metadata->getKeys() as $key) { + $isIndex = in_array($key, $indexes); + $type = match ($metadata->getType($key)) { + IMetadataValueWrapper::TYPE_INT => SearchPropertyDefinition::DATATYPE_INTEGER, + IMetadataValueWrapper::TYPE_FLOAT => SearchPropertyDefinition::DATATYPE_DECIMAL, + IMetadataValueWrapper::TYPE_BOOL => SearchPropertyDefinition::DATATYPE_BOOLEAN, + default => SearchPropertyDefinition::DATATYPE_STRING + }; + $metadataProps[] = new SearchPropertyDefinition( + FilesPlugin::FILE_METADATA_PREFIX . $key, + true, + $isIndex, + $isIndex, + $type + ); + } + + return $metadataProps; } /** @@ -300,11 +306,20 @@ private function getHrefForNode(Node $node) { /** * @param Query $query + * * @return ISearchQuery */ private function transformQuery(Query $query): 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)) { + return new SearchOrder($direction, substr($order->property->name, strlen(FilesPlugin::FILE_METADATA_PREFIX)), 'metadata'); + } else { + return new SearchOrder($direction, $this->mapPropertyNameToColumn($order->property)); + } + }, $query->orderBy); + $limit = $query->limit; - $orders = array_map([$this, 'mapSearchOrder'], $query->orderBy); $offset = $limit->firstResult; $limitHome = false; @@ -352,14 +367,6 @@ private function countSearchOperators(Operator $operator): int { } } - /** - * @param Order $order - * @return ISearchOrder - */ - private function mapSearchOrder(Order $order) { - return new SearchOrder($order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING, $this->mapPropertyNameToColumn($order->property)); - } - /** * @param Operator $operator * @return ISearchOperator @@ -387,7 +394,17 @@ private function transformSearchOperation(Operator $operator) { if (!($operator->arguments[1] instanceof Literal)) { throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal'); } - return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($operator->arguments[0]), $this->castValue($operator->arguments[0], $operator->arguments[1]->value)); + + $property = $operator->arguments[0]; + $value = $this->castValue($property, $operator->arguments[1]->value); + if (str_starts_with($property->name, FilesPlugin::FILE_METADATA_PREFIX)) { +// return new SearchComparison($trimmedType, substr($order->property->name, strlen(FilesPlugin::FILE_METADATA_PREFIX)), $value, 'metadata'); + return new SearchComparison($trimmedType, 'photo-taken', $value, 'metadata'); + } else { + return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($property), $value); + } + + // no break case Operator::OPERATION_IS_COLLECTION: return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE); default: diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php index 603e015fca952..ff3499a0806ce 100644 --- a/apps/dav/lib/Server.php +++ b/apps/dav/lib/Server.php @@ -76,6 +76,7 @@ use OCP\AppFramework\Http\Response; use OCP\Diagnostics\IEventLogger; use OCP\EventDispatcher\IEventDispatcher; +use OCP\FilesMetadata\IFilesMetadataManager; use OCP\ICacheFactory; use OCP\IRequest; use OCP\Profiler\IProfiler; @@ -316,7 +317,8 @@ public function __construct(IRequest $request, string $baseUri) { $user, \OC::$server->getRootFolder(), \OC::$server->getShareManager(), - $view + $view, + \OCP\Server::get(IFilesMetadataManager::class) )); $this->server->addPlugin( new BulkUploadPlugin( diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index 5d0422d6efdd3..2f5bdb661e369 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -140,23 +140,14 @@ protected function equipQueryForMetadata(CacheQueryBuilder $query, ISearchQuery $metadataQuery = $this->filesMetadataManager->getMetadataQuery($query, 'file', 'fileid'); $metadataQuery->retrieveMetadata(); - $order = $searchQuery->getOrder(); - if ($order) { - foreach ($order as $orderField) { - $field = $orderField->getField(); - if (!str_starts_with($field, 'metakey_')) { - continue; - } - $metadataQuery->joinIndex(substr($field, 8)); - $query->orderBy($metadataQuery->getMetadataValueIntField(), $orderField->getDirection()); + foreach ($searchQuery->getOrder() as $order) { + if ($order->getExtra() !== 'metadata') { + continue; // only metadata search order are managed here. } - } - // TODO: add filter on metadatakey / metadatavalue - // is it possible to get information from the webdav request ? - // $expr = $query->expr(); - // $query->andWhere($expr->eq($metadataQuery->getMetadataKeyField(), $query->createNamedParameter('my_key'))); - // $query->andWhere($expr->eq($metadataQuery->getMetadataValueField(), $query->createNamedParameter('my_value'))); + $alias = $metadataQuery->joinIndex($order->getField()); + $query->addOrderBy($metadataQuery->getMetadataValueIntField($alias), $order->getDirection()); + } return $metadataQuery; } diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php index b9a70bbd39b2c..f7f2645ce266f 100644 --- a/lib/private/Files/Cache/SearchBuilder.php +++ b/lib/private/Files/Cache/SearchBuilder.php @@ -76,7 +76,7 @@ public function extractRequestedFields(ISearchOperator $operator): array { return array_reduce($operator->getArguments(), function (array $fields, ISearchOperator $operator) { return array_unique(array_merge($fields, $this->extractRequestedFields($operator))); }, []); - } elseif ($operator instanceof ISearchComparison) { + } elseif ($operator instanceof ISearchComparison && !$operator->isExtra()) { return [$operator->getField()]; } return []; @@ -124,6 +124,10 @@ public function searchOperatorToDBExpr(IQueryBuilder $builder, ISearchOperator $ } private function searchComparisonToDBExpr(IQueryBuilder $builder, ISearchComparison $comparison, array $operatorMap) { + if ($comparison->isExtra()) { + return null; + } + $this->validateComparison($comparison); [$field, $value, $type] = $this->getOperatorFieldAndValue($comparison); @@ -231,6 +235,9 @@ private function getParameterForValue(IQueryBuilder $builder, $value) { */ public function addSearchOrdersToQuery(IQueryBuilder $query, array $orders) { foreach ($orders as $order) { + if ($order->isExtra()) { + continue; // extra search orders are managed elsewhere + } $field = $order->getField(); if ($field === 'fileid') { $field = 'file.fileid'; diff --git a/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php b/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php index 0caa9b12a02c2..6f3665ec26d7d 100644 --- a/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php +++ b/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php @@ -48,7 +48,7 @@ public function inspectOperator(ISearchOperator $operator): void { } public function processOperator(ISearchOperator &$operator) { - if (!$this->useHashEq && $operator instanceof ISearchComparison && $operator->getField() === 'path' && $operator->getType() === ISearchComparison::COMPARE_EQUAL) { + if (!$this->useHashEq && $operator instanceof ISearchComparison && !$operator->isExtra() && $operator->getField() === 'path' && $operator->getType() === ISearchComparison::COMPARE_EQUAL) { $operator->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, false); } @@ -69,7 +69,7 @@ private function isPathPrefixOperator(ISearchOperator $operator): bool { private function operatorPairIsPathPrefix(ISearchOperator $like, ISearchOperator $equal): bool { return ( $like instanceof ISearchComparison && $equal instanceof ISearchComparison && - $like->getField() === 'path' && $equal->getField() === 'path' && + !$like->isExtra() && !$equal->isExtra() && $like->getField() === 'path' && $equal->getField() === 'path' && $like->getType() === ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE && $equal->getType() === ISearchComparison::COMPARE_EQUAL && $like->getValue() === SearchComparison::escapeLikeParameter($equal->getValue()) . '/%' ); diff --git a/lib/private/Files/Search/SearchComparison.php b/lib/private/Files/Search/SearchComparison.php index 122a1f730b403..b234e5e8e7efa 100644 --- a/lib/private/Files/Search/SearchComparison.php +++ b/lib/private/Files/Search/SearchComparison.php @@ -25,25 +25,15 @@ use OCP\Files\Search\ISearchComparison; class SearchComparison implements ISearchComparison { - /** @var string */ - private $type; - /** @var string */ - private $field; - /** @var string|integer|\DateTime */ - private $value; - private $hints = []; + private array $hints = []; - /** - * SearchComparison constructor. - * - * @param string $type - * @param string $field - * @param \DateTime|int|string $value - */ - public function __construct($type, $field, $value) { - $this->type = $type; - $this->field = $field; - $this->value = $value; + + public function __construct( + private string $type, + private string $field, + private \DateTime|int|string $value, + private string $extra = '' + ) { } /** @@ -67,6 +57,14 @@ public function getValue() { return $this->value; } + public function getExtra(): string { + return $this->extra; + } + + public function isExtra(): bool { + return ($this->extra !== ''); + } + public function getQueryHint(string $name, $default) { return $this->hints[$name] ?? $default; } diff --git a/lib/private/Files/Search/SearchOrder.php b/lib/private/Files/Search/SearchOrder.php index 1395a87ac7219..353c75cfb2b6c 100644 --- a/lib/private/Files/Search/SearchOrder.php +++ b/lib/private/Files/Search/SearchOrder.php @@ -2,6 +2,7 @@ /** * @copyright Copyright (c) 2017 Robin Appelman * + * @author Maxence Lange * @author Robin Appelman * * @license GNU AGPL version 3 or any later version @@ -26,36 +27,36 @@ use OCP\Files\Search\ISearchOrder; class SearchOrder implements ISearchOrder { - /** @var string */ - private $direction; - /** @var string */ - private $field; - /** - * SearchOrder constructor. - * - * @param string $direction - * @param string $field - */ - public function __construct($direction, $field) { - $this->direction = $direction; - $this->field = $field; + public function __construct( + private string $direction, + private string $field, + private string $extra = '' + ) { } /** * @return string */ - public function getDirection() { + public function getDirection(): string { return $this->direction; } /** * @return string */ - public function getField() { + public function getField(): string { return $this->field; } + public function getExtra(): string { + return $this->extra; + } + + public function isExtra(): bool { + return ($this->extra !== ''); + } + public function sortFileInfo(FileInfo $a, FileInfo $b): int { $cmp = $this->sortFileInfoNoDirection($a, $b); return $cmp * ($this->direction === ISearchOrder::DIRECTION_ASCENDING ? 1 : -1); diff --git a/lib/private/FilesMetadata/FilesMetadataManager.php b/lib/private/FilesMetadata/FilesMetadataManager.php index 7b3a5681110a3..4b9f01fc11483 100644 --- a/lib/private/FilesMetadata/FilesMetadataManager.php +++ b/lib/private/FilesMetadata/FilesMetadataManager.php @@ -25,6 +25,7 @@ namespace OC\FilesMetadata; +use JsonException; use OC\FilesMetadata\Job\UpdateSingleMetadata; use OC\FilesMetadata\Listener\MetadataDelete; use OC\FilesMetadata\Listener\MetadataUpdate; @@ -50,6 +51,7 @@ use OCP\FilesMetadata\IFilesMetadataManager; use OCP\FilesMetadata\Model\IFilesMetadata; use OCP\FilesMetadata\Model\IMetadataQuery; +use OCP\IConfig; use Psr\Log\LoggerInterface; /** @@ -57,11 +59,13 @@ * @since 28.0.0 */ class FilesMetadataManager implements IFilesMetadataManager { + public const CONFIG_KEY = 'files_metadata'; private const JSON_MAXSIZE = 100000; public function __construct( private IEventDispatcher $eventDispatcher, private IJobList $jobList, + private IConfig $config, private LoggerInterface $logger, private MetadataRequestService $metadataRequestService, private IndexRequestService $indexRequestService, @@ -141,9 +145,7 @@ public function saveMetadata(IFilesMetadata $filesMetadata): void { $json = json_encode($filesMetadata->jsonSerialize()); if (strlen($json) > self::JSON_MAXSIZE) { - throw new FilesMetadataException( - 'json cannot exceed ' . self::JSON_MAXSIZE . ' characters long' - ); + throw new FilesMetadataException('json cannot exceed ' . self::JSON_MAXSIZE . ' characters long'); } try { @@ -155,13 +157,12 @@ public function saveMetadata(IFilesMetadata $filesMetadata): void { } catch (DBException $e) { // most of the logged exception are the result of race condition // between 2 simultaneous process trying to create/update metadata - $this->logger->warning( - 'issue while saveMetadata', ['exception' => $e, 'metadata' => $filesMetadata] - ); + $this->logger->warning('issue while saveMetadata', ['exception' => $e, 'metadata' => $filesMetadata]); return; } + // update indexes foreach ($filesMetadata->getIndexes() as $index) { try { $this->indexRequestService->updateIndex($filesMetadata, $index); @@ -169,6 +170,11 @@ public function saveMetadata(IFilesMetadata $filesMetadata): void { $this->logger->warning('issue while updateIndex', ['exception' => $e]); } } + + // update metadata list + $current = $this->getAllMetadata(); + $current->import($filesMetadata->jsonSerialize()); + $this->config->setAppValue('core', self::CONFIG_KEY, json_encode($current)); } /** @@ -197,8 +203,8 @@ public function deleteMetadata(int $fileId): void { * @param string $fileIdField alias of the field that contains file ids * * @inheritDoc - * @see IMetadataQuery * @return IMetadataQuery + * @see IMetadataQuery * @since 28.0.0 */ public function getMetadataQuery( @@ -209,6 +215,25 @@ public function getMetadataQuery( return new MetadataQuery($qb, $fileTableAlias, $fileIdField); } + /** + * @inheritDoc + * @return IFilesMetadata + * @since 28.0.0 + */ + public function getAllMetadata(): IFilesMetadata { + $all = new FilesMetadata(); + + try { + $data = json_decode($this->config->getAppValue('core', self::CONFIG_KEY, '[]'), true, 127, JSON_THROW_ON_ERROR); + $all->import($data); + } catch (JsonException) { + $this->logger->warning('issue while reading stored list of metadata. Adviced to run ./occ files:scan --all --generate-metadata'); + } + + return $all; + } + + /** * load listeners * diff --git a/lib/private/FilesMetadata/Model/MetadataQuery.php b/lib/private/FilesMetadata/Model/MetadataQuery.php index 11904b8329993..100a8764b4fd1 100644 --- a/lib/private/FilesMetadata/Model/MetadataQuery.php +++ b/lib/private/FilesMetadata/Model/MetadataQuery.php @@ -37,12 +37,13 @@ * @since 28.0.0 */ class MetadataQuery implements IMetadataQuery { + private int $aliasIndexCount = 0; public function __construct( private IQueryBuilder $queryBuilder, private string $fileTableAlias = 'fc', private string $fileIdField = 'fileid', private string $alias = 'meta', - private string $aliasIndex = 'meta_index' + private string $aliasIndexPrefix = 'meta_index' ) { } @@ -86,26 +87,29 @@ public function extractMetadata(array $row): IFilesMetadata { * @inheritDoc * @since 28.0.0 */ - public function joinIndex(string $metadataKey, bool $enforce = false): void { + public function joinIndex(string $metadataKey, bool $enforce = false): string { $expr = $this->queryBuilder->expr(); - $andX = $expr->andX($expr->eq($this->aliasIndex . '.file_id', $this->fileTableAlias . '.' . $this->fileIdField)); - $andX->add($expr->eq($this->getMetadataKeyField(), $this->queryBuilder->createNamedParameter($metadataKey))); + $aliasIndex = $this->aliasIndexPrefix . '_' . $this->aliasIndexCount++; + $andX = $expr->andX($expr->eq($aliasIndex . '.file_id', $this->fileTableAlias . '.' . $this->fileIdField)); + $andX->add($expr->eq($this->getMetadataKeyField($aliasIndex), $this->queryBuilder->createNamedParameter($metadataKey))); if ($enforce) { $this->queryBuilder->rightJoin( $this->fileTableAlias, IndexRequestService::TABLE_METADATA_INDEX, - $this->aliasIndex, + $aliasIndex, $andX ); } else { $this->queryBuilder->leftJoin( $this->fileTableAlias, IndexRequestService::TABLE_METADATA_INDEX, - $this->aliasIndex, + $aliasIndex, $andX ); } + + return $aliasIndex; } /** @@ -113,11 +117,11 @@ public function joinIndex(string $metadataKey, bool $enforce = false): void { * @inheritDoc * @since 28.0.0 */ - public function enforceMetadataValue(string $value): void { + public function enforceMetadataValue(string $aliasIndex, string $value): void { $expr = $this->queryBuilder->expr(); $this->queryBuilder->andWhere( $expr->eq( - $this->getMetadataValueField(), + $this->getMetadataValueField($aliasIndex), $this->queryBuilder->createNamedParameter($value) ) ); @@ -128,11 +132,11 @@ public function enforceMetadataValue(string $value): void { * @inheritDoc * @since 28.0.0 */ - public function enforceMetadataValueInt(int $value): void { + public function enforceMetadataValueInt(string $aliasIndex, int $value): void { $expr = $this->queryBuilder->expr(); $this->queryBuilder->andWhere( $expr->eq( - $this->getMetadataValueIntField(), + $this->getMetadataValueIntField($aliasIndex), $this->queryBuilder->createNamedParameter($value, IQueryBuilder::PARAM_INT) ) ); @@ -143,8 +147,8 @@ public function enforceMetadataValueInt(int $value): void { * @return string table field * @since 28.0.0 */ - public function getMetadataKeyField(): string { - return $this->aliasIndex . '.meta_key'; + public function getMetadataKeyField(string $aliasIndex): string { + return $aliasIndex . '.meta_key'; } /** @@ -152,8 +156,8 @@ public function getMetadataKeyField(): string { * @return string table field * @since 28.0.0 */ - public function getMetadataValueField(): string { - return $this->aliasIndex . '.meta_value_string'; + public function getMetadataValueField(string $aliasIndex): string { + return $aliasIndex . '.meta_value_string'; } /** @@ -161,7 +165,7 @@ public function getMetadataValueField(): string { * @return string table field * @since 28.0.0 */ - public function getMetadataValueIntField(): string { - return $this->aliasIndex . '.meta_value_int'; + public function getMetadataValueIntField(string $aliasIndex): string { + return $aliasIndex . '.meta_value_int'; } } diff --git a/lib/private/FilesMetadata/Model/MetadataValueWrapper.php b/lib/private/FilesMetadata/Model/MetadataValueWrapper.php index d3c4cd321413e..b26e2931be848 100644 --- a/lib/private/FilesMetadata/Model/MetadataValueWrapper.php +++ b/lib/private/FilesMetadata/Model/MetadataValueWrapper.php @@ -141,7 +141,6 @@ public function setValueInt(int $value): self { $this->assertType(self::TYPE_INT); $this->value = $value; - return $this; } @@ -157,7 +156,6 @@ public function setValueFloat(float $value): self { $this->assertType(self::TYPE_FLOAT); $this->value = $value; - return $this; } @@ -189,7 +187,6 @@ public function setValueArray(array $value): self { $this->assertType(self::TYPE_ARRAY); $this->value = $value; - return $this; } diff --git a/lib/public/Files/Search/ISearchComparison.php b/lib/public/Files/Search/ISearchComparison.php index 8ebaeced304a1..ac7bfe2aa7e10 100644 --- a/lib/public/Files/Search/ISearchComparison.php +++ b/lib/public/Files/Search/ISearchComparison.php @@ -55,6 +55,23 @@ public function getType(); */ public function getField(); + + /** + * extra means data are not related to the main files table + * + * @return string + * @since 28.0.0 + */ + public function getExtra(): string; + + /** + * returns if data are 'extra' or not + * + * @return bool + * @since 28.0.0 + */ + public function isExtra(): bool; + /** * Get the value to compare the field with * diff --git a/lib/public/Files/Search/ISearchOrder.php b/lib/public/Files/Search/ISearchOrder.php index 3b9e6e6713af4..bcb927e73db7c 100644 --- a/lib/public/Files/Search/ISearchOrder.php +++ b/lib/public/Files/Search/ISearchOrder.php @@ -3,6 +3,7 @@ * @copyright Copyright (c) 2017 Robin Appelman * * @author Christoph Wurst + * @author Maxence Lange * @author Robin Appelman * * @license GNU AGPL version 3 or any later version @@ -38,7 +39,7 @@ interface ISearchOrder { * @return string * @since 12.0.0 */ - public function getDirection(); + public function getDirection(): string; /** * The field to sort on @@ -46,7 +47,24 @@ public function getDirection(); * @return string * @since 12.0.0 */ - public function getField(); + public function getField(): string; + + /** + * extra means data are not related to the main files table + * + * @return string + * @since 28.0.0 + */ + public function getExtra(): string; + + /** + * returns if data are 'extra' or not + * + * @return bool + * @since 28.0.0 + */ + public function isExtra(): bool; + /** * Apply the sorting on 2 FileInfo objects diff --git a/lib/public/FilesMetadata/IFilesMetadataManager.php b/lib/public/FilesMetadata/IFilesMetadataManager.php index a03ab868fdeca..965ade739795f 100644 --- a/lib/public/FilesMetadata/IFilesMetadataManager.php +++ b/lib/public/FilesMetadata/IFilesMetadataManager.php @@ -114,4 +114,10 @@ public function getMetadataQuery( string $fileTableAlias, string $fileIdField ): IMetadataQuery; + + /** + * @return IFilesMetadata + * @since 28.0.0 + */ + public function getAllMetadata(): IFilesMetadata; } diff --git a/lib/public/FilesMetadata/Model/IMetadataQuery.php b/lib/public/FilesMetadata/Model/IMetadataQuery.php index dc58ae359b59d..181aa6b6530e2 100644 --- a/lib/public/FilesMetadata/Model/IMetadataQuery.php +++ b/lib/public/FilesMetadata/Model/IMetadataQuery.php @@ -57,49 +57,58 @@ public function extractMetadata(array $row): IFilesMetadata; * @param string $metadataKey metadata key * @param bool $enforce limit the request only to existing metadata * + * @return string generated table alias * @since 28.0.0 */ - public function joinIndex(string $metadataKey, bool $enforce = false): void; + public function joinIndex(string $metadataKey, bool $enforce = false): string; /** * entry must have a specific value (string) for linked metadata * + * @param string $aliasIndex table alias related to metadata key * @param string $value metadata value * * @since 28.0.0 */ - public function enforceMetadataValue(string $value): void; + public function enforceMetadataValue(string $aliasIndex, string $value): void; /** * entry must have a specific value (int) for linked metadata * + * @param string $aliasIndex table alias related to metadata key * @param int $value metadata value * * @since 28.0.0 */ - public function enforceMetadataValueInt(int $value): void; + public function enforceMetadataValueInt(string $aliasIndex, int $value): void; /** * returns the name of the field for metadata key to be used in query expressions * + * @param string $aliasIndex table alias related to metadata key + * * @return string * @since 28.0.0 */ - public function getMetadataKeyField(): string; + public function getMetadataKeyField(string $aliasIndex): string; /** * returns the name of the field for metadata string value to be used in query expressions * + * @param string $aliasIndex table alias related to metadata key + * * @return string table field * @since 28.0.0 */ - public function getMetadataValueField(): string; + public function getMetadataValueField(string $aliasIndex): string; /** * returns the name of the field for metadata int value to be used in query expressions * + * @param string $aliasIndex table alias related to metadata key + * * @return string table field * @since 28.0.0 */ - public function getMetadataValueIntField(): string; + public function getMetadataValueIntField(string $aliasIndex): string; }