This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit d7d1e58251a610f1f81eb759854ce41e070366e0
Author: Quan Tran <[email protected]>
AuthorDate: Fri Jan 30 16:19:27 2026 +0700

    JAMES-4166 Modify the search API with SearchOptions and push the offset 
onto the search engine
---
 mailbox/api/pom.xml                                |  4 ++
 .../org/apache/james/mailbox/MailboxManager.java   |  5 +-
 .../apache/james/mailbox/model/SearchOptions.java  | 43 ++++++++++++++++
 .../apache/james/mailbox/MailboxManagerTest.java   |  4 +-
 .../lucene/search/LuceneMessageSearchIndex.java    | 10 ++--
 .../LuceneMailboxMessageSearchIndexTest.java       |  6 ++-
 .../DisabledListeningMessageSearchIndex.java       |  3 +-
 .../OpenSearchListeningMessageSearchIndex.java     |  5 +-
 .../opensearch/search/OpenSearchSearcher.java      | 14 +++---
 .../opensearch/OpenSearchIntegrationTest.java      |  6 ++-
 .../opensearch/search/OpenSearchSearcherTest.java  |  4 +-
 .../james/mailbox/store/StoreMailboxManager.java   |  5 +-
 .../mail/SearchThreadIdGuessingAlgorithm.java      |  6 ++-
 .../store/search/LazyMessageSearchIndex.java       |  3 +-
 .../mailbox/store/search/MessageSearchIndex.java   |  3 +-
 .../store/search/SimpleMessageSearchIndex.java     | 58 +++++++++++-----------
 .../store/AbstractCombinationManagerTest.java      |  4 +-
 .../store/SystemMailboxesProviderImplTest.java     |  3 +-
 .../search/AbstractMessageSearchIndexTest.java     | 12 +++--
 .../org/apache/james/modules/MailboxProbeImpl.java |  4 +-
 .../org/apache/james/FakeMessageSearchIndex.java   |  3 +-
 .../java/org/apache/james/SearchModuleChooser.java |  3 +-
 .../mailbox/MailboxUserDeletionTaskStepTest.java   |  6 ++-
 .../mailet/ExtractMDNOriginalJMAPMessageId.java    |  4 +-
 .../james/jmap/method/EmailQueryMethod.scala       |  7 ++-
 .../apache/james/jmap/method/MDNParseMethod.scala  |  5 +-
 .../routes/DeletedMessagesVaultRoutesTest.java     |  4 +-
 .../webadmin/service/ExpireMailboxServiceTest.java |  5 +-
 28 files changed, 162 insertions(+), 77 deletions(-)

diff --git a/mailbox/api/pom.xml b/mailbox/api/pom.xml
index c651868e5a..59a9b76234 100644
--- a/mailbox/api/pom.xml
+++ b/mailbox/api/pom.xml
@@ -49,6 +49,10 @@
             <artifactId>james-server-task-memory</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-util</artifactId>
+        </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>metrics-tests</artifactId>
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
index 26f1f9087b..ec9333e381 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
@@ -35,6 +35,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.reactivestreams.Publisher;
@@ -383,8 +384,10 @@ public interface MailboxManager extends RequestAware, 
RightManager, MailboxAnnot
      *            not null
      * @param session
      *            the context for this call, not null
+     * @param searchOptions
+     *            options for the search
      */
