diff options
Diffstat (limited to 'lib/private/Files/Cache/QuerySearchHelper.php')
-rw-r--r-- | lib/private/Files/Cache/QuerySearchHelper.php | 203 |
1 files changed, 7 insertions, 196 deletions
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index c16d660051e..963c964aa61 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -27,41 +27,15 @@ namespace OC\Files\Cache; use OC\Files\Search\SearchBinaryOperator; use OC\SystemConfig; -use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchBinaryOperator; -use OCP\Files\Search\ISearchComparison; -use OCP\Files\Search\ISearchOperator; -use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; use OCP\IDBConnection; use OCP\ILogger; -/** - * Tools for transforming search queries into database queries - */ class QuerySearchHelper { - protected static $searchOperatorMap = [ - ISearchComparison::COMPARE_LIKE => 'iLike', - ISearchComparison::COMPARE_EQUAL => 'eq', - ISearchComparison::COMPARE_GREATER_THAN => 'gt', - ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'gte', - ISearchComparison::COMPARE_LESS_THAN => 'lt', - ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lte', - ]; - - protected static $searchOperatorNegativeMap = [ - ISearchComparison::COMPARE_LIKE => 'notLike', - ISearchComparison::COMPARE_EQUAL => 'neq', - ISearchComparison::COMPARE_GREATER_THAN => 'lte', - ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt', - ISearchComparison::COMPARE_LESS_THAN => 'gte', - ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lt', - ]; - - public const TAG_FAVORITE = '_$!<Favorite>!$_'; /** @var IMimeTypeLoader */ private $mimetypeLoader; @@ -71,6 +45,8 @@ class QuerySearchHelper { private $systemConfig; /** @var ILogger */ private $logger; + /** @var SearchBuilder */ + private $searchBuilder; public function __construct( IMimeTypeLoader $mimetypeLoader, @@ -82,172 +58,7 @@ class QuerySearchHelper { $this->connection = $connection; $this->systemConfig = $systemConfig; $this->logger = $logger; - } - - /** - * Whether or not the tag tables should be joined to complete the search - * - * @param ISearchOperator $operator - * @return boolean - */ - public function shouldJoinTags(ISearchOperator $operator) { - if ($operator instanceof ISearchBinaryOperator) { - return array_reduce($operator->getArguments(), function ($shouldJoin, ISearchOperator $operator) { - return $shouldJoin || $this->shouldJoinTags($operator); - }, false); - } elseif ($operator instanceof ISearchComparison) { - return $operator->getField() === 'tagname' || $operator->getField() === 'favorite'; - } - return false; - } - - /** - * @param IQueryBuilder $builder - * @param ISearchOperator $operator - */ - public function searchOperatorArrayToDBExprArray(IQueryBuilder $builder, array $operators) { - return array_filter(array_map(function ($operator) use ($builder) { - return $this->searchOperatorToDBExpr($builder, $operator); - }, $operators)); - } - - public function searchOperatorToDBExpr(IQueryBuilder $builder, ISearchOperator $operator) { - $expr = $builder->expr(); - if ($operator instanceof ISearchBinaryOperator) { - if (count($operator->getArguments()) === 0) { - return null; - } - - switch ($operator->getType()) { - case ISearchBinaryOperator::OPERATOR_NOT: - $negativeOperator = $operator->getArguments()[0]; - if ($negativeOperator instanceof ISearchComparison) { - return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap); - } else { - throw new \InvalidArgumentException('Binary operators inside "not" is not supported'); - } - // no break - case ISearchBinaryOperator::OPERATOR_AND: - return call_user_func_array([$expr, 'andX'], $this->searchOperatorArrayToDBExprArray($builder, $operator->getArguments())); - case ISearchBinaryOperator::OPERATOR_OR: - return call_user_func_array([$expr, 'orX'], $this->searchOperatorArrayToDBExprArray($builder, $operator->getArguments())); - default: - throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType()); - } - } elseif ($operator instanceof ISearchComparison) { - return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap); - } else { - throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator)); - } - } - - private function searchComparisonToDBExpr(IQueryBuilder $builder, ISearchComparison $comparison, array $operatorMap) { - $this->validateComparison($comparison); - - [$field, $value, $type] = $this->getOperatorFieldAndValue($comparison); - if (isset($operatorMap[$type])) { - $queryOperator = $operatorMap[$type]; - return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value)); - } else { - throw new \InvalidArgumentException('Invalid operator type: ' . $comparison->getType()); - } - } - - private function getOperatorFieldAndValue(ISearchComparison $operator) { - $field = $operator->getField(); - $value = $operator->getValue(); - $type = $operator->getType(); - if ($field === 'mimetype') { - if ($operator->getType() === ISearchComparison::COMPARE_EQUAL) { - $value = (int)$this->mimetypeLoader->getId($value); - } elseif ($operator->getType() === ISearchComparison::COMPARE_LIKE) { - // transform "mimetype='foo/%'" to "mimepart='foo'" - if (preg_match('|(.+)/%|', $value, $matches)) { - $field = 'mimepart'; - $value = (int)$this->mimetypeLoader->getId($matches[1]); - $type = ISearchComparison::COMPARE_EQUAL; - } elseif (strpos($value, '%') !== false) { - throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported'); - } else { - $field = 'mimetype'; - $value = (int)$this->mimetypeLoader->getId($value); - $type = ISearchComparison::COMPARE_EQUAL; - } - } - } elseif ($field === 'favorite') { - $field = 'tag.category'; - $value = self::TAG_FAVORITE; - } elseif ($field === 'tagname') { - $field = 'tag.category'; - } elseif ($field === 'fileid') { - $field = 'file.fileid'; - } elseif ($field === 'path' && $type === ISearchComparison::COMPARE_EQUAL) { - $field = 'path_hash'; - $value = md5((string)$value); - } - return [$field, $value, $type]; - } - - private function validateComparison(ISearchComparison $operator) { - $types = [ - 'mimetype' => 'string', - 'mtime' => 'integer', - 'name' => 'string', - 'path' => 'string', - 'size' => 'integer', - 'tagname' => 'string', - 'favorite' => 'boolean', - 'fileid' => 'integer', - 'storage' => 'integer', - ]; - $comparisons = [ - 'mimetype' => ['eq', 'like'], - 'mtime' => ['eq', 'gt', 'lt', 'gte', 'lte'], - 'name' => ['eq', 'like'], - 'path' => ['eq', 'like'], - 'size' => ['eq', 'gt', 'lt', 'gte', 'lte'], - 'tagname' => ['eq', 'like'], - 'favorite' => ['eq'], - 'fileid' => ['eq'], - 'storage' => ['eq'], - ]; - - if (!isset($types[$operator->getField()])) { - throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField()); - } - $type = $types[$operator->getField()]; - if (gettype($operator->getValue()) !== $type) { - throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField()); - } - if (!in_array($operator->getType(), $comparisons[$operator->getField()])) { - throw new \InvalidArgumentException('Unsupported comparison for field ' . $operator->getField() . ': ' . $operator->getType()); - } - } - - private function getParameterForValue(IQueryBuilder $builder, $value) { - if ($value instanceof \DateTime) { - $value = $value->getTimestamp(); - } - if (is_numeric($value)) { - $type = IQueryBuilder::PARAM_INT; - } else { - $type = IQueryBuilder::PARAM_STR; - } - return $builder->createNamedParameter($value, $type); - } - - /** - * @param IQueryBuilder $query - * @param ISearchOrder[] $orders - */ - public function addSearchOrdersToQuery(IQueryBuilder $query, array $orders) { - foreach ($orders as $order) { - $field = $order->getField(); - if ($field === 'fileid') { - $field = 'file.fileid'; - } - $query->addOrderBy($field, $order->getDirection()); - } + $this->searchBuilder = new SearchBuilder($this->mimetypeLoader); } protected function getQueryBuilder() { @@ -288,7 +99,7 @@ class QuerySearchHelper { $query = $builder->selectFileCache('file'); - if ($this->shouldJoinTags($searchQuery->getSearchOperation())) { + if ($this->searchBuilder->shouldJoinTags($searchQuery->getSearchOperation())) { $user = $searchQuery->getUser(); if ($user === null) { throw new \InvalidArgumentException("Searching by tag requires the user to be set in the query"); @@ -303,7 +114,7 @@ class QuerySearchHelper { ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($user->getUID()))); } - $searchExpr = $this->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation()); + $searchExpr = $this->searchBuilder->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation()); if ($searchExpr) { $query->andWhere($searchExpr); } @@ -311,9 +122,9 @@ class QuerySearchHelper { $storageFilters = array_values(array_map(function (ICache $cache) { return $cache->getQueryFilterForStorage(); }, $caches)); - $query->andWhere($this->searchOperatorToDBExpr($builder, new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $storageFilters))); + $query->andWhere($this->searchBuilder->searchOperatorToDBExpr($builder, new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $storageFilters))); - $this->addSearchOrdersToQuery($query, $searchQuery->getOrder()); + $this->searchBuilder->addSearchOrdersToQuery($query, $searchQuery->getOrder()); if ($searchQuery->getLimit()) { $query->setMaxResults($searchQuery->getLimit()); |