@@ -50,6 +50,14 @@ class MessageMapper extends QBMapper {
5050
5151 use TTransactional;
5252
53+ private const PARAM_UIDS = 'uids ' ;
54+ private const PARAM_IDS = 'ids ' ;
55+
56+ /**
57+ * TODO: replace with IQueryBuilder::MAX_IN_PARAMETERS once the minimum server version is 35
58+ */
59+ private const MAX_IN_PARAMETERS = 1000 ;
60+
5361 /** @var ITimeFactory */
5462 private $ timeFactory ;
5563
@@ -791,14 +799,12 @@ public function findByMessageId(Account $account, string $messageId): array {
791799 }
792800
793801 /**
794- * @param Mailbox $mailbox
795- * @param SearchQuery $query
796- * @param int|null $limit
797- * @param int[]|null $uids
802+ * @param int[]|null $uids IMAP body search matches, combined with the text conditions via OR
803+ * @param int[]|null $ids restricts the result to these message ids
798804 *
799805 * @return int[]
800806 */
801- public function findIdsByQuery (Mailbox $ mailbox , SearchQuery $ query , string $ sortOrder , ?int $ limit , ?array $ uids = null ): array {
807+ public function findIdsByQuery (Mailbox $ mailbox , SearchQuery $ query , string $ sortOrder , ?int $ limit , ?array $ uids = null , ? array $ ids = null ): array {
802808 $ qb = $ this ->db ->getQueryBuilder ();
803809
804810 if ($ this ->needDistinct ($ query )) {
@@ -925,17 +931,23 @@ public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, string $sor
925931 // In the case of body+subject search we need a combination of both results,
926932 // thus the orWhere in every other case andWhere should do the job.
927933 if (!empty ($ query ->getSubjects ())) {
928- $ textOrs [] = $ qb ->expr ()->in ('m.uid ' , $ qb ->createParameter (' uids ' ));
934+ $ textOrs [] = $ qb ->expr ()->in ('m.uid ' , $ qb ->createParameter (self :: PARAM_UIDS ));
929935 } else {
930936 $ select ->andWhere (
931- $ qb ->expr ()->in ('m.uid ' , $ qb ->createParameter (' uids ' ))
937+ $ qb ->expr ()->in ('m.uid ' , $ qb ->createParameter (self :: PARAM_UIDS ))
932938 );
933939 }
934940 }
935941 if (!empty ($ textOrs )) {
936942 $ select ->andWhere ($ qb ->expr ()->orX (...$ textOrs ));
937943 }
938944
945+ if ($ ids !== null ) {
946+ $ select ->andWhere (
947+ $ qb ->expr ()->in ('m.id ' , $ qb ->createParameter (self ::PARAM_IDS ), IQueryBuilder::PARAM_INT_ARRAY )
948+ );
949+ }
950+
939951 if (!empty ($ query ->getStart ())) {
940952 $ select ->andWhere (
941953 $ qb ->expr ()->gte ('m.sent_at ' , $ qb ->createNamedParameter ($ query ->getStart ()), IQueryBuilder::PARAM_INT )
@@ -994,16 +1006,31 @@ public function findIdsByQuery(Mailbox $mailbox, SearchQuery $query, string $sor
9941006 }
9951007
9961008 if ($ uids !== null ) {
997- return array_flat_map (function (array $ chunk ) use ($ qb , $ select ) {
998- $ qb ->setParameter ('uids ' , $ chunk , IQueryBuilder::PARAM_INT_ARRAY );
999- return array_map (static fn (Message $ message ) => $ message ->getId (), $ this ->findEntities ($ select ));
1000- }, array_chunk ($ uids , 1000 ));
1009+ return $ this ->findIdsByChunkedParameter ($ select , self ::PARAM_UIDS , $ uids );
1010+ }
1011+
1012+ if ($ ids !== null ) {
1013+ return $ this ->findIdsByChunkedParameter ($ select , self ::PARAM_IDS , $ ids );
10011014 }
10021015
10031016 $ result = array_map (static fn (Message $ message ) => $ message ->getId (), $ this ->findEntities ($ select ));
10041017 return $ result ;
10051018 }
10061019
1020+ /**
1021+ * Run $select once per chunk of $values, binding each chunk to $parameter.
1022+ *
1023+ * @param int[] $values
1024+ *
1025+ * @return int[]
1026+ */
1027+ private function findIdsByChunkedParameter (IQueryBuilder $ select , string $ parameter , array $ values ): array {
1028+ return array_flat_map (function (array $ chunk ) use ($ select , $ parameter ) {
1029+ $ select ->setParameter ($ parameter , $ chunk , IQueryBuilder::PARAM_INT_ARRAY );
1030+ return array_map (static fn (Message $ message ) => $ message ->getId (), $ this ->findEntities ($ select ));
1031+ }, array_chunk ($ values , self ::MAX_IN_PARAMETERS ));
1032+ }
1033+
10071034 public function findIdsGloballyByQuery (IUser $ user , SearchQuery $ query , ?int $ limit , ?array $ uids = null ): array {
10081035 $ qb = $ this ->db ->getQueryBuilder ();
10091036 $ qbMailboxes = $ this ->db ->getQueryBuilder ();
@@ -1112,7 +1139,7 @@ public function findIdsGloballyByQuery(IUser $user, SearchQuery $query, ?int $li
11121139 }
11131140 if ($ uids !== null ) {
11141141 $ select ->andWhere (
1115- $ qb ->expr ()->in ('m.uid ' , $ qb ->createParameter (' uids ' ))
1142+ $ qb ->expr ()->in ('m.uid ' , $ qb ->createParameter (self :: PARAM_UIDS ))
11161143 );
11171144 }
11181145 foreach ($ query ->getFlags () as $ flag ) {
@@ -1133,10 +1160,7 @@ public function findIdsGloballyByQuery(IUser $user, SearchQuery $query, ?int $li
11331160 }
11341161
11351162 if ($ uids !== null ) {
1136- return array_flat_map (function (array $ chunk ) use ($ select ) {
1137- $ select ->setParameter ('uids ' , $ chunk , IQueryBuilder::PARAM_INT_ARRAY );
1138- return array_map (static fn (Message $ message ) => $ message ->getId (), $ this ->findEntities ($ select ));
1139- }, array_chunk ($ uids , 1000 ));
1163+ return $ this ->findIdsByChunkedParameter ($ select , self ::PARAM_UIDS , $ uids );
11401164 }
11411165
11421166 return array_map (static fn (Message $ message ) => $ message ->getId (), $ this ->findEntities ($ select ));
0 commit comments