-    Publisher<MessageId> search(MultimailboxesSearchQuery expression, 
MailboxSession session, long limit);
+    Publisher<MessageId> search(MultimailboxesSearchQuery expression, 
MailboxSession session, SearchOptions searchOptions);
 
     /**
      * Returns the list of MessageId of messages belonging to that Thread
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchOptions.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchOptions.java
new file mode 100644
index 0000000000..0ef1b77cc4
--- /dev/null
+++ 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchOptions.java
@@ -0,0 +1,43 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.model;
+
+import org.apache.james.util.streams.Limit;
+import org.apache.james.util.streams.Offset;
+
+import com.google.common.base.Preconditions;
+
+public record SearchOptions(Offset offset, Limit limit) {
+    public static final SearchOptions FIRST = 
SearchOptions.limit(Limit.from(1));
+
+    public static SearchOptions limit(Limit limit) {
+        return SearchOptions.of(Offset.none(), limit);
+    }
+
+    public static SearchOptions of(Offset offset, Limit limit) {
+        return new SearchOptions(offset, limit);
+    }
+
+    public SearchOptions {
+        Preconditions.checkNotNull(offset, "'offset' is mandatory");
+        Preconditions.checkNotNull(limit, "'limit' is mandatory");
+        Preconditions.checkArgument(!limit.isUnlimited(), "'limit' cannot be 
unlimited");
+    }
+}
diff --git 
a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java 
b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
index 76e747aba0..468f84105c 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
@@ -86,12 +86,14 @@ import 
org.apache.james.mailbox.model.MultimailboxesSearchQuery.AccessibleNamesp
 import 
org.apache.james.mailbox.model.MultimailboxesSearchQuery.PersonalNamespace;
 import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.model.QuotaRoot;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.util.EventCollector;
 import org.apache.james.mime4j.dom.Message;
 import org.apache.james.util.ClassLoaderUtils;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
+import org.apache.james.util.streams.Limit;
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -118,7 +120,7 @@ import reactor.core.publisher.Mono;
 public abstract class MailboxManagerTest<T extends MailboxManager> {
     public static final Username USER_1 = Username.of("USER_1");
     public static final Username USER_2 = Username.of("USER_2");
-    private static final int DEFAULT_MAXIMUM_LIMIT = 256;
+    private static final SearchOptions DEFAULT_MAXIMUM_LIMIT = 
SearchOptions.limit(Limit.limit(256));
 
     protected T mailboxManager;
     private  SubscriptionManager subscriptionManager;
diff --git 
a/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
 
b/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
index cceff65e36..60fc932240 100644
--- 
a/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
+++ 
b/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
@@ -89,6 +89,7 @@ import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.SearchQuery.AttachmentCriterion;
 import org.apache.james.mailbox.model.SearchQuery.ContainsOperator;
@@ -301,18 +302,21 @@ public class LuceneMessageSearchIndex extends 
ListeningMessageSearchIndex {
     }
 
     @Override
-    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) throws 
MailboxException {
+    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) throws MailboxException {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
         if (mailboxIds.isEmpty()) {
             return Flux.empty();
         }
 
+        long requestedLimit = 
Math.addExact(searchOptions.offset().getOffset(), 
searchOptions.limit().getLimit().orElseThrow());
+
         return Flux.fromIterable(searchMultimap(mailboxIds, searchQuery)
             .stream()
             .filter(searchResult -> searchResult.getMessageId().isPresent())
             .map(searchResult -> searchResult.getMessageId().get())
             .filter(SearchUtil.distinct())
-            .limit(Long.valueOf(limit).intValue())
+            .limit(requestedLimit)
+            .skip(searchOptions.offset().getOffset())
             .collect(ImmutableList.toImmutableList()));
     }
 
@@ -864,4 +868,4 @@ public class LuceneMessageSearchIndex extends 
ListeningMessageSearchIndex {
             default -> Optional.empty();
         };
     }
-}
\ No newline at end of file
+}
diff --git 
a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
 
b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
index fb2ff37c3c..01a26ee4bf 100644
--- 
a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
+++ 
b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
@@ -41,6 +41,7 @@ import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.SearchQuery.AddressType;
 import org.apache.james.mailbox.model.SearchQuery.DateResolution;
@@ -55,6 +56,7 @@ import org.apache.james.mailbox.store.MessageBuilder;
 import org.apache.james.mailbox.store.extractor.JsoupTextExtractor;
 import org.apache.james.mailbox.store.search.ListeningMessageSearchIndex;
 import 
org.apache.james.mailbox.store.search.ListeningMessageSearchIndexContract;
+import org.apache.james.util.streams.Limit;
 import org.apache.lucene.store.ByteBuffersDirectory;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
@@ -64,7 +66,7 @@ import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 class LuceneMailboxMessageSearchIndexTest {
-    static final long LIMIT = 100L;
+    static final SearchOptions LIMIT = SearchOptions.limit(Limit.limit(100));
     static final TestId TEST_ID_1 = TestId.of(0);
     static final TestId TEST_ID_2 = TestId.of(1);
     static final TestId TEST_ID_3 = TestId.of(2);
@@ -359,7 +361,7 @@ class LuceneMailboxMessageSearchIndexTest {
         SearchQuery query = SearchQuery.of(SearchQuery.all());
 
         int limit = 1;
-        List<MessageId> result = index.search(session, 
ImmutableList.of(mailbox.getMailboxId(), mailbox2.getMailboxId(), 
mailbox3.getMailboxId()), query, limit)
+        List<MessageId> result = index.search(session, 
ImmutableList.of(mailbox.getMailboxId(), mailbox2.getMailboxId(), 
mailbox3.getMailboxId()), query, SearchOptions.limit(Limit.limit(1)))
             .collectList().block();
 
         assertThat(result).hasSize(limit);
diff --git 
a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/DisabledListeningMessageSearchIndex.java
 
b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/DisabledListeningMessageSearchIndex.java
index c6fcc023cb..3909e83e0f 100644
--- 
a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/DisabledListeningMessageSearchIndex.java
+++ 
b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/DisabledListeningMessageSearchIndex.java
@@ -39,6 +39,7 @@ import org.apache.james.mailbox.events.MailboxEvents;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
@@ -126,7 +127,7 @@ public class DisabledListeningMessageSearchIndex extends 
ListeningMessageSearchI
     }
     
     @Override
-    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
+    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
 
         return Flux.error(new NotImplementedException());
diff --git 
a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java
 
b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java
index 15a3227a99..cfbc448c7d 100644
--- 
a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java
+++ 
b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndex.java
@@ -58,6 +58,7 @@ import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageMetaData;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.opensearch.IndexBody;
@@ -362,14 +363,14 @@ public class OpenSearchListeningMessageSearchIndex 
extends ListeningMessageSearc
     }
     
     @Override
-    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
+    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
 
         if (mailboxIds.isEmpty()) {
             return Flux.empty();
         }
 
-        return searcher.searchCollapsedByMessageId(mailboxIds, searchQuery, 
Math.toIntExact(limit), MESSAGE_ID_FIELD, !SEARCH_HIGHLIGHT)
+        return searcher.searchCollapsedByMessageId(mailboxIds, searchQuery, 
searchOptions, MESSAGE_ID_FIELD, !SEARCH_HIGHLIGHT)
             .handle(this::extractMessageIdFromHit);
     }
 
diff --git 
a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcher.java
 
b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcher.java
index d11bdf8333..3643ddc1bf 100644
--- 
a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcher.java
+++ 
b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcher.java
@@ -33,6 +33,7 @@ import org.apache.james.backends.opensearch.ReadAliasName;
 import org.apache.james.backends.opensearch.RoutingKey;
 import org.apache.james.backends.opensearch.search.ScrolledSearch;
 import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.opensearch.json.JsonMessageConstants;
 import org.apache.james.mailbox.opensearch.query.QueryConverter;
@@ -106,13 +107,9 @@ public class OpenSearchSearcher {
     }
 
     public Flux<Hit<ObjectNode>> 
searchCollapsedByMessageId(Collection<MailboxId> mailboxIds, SearchQuery query,
-                                                            int limit, 
List<String> fields,
+                                                            SearchOptions 
searchOptions, List<String> fields,
                                                             boolean 
searchHighlight) {
-        if (limit == 0) {
-            return Flux.empty();
-        }
-
-        SearchRequest searchRequest = 
prepareCollapsedSearchByMessageId(mailboxIds, query, 0, limit, fields, 
searchHighlight);
+        SearchRequest searchRequest = 
prepareCollapsedSearchByMessageId(mailboxIds, query, searchOptions, fields, 
searchHighlight);
         try {
             return client.search(searchRequest)
                 .flatMapMany(response -> 
Flux.fromIterable(response.hits().hits()));
@@ -148,13 +145,16 @@ public class OpenSearchSearcher {
     }
 
     private SearchRequest 
prepareCollapsedSearchByMessageId(Collection<MailboxId> mailboxIds, SearchQuery 
query,
-                                                            int from, int 
size, List<String> fields, boolean highlight) {
+                                                            SearchOptions 
searchOptions, List<String> fields, boolean highlight) {
         List<SortOptions> sorts = query.getSorts()
             .stream()
             .flatMap(SortConverter::convertSort)
             .map(fieldSort -> new 
SortOptions.Builder().field(fieldSort).build())
             .collect(Collectors.toList());
 
+        int from = searchOptions.offset().getOffset();
+        int size = searchOptions.limit().getLimit().orElseThrow();
+
         SearchRequest.Builder request = new SearchRequest.Builder()
             .index(aliasName.getValue())
             .query(queryConverter.from(mailboxIds, query))
diff --git 
a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/OpenSearchIntegrationTest.java
 
b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/OpenSearchIntegrationTest.java
index 4a3182dafd..f8ff7175e7 100644
--- 
a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/OpenSearchIntegrationTest.java
+++ 
b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/OpenSearchIntegrationTest.java
@@ -47,6 +47,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import 
org.apache.james.mailbox.opensearch.events.OpenSearchListeningMessageSearchIndex;
 import org.apache.james.mailbox.opensearch.json.MessageToOpenSearchJson;
@@ -62,6 +63,7 @@ import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.apache.james.mime4j.dom.Message;
 import org.apache.james.mime4j.stream.RawField;
 import org.apache.james.util.ClassLoaderUtils;
+import org.apache.james.util.streams.Limit;
 import org.awaitility.Awaitility;
 import org.awaitility.Durations;
 import org.awaitility.core.ConditionFactory;
@@ -531,7 +533,7 @@ class OpenSearchIntegrationTest extends 
AbstractMessageSearchIndexTest {
             .blockLast();
 
         MultimailboxesSearchQuery query = 
MultimailboxesSearchQuery.from(SearchQuery.of(SearchQuery.address(SearchQuery.AddressType.To,
 "other"))).build();
-        assertThat(Flux.from(storeMailboxManager.search(query, session, 
10)).collectList().block())
+        assertThat(Flux.from(storeMailboxManager.search(query, session, 
SearchOptions.limit(Limit.limit(10)))).collectList().block())
             .containsOnly(messageId2.getMessageId());
     }
 
@@ -716,7 +718,7 @@ class OpenSearchIntegrationTest extends 
AbstractMessageSearchIndexTest {
 
     private void awaitUntilAsserted(MailboxId mailboxId, long 
expectedCountResult) {
         CALMLY_AWAIT.atMost(Durations.TEN_SECONDS)
-            .untilAsserted(() -> assertThat(messageSearchIndex.search(session, 
List.of(mailboxId), SearchQuery.matchAll(), 100L).toStream().count())
+            .untilAsserted(() -> assertThat(messageSearchIndex.search(session, 
List.of(mailboxId), SearchQuery.matchAll(), 
SearchOptions.limit(Limit.limit(100))).toStream().count())
                 .isEqualTo(expectedCountResult));
     }
 }
\ No newline at end of file
diff --git 
a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcherTest.java
 
b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcherTest.java
index eb89876a7f..b62509fedf 100644
--- 
a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcherTest.java
+++ 
b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/search/OpenSearchSearcherTest.java
@@ -47,6 +47,7 @@ import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.opensearch.DefaultMailboxMappingFactory;
 import org.apache.james.mailbox.opensearch.IndexAttachments;
@@ -64,6 +65,7 @@ import org.apache.james.mailbox.tika.TikaHttpClientImpl;
 import org.apache.james.mailbox.tika.TikaTextExtractor;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.apache.james.mime4j.dom.Message;
+import org.apache.james.util.streams.Limit;
 import org.awaitility.Awaitility;
 import org.awaitility.Durations;
 import org.awaitility.core.ConditionFactory;
@@ -176,7 +178,7 @@ class OpenSearchSearcherTest {
             .stream()
             .map(ComposedMessageId::getMessageId)
             .collect(ImmutableList.toImmutableList());
-        assertThat(storeMailboxManager.search(multimailboxesSearchQuery, 
session, numberOfMailboxes + 1)
+        assertThat(storeMailboxManager.search(multimailboxesSearchQuery, 
session, SearchOptions.limit(Limit.limit(numberOfMailboxes + 1)))
             .collectList().block())
             .containsExactlyInAnyOrderElementsOf(expectedMessageIds);
     }
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index b4a92f45e7..ba95f9a5e5 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -72,6 +72,7 @@ import org.apache.james.mailbox.model.MessageId.Factory;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
 import org.apache.james.mailbox.model.QuotaRoot;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.model.search.MailboxQuery;
@@ -946,11 +947,11 @@ public class StoreMailboxManager implements 
MailboxManager {
     }
 
     @Override
-    public Flux<MessageId> search(MultimailboxesSearchQuery expression, 
MailboxSession session, long limit) {
+    public Flux<MessageId> search(MultimailboxesSearchQuery expression, 
MailboxSession session, SearchOptions searchOptions) {
         return getInMailboxIds(expression, session)
             .filter(id -> !expression.getNotInMailboxes().contains(id))
             .collect(ImmutableSet.toImmutableSet())
-            .flatMapMany(Throwing.function(ids -> index.search(session, ids, 
expression.getSearchQuery(), limit)));
+            .flatMapMany(Throwing.function(ids -> index.search(session, ids, 
expression.getSearchQuery(), searchOptions)));
     }
 
     @Override
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
index f143ea0aa3..b47552115e 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
@@ -34,11 +34,13 @@ import org.apache.james.mailbox.model.FetchGroup;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.store.mail.model.MimeMessageId;
 import org.apache.james.mailbox.store.mail.model.Subject;
 import org.apache.james.mailbox.store.search.SearchUtil;
+import org.apache.james.util.streams.Limit;
 
 import com.google.common.collect.ImmutableList;
 
@@ -59,7 +61,7 @@ public class SearchThreadIdGuessingAlgorithm implements 
ThreadIdGuessingAlgorith
     public Mono<ThreadId> guessThreadIdReactive(MessageId messageId, 
Optional<MimeMessageId> mimeMessageId, Optional<MimeMessageId> inReplyTo, 
Optional<List<MimeMessageId>> references, Optional<Subject> subject, 
MailboxSession session) {
         MultimailboxesSearchQuery expression = buildSearchQuery(mimeMessageId, 
inReplyTo, references, subject);
 
-        return Flux.from(mailboxManager.search(expression, session, 1))
+        return Flux.from(mailboxManager.search(expression, session, 
SearchOptions.limit(Limit.limit(1))))
             .collectList()
             .flatMapMany(messageIds -> 
messageIdManager.getMessagesReactive(messageIds, FetchGroup.MINIMAL, session))
             .map(MessageResult::getThreadId)
@@ -78,7 +80,7 @@ public class SearchThreadIdGuessingAlgorithm implements 
ThreadIdGuessingAlgorith
             .from(searchQuery)
             .build();
 
-        return Flux.from(mailboxManager.search(expression, session, 
Integer.MAX_VALUE))
+        return Flux.from(mailboxManager.search(expression, session, 
SearchOptions.limit(Limit.limit(Integer.MAX_VALUE))))
             .switchIfEmpty(Mono.error(() -> new 
ThreadNotFoundException(threadId)));
     }
 
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/LazyMessageSearchIndex.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/LazyMessageSearchIndex.java
index 2f71a4969a..f3b0c63481 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/LazyMessageSearchIndex.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/LazyMessageSearchIndex.java
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
@@ -150,7 +151,7 @@ public class LazyMessageSearchIndex extends 
ListeningMessageSearchIndex {
     
 
     @Override
-    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) throws 
MailboxException {
+    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) throws MailboxException {
         throw new UnsupportedSearchException();
     }
 
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearchIndex.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearchIndex.java
index 30269adcd2..dc15e12cff 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearchIndex.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearchIndex.java
@@ -30,6 +30,7 @@ import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 
 import reactor.core.publisher.Flux;
@@ -50,7 +51,7 @@ public interface MessageSearchIndex {
     /**
      * Return all uids of all {@link Mailbox}'s the current user has access to 
which match the {@link SearchQuery}
      */
