From 8451342697480640d3abf3c6f43cb67d41e50b67 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 9 Sep 2026 00:01:31 +0200 Subject: [PATCH 1/4] feat: add `regexSubstring` to query function builder Signed-off-by: Robin Appelman --- .../FunctionBuilder/PgSqlFunctionBuilder.php | 5 ++++ lib/private/DB/SQLiteSessionInit.php | 20 +++++++++++++ .../DB/QueryBuilder/IFunctionBuilder.php | 11 ++++++++ .../DB/QueryBuilder/FunctionBuilderTest.php | 28 +++++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php index 354a2b126d758..c3ab73009cc30 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php @@ -30,4 +30,9 @@ public function groupConcat($expr, ?string $separator = ','): IQueryFunction { $separator = $this->connection->quote($separator); return new QueryFunction('string_agg(' . $castedExpression . ', ' . $separator . ')'); } + + #[\Override] + public function regexSubstring($input, $pattern): IQueryFunction { + return new QueryFunction('substring(' . $this->helper->quoteColumnName($input) . ' from ' . $this->helper->quoteColumnName($pattern) . ')'); + } } diff --git a/lib/private/DB/SQLiteSessionInit.php b/lib/private/DB/SQLiteSessionInit.php index 79ed20e1da1db..318cf43069c14 100644 --- a/lib/private/DB/SQLiteSessionInit.php +++ b/lib/private/DB/SQLiteSessionInit.php @@ -45,10 +45,30 @@ public function postConnect(ConnectionEventArgs $args) { /** @var \Doctrine\DBAL\Driver\PDO\Connection $connection */ $connection = $args->getConnection()->getWrappedConnection(); $pdo = $connection->getWrappedConnection(); + + $regexSubstr = function ($string, $pattern): ?string { + if (is_null($string) || is_null($pattern)) { + return null; + } else { + $string = (string)$string; + $pattern = str_replace('#', '\#', (string)$pattern); + } + + $matches = []; + $result = preg_match("#$pattern#", $string, $matches); + if ($result === 0 || $result === false) { + return null; + } else { + return $matches[0]; + } + }; + if (PHP_VERSION_ID >= 80500 && method_exists($pdo, 'createFunction')) { $pdo->createFunction('md5', 'md5', 1); + $pdo->createFunction('regexp_substr', $regexSubstr, 2); } else { $pdo->sqliteCreateFunction('md5', 'md5', 1); + $pdo->sqliteCreateFunction('regexp_substr', $regexSubstr, 2); } } diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 480ec1cb1ac8c..0162b11a73f14 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -61,6 +61,17 @@ public function groupConcat($expr, ?string $separator = ','): IQueryFunction; */ public function substring($input, $start, $length = null): IQueryFunction; + /** + * Takes a substring from the input string using a regex pattern + * + * @param string|ILiteral|IParameter|IQueryFunction $input The input string + * @param string|ILiteral|IParameter|IQueryFunction $pattern The pattern to match and return + * + * @return IQueryFunction + * @since 36.0.0 + */ + public function regexSubstring($input, $pattern): IQueryFunction; + /** * Takes the sum of all rows in a column * diff --git a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php index 2202e6879f331..304c87d4e2811 100644 --- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php +++ b/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php @@ -11,6 +11,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\Server; +use PHPUnit\Framework\Attributes\DataProvider; use Test\TestCase; /** @@ -486,4 +487,31 @@ public function testLeast(): void { $result->closeCursor(); $this->assertEquals(1, $row); } + + public static function regexSubstringData(): array { + return [ + ['foobar', 'foo', 'foo'], + ['foobar', 'b.+$', 'bar'], + ['foo#bar', 'ba.+r$', null], + ['foo#bar', 'o#.', 'o#b'], + ['a/file/path', '[^/]+$', 'path'], + ]; + } + + #[DataProvider('regexSubstringData')] + public function testRegexSubstring(string $input, string $pattern, ?string $expected): void { + $query = $this->connection->getQueryBuilder(); + + $query->select($query->func()->regexSubstring( + $query->createNamedParameter($input), + $query->createNamedParameter($pattern), + )); + $query->from('appconfig') + ->setMaxResults(1); + + $result = $query->executeQuery(); + $row = $result->fetchOne(); + $result->closeCursor(); + $this->assertEquals($expected, $row); + } } From 65046198fb4b10b1af4a110ff9dc5199feccd2e5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 9 Sep 2026 00:02:22 +0200 Subject: [PATCH 2/4] fix: also include mountpoint name in search filter Signed-off-by: Robin Appelman --- apps/dav/lib/Files/FileSearchBackend.php | 17 ++++++++++++++++ .../unit/Files/FileSearchBackendTest.php | 18 ++++++++++++----- lib/private/DB/SQLiteSessionInit.php | 6 +++--- lib/private/Files/Cache/QuerySearchHelper.php | 3 +++ lib/private/Files/Cache/SearchBuilder.php | 14 +++++++++---- lib/private/Files/Node/Folder.php | 20 ++++++++++++++++++- tests/lib/Files/Node/FolderTest.php | 2 ++ 7 files changed, 67 insertions(+), 13 deletions(-) diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 7045c5407c13d..1383db8c80edc 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -454,6 +454,23 @@ private function transformSearchOperation(Operator $operator) { throw new \InvalidArgumentException('Invalid property value for ' . $property->name, previous: $e); } + if ($field === 'name') { + return new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison( + $trimmedType, + $field, + $castedValue, + $extra ?? '' + ), + new SearchComparison( + $trimmedType, + 'mount_point_name', + $castedValue, + $extra ?? '' + ) + ]); + } + return new SearchComparison( $trimmedType, $field, diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php index df98df60e9828..25c10bcfb7cd1 100644 --- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -8,6 +8,7 @@ namespace OCA\DAV\Tests\unit\Files; +use OC\Files\Search\SearchBinaryOperator; use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; use OC\Files\View; @@ -92,11 +93,18 @@ public function testSearchFilename(): void { $this->searchFolder->expects($this->once()) ->method('search') ->with(new SearchQuery( - new SearchComparison( - ISearchComparison::COMPARE_EQUAL, - 'name', - 'foo' - ), + new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'name', + 'foo' + ), + new SearchComparison( + ISearchComparison::COMPARE_EQUAL, + 'mount_point_name', + 'foo' + ), + ]), 100, 0, [], diff --git a/lib/private/DB/SQLiteSessionInit.php b/lib/private/DB/SQLiteSessionInit.php index 318cf43069c14..ae155ed2c918d 100644 --- a/lib/private/DB/SQLiteSessionInit.php +++ b/lib/private/DB/SQLiteSessionInit.php @@ -46,9 +46,9 @@ public function postConnect(ConnectionEventArgs $args) { $connection = $args->getConnection()->getWrappedConnection(); $pdo = $connection->getWrappedConnection(); - $regexSubstr = function ($string, $pattern): ?string { + $regexSubstr = function ($string, $pattern): string { if (is_null($string) || is_null($pattern)) { - return null; + return ''; } else { $string = (string)$string; $pattern = str_replace('#', '\#', (string)$pattern); @@ -57,7 +57,7 @@ public function postConnect(ConnectionEventArgs $args) { $matches = []; $result = preg_match("#$pattern#", $string, $matches); if ($result === 0 || $result === false) { - return null; + return ''; } else { return $matches[0]; } diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index 5da74b84bcee3..12109d5dfa902 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -172,6 +172,9 @@ public function searchInCaches(ISearchQuery $searchQuery, array $caches): array if (in_array('owner', $requestedFields) || in_array('share_with', $requestedFields) || in_array('share_type', $requestedFields)) { $this->equipQueryForShares($query); } + if (in_array('mount_point_name', $requestedFields, true)) { + $this->equipQueryForMounts($query, $this->requireUser($searchQuery)); + } $metadataQuery = $query->selectMetadata(); diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php index 53a6c6f6596fe..4c2b4be372a78 100644 --- a/lib/private/Files/Cache/SearchBuilder.php +++ b/lib/private/Files/Cache/SearchBuilder.php @@ -8,6 +8,7 @@ namespace OC\Files\Cache; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\QueryBuilder\IQueryFunction; use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; @@ -66,6 +67,7 @@ class SearchBuilder { 'owner' => 'string', 'creation_time' => 'integer', 'upload_time' => 'integer', + 'mount_point_name' => 'string', ]; /** @var array */ @@ -162,7 +164,7 @@ private function searchComparisonToDBExpr( if ($comparison->getExtra()) { [$field, $value, $type, $paramType] = $this->getExtraOperatorField($comparison, $metadataQuery); } else { - [$field, $value, $type, $paramType] = $this->getOperatorFieldAndValue($comparison); + [$field, $value, $type, $paramType] = $this->getOperatorFieldAndValue($builder, $comparison); } if (isset($operatorMap[$type])) { @@ -177,13 +179,13 @@ private function searchComparisonToDBExpr( * @param ISearchComparison $operator * @return list{string, ParamValue, string, string} */ - private function getOperatorFieldAndValue(ISearchComparison $operator): array { + private function getOperatorFieldAndValue(IQueryBuilder $builder, ISearchComparison $operator): array { $this->validateComparison($operator); $field = $operator->getField(); $value = $operator->getValue(); $type = $operator->getType(); $pathEqHash = $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true); - return $this->getOperatorFieldAndValueInner($field, $value, $type, $pathEqHash); + return $this->getOperatorFieldAndValueInner($builder, $field, $value, $type, $pathEqHash); } /** @@ -199,7 +201,7 @@ private function getOperatorFieldAndValueInner(string $field, mixed $value, stri $values = []; foreach ($value as $arrayValue) { /** @var ParamSingleValue $arrayValue */ - [$arrayField, $arrayValue] = $this->getOperatorFieldAndValueInner($field, $arrayValue, ISearchComparison::COMPARE_EQUAL, $pathEqHash); + [$arrayField, $arrayValue] = $this->getOperatorFieldAndValueInner($builder, $field, $arrayValue, ISearchComparison::COMPARE_EQUAL, $pathEqHash); $resultField = $arrayField; $values[] = $arrayValue; } @@ -240,6 +242,9 @@ private function getOperatorFieldAndValueInner(string $field, mixed $value, stri $value = md5((string)$value); } elseif ($field === 'owner') { $field = 'uid_owner'; + } elseif ($field === 'mount_point_name') { + $field = $builder->func()->regexSubstring('mount_point', $builder->createNamedParameter('[^/]+/$')); + $value = $value . '/'; } return [$field, $value, $type, $paramType]; } @@ -261,6 +266,7 @@ private function validateComparison(ISearchComparison $operator) { 'owner' => ['eq'], 'creation_time' => ['eq', 'gt', 'lt', 'gte', 'lte'], 'upload_time' => ['eq', 'gt', 'lt', 'gte', 'lte'], + 'mount_point_name' => ['eq', 'like', 'clike', 'in'], ]; if (!isset(self::$fieldTypes[$operator->getField()])) { diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index cff27bb8ec467..58fdff71964ea 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -206,7 +206,25 @@ private function queryFromOperator(ISearchOperator $operator, ?string $uid = nul */ public function search($query) { if (is_string($query)) { - $query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%')); + $operator = new SearchComparison( + ISearchComparison::COMPARE_LIKE, + 'name', + '%' . $query . '%', + ); + $parts = explode('/', $this->path); + $uid = null; + if (count($parts) > 2) { + [, $uid] = $parts; + $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [ + $operator, + new SearchComparison( + ISearchComparison::COMPARE_LIKE, + 'mount_point_name', + '%' . $query . '%', + ) + ]); + } + $query = $this->queryFromOperator($operator, $uid); } // search is handled by a single query covering all caches that this folder contains diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index e0ed345049bf1..2f505da01ca79 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -39,8 +39,10 @@ use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOrder; use OCP\Files\Storage\IStorage; +use OCP\IUser; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; +use Test\Traits\UserTrait; /** * Class FolderTest From 14679d15156afd33e263f5acc7a3614e9d0aebc2 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 9 Sep 2026 18:55:11 +0200 Subject: [PATCH 3/4] test: add test for searching for mount name Signed-off-by: Robin Appelman --- lib/private/Files/Cache/SearchBuilder.php | 2 +- tests/lib/Files/Node/FolderTest.php | 1 - .../Files/Search/SearchIntegrationTest.php | 52 +++++++++++++++++-- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php index 4c2b4be372a78..385f9b1f957e3 100644 --- a/lib/private/Files/Cache/SearchBuilder.php +++ b/lib/private/Files/Cache/SearchBuilder.php @@ -243,7 +243,7 @@ private function getOperatorFieldAndValueInner(string $field, mixed $value, stri } elseif ($field === 'owner') { $field = 'uid_owner'; } elseif ($field === 'mount_point_name') { - $field = $builder->func()->regexSubstring('mount_point', $builder->createNamedParameter('[^/]+/$')); + $field = $builder->func()->regexSubstring('m.mount_point', $builder->createNamedParameter('[^/]+/$')); $value = $value . '/'; } return [$field, $value, $type, $paramType]; diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index 2f505da01ca79..3c9fc28610c27 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -39,7 +39,6 @@ use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOrder; use OCP\Files\Storage\IStorage; -use OCP\IUser; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use Test\Traits\UserTrait; diff --git a/tests/lib/Files/Search/SearchIntegrationTest.php b/tests/lib/Files/Search/SearchIntegrationTest.php index c1efb839eaba6..62117a7057772 100644 --- a/tests/lib/Files/Search/SearchIntegrationTest.php +++ b/tests/lib/Files/Search/SearchIntegrationTest.php @@ -10,21 +10,44 @@ use OC\Files\Search\SearchComparison; use OC\Files\Search\SearchQuery; use OC\Files\Storage\Temporary; +use OCP\Files\Cache\ICache; +use OCP\Files\Config\IUserMountCache; use OCP\Files\Search\ISearchBinaryOperator; use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchOperator; +use OCP\Files\Storage\IStorage; +use OCP\IUser; +use OCP\Server; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use Test\TestCase; -#[\PHPUnit\Framework\Attributes\Group('DB')] +#[Group('DB')] class SearchIntegrationTest extends TestCase { - private $cache; - private $storage; + private ICache $cache; + private IStorage $storage; + private string $mountPoint; + private IUserMountCache $mountCache; + private IUser $user; protected function setUp(): void { parent::setUp(); + $this->user = $this->createMock(IUser::class); + $this->user->method('getUID') + ->willReturn('user'); $this->storage = new Temporary([]); $this->cache = $this->storage->getCache(); $this->storage->getScanner()->scan(''); + $this->mountCache = Server::get(IUserMountCache::class); + $this->mountPoint = '/user/files/search_test/'; + $this->mountCache->addMount($this->user, $this->mountPoint, $this->cache->get(''), 'dummy'); + } + + protected function tearDown(): void { + $this->mountCache->removeMount($this->mountPoint); + + parent::tearDown(); } @@ -43,4 +66,27 @@ public function testThousandAndOneFilters(): void { $this->assertCount(1, $results); $this->assertEquals($id, $results[0]->getId()); } + + public static function searchMountNameProvider(): array { + return [ + [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mount_point_name', '%search%'), ''], + [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mount_point_name', 'search_test'), ''], + [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mount_point_name', '%search_test%'), ''], + [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mount_point_name', '%files%'), null], + ]; + } + + #[DataProvider('searchMountNameProvider')] + public function testSearchMountName(ISearchOperator $operator, ?string $resultPath): void { + $query = new SearchQuery($operator, 10, 0, [], $this->user); + + $results = $this->cache->searchQuery($query); + + if (is_null($resultPath)) { + $this->assertCount(0, $results); + } else { + $this->assertCount(1, $results); + $this->assertEquals($this->cache->getId($resultPath), $results[0]->getId()); + } + } } From 17dc933782f47c141dc068b598070787930840e3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 22 Sep 2026 12:06:05 +0200 Subject: [PATCH 4/4] fix: make mount point name compatible with sharding by switching it to the list of root ids fix: make mount point name compatible with sharding by switching it to the list of root ids Signed-off-by: Robin Appelman [skip ci] --- lib/private/Files/Cache/QuerySearchHelper.php | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index 12109d5dfa902..2577f4b87e548 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -17,6 +17,8 @@ use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Search\ISearchBinaryOperator; +use OCP\Files\Search\ISearchComparison; +use OCP\Files\Search\ISearchOperator; use OCP\Files\Search\ISearchQuery; use OCP\FilesMetadata\IFilesMetadataManager; use OCP\FilesMetadata\IMetadataQuery; @@ -98,7 +100,7 @@ public function findUsedTagsInCaches(ISearchQuery $searchQuery, array $caches): protected function equipQueryForSystemTags(CacheQueryBuilder $query, IUser $user): void { $query->leftJoin('file', 'systemtag_object_mapping', 'systemtagmap', $query->expr()->andX( $query->expr()->eq('file.fileid', $query->expr()->castColumn('systemtagmap.objectid', IQueryBuilder::PARAM_INT)), - $query->expr()->eq('systemtagmap.objecttype', $query->createNamedParameter('files')) + $query->expr()->eq('systemtagmap.objecttype', $query->createNamedParameter('files')), )); $on = $query->expr()->andX($query->expr()->eq('systemtag.id', 'systemtagmap.systemtagid')); if (!$this->groupManager->isAdmin($user->getUID())) { @@ -113,7 +115,7 @@ protected function equipQueryForDavTags(CacheQueryBuilder $query, IUser $user): ->leftJoin('tagmap', 'vcategory', 'tag', $query->expr()->andX( $query->expr()->eq('tagmap.categoryid', 'tag.id'), $query->expr()->eq('tag.type', $query->createNamedParameter('files')), - $query->expr()->eq('tag.uid', $query->createNamedParameter($user->getUID())) + $query->expr()->eq('tag.uid', $query->createNamedParameter($user->getUID())), )); } @@ -148,6 +150,8 @@ public function searchInCaches(ISearchQuery $searchQuery, array $caches): array // 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. + $searchQuery = $this->preProcessQuery($searchQuery); + $builder = $this->getQueryBuilder(); $requestedFields = array_merge( @@ -249,4 +253,49 @@ public function getCachesAndMountPointsForSearch(IRootFolder $root, string $path return [$caches, $mountByMountPoint]; } + + private function preProcessQuery(ISearchQuery $searchQuery): ISearchQuery { + // when sharding is enabled, we can't join on the mounts table + // so instead we need to fetch the matching mount root ids and filter on those + if ($this->connection->getShardDefinition('filecache') !== null) { + $operation = $this->replaceMountNameWithRootIds($searchQuery->getSearchOperation()); + return new SearchQuery( + $operation, + $searchQuery->getLimit(), + $searchQuery->getOffset(), + $searchQuery->getOrder(), + $searchQuery->getUser(), + $searchQuery->limitToHome(), + $searchQuery->getSelectFields(), + ); + } else { + return $searchQuery; + } + } + + private function replaceMountNameWithRootIds(ISearchOperator $searchOperator): ISearchOperator { + if ($searchOperator instanceof ISearchBinaryOperator) { + return new SearchBinaryOperator( + $searchOperator->getType(), + array_map($this->replaceMountNameWithRootIds(...), $searchOperator->getArguments()) + ); + } elseif ($searchOperator instanceof ISearchComparison && $searchOperator->getField() === 'mount_point_name') { + if (!in_array($searchOperator->getType(), [ + ISearchComparison::COMPARE_LIKE, + ISearchComparison::COMPARE_EQUAL, + ISearchComparison::COMPARE_IN, + ], true)) { + throw new \InvalidArgumentException('Filtering mount name with ' . $searchOperator->getType() . ' is not supported'); + } + + $query = $this->connection->getQueryBuilder(); + $query->select('root_id') + ->from('mounts', 'm') + ->where($this->searchBuilder->searchOperatorToDBExpr($query, $searchOperator)); + $rootIds = $query->executeQuery()->fetchAll(\PDO::FETCH_COLUMN); + return new SearchComparison(ISearchComparison::COMPARE_IN, 'fileid', $rootIds); + } else { + return $searchOperator; + } + } }