aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/Files/Cache/QuerySearchHelper.php
diff options
context:
space:
mode:
authorRobin Appelman <robin@icewind.nl>2021-05-04 19:06:02 +0200
committerRobin Appelman <robin@icewind.nl>2021-06-14 16:11:22 +0200
commite198dc1b200f3ade93498e0ea7b468c87d46748a (patch)
tree235c30d6035bec46bef5ee2f2b134333dfb65409 /lib/private/Files/Cache/QuerySearchHelper.php
parentdfbac05f7ba00c78ac15df61a425317a890b08d1 (diff)
downloadnextcloud-server-e198dc1b200f3ade93498e0ea7b468c87d46748a.tar.gz
nextcloud-server-e198dc1b200f3ade93498e0ea7b468c87d46748a.zip
rework search api to allow searching on multiple caches at once
Signed-off-by: Robin Appelman <robin@icewind.nl>
Diffstat (limited to 'lib/private/Files/Cache/QuerySearchHelper.php')
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php119
1 files changed, 113 insertions, 6 deletions
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php
index fe0f1aae083..0b1bffa8e94 100644
--- a/lib/private/Files/Cache/QuerySearchHelper.php
+++ b/lib/private/Files/Cache/QuerySearchHelper.php
@@ -25,12 +25,17 @@
*/
namespace OC\Files\Cache;
+use OC\SystemConfig;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\Files\Cache\ICache;
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
@@ -58,14 +63,23 @@ class QuerySearchHelper {
/** @var IMimeTypeLoader */
private $mimetypeLoader;
+ /** @var IDBConnection */
+ private $connection;
+ /** @var SystemConfig */
+ private $systemConfig;
+ /** @var ILogger */
+ private $logger;
- /**
- * QuerySearchUtil constructor.
- *
- * @param IMimeTypeLoader $mimetypeLoader
- */
- public function __construct(IMimeTypeLoader $mimetypeLoader) {
+ public function __construct(
+ IMimeTypeLoader $mimetypeLoader,
+ IDBConnection $connection,
+ SystemConfig $systemConfig,
+ ILogger $logger
+ ) {
$this->mimetypeLoader = $mimetypeLoader;
+ $this->connection = $connection;
+ $this->systemConfig = $systemConfig;
+ $this->logger = $logger;
}
/**
@@ -227,4 +241,97 @@ class QuerySearchHelper {
$query->addOrderBy($order->getField(), $order->getDirection());
}
}
+
+ protected function getQueryBuilder() {
+ return new CacheQueryBuilder(
+ $this->connection,
+ $this->systemConfig,
+ $this->logger
+ );
+ }
+
+ /**
+ * @param ISearchQuery $searchQuery
+ * @param ICache[] $caches
+ * @return CacheEntry[]
+ */
+ public function searchInCaches(ISearchQuery $searchQuery, array $caches): array {
+ // search in multiple caches at once by creating one query in the following format
+ // SELECT ... FROM oc_filecache WHERE
+ // <filter expressions from the search query>
+ // AND (
+ // <filter expression for storage1> OR
+ // <filter expression for storage2> OR
+ // ...
+ // );
+ //
+ // This gives us all the files matching the search query from all caches
+ //
+ // while the resulting rows don't have a way to tell what storage they came from (multiple storages/caches can share storage_id)
+ // we can just ask every cache if the row belongs to them and give them the cache to do any post processing on the result.
+
+ $builder = $this->getQueryBuilder();
+
+ $query = $builder->selectFileCache('file');
+
+ $storageFilters = array_map(function (ICache $cache) use ($builder) {
+ return $cache->getQueryFilterForStorage($builder);
+ }, $caches);
+
+ $query->andWhere($query->expr()->orX(...$storageFilters));
+
+ if ($this->shouldJoinTags($searchQuery->getSearchOperation())) {
+ $user = $searchQuery->getUser();
+ if ($user === null) {
+ throw new \InvalidArgumentException("Searching by tag requires the user to be set in the query");
+ }
+ $query
+ ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
+ ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
+ $builder->expr()->eq('tagmap.type', 'tag.type'),
+ $builder->expr()->eq('tagmap.categoryid', 'tag.id')
+ ))
+ ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
+ ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($user->getUID())));
+ }
+
+ $searchExpr = $this->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation());
+ if ($searchExpr) {
+ $query->andWhere($searchExpr);
+ }
+
+ if ($searchQuery->limitToHome() && ($this instanceof HomeCache)) {
+ $query->andWhere($builder->expr()->like('path', $query->expr()->literal('files/%')));
+ }
+
+ $this->addSearchOrdersToQuery($query, $searchQuery->getOrder());
+
+ if ($searchQuery->getLimit()) {
+ $query->setMaxResults($searchQuery->getLimit());
+ }
+ if ($searchQuery->getOffset()) {
+ $query->setFirstResult($searchQuery->getOffset());
+ }
+
+ $result = $query->execute();
+ $files = $result->fetchAll();
+
+ $rawEntries = array_map(function (array $data) {
+ return Cache::cacheEntryFromData($data, $this->mimetypeLoader);
+ }, $files);
+
+ $result->closeCursor();
+
+ // loop trough all caches for each result to see if the result matches that storage
+ $results = [];
+ foreach ($rawEntries as $rawEntry) {
+ foreach ($caches as $cache) {
+ $entry = $cache->getCacheEntryFromSearchResult($rawEntry);
+ if ($entry) {
+ $results[] = $entry;
+ }
+ }
+ }
+ return $results;
+ }
}