-    Flux<MessageId> search(MailboxSession session, Collection<MailboxId> 
mailboxIds, SearchQuery searchQuery, long limit) throws MailboxException;
+    Flux<MessageId> search(MailboxSession session, Collection<MailboxId> 
mailboxIds, SearchQuery searchQuery, SearchOptions searchOptions) throws 
MailboxException;
 
     EnumSet<MailboxManager.SearchCapabilities> 
getSupportedCapabilities(EnumSet<MailboxManager.MessageCapabilities> 
messageCapabilities);
 
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
index 4d053aa2a0..22d65668be 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
@@ -38,15 +38,16 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.extractor.TextExtractor;
-import org.apache.james.mailbox.model.Mailbox;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.MessageRange;
-import org.apache.james.mailbox.model.SearchQuery;
-import org.apache.james.mailbox.model.SearchQuery.ConjunctionCriterion;
-import org.apache.james.mailbox.model.SearchQuery.Criterion;
-import org.apache.james.mailbox.model.SearchQuery.UidCriterion;
-import org.apache.james.mailbox.model.SearchQuery.UidRange;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.SearchOptions;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.model.SearchQuery.ConjunctionCriterion;
+import org.apache.james.mailbox.model.SearchQuery.Criterion;
+import org.apache.james.mailbox.model.SearchQuery.UidCriterion;
+import org.apache.james.mailbox.model.SearchQuery.UidRange;
 import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.MailboxMapperFactory;
 import org.apache.james.mailbox.store.mail.MessageMapper;
