IndexRequestService.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright 2023 Maxence Lange <maxence@artificial-owl.com>
  5. *
  6. * @author Maxence Lange <maxence@artificial-owl.com>
  7. *
  8. * @license GNU AGPL version 3 or any later version
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as
  12. * published by the Free Software Foundation, either version 3 of the
  13. * License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. */
  24. namespace OC\FilesMetadata\Service;
  25. use OCP\DB\Exception as DbException;
  26. use OCP\DB\QueryBuilder\IQueryBuilder;
  27. use OCP\FilesMetadata\Exceptions\FilesMetadataNotFoundException;
  28. use OCP\FilesMetadata\Exceptions\FilesMetadataTypeException;
  29. use OCP\FilesMetadata\Model\IFilesMetadata;
  30. use OCP\FilesMetadata\Model\IMetadataValueWrapper;
  31. use OCP\IDBConnection;
  32. use Psr\Log\LoggerInterface;
  33. /**
  34. * manage sql request to the metadata_index table
  35. */
  36. class IndexRequestService {
  37. public const TABLE_METADATA_INDEX = 'files_metadata_index';
  38. public function __construct(
  39. private IDBConnection $dbConnection,
  40. private LoggerInterface $logger
  41. ) {
  42. }
  43. /**
  44. * update the index for a specific metadata key
  45. *
  46. * @param IFilesMetadata $filesMetadata metadata
  47. * @param string $key metadata key to update
  48. *
  49. * @throws DbException
  50. */
  51. public function updateIndex(IFilesMetadata $filesMetadata, string $key): void {
  52. $fileId = $filesMetadata->getFileId();
  53. try {
  54. $metadataType = $filesMetadata->getType($key);
  55. } catch (FilesMetadataNotFoundException $e) {
  56. return;
  57. }
  58. /**
  59. * might look harsh, but a lot simpler than comparing current indexed data, as we can expect
  60. * conflict with a change of types.
  61. * We assume that each time one random metadata were modified we can drop all index for this
  62. * key and recreate them.
  63. * To make it slightly cleaner, we'll use transaction
  64. */
  65. $this->dbConnection->beginTransaction();
  66. try {
  67. $this->dropIndex($fileId, $key);
  68. match ($metadataType) {
  69. IMetadataValueWrapper::TYPE_STRING => $this->insertIndexString($fileId, $key, $filesMetadata->getString($key)),
  70. IMetadataValueWrapper::TYPE_INT => $this->insertIndexInt($fileId, $key, $filesMetadata->getInt($key)),
  71. IMetadataValueWrapper::TYPE_BOOL => $this->insertIndexBool($fileId, $key, $filesMetadata->getBool($key)),
  72. IMetadataValueWrapper::TYPE_STRING_LIST => $this->insertIndexStringList($fileId, $key, $filesMetadata->getStringList($key)),
  73. IMetadataValueWrapper::TYPE_INT_LIST => $this->insertIndexIntList($fileId, $key, $filesMetadata->getIntList($key))
  74. };
  75. } catch (FilesMetadataNotFoundException|FilesMetadataTypeException|DbException $e) {
  76. $this->dbConnection->rollBack();
  77. $this->logger->warning('issue while updateIndex', ['exception' => $e, 'fileId' => $fileId, 'key' => $key]);
  78. }
  79. $this->dbConnection->commit();
  80. }
  81. /**
  82. * insert a new entry in the metadata_index table for a string value
  83. *
  84. * @param int $fileId file id
  85. * @param string $key metadata key
  86. * @param string $value metadata value
  87. *
  88. * @throws DbException
  89. */
  90. private function insertIndexString(int $fileId, string $key, string $value): void {
  91. $qb = $this->dbConnection->getQueryBuilder();
  92. $qb->insert(self::TABLE_METADATA_INDEX)
  93. ->setValue('meta_key', $qb->createNamedParameter($key))
  94. ->setValue('meta_value_string', $qb->createNamedParameter($value))
  95. ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
  96. $qb->executeStatement();
  97. }
  98. /**
  99. * insert a new entry in the metadata_index table for an int value
  100. *
  101. * @param int $fileId file id
  102. * @param string $key metadata key
  103. * @param int $value metadata value
  104. *
  105. * @throws DbException
  106. */
  107. public function insertIndexInt(int $fileId, string $key, int $value): void {
  108. $qb = $this->dbConnection->getQueryBuilder();
  109. $qb->insert(self::TABLE_METADATA_INDEX)
  110. ->setValue('meta_key', $qb->createNamedParameter($key))
  111. ->setValue('meta_value_int', $qb->createNamedParameter($value, IQueryBuilder::PARAM_INT))
  112. ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
  113. $qb->executeStatement();
  114. }
  115. /**
  116. * insert a new entry in the metadata_index table for a bool value
  117. *
  118. * @param int $fileId file id
  119. * @param string $key metadata key
  120. * @param bool $value metadata value
  121. *
  122. * @throws DbException
  123. */
  124. public function insertIndexBool(int $fileId, string $key, bool $value): void {
  125. $qb = $this->dbConnection->getQueryBuilder();
  126. $qb->insert(self::TABLE_METADATA_INDEX)
  127. ->setValue('meta_key', $qb->createNamedParameter($key))
  128. ->setValue('meta_value_int', $qb->createNamedParameter(($value) ? '1' : '0', IQueryBuilder::PARAM_INT))
  129. ->setValue('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT));
  130. $qb->executeStatement();
  131. }
  132. /**
  133. * insert entries in the metadata_index table for list of string
  134. *
  135. * @param int $fileId file id
  136. * @param string $key metadata key
  137. * @param string[] $values metadata values
  138. *
  139. * @throws DbException
  140. */
  141. public function insertIndexStringList(int $fileId, string $key, array $values): void {
  142. foreach ($values as $value) {
  143. $this->insertIndexString($fileId, $key, $value);
  144. }
  145. }
  146. /**
  147. * insert entries in the metadata_index table for list of int
  148. *
  149. * @param int $fileId file id
  150. * @param string $key metadata key
  151. * @param int[] $values metadata values
  152. *
  153. * @throws DbException
  154. */
  155. public function insertIndexIntList(int $fileId, string $key, array $values): void {
  156. foreach ($values as $value) {
  157. $this->insertIndexInt($fileId, $key, $value);
  158. }
  159. }
  160. /**
  161. * drop indexes related to a file id
  162. * if a key is specified, only drop entries related to it
  163. *
  164. * @param int $fileId file id
  165. * @param string $key metadata key
  166. *
  167. * @throws DbException
  168. */
  169. public function dropIndex(int $fileId, string $key = ''): void {
  170. $qb = $this->dbConnection->getQueryBuilder();
  171. $expr = $qb->expr();
  172. $qb->delete(self::TABLE_METADATA_INDEX)
  173. ->where($expr->eq('file_id', $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
  174. if ($key !== '') {
  175. $qb->andWhere($expr->eq('meta_key', $qb->createNamedParameter($key)));
  176. }
  177. $qb->executeStatement();
  178. }
  179. }