Skip to content
Open
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
4 changes: 4 additions & 0 deletions lib/Contracts/IMailSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
use OCA\Mail\Db\Mailbox;
use OCA\Mail\Db\Message;
use OCA\Mail\Exception\ClientException;
use OCA\Mail\Exception\MailboxLockedException;
use OCA\Mail\Exception\MailboxNotCachedException;
use OCA\Mail\Exception\ServiceException;
use OCA\Mail\Service\Search\SearchQuery;
use OCP\AppFramework\Db\DoesNotExistException;
Expand Down Expand Up @@ -46,6 +48,8 @@ public function findMessage(Account $account,
*
* @throws ClientException
* @throws ServiceException
* @throws MailboxLockedException
* @throws MailboxNotCachedException
*/
public function findMessages(Account $account,
Mailbox $mailbox,
Expand Down
77 changes: 64 additions & 13 deletions lib/Db/MessageMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -793,20 +793,70 @@ public function findByMessageId(Account $account, string $messageId): array {
}

/**
* @param Mailbox $mailbox
* @param SearchQuery $query
* @param int|null $limit
* @param int[]|null $uids
*
* @return int[]
*
* @throws Exception
*/
public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, string $sortOrder, ?int $limit, ?array $uids = null): array {
$rows = $this->findByQueryWithoutRelatedData(
'id',
$mailbox,
$query,
$sortOrder,
$limit,
$uids,
);
return array_map(static fn (array $row) => $row['id'], $rows);
}

/**
* @param int[]|null $uids
* @return Message[]
*
* @throws Exception
*/
public function findByQuery(Mailbox $mailbox, string $userId, SearchQuery $query, string $sortOrder, ?int $limit, ?array $uids = null): array {
$rows = $this->findByQueryWithoutRelatedData(
'*',
$mailbox,
$query,
$sortOrder,
$limit,
$uids,
);

/** @var Message[] $messages */
$messages = array_map(fn (array $row) => $this->mapRowToEntity($row), $rows);

$results = [];
foreach (array_chunk($messages, 1000) as $chunk) {
$results[] = $this->findRelatedData($chunk, $userId);
}
return array_merge([], ...$results);
}

/**
* @param string $columns Either a column from the database or * in case all columns should be fetched
* @param int[]|null $uids
* @return array[] Raw rows as fetched from the database
*
* @throws Exception
*/
private function findByQueryWithoutRelatedData(
string $columns,
Mailbox $mailbox,
SearchQuery $query,
string $sortOrder,
?int $limit,
?array $uids = null,
): array {
$qb = $this->db->getQueryBuilder();

if ($this->needDistinct($query)) {
$select = $qb->selectDistinct(['m.id', 'm.sent_at']);
$select = $qb->selectDistinct("m.$columns");
} else {
$select = $qb->select(['m.id', 'm.sent_at']);
$select = $qb->select("m.$columns");
}

$select->from($this->getTableName(), 'm');
Expand Down Expand Up @@ -1027,16 +1077,17 @@ public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, string $sor
if ($uids !== null) {
return array_flat_map(function (array $chunk) use ($qb, $select) {
$qb->setParameter('uids', $chunk, IQueryBuilder::PARAM_INT_ARRAY);
return array_map(static function (Message $message) {
return $message->getId();
}, $this->findEntities($select));
$result = $select->executeQuery();
$rows = $result->fetchAll();
$result->closeCursor();
return $rows;
}, array_chunk($uids, 1000));
}

$result = array_map(static function (Message $message) {
return $message->getId();
}, $this->findEntities($select));
return $result;
$result = $select->executeQuery();
$rows = $result->fetchAll();
$result->closeCursor();
return $rows;
}

public function findIdsGloballyByQuery(IUser $user, SearchQuery $query, ?int $limit, ?array $uids = null): array {
Expand Down
64 changes: 25 additions & 39 deletions lib/Service/Search/MailSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
use OCA\Mail\Db\Mailbox;
use OCA\Mail\Db\Message;
use OCA\Mail\Db\MessageMapper;
use OCA\Mail\Exception\ClientException;
use OCA\Mail\Exception\MailboxLockedException;
use OCA\Mail\Exception\MailboxNotCachedException;
use OCA\Mail\Exception\ServiceException;
Expand Down Expand Up @@ -68,29 +67,17 @@ public function findMessage(Account $account,
return $processed[0];
}

/**
* @param Account $account
* @param Mailbox $mailbox
* @param string $sortOrder
* @param string|null $filter
* @param int|null $cursor
* @param int|null $limit
* @param string|null $view
*
* @return Message[]
*
* @throws ClientException
* @throws ServiceException
*/
#[\Override]
public function findMessages(Account $account,
public function findMessages(
Account $account,
Mailbox $mailbox,
string $sortOrder,
?string $filter,
?int $cursor,
?int $limit,
?string $userId,
?string $view): array {
?string $view,
): array {
if ($mailbox->hasLocks($this->timeFactory->getTime())) {
throw MailboxLockedException::from($mailbox);
}
Expand All @@ -114,13 +101,30 @@ public function findMessages(Account $account,
$query->addFlag(Flag::not(Flag::DELETED));
}

// Need to search IMAP if the user requested to search in email bodies (since they are not
// cached locally)
$fromImap = null;
if (!empty($query->getBodies())) {
$fromImap = $this->imapSearchProvider->findMatches(
$account,
$mailbox,
$query
);
}

$messages = $this->messageMapper->findByQuery(
$mailbox,
$userId ?? $account->getUserId(),
$query,
$sortOrder,
$limit,
$fromImap,
);

return $this->previewEnhancer->process(
$account,
$mailbox,
$this->messageMapper->findByIds($account->getUserId(),
$this->getIdsLocally($account, $mailbox, $query, $sortOrder, $limit),
$sortOrder,
),
$messages,
true,
$userId
);
Expand All @@ -144,24 +148,6 @@ public function findMessagesGlobally(
);
}

/**
* We combine local flag and headers merge with UIDs that match the body search if necessary
*
* @throws ServiceException
*/
private function getIdsLocally(Account $account, Mailbox $mailbox, SearchQuery $query, string $sortOrder, ?int $limit): array {
if (empty($query->getBodies())) {
return $this->messageMapper->findIdsByQuery($mailbox, $query, $sortOrder, $limit);
}

$fromImap = $this->imapSearchProvider->findMatches(
$account,
$mailbox,
$query
);
return $this->messageMapper->findIdsByQuery($mailbox, $query, $sortOrder, $limit, $fromImap);
}

/**
* We combine local flag and headers merge with UIDs that match the body search if necessary
*
Expand Down
4 changes: 2 additions & 2 deletions tests/Unit/Service/Search/MailSearchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public function testFindFlagsLocally() {
->with('my search')
->willReturn($query);
$this->messageMapper->expects($this->once())
->method('findByIds')
->method('findByQuery')
->willReturn([
$this->createMock(Message::class),
$this->createMock(Message::class),
Expand Down Expand Up @@ -185,7 +185,7 @@ public function testFindText() {
->with($account, $mailbox, $query)
->willReturn([2, 3]);
$this->messageMapper->expects($this->once())
->method('findByIds')
->method('findByQuery')
->willReturn([
$this->createMock(Message::class),
$this->createMock(Message::class),
Expand Down
Loading