@@ -210,17 +211,17 @@ public class SimpleMessageSearchIndex implements 
MessageSearchIndex {
         }
         return hitSet;
     }
-
-    @Override
-    public Flux<MessageId> search(MailboxSession session, final 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) throws 
MailboxException {
-        MailboxMapper mailboxMapper = 
mailboxMapperFactory.getMailboxMapper(session);
-
-        Flux<Mailbox> filteredMailboxes =
-            Flux.fromIterable(mailboxIds)
-            .concatMap(mailboxMapper::findMailboxById);
-
-        return getAsMessageIds(searchResults(session, filteredMailboxes, 
searchQuery), limit);
-    }
+
+    @Override
+    public Flux<MessageId> search(MailboxSession session, final 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) throws MailboxException {
+        MailboxMapper mailboxMapper = 
mailboxMapperFactory.getMailboxMapper(session);
+
+        Flux<Mailbox> filteredMailboxes =
+            Flux.fromIterable(mailboxIds)
+            .concatMap(mailboxMapper::findMailboxById);
+
+        return getAsMessageIds(searchResults(session, filteredMailboxes, 
searchQuery), searchOptions);
+    }
 
     private Flux<? extends SearchResult> searchResults(MailboxSession session, 
Flux<Mailbox> mailboxes, SearchQuery query) {
         return mailboxes.concatMap(mailbox -> Mono.fromCallable(() -> 
getSearchResultStream(session, query, mailbox))
@@ -238,11 +239,12 @@ public class SimpleMessageSearchIndex implements 
MessageSearchIndex {
             throw new RuntimeException(e);
         }
     }
-
-    private Flux<MessageId> getAsMessageIds(Flux<? extends SearchResult> temp, 
long limit) {
-        return temp.map(searchResult -> searchResult.getMessageId().get())
-            .filter(SearchUtil.distinct())
-            .take(Long.valueOf(limit).intValue());
-    }
-
-}
+
+    private Flux<MessageId> getAsMessageIds(Flux<? extends SearchResult> temp, 
SearchOptions searchOptions) {
+        return temp.map(searchResult -> searchResult.getMessageId().get())
+            .filter(SearchUtil.distinct())
+            .skip(searchOptions.offset().getOffset())
+            .transform(searchOptions.limit()::applyOnFlux);
+    }
+
+}
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractCombinationManagerTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractCombinationManagerTest.java
index 2bd8f3a00e..b8800d519e 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractCombinationManagerTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractCombinationManagerTest.java
@@ -46,8 +46,10 @@ import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mime4j.dom.Message;
+import org.apache.james.util.streams.Limit;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -60,7 +62,7 @@ import reactor.core.scheduler.Schedulers;
 
 public abstract class AbstractCombinationManagerTest {
 
-    private static final int DEFAULT_MAXIMUM_LIMIT = 256;
+    private static final SearchOptions DEFAULT_MAXIMUM_LIMIT = 
SearchOptions.limit(Limit.limit(256));
 
     private static final String USER_FLAGS_VALUE = "User Flags";
     private static final String ANOTHER_USER_FLAGS_VALUE = "Another User 
Flags";
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/SystemMailboxesProviderImplTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/SystemMailboxesProviderImplTest.java
index 8d38d96266..311ecc218b 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/SystemMailboxesProviderImplTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/SystemMailboxesProviderImplTest.java
@@ -32,6 +32,7 @@ import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.Role;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.fixture.MailboxFixture;
+import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -60,7 +61,7 @@ class SystemMailboxesProviderImplTest {
         
when(mailboxManager.createSystemSession(MailboxFixture.ALICE)).thenReturn(mailboxSession);
         when(mailboxManager.getMailboxReactive(eq(MailboxFixture.INBOX_ALICE), 
eq(mailboxSession)))
             .thenReturn(Mono.error(new MailboxNotFoundException("Not found")));
-        when(mailboxManager.search(any(), any(), 
any())).thenReturn(Flux.empty());
+        when(mailboxManager.search(any(MailboxQuery.class), any(), 
any(MailboxSession.class))).thenReturn(Flux.empty());
 
         
assertThat(Flux.from(systemMailboxProvider.getMailboxByRole(Role.INBOX, 
mailboxSession.getUser())).toStream())
             .isEmpty();
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
index ca66422660..3e4a35a7a6 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/search/AbstractMessageSearchIndexTest.java
@@ -56,6 +56,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.SearchQuery.AddressType;
 import org.apache.james.mailbox.model.SearchQuery.DateResolution;
@@ -76,6 +77,7 @@ import org.apache.james.mime4j.message.BodyPartBuilder;
 import org.apache.james.mime4j.message.MultipartBuilder;
 import org.apache.james.mime4j.message.SingleBodyBuilder;
 import org.apache.james.util.ClassLoaderUtils;
+import org.apache.james.util.streams.Limit;
 import org.apache.james.utils.UpdatableTickingClock;
 import org.awaitility.Awaitility;
 import org.awaitility.core.ConditionFactory;
@@ -92,7 +94,7 @@ public abstract class AbstractMessageSearchIndexTest {
             .with().pollInterval(ONE_HUNDRED_MILLISECONDS)
             .and().pollDelay(ONE_HUNDRED_MILLISECONDS)
             .await();
-    private static final long LIMIT = 100L;
+    private static final SearchOptions LIMIT = 
SearchOptions.limit(Limit.limit(100));
     private static final boolean RECENT = true;
     private static final boolean NOT_RECENT = false;
 
@@ -372,7 +374,7 @@ public abstract class AbstractMessageSearchIndexTest {
         List<MessageId> result = messageSearchIndex.search(session,
             ImmutableList.of(mailbox2.getMailboxId(), mailbox.getMailboxId()),
             searchQuery,
-            limit)
+            SearchOptions.limit(Limit.limit(limit)))
             .collectList().block();
 
         assertThat(result)
@@ -410,7 +412,7 @@ public abstract class AbstractMessageSearchIndexTest {
         List<MessageId> result = messageSearchIndex.search(session,
             ImmutableList.of(mailbox2.getMailboxId(), mailbox.getMailboxId()),
             searchQuery,
-            limit)
+            SearchOptions.limit(Limit.limit(limit)))
             .collectList().block();
 
         assertThat(result)
@@ -771,7 +773,7 @@ public abstract class AbstractMessageSearchIndexTest {
             session,
             ImmutableList.of(mailbox.getMailboxId(), mailbox2.getMailboxId()),
             searchQuery,
-            limit)
+            SearchOptions.limit(Limit.limit(Math.toIntExact(limit))))
             .collectList().block();
         // Two messages matches this query : mOther and m6
 
@@ -788,7 +790,7 @@ public abstract class AbstractMessageSearchIndexTest {
             session,
             ImmutableList.of(otherMailbox.getMailboxId()),
             searchQuery,
-            limit)
+            SearchOptions.limit(Limit.limit(Math.toIntExact(limit))))
             .collectList().block();
 
         assertThat(actual).contains(m10.getMessageId());
diff --git 
a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
 
b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
index 27c3cb4d61..b7ec9c59f9 100644
--- 
a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
+++ 
b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
@@ -47,9 +47,11 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.model.search.Wildcard;
 import org.apache.james.mailbox.probe.MailboxProbe;
+import org.apache.james.util.streams.Limit;
 import org.apache.james.utils.GuiceProbe;
 
 import com.google.common.collect.ImmutableList;
@@ -216,7 +218,7 @@ public class MailboxProbeImpl implements GuiceProbe, 
MailboxProbe {
         MailboxSession mailboxSession = null;
         try {
             mailboxSession = 
mailboxManager.createSystemSession(Username.of(user));
-            return block(Flux.from(mailboxManager.search(expression, 
mailboxSession, limit)).collectList());
+            return block(Flux.from(mailboxManager.search(expression, 
mailboxSession, 
SearchOptions.limit(Limit.limit(Math.toIntExact(limit))))).collectList());
         } catch (MailboxException e) {
             throw new RuntimeException(e);
         } finally {
diff --git 
a/server/container/guice/memory/src/main/java/org/apache/james/FakeMessageSearchIndex.java
 
b/server/container/guice/memory/src/main/java/org/apache/james/FakeMessageSearchIndex.java
index 0e2d947125..9e55bcad44 100644
--- 
a/server/container/guice/memory/src/main/java/org/apache/james/FakeMessageSearchIndex.java
+++ 
b/server/container/guice/memory/src/main/java/org/apache/james/FakeMessageSearchIndex.java
@@ -34,6 +34,7 @@ import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
@@ -91,7 +92,7 @@ public class FakeMessageSearchIndex extends 
ListeningMessageSearchIndex {
     }
 
     @Override
-    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
+    public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) {
         throw new NotImplementedException("not implemented");
     }
 
diff --git 
a/server/container/guice/opensearch/src/main/java/org/apache/james/SearchModuleChooser.java
 
b/server/container/guice/opensearch/src/main/java/org/apache/james/SearchModuleChooser.java
index 51a0477372..aa01b9744d 100644
--- 
a/server/container/guice/opensearch/src/main/java/org/apache/james/SearchModuleChooser.java
+++ 
b/server/container/guice/opensearch/src/main/java/org/apache/james/SearchModuleChooser.java
@@ -35,6 +35,7 @@ import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.searchhighligt.SearchHighlighter;
@@ -118,7 +119,7 @@ public class SearchModuleChooser {
         }
 
         @Override
-        public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
+        public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) {
             throw new NotImplementedException("not implemented");
         }
 
diff --git 
a/server/container/mailbox-adapter/src/test/java/org/apache/james/adapter/mailbox/MailboxUserDeletionTaskStepTest.java
 
b/server/container/mailbox-adapter/src/test/java/org/apache/james/adapter/mailbox/MailboxUserDeletionTaskStepTest.java
index 4040a4050b..d42efb1336 100644
--- 
a/server/container/mailbox-adapter/src/test/java/org/apache/james/adapter/mailbox/MailboxUserDeletionTaskStepTest.java
+++ 
b/server/container/mailbox-adapter/src/test/java/org/apache/james/adapter/mailbox/MailboxUserDeletionTaskStepTest.java
@@ -33,9 +33,11 @@ import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.model.MailboxMetaData;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.store.StoreSubscriptionManager;
+import org.apache.james.util.streams.Limit;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -122,7 +124,7 @@ class MailboxUserDeletionTaskStepTest {
 
         Mono.from(testee.deleteUserData(ALICE)).block();
 
-        
assertThat(mailboxManager.search(MultimailboxesSearchQuery.from(SearchQuery.matchAll()).build(),
 session, 100L)
+        
assertThat(mailboxManager.search(MultimailboxesSearchQuery.from(SearchQuery.matchAll()).build(),
 session, SearchOptions.limit(Limit.limit(100)))
             .collectList().block())
             .isEmpty();
     }
@@ -150,7 +152,7 @@ class MailboxUserDeletionTaskStepTest {
 
         Mono.from(testee.deleteUserData(ALICE)).block();
 
-        
assertThat(mailboxManager.search(MultimailboxesSearchQuery.from(SearchQuery.matchAll()).build(),
 session, 100L)
+        
assertThat(mailboxManager.search(MultimailboxesSearchQuery.from(SearchQuery.matchAll()).build(),
 session, SearchOptions.limit(Limit.limit(100)))
             .collectList().block())
             .isEmpty();
     }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
 
b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
index 8f1a4f187e..13d8ccff99 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
+++ 
b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/ExtractMDNOriginalJMAPMessageId.java
@@ -29,6 +29,7 @@ import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mdn.MDN;
 import org.apache.james.mdn.MDNReport;
@@ -101,11 +102,10 @@ public class ExtractMDNOriginalJMAPMessageId extends 
GenericMailet {
         LOGGER.debug("Searching message {} for recipient {}", messageId, 
recipient.asPrettyString());
         try {
             MailboxSession session = 
mailboxManager.createSystemSession(usersRepository.getUsername(recipient));
-            int limit = 1;
             MultimailboxesSearchQuery searchByRFC822MessageId = 
MultimailboxesSearchQuery
                 .from(SearchQuery.of(SearchQuery.mimeMessageID(messageId)))
                 .build();
-            return Flux.from(mailboxManager.search(searchByRFC822MessageId, 
session, limit)).toStream().findFirst();
+            return Flux.from(mailboxManager.search(searchByRFC822MessageId, 
session, SearchOptions.FIRST)).toStream().findFirst();
         } catch (UsersRepositoryException e) {
             LOGGER.error("unable to find message with Message-Id: " + 
messageId, e);
         }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index e164e3bba3..5a7672a8a6 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -38,10 +38,10 @@ import org.apache.james.jmap.utils.search.MailboxFilter
 import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery.Namespace
-import org.apache.james.mailbox.model.{MailboxId, MessageId, 
MultimailboxesSearchQuery, SearchQuery}
+import org.apache.james.mailbox.model.{MailboxId, MessageId, 
MultimailboxesSearchQuery, SearchOptions, SearchQuery}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
-import org.apache.james.util.streams.{Limit => JavaLimit}
+import org.apache.james.util.streams.{Offset, Limit => JavaLimit}
 import reactor.core.scala.publisher.{SFlux, SMono}
 
 import scala.jdk.CollectionConverters._
@@ -203,8 +203,7 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
     SFlux.fromPublisher(mailboxManager.search(
         searchQuery.addCriterion(SearchQuery.flagIsUnSet(DELETED)),
         mailboxSession,
-        position.value + limitToUse))
-      .drop(position.value)
+        SearchOptions.of(Offset.from(position.value), 
JavaLimit.limit(limitToUse.value))))
       .collectSeq()
 
   private def searchQueryFromRequest(request: EmailQueryRequest, capabilities: 
Set[CapabilityIdentifier], session: MailboxSession): 
Either[UnsupportedOperationException, MultimailboxesSearchQuery] = {
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
index 356016babc..063aff436d 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MDNParseMethod.scala
@@ -29,13 +29,14 @@ import org.apache.james.jmap.core.{Invocation, 
JmapRfc8621Configuration, Session
 import org.apache.james.jmap.json.MDNSerializer
 import org.apache.james.jmap.mail.{BlobId, BlobUnParsableException, 
MDNParseRequest, MDNParseResponse, MDNParseResults, MDNParsed}
 import org.apache.james.jmap.routes.{BlobNotFoundException, BlobResolvers, 
SessionSupplier}
-import org.apache.james.mailbox.model.{MessageId, MultimailboxesSearchQuery, 
SearchQuery}
+import org.apache.james.mailbox.model.{MessageId, MultimailboxesSearchQuery, 
SearchOptions, SearchQuery}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.mdn.MDN
 import org.apache.james.mdn.fields.OriginalMessageId
 import org.apache.james.metrics.api.MetricFactory
 import org.apache.james.mime4j.dom.Message
 import org.apache.james.mime4j.message.DefaultMessageBuilder
+import org.apache.james.util.streams.{Limit => JavaLimit}
 import play.api.libs.json.JsObject
 import reactor.core.scala.publisher.{SFlux, SMono}
 
@@ -132,7 +133,7 @@ case class MDNEmailIdResolver @Inject()(mailboxManager: 
MailboxManager) {
   def resolveForEmailId(originalMessageId: Option[OriginalMessageId], session: 
MailboxSession): SMono[Option[MessageId]] =
     originalMessageId.map(originalMsg => {
       val searchByRFC822MessageId: MultimailboxesSearchQuery = 
MultimailboxesSearchQuery.from(SearchQuery.of(SearchQuery.mimeMessageID(originalMsg.getOriginalMessageId))).build
-      SFlux.fromPublisher(mailboxManager.search(searchByRFC822MessageId, 
session, NUMBER_OF_ORIGINAL_MESSAGE_ID_VALID + 1)).collectSeq().map {
+      SFlux.fromPublisher(mailboxManager.search(searchByRFC822MessageId, 
session, 
SearchOptions.limit(JavaLimit.limit(NUMBER_OF_ORIGINAL_MESSAGE_ID_VALID + 
1)))).collectSeq().map {
         case Seq(first) => Some(first)
         case _ => None
       }
diff --git 
a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
 
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
index 33b7539449..b1d8e0f26e 100644
--- 
a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
+++ 
b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
@@ -102,12 +102,14 @@ import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.apache.james.server.blob.deduplication.BlobStoreFactory;
 import org.apache.james.task.Hostname;
 import org.apache.james.task.MemoryTaskManager;
 import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.james.util.streams.Limit;
 import org.apache.james.utils.UpdatableTickingClock;
 import org.apache.james.vault.DeletedMessage;
 import org.apache.james.vault.DeletedMessageVault;
@@ -2321,7 +2323,7 @@ class DeletedMessagesVaultRoutesTest {
         MailboxSession session = mailboxManager.createSystemSession(username);
         int limitToOneMessage = 1;
 
-        return 
!Flux.from(mailboxManager.search(MultimailboxesSearchQuery.from(SearchQuery.of()).build(),
 session, limitToOneMessage))
+        return 
!Flux.from(mailboxManager.search(MultimailboxesSearchQuery.from(SearchQuery.of()).build(),
 session, SearchOptions.limit(Limit.limit(limitToOneMessage))))
             .collectList().block()
             .isEmpty();
     }
diff --git 
a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExpireMailboxServiceTest.java
 
b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExpireMailboxServiceTest.java
index c65ae91667..a8750da432 100644
--- 
a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExpireMailboxServiceTest.java
+++ 
b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/service/ExpireMailboxServiceTest.java
@@ -45,6 +45,7 @@ import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.SearchOptions;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
@@ -90,9 +91,9 @@ class ExpireMailboxServiceTest {
         }
 
         @Override
-        public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) throws 
MailboxException {
+        public Flux<MessageId> search(MailboxSession session, 
Collection<MailboxId> mailboxIds, SearchQuery searchQuery, SearchOptions 
searchOptions) throws MailboxException {
             handleFailure();
-            return delegate.search(session, mailboxIds, searchQuery, limit);
+            return delegate.search(session, mailboxIds, searchQuery, 
searchOptions);
         }
 
         @Override


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to