aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/FilesMetadata/MetadataQuery.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/FilesMetadata/MetadataQuery.php')
-rw-r--r--lib/private/FilesMetadata/MetadataQuery.php176
1 files changed, 176 insertions, 0 deletions
diff --git a/lib/private/FilesMetadata/MetadataQuery.php b/lib/private/FilesMetadata/MetadataQuery.php
new file mode 100644
index 00000000000..deae433e2fc
--- /dev/null
+++ b/lib/private/FilesMetadata/MetadataQuery.php
@@ -0,0 +1,176 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\FilesMetadata;
+
+use OC\FilesMetadata\Model\FilesMetadata;
+use OC\FilesMetadata\Service\IndexRequestService;
+use OC\FilesMetadata\Service\MetadataRequestService;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
+use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException;
+use OCP\FilesMetadata\IFilesMetadataManager;
+use OCP\FilesMetadata\IMetadataQuery;
+use OCP\FilesMetadata\Model\IFilesMetadata;
+use OCP\FilesMetadata\Model\IMetadataValueWrapper;
+use Psr\Log\LoggerInterface;
+
+/**
+ * @inheritDoc
+ * @since 28.0.0
+ */
+class MetadataQuery implements IMetadataQuery {
+ private array $knownJoinedIndex = [];
+ public function __construct(
+ private IQueryBuilder $queryBuilder,
+ private IFilesMetadata|IFilesMetadataManager $manager,
+ private string $fileTableAlias = 'fc',
+ private string $fileIdField = 'fileid',
+ private string $alias = 'meta',
+ private string $aliasIndexPrefix = 'meta_index',
+ ) {
+ if ($manager instanceof IFilesMetadata) {
+ /**
+ * Since 29, because knownMetadata is stored in lazy appconfig, it seems smarter
+ * to not call getKnownMetadata() at the load of this class as it is only needed
+ * in {@see getMetadataValueField}.
+ *
+ * FIXME: remove support for IFilesMetadata
+ */
+ $logger = \OCP\Server::get(LoggerInterface::class);
+ $logger->debug('It is deprecated to use IFilesMetadata as second parameter when calling MetadataQuery::__construct()');
+ }
+ }
+
+ /**
+ * @inheritDoc
+ * @see self::extractMetadata()
+ * @since 28.0.0
+ */
+ public function retrieveMetadata(): void {
+ $this->queryBuilder->selectAlias($this->alias . '.json', 'meta_json');
+ $this->queryBuilder->selectAlias($this->alias . '.sync_token', 'meta_sync_token');
+ $this->queryBuilder->leftJoin(
+ $this->fileTableAlias, MetadataRequestService::TABLE_METADATA, $this->alias,
+ $this->queryBuilder->expr()->eq($this->fileTableAlias . '.' . $this->fileIdField, $this->alias . '.file_id')
+ );
+ }
+
+ /**
+ * @param array $row result row
+ *
+ * @inheritDoc
+ * @return IFilesMetadata metadata
+ * @see self::retrieveMetadata()
+ * @since 28.0.0
+ */
+ public function extractMetadata(array $row): IFilesMetadata {
+ $fileId = (array_key_exists($this->fileIdField, $row)) ? $row[$this->fileIdField] : 0;
+ $metadata = new FilesMetadata((int)$fileId);
+ try {
+ $metadata->importFromDatabase($row, $this->alias . '_');
+ } catch (FilesMetadataNotFoundException) {
+ // can be ignored as files' metadata are optional and might not exist in database
+ }
+
+ return $metadata;
+ }
+
+ /**
+ * @param string $metadataKey metadata key
+ * @param bool $enforce limit the request only to existing metadata
+ *
+ * @inheritDoc
+ * @since 28.0.0
+ */
+ public function joinIndex(string $metadataKey, bool $enforce = false): string {
+ if (array_key_exists($metadataKey, $this->knownJoinedIndex)) {
+ return $this->knownJoinedIndex[$metadataKey];
+ }
+
+ $aliasIndex = $this->aliasIndexPrefix . '_' . count($this->knownJoinedIndex);
+ $this->knownJoinedIndex[$metadataKey] = $aliasIndex;
+
+ $expr = $this->queryBuilder->expr();
+ $andX = $expr->andX($expr->eq($aliasIndex . '.file_id', $this->fileTableAlias . '.' . $this->fileIdField));
+ $andX->add($expr->eq($this->getMetadataKeyField($metadataKey), $this->queryBuilder->createNamedParameter($metadataKey)));
+
+ if ($enforce) {
+ $this->queryBuilder->innerJoin(
+ $this->fileTableAlias,
+ IndexRequestService::TABLE_METADATA_INDEX,
+ $aliasIndex,
+ $andX
+ );
+ } else {
+ $this->queryBuilder->leftJoin(
+ $this->fileTableAlias,
+ IndexRequestService::TABLE_METADATA_INDEX,
+ $aliasIndex,
+ $andX
+ );
+ }
+
+ return $aliasIndex;
+ }
+
+ /**
+ * @throws FilesMetadataNotFoundException
+ */
+ private function joinedTableAlias(string $metadataKey): string {
+ if (!array_key_exists($metadataKey, $this->knownJoinedIndex)) {
+ throw new FilesMetadataNotFoundException('table related to ' . $metadataKey . ' not initiated, you need to use leftJoin() first.');
+ }
+
+ return $this->knownJoinedIndex[$metadataKey];
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $metadataKey metadata key
+ *
+ * @return string table field
+ * @throws FilesMetadataNotFoundException
+ * @since 28.0.0
+ */
+ public function getMetadataKeyField(string $metadataKey): string {
+ return $this->joinedTableAlias($metadataKey) . '.meta_key';
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $metadataKey metadata key
+ *
+ * @return string table field
+ * @throws FilesMetadataNotFoundException if metadataKey is not known
+ * @throws FilesMetadataTypeException is metadataKey is not set as indexed
+ * @since 28.0.0
+ */
+ public function getMetadataValueField(string $metadataKey): string {
+ if ($this->manager instanceof IFilesMetadataManager) {
+ /**
+ * Since 29, because knownMetadata is stored in lazy appconfig, it seems smarter
+ * to not call getKnownMetadata() at the load of this class as it is only needed
+ * in this method.
+ *
+ * FIXME: keep only this line and remove support for previous IFilesMetadata in constructor
+ */
+ $knownMetadata = $this->manager->getKnownMetadata();
+ } else {
+ $knownMetadata = $this->manager;
+ }
+
+ return match ($knownMetadata->getType($metadataKey)) {
+ IMetadataValueWrapper::TYPE_STRING => $this->joinedTableAlias($metadataKey) . '.meta_value_string',
+ IMetadataValueWrapper::TYPE_INT, IMetadataValueWrapper::TYPE_BOOL => $this->joinedTableAlias($metadataKey) . '.meta_value_int',
+ default => throw new FilesMetadataTypeException('metadata is not set as indexed'),
+ };
+ }
+}