mirror of
https://github.com/nextcloud/server.git
synced 2026-04-15 22:11:17 -04:00
Merge pull request #41459 from nextcloud/enh/noid/search-metadata-null
implements search on null/notnull metadata
This commit is contained in:
commit
ee787cd1c6
6 changed files with 62 additions and 14 deletions
2
3rdparty
2
3rdparty
|
|
@ -1 +1 @@
|
|||
Subproject commit d7b9f6f5f0513adc3ed652eb84b1822fb5b53032
|
||||
Subproject commit b3d52b32c65999204aefeb85548f95c76391d632
|
||||
|
|
@ -169,11 +169,11 @@ class CachedSubscription extends \Sabre\CalDAV\Calendar {
|
|||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param null|resource|string $calendarData
|
||||
* @param null|resource|string $data
|
||||
* @return null|string
|
||||
* @throws MethodNotAllowed
|
||||
*/
|
||||
public function createFile($name, $calendarData = null) {
|
||||
public function createFile($name, $data = null) {
|
||||
throw new MethodNotAllowed('Creating objects in cached subscription is not allowed');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ use OCP\Files\Cache\ICacheEntry;
|
|||
use OCP\Files\Folder;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\Node;
|
||||
use OCP\Files\Search\ISearchComparison;
|
||||
use OCP\Files\Search\ISearchOperator;
|
||||
use OCP\Files\Search\ISearchOrder;
|
||||
use OCP\Files\Search\ISearchQuery;
|
||||
|
|
@ -367,22 +368,30 @@ class FileSearchBackend implements ISearchBackend {
|
|||
if (count($operator->arguments) !== 2) {
|
||||
throw new \InvalidArgumentException('Invalid number of arguments for ' . $trimmedType . ' operation');
|
||||
}
|
||||
if (!($operator->arguments[0] instanceof SearchPropertyDefinition)) {
|
||||
throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property');
|
||||
}
|
||||
if (!($operator->arguments[1] instanceof Literal)) {
|
||||
throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal');
|
||||
}
|
||||
|
||||
$value = $operator->arguments[1]->value;
|
||||
case Operator::OPERATION_IS_DEFINED:
|
||||
if (!($operator->arguments[0] instanceof SearchPropertyDefinition)) {
|
||||
throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property');
|
||||
}
|
||||
$property = $operator->arguments[0];
|
||||
$value = $this->castValue($property, $operator->arguments[1]->value);
|
||||
|
||||
if (str_starts_with($property->name, FilesPlugin::FILE_METADATA_PREFIX)) {
|
||||
return new SearchComparison($trimmedType, substr($property->name, strlen(FilesPlugin::FILE_METADATA_PREFIX)), $value, IMetadataQuery::EXTRA);
|
||||
$field = substr($property->name, strlen(FilesPlugin::FILE_METADATA_PREFIX));
|
||||
$extra = IMetadataQuery::EXTRA;
|
||||
} else {
|
||||
return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($property), $value);
|
||||
$field = $this->mapPropertyNameToColumn($property);
|
||||
}
|
||||
|
||||
// no break
|
||||
return new SearchComparison(
|
||||
$trimmedType,
|
||||
$field,
|
||||
$this->castValue($property, $value ?? ''),
|
||||
$extra ?? ''
|
||||
);
|
||||
|
||||
case Operator::OPERATION_IS_COLLECTION:
|
||||
return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE);
|
||||
default:
|
||||
|
|
@ -416,7 +425,11 @@ class FileSearchBackend implements ISearchBackend {
|
|||
}
|
||||
|
||||
private function castValue(SearchPropertyDefinition $property, $value) {
|
||||
switch ($property->dataType) {
|
||||
if ($value === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
switch ($property->dataType) {
|
||||
case SearchPropertyDefinition::DATATYPE_BOOLEAN:
|
||||
return $value === 'yes';
|
||||
case SearchPropertyDefinition::DATATYPE_DECIMAL:
|
||||
|
|
|
|||
|
|
@ -49,6 +49,37 @@ class CommentsContext implements \Behat\Behat\Context\Context {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* get a named entry from response instead of picking a random entry from values
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return array|string
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getValueFromNamedEntries(string $path, array $response): mixed {
|
||||
$next = '';
|
||||
if (str_contains($path, ' ')) {
|
||||
[$key, $next] = explode(' ', $path, 2);
|
||||
} else {
|
||||
$key = $path;
|
||||
}
|
||||
|
||||
foreach ($response as $entry) {
|
||||
if ($entry['name'] === $key) {
|
||||
if ($next !== '') {
|
||||
return $this->getValueFromNamedEntries($next, $entry['value']);
|
||||
} else {
|
||||
return $entry['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @AfterScenario */
|
||||
public function teardownScenario() {
|
||||
$client = new \GuzzleHttp\Client();
|
||||
|
|
@ -175,7 +206,7 @@ class CommentsContext implements \Behat\Behat\Context\Context {
|
|||
if ($res->getStatusCode() === 207) {
|
||||
$service = new Sabre\Xml\Service();
|
||||
$this->response = $service->parse($res->getBody()->getContents());
|
||||
$this->commentId = (int) ($this->response[0]['value'][2]['value'][0]['value'][0]['value'] ?? 0);
|
||||
$this->commentId = (int) ($this->getValueFromNamedEntries('{DAV:}response {DAV:}propstat {DAV:}prop {http://owncloud.org/ns}id', $this->response ?? []) ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,7 +269,8 @@ class CommentsContext implements \Behat\Behat\Context\Context {
|
|||
* @throws \Exception
|
||||
*/
|
||||
public function theResponseShouldContainAPropertyWithValue($key, $value) {
|
||||
$keys = $this->response[0]['value'][2]['value'][0]['value'];
|
||||
// $keys = $this->response[0]['value'][1]['value'][0]['value'];
|
||||
$keys = $this->getValueFromNamedEntries('{DAV:}response {DAV:}propstat {DAV:}prop', $this->response);
|
||||
$found = false;
|
||||
foreach ($keys as $singleKey) {
|
||||
if ($singleKey['name'] === '{http://owncloud.org/ns}' . substr($key, 3)) {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class SearchBuilder {
|
|||
ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'gte',
|
||||
ISearchComparison::COMPARE_LESS_THAN => 'lt',
|
||||
ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lte',
|
||||
ISearchComparison::COMPARE_DEFINED => 'isNotNull',
|
||||
];
|
||||
|
||||
protected static $searchOperatorNegativeMap = [
|
||||
|
|
@ -57,6 +58,7 @@ class SearchBuilder {
|
|||
ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt',
|
||||
ISearchComparison::COMPARE_LESS_THAN => 'gte',
|
||||
ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'gt',
|
||||
ISearchComparison::COMPARE_DEFINED => 'isNull',
|
||||
];
|
||||
|
||||
public const TAG_FAVORITE = '_$!<Favorite>!$_';
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ interface ISearchComparison extends ISearchOperator {
|
|||
public const COMPARE_LESS_THAN_EQUAL = 'lte';
|
||||
public const COMPARE_LIKE = 'like';
|
||||
public const COMPARE_LIKE_CASE_SENSITIVE = 'clike';
|
||||
public const COMPARE_DEFINED = 'is-defined';
|
||||
|
||||
public const HINT_PATH_EQ_HASH = 'path_eq_hash'; // transform `path = "$path"` into `path_hash = md5("$path")`, on by default
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue