Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced search: backend allows multiples terms to search #40618

Merged
merged 5 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 40 additions & 33 deletions apps/dav/lib/CalDAV/CalDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,36 +208,22 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
protected array $userDisplayNames;

private IDBConnection $db;
private Backend $calendarSharingBackend;
private Principal $principalBackend;
private IUserManager $userManager;
private ISecureRandom $random;
private LoggerInterface $logger;
private IEventDispatcher $dispatcher;
private IConfig $config;
private bool $legacyEndpoint;
private string $dbObjectPropertiesTable = 'calendarobjects_props';
private array $cachedObjects = [];

public function __construct(IDBConnection $db,
Principal $principalBackend,
IUserManager $userManager,
IGroupManager $groupManager,
ISecureRandom $random,
LoggerInterface $logger,
IEventDispatcher $dispatcher,
IConfig $config,
bool $legacyEndpoint = false) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->userManager = $userManager;
public function __construct(
private IDBConnection $db,
private Principal $principalBackend,
private IUserManager $userManager,
IGroupManager $groupManager,
private ISecureRandom $random,
private LoggerInterface $logger,
private IEventDispatcher $dispatcher,
private IConfig $config,
private bool $legacyEndpoint = false,
) {
$this->calendarSharingBackend = new Backend($this->db, $this->userManager, $groupManager, $principalBackend, 'calendar');
$this->random = $random;
$this->logger = $logger;
$this->dispatcher = $dispatcher;
$this->config = $config;
$this->legacyEndpoint = $legacyEndpoint;
}

/**
Expand Down Expand Up @@ -1855,8 +1841,14 @@ public function calendarSearch($principalUri, array $filters, $limit = null, $of
*
* @return array
*/
public function search(array $calendarInfo, $pattern, array $searchProperties,
array $options, $limit, $offset) {
public function search(
array $calendarInfo,
$pattern,
array $searchProperties,
array $options,
$limit,
$offset
) {
$outerQuery = $this->db->getQueryBuilder();
$innerQuery = $this->db->getQueryBuilder();

Expand Down Expand Up @@ -2074,11 +2066,12 @@ private function transformSearchProperty(Property $prop) {
* @return array
*/
public function searchPrincipalUri(string $principalUri,
string $pattern,
array $componentTypes,
array $searchProperties,
array $searchParameters,
array $options = []): array {
string $pattern,
array $componentTypes,
array $searchProperties,
array $searchParameters,
array $options = []
): array {
return $this->atomic(function () use ($principalUri, $pattern, $componentTypes, $searchProperties, $searchParameters, $options) {
$escapePattern = !\array_key_exists('escape_like_param', $options) || $options['escape_like_param'] !== false;

Expand Down Expand Up @@ -2160,6 +2153,20 @@ public function searchPrincipalUri(string $principalUri,
if (isset($options['offset'])) {
$calendarObjectIdQuery->setFirstResult($options['offset']);
}
if (isset($options['timerange'])) {
if (isset($options['timerange']['start']) && $options['timerange']['start'] instanceof DateTimeInterface) {
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->gt(
'lastoccurence',
$calendarObjectIdQuery->createNamedParameter($options['timerange']['start']->getTimeStamp()),
));
}
if (isset($options['timerange']['end']) && $options['timerange']['end'] instanceof DateTimeInterface) {
$calendarObjectIdQuery->andWhere($calendarObjectIdQuery->expr()->lt(
'firstoccurence',
$calendarObjectIdQuery->createNamedParameter($options['timerange']['end']->getTimeStamp()),
));
}
}

$result = $calendarObjectIdQuery->executeQuery();
$matches = [];
Expand Down Expand Up @@ -3187,7 +3194,7 @@ public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
$maxId = (int) $result->fetchOne();
$result->closeCursor();
if (!$maxId || $maxId < $keep) {
return 0;
return 0;
}

$query = $this->db->getQueryBuilder();
Expand Down
54 changes: 42 additions & 12 deletions apps/dav/lib/CardDAV/CardDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;
use OC\Search\Filter\DateTimeFilter;
use PDO;
use Sabre\CardDAV\Backend\BackendInterface;
use Sabre\CardDAV\Backend\SyncSupport;
Expand Down Expand Up @@ -1109,7 +1110,15 @@ public function searchPrincipalUri(string $principalUri,
* @param string $pattern
* @param array $searchProperties
* @param array $options
* @psalm-param array{types?: bool, escape_like_param?: bool, limit?: int, offset?: int, wildcard?: bool} $options
* @psalm-param array{
* types?: bool,
* escape_like_param?: bool,
* limit?: int,
* offset?: int,
* wildcard?: bool,
* since?: DateTimeFilter|null,
* until?: DateTimeFilter|null,
* } $options
Fixed Show fixed Hide fixed
* @return array
*/
private function searchByAddressBookIds(array $addressBookIds,
Expand All @@ -1130,32 +1139,31 @@ private function searchByAddressBookIds(array $addressBookIds,
return [];
}

$propertyOr = $query2->expr()->orX();
foreach ($searchProperties as $property) {
if ($escapePattern) {
if ($escapePattern) {
$searchProperties = array_filter($searchProperties, function ($property) use ($pattern) {
if ($property === 'EMAIL' && str_contains($pattern, ' ')) {
// There can be no spaces in emails
continue;
return false;
}

if ($property === 'CLOUD' && preg_match('/[^a-zA-Z0-9 :_.@\/\-\']/', $pattern) === 1) {
// There can be no chars in cloud ids which are not valid for user ids plus :/
// worst case: CA61590A-BBBC-423E-84AF-E6DF01455A53@https://my.nxt/srv/
continue;
return false;
}
}

$propertyOr->add($query2->expr()->eq('cp.name', $query2->createNamedParameter($property)));
return true;
});
}

if ($propertyOr->count() === 0) {
if (empty($searchProperties)) {
return [];
}

$query2->selectDistinct('cp.cardid')
->from($this->dbCardsPropertiesTable, 'cp')
->andWhere($addressBookOr)
->andWhere($propertyOr);
->andWhere($query2->expr()->in('cp.name', $query2->createNamedParameter($searchProperties, IQueryBuilder::PARAM_STR_ARRAY)));

// No need for like when the pattern is empty
if ('' !== $pattern) {
Expand All @@ -1167,14 +1175,36 @@ private function searchByAddressBookIds(array $addressBookIds,
$query2->andWhere($query2->expr()->ilike('cp.value', $query2->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')));
}
}

if (isset($options['limit'])) {
$query2->setMaxResults($options['limit']);
}
if (isset($options['offset'])) {
$query2->setFirstResult($options['offset']);
}

if (isset($options['since']) || isset($options['until'])) {
$query2->join('cp', $this->dbCardsPropertiesTable, 'cp_bday', 'cp.cardid = cp_bday.cardid');
$query2->andWhere($query2->expr()->eq('cp_bday.name', $query2->createNamedParameter('BDAY')));
/**
* FIXME Find a way to match only 4 last digits
* BDAY can be --1018 without year or 20001019 with it
* $bDayOr = $query2->expr()->orX();
* if ($options['since'] instanceof DateTimeFilter) {
* $bDayOr->add(
* $query2->expr()->gte('SUBSTR(cp_bday.value, -4)',
* $query2->createNamedParameter($options['since']->get()->format('md')))
* );
* }
* if ($options['until'] instanceof DateTimeFilter) {
* $bDayOr->add(
* $query2->expr()->lte('SUBSTR(cp_bday.value, -4)',
* $query2->createNamedParameter($options['until']->get()->format('md')))
* );
* }
* $query2->andWhere($bDayOr);
*/
}

$result = $query2->execute();
$matches = $result->fetchAll();
$result->closeCursor();
Expand Down Expand Up @@ -1410,7 +1440,7 @@ public function pruneOutdatedSyncTokens(int $keep = 10_000): int {
$maxId = (int) $result->fetchOne();
$result->closeCursor();
if (!$maxId || $maxId < $keep) {
return 0;
return 0;
}

$query = $this->db->getQueryBuilder();
Expand Down
Loading
Loading