You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2017/01/11 16:45:40 UTC

[13/22] james-project git commit: JAMES-1894 Push GetMessageList sorting and limit to the back-end

JAMES-1894 Push GetMessageList sorting and limit to the back-end


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/299addd9
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/299addd9
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/299addd9

Branch: refs/heads/master
Commit: 299addd9415e3744702c44c61bebe3d15e0e5e77
Parents: 7a893c4
Author: Benoit Tellier <bt...@linagora.com>
Authored: Thu Dec 22 18:40:05 2016 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Mon Jan 9 22:01:01 2017 +0700

----------------------------------------------------------------------
 .../apache/james/mailbox/MailboxManager.java    |   7 +-
 ...lasticSearchListeningMessageSearchIndex.java |  18 +-
 .../search/ElasticSearchSearcher.java           |  38 +--
 .../ElasticSearchIntegrationTest.java           |   5 +-
 .../lucene/search/LuceneMessageSearchIndex.java |  65 +++--
 .../LuceneMailboxMessageSearchIndexTest.java    |  68 +++--
 .../search/LuceneMessageSearchIndexTest.java    |   6 +-
 .../mailbox/store/StoreMailboxManager.java      |   9 +-
 .../store/search/LazyMessageSearchIndex.java    |   5 +-
 .../store/search/MessageSearchIndex.java        |  31 ++-
 .../mailbox/store/search/MessageSearches.java   |  25 +-
 .../store/search/SimpleMessageSearchIndex.java  |  77 ++++--
 .../james/mailbox/store/MessageBuilder.java     |   7 +-
 .../search/AbstractMessageSearchIndexTest.java  | 255 ++++++++++---------
 .../host/ElasticSearchHostSystem.java           |   7 +-
 .../host/LuceneSearchHostSystem.java            |   2 +-
 .../base/MailboxEventAnalyserTest.java          |   2 +-
 .../jmap/methods/GetMessageListMethod.java      |  83 +-----
 .../jmap/utils/SortToComparatorConvertor.java   |  85 -------
 .../FirstUserConnectionFilterThreadTest.java    |   6 +-
 .../utils/SortToComparatorConvertorTest.java    | 146 -----------
 21 files changed, 396 insertions(+), 551 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java
----------------------------------------------------------------------
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 44d1df5..d152466 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
@@ -19,10 +19,8 @@
 
 package org.apache.james.mailbox;
 
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 import org.apache.james.mailbox.exception.AnnotationException;
@@ -33,11 +31,12 @@ import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.exception.UnsupportedRightException;
 import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.model.MailboxAnnotation;
-import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxAnnotationKey;
+import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxMetaData;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MailboxQuery;
+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.SimpleMailboxACL;
@@ -239,7 +238,7 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport {
      *            the context for this call, not null
      * @throws MailboxException
      */
-    Map<MailboxId, Collection<MessageUid>> search(MultimailboxesSearchQuery expression, MailboxSession session) throws MailboxException;
+    List<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) throws MailboxException;
 
     /**
      * Does the given mailbox exist?

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
----------------------------------------------------------------------
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
index f4c60f8..281f429 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMessageSearchIndex.java
@@ -20,11 +20,10 @@ package org.apache.james.mailbox.elasticsearch.events;
 
 import static org.elasticsearch.index.query.QueryBuilders.termQuery;
 
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
@@ -38,6 +37,7 @@ import org.apache.james.mailbox.elasticsearch.json.MessageToElasticSearchJson;
 import org.apache.james.mailbox.elasticsearch.search.ElasticSearchSearcher;
 import org.apache.james.mailbox.exception.MailboxException;
 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.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
@@ -49,6 +49,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.github.steveash.guavate.Guavate;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
@@ -85,17 +86,20 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
         Preconditions.checkArgument(session != null, "'session' is mandatory");
         MailboxId mailboxId = mailbox.getMailboxId();
         MultimailboxesSearchQuery query = MultimailboxesSearchQuery.from(searchQuery).inMailboxes(mailboxId).build();
+        Optional<Long> noLimit = Optional.empty();
         return searcher
-                .search(ImmutableList.of(session.getUser()), query)
-                .get(mailboxId)
+                .search(ImmutableList.of(session.getUser()), query, noLimit)
+                .map(SearchResult::getMessageUid)
                 .iterator();
     }
     
     @Override
-    public Map<MailboxId, Collection<MessageUid>> search(MailboxSession session, MultimailboxesSearchQuery searchQuery)
+    public List<MessageId> search(MailboxSession session, MultimailboxesSearchQuery searchQuery, long limit)
             throws MailboxException {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
-        return searcher.search(ImmutableList.of(session.getUser()), searchQuery).asMap();
+        return searcher.search(ImmutableList.of(session.getUser()), searchQuery, Optional.of(limit))
+            .map(SearchResult::getMessageId)
+            .collect(Guavate.toImmutableList());
     }
 
     @Override
@@ -156,5 +160,5 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     private String indexIdFor(Mailbox mailbox, MessageUid uid) {
         return String.join(ID_SEPARATOR, mailbox.getMailboxId().serialize(), String.valueOf(uid.asLong()));
     }
-    
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
----------------------------------------------------------------------
diff --git a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
index 1a5cd1e..e508e7d 100644
--- a/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
+++ b/mailbox/elasticsearch/src/main/java/org/apache/james/mailbox/elasticsearch/search/ElasticSearchSearcher.java
@@ -26,7 +26,6 @@ import java.util.stream.StreamSupport;
 
 import javax.inject.Inject;
 
-import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.mailbox.MailboxSession.User;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.elasticsearch.ElasticSearchIndexer;
@@ -36,7 +35,10 @@ import org.apache.james.mailbox.elasticsearch.query.SortConverter;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxId.Factory;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.store.search.MessageSearchIndex;
+import org.apache.james.mailbox.store.search.SimpleMessageSearchIndex;
 import org.elasticsearch.action.search.SearchRequestBuilder;
 import org.elasticsearch.action.search.SearchResponse;
 import org.elasticsearch.client.Client;
@@ -46,9 +48,6 @@ import org.elasticsearch.search.SearchHitField;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.github.steveash.guavate.Guavate;
-import com.google.common.collect.Multimap;
-
 public class ElasticSearchSearcher {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchSearcher.class);
@@ -59,23 +58,26 @@ public class ElasticSearchSearcher {
     private final QueryConverter queryConverter;
     private final int size;
     private final Factory mailboxIdFactory;
+    private final MessageId.Factory messageIdFactory;
 
     @Inject
-    public ElasticSearchSearcher(Client client, QueryConverter queryConverter, MailboxId.Factory mailboxIdFactory) {
-        this(client, queryConverter, DEFAULT_SIZE, mailboxIdFactory);
+    public ElasticSearchSearcher(Client client, QueryConverter queryConverter, MailboxId.Factory mailboxIdFactory, MessageId.Factory messageIdFactory) {
+        this(client, queryConverter, DEFAULT_SIZE, mailboxIdFactory, messageIdFactory);
     }
 
-    public ElasticSearchSearcher(Client client, QueryConverter queryConverter, int size, MailboxId.Factory mailboxIdFactory) {
+    public ElasticSearchSearcher(Client client, QueryConverter queryConverter, int size, Factory mailboxIdFactory, MessageId.Factory messageIdFactory) {
         this.client = client;
         this.queryConverter = queryConverter;
         this.size = size;
         this.mailboxIdFactory = mailboxIdFactory;
+        this.messageIdFactory = messageIdFactory;
     }
     
-    public Multimap<MailboxId, MessageUid> search(List<User> users, MultimailboxesSearchQuery query) throws MailboxException {
-        return new ScrollIterable(client, getSearchRequestBuilder(client, users, query)).stream()
-            .flatMap(this::transformResponseToUidStream)
-            .collect(Guavate.toImmutableListMultimap(Pair::getLeft, Pair::getRight));
+    public Stream<MessageSearchIndex.SearchResult> search(List<User> users, MultimailboxesSearchQuery query, Optional<Long> limit) throws MailboxException {
+        Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, getSearchRequestBuilder(client, users, query)).stream()
+            .flatMap(this::transformResponseToUidStream);
+        return limit.map(pairStream::limit)
+            .orElse(pairStream);
     }
     
     private SearchRequestBuilder getSearchRequestBuilder(Client client, List<User> users, MultimailboxesSearchQuery query) {
@@ -92,21 +94,25 @@ public class ElasticSearchSearcher {
                 (partialResult1, partialResult2) -> partialResult1);
     }
 
-    private Stream<Pair<MailboxId, MessageUid>> transformResponseToUidStream(SearchResponse searchResponse) {
+    private Stream<MessageSearchIndex.SearchResult> transformResponseToUidStream(SearchResponse searchResponse) {
         return StreamSupport.stream(searchResponse.getHits().spliterator(), false)
             .map(this::extractContentFromHit)
             .filter(Optional::isPresent)
             .map(Optional::get);
     }
 
-    private Optional<Pair<MailboxId, MessageUid>> extractContentFromHit(SearchHit hit) {
+    private Optional<MessageSearchIndex.SearchResult> extractContentFromHit(SearchHit hit) {
         SearchHitField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
         SearchHitField uid = hit.field(JsonMessageConstants.UID);
-        if (mailboxId != null && uid != null) {
+        SearchHitField id = hit.field(JsonMessageConstants.ID);
+        if (mailboxId != null && uid != null && id != null) {
             Number uidAsNumber = uid.getValue();
-            return Optional.of(Pair.of(mailboxIdFactory.fromString(mailboxId.getValue()), MessageUid.of(uidAsNumber.longValue())));
+            return Optional.of(
+                new MessageSearchIndex.SearchResult(messageIdFactory.fromString(id.getValue()),
+                    mailboxIdFactory.fromString(mailboxId.getValue()),
+                    MessageUid.of(uidAsNumber.longValue())));
         } else {
-            LOGGER.warn("Can not extract UID and/or MailboxId for search result " + hit.getId());
+            LOGGER.warn("Can not extract UID, MessageID and/or MailboxId for search result " + hit.getId());
             return Optional.empty();
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
index e633bb4..749f11b 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
@@ -67,9 +67,10 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest
             IndexCreationFactory.createIndex(new TestingClientProvider(embeddedElasticSearch.getNode()).get())
         );
         MailboxSessionMapperFactory mapperFactory = new InMemoryMailboxSessionMapperFactory();
+        InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();
         messageSearchIndex = new ElasticSearchListeningMessageSearchIndex(mapperFactory,
             new ElasticSearchIndexer(client, new DeleteByQueryPerformer(client, Executors.newSingleThreadExecutor(), BATCH_SIZE)),
-            new ElasticSearchSearcher(client, new QueryConverter(new CriterionConverter()), SEARCH_SIZE, new InMemoryId.Factory()),
+            new ElasticSearchSearcher(client, new QueryConverter(new CriterionConverter()), SEARCH_SIZE, new InMemoryId.Factory(), messageIdFactory),
             new MessageToElasticSearchJson(new DefaultTextExtractor(), ZoneId.of("Europe/Paris"), IndexAttachments.YES));
         storeMailboxManager = new InMemoryMailboxManager(
             mapperFactory,
@@ -78,7 +79,7 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest
             new UnionMailboxACLResolver(),
             new SimpleGroupMembershipResolver(),
             new MessageParser(),
-            new InMemoryMessageId.Factory());
+            messageIdFactory);
         storeMailboxManager.setMessageSearchIndex(messageSearchIndex);
         storeMailboxManager.init();
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
----------------------------------------------------------------------
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 8a683d9..7dee3cf 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
@@ -33,7 +33,6 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
 
@@ -48,6 +47,7 @@ import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.UnsupportedSearchException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxId.Factory;
+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.SearchQuery;
@@ -117,10 +117,11 @@ import org.apache.lucene.store.LockObtainFailedException;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.Version;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.LinkedHashMultimap;
-import com.google.common.collect.Multimap;
 
 /**
  * Lucene based {@link ListeningMessageSearchIndex} which offers message searching via a Lucene index
@@ -264,6 +265,11 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
     public final static String MAILBOX_ID_FIELD ="mailboxid";
 
     /**
+     * {@link Field} which will contain the id of the {@link MessageId}
+     */
+    public final static String MESSAGE_ID_FIELD ="messageid";
+
+    /**
      * {@link Field} which contain the Date header of the message with YEAR-Resolution
      */
     public final static String SENT_DATE_FIELD_YEAR_RESOLUTION ="sentdateYearResolution";
@@ -341,6 +347,7 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
     private final static SortField FIRST_FROM_MAILBOX_DISPLAY_SORT_REVERSE = new SortField(FIRST_FROM_MAILBOX_DISPLAY_FIELD, SortField.STRING, true);
     
     private final Factory mailboxIdFactory;
+    private final MessageId.Factory messageIdFactory;
     private final IndexWriter writer;
     
     private int maxQueryResults = DEFAULT_MAX_QUERY_RESULTS;
@@ -348,21 +355,23 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
     private boolean suffixMatch = false;
 
     @Inject
-    public LuceneMessageSearchIndex(MessageMapperFactory factory, MailboxId.Factory mailboxIdFactory, Directory directory) throws CorruptIndexException, LockObtainFailedException, IOException {
-        this(factory, mailboxIdFactory, directory, false, true);
+    public LuceneMessageSearchIndex(MessageMapperFactory factory, Factory mailboxIdFactory, Directory directory, MessageId.Factory messageIdFactory) throws CorruptIndexException, LockObtainFailedException, IOException {
+        this(factory, mailboxIdFactory, directory, false, true, messageIdFactory);
     }
     
     
-    public LuceneMessageSearchIndex(MessageMapperFactory factory, MailboxId.Factory mailboxIdFactory, Directory directory, boolean dropIndexOnStart, boolean lenient) throws CorruptIndexException, LockObtainFailedException, IOException {
+    public LuceneMessageSearchIndex(MessageMapperFactory factory, Factory mailboxIdFactory, Directory directory, boolean dropIndexOnStart, boolean lenient, MessageId.Factory messageIdFactory) throws CorruptIndexException, LockObtainFailedException, IOException {
         super(factory);
         this.mailboxIdFactory = mailboxIdFactory;
+        this.messageIdFactory = messageIdFactory;
         this.writer = new IndexWriter(directory,  createConfig(createAnalyzer(lenient), dropIndexOnStart));
     }
     
     
-    public LuceneMessageSearchIndex(MessageMapperFactory factory, MailboxId.Factory mailboxIdFactory, IndexWriter writer) {
+    public LuceneMessageSearchIndex(MessageMapperFactory factory, Factory mailboxIdFactory, MessageId.Factory messageIdFactory, IndexWriter writer) {
         super(factory);
         this.mailboxIdFactory = mailboxIdFactory;
+        this.messageIdFactory = messageIdFactory;
         this.writer = writer;
     }
 
@@ -431,24 +440,36 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
     public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) throws MailboxException {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
         MailboxId mailboxId = mailbox.getMailboxId();
-        Multimap<MailboxId, MessageUid> results = 
-                searchMultimap(
-                    session, 
-                    MultimailboxesSearchQuery
-                        .from(searchQuery)
-                        .inMailboxes(mailboxId)
-                        .build());
-        return results.get(mailboxId).iterator();
+        return FluentIterable.from(searchMultimap(
+            MultimailboxesSearchQuery
+                .from(searchQuery)
+                .inMailboxes(mailboxId)
+                .build()))
+            .transform(new Function<SearchResult, MessageUid>() {
+                @Override
+                public MessageUid apply(SearchResult input) {
+                    return input.getMessageUid();
+                }
+            })
+            .iterator();
     }
 
     @Override
-    public Map<MailboxId, Collection<MessageUid>> search(MailboxSession session, MultimailboxesSearchQuery searchQuery) throws MailboxException {
+    public List<MessageId> search(MailboxSession session, MultimailboxesSearchQuery searchQuery, long limit) throws MailboxException {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
-        return searchMultimap(session, searchQuery).asMap();
+        return FluentIterable.from(searchMultimap(searchQuery))
+            .transform(new Function<SearchResult, MessageId>() {
+                @Override
+                public MessageId apply(SearchResult input) {
+                    return input.getMessageId();
+                }
+            })
+            .limit(Long.valueOf(limit).intValue())
+            .toList();
     }
     
-    private Multimap<MailboxId, MessageUid> searchMultimap(MailboxSession session, MultimailboxesSearchQuery searchQuery) throws MailboxException {
-        Multimap<MailboxId, MessageUid> results = LinkedHashMultimap.create();
+    private List<SearchResult> searchMultimap(MultimailboxesSearchQuery searchQuery) throws MailboxException {
+        ImmutableList.Builder<SearchResult> results = ImmutableList.builder();
         IndexSearcher searcher = null;
 
         Query inMailboxes = buildQueryFromMailboxes(searchQuery.getInMailboxes());
@@ -471,7 +492,8 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
                 Document doc = searcher.doc(sDoc.doc);
                 MessageUid uid = MessageUid.of(Long.valueOf(doc.get(UID_FIELD)));
                 MailboxId mailboxId = mailboxIdFactory.fromString(doc.get(MAILBOX_ID_FIELD));
-                results.put(mailboxId, uid);
+                MessageId messageId = messageIdFactory.fromString(doc.get(MESSAGE_ID_FIELD));
+                results.add(new SearchResult(messageId, mailboxId, uid));
             }
         } catch (IOException e) {
             throw new MailboxException("Unable to search the mailbox", e);
@@ -484,7 +506,7 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
                 }
             }
         }
-        return results;
+        return results.build();
     }
    
     private Query buildQueryFromMailboxes(ImmutableSet<MailboxId> mailboxIds) {
@@ -513,6 +535,7 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
         // TODO: Better handling
         doc.add(new Field(MAILBOX_ID_FIELD, membership.getMailboxId().serialize().toUpperCase(Locale.ENGLISH), Store.YES, Index.NOT_ANALYZED));
         doc.add(new NumericField(UID_FIELD,Store.YES, true).setLongValue(membership.getUid().asLong()));
+        doc.add(new Field(MESSAGE_ID_FIELD, membership.getMessageId().serialize(), Store.YES, Index.NOT_ANALYZED));
         
         // create an unqiue key for the document which can be used later on updates to find the document
         doc.add(new Field(ID_FIELD, membership.getMailboxId().serialize().toUpperCase(Locale.ENGLISH) +"-" + Long.toString(membership.getUid().asLong()), Store.YES, Index.NOT_ANALYZED));

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
----------------------------------------------------------------------
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 6bf144c..82d8cba 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
@@ -23,10 +23,10 @@ import static org.assertj.core.api.Assertions.assertThat;
 import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.Calendar;
-import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.mock.MockMailboxSession;
 import org.apache.james.mailbox.model.MailboxACL;
 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.SearchQuery;
 import org.apache.james.mailbox.model.SearchQuery.AddressType;
@@ -45,9 +46,9 @@ import org.apache.james.mailbox.model.SearchQuery.DateResolution;
 import org.apache.james.mailbox.model.SearchQuery.Sort.SortClause;
 import org.apache.james.mailbox.model.SimpleMailboxACL;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.apache.james.mailbox.store.MessageBuilder;
 import org.apache.james.mailbox.store.SimpleMailboxMembership;
-import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.lucene.store.RAMDirectory;
 import org.junit.Before;
@@ -55,7 +56,8 @@ import org.junit.Test;
 
 public class LuceneMailboxMessageSearchIndexTest {
 
-	private LuceneMessageSearchIndex index;
+    public static final long LIMIT = 100L;
+    private LuceneMessageSearchIndex index;
     
     private SimpleMailbox mailbox = new SimpleMailbox(0);
     private SimpleMailbox mailbox2 = new SimpleMailbox(1);
@@ -79,6 +81,11 @@ public class LuceneMailboxMessageSearchIndexTest {
     private MessageUid uid3;
     private MessageUid uid4;
     private MessageUid uid5;
+    private MessageId id1;
+    private MessageId id2;
+    private MessageId id3;
+    private MessageId id4;
+    private MessageId id5;
 
     protected boolean useLenient() {
         return true;
@@ -86,7 +93,13 @@ public class LuceneMailboxMessageSearchIndexTest {
     
     @Before
     public void setUp() throws Exception {
-        index = new LuceneMessageSearchIndex(null, new TestId.Factory(), new RAMDirectory(), true, useLenient());
+        TestMessageId.Factory factory = new TestMessageId.Factory();
+        id1 = factory.generate();
+        id2 = factory.generate();
+        id3 = factory.generate();
+        id4 = factory.generate();
+        id5 = factory.generate();
+        index = new LuceneMessageSearchIndex(null, new TestId.Factory(), new RAMDirectory(), true, useLenient(), factory);
         index.setEnableSuffixMatch(true);
         Map<String, String> headersSubject = new HashMap<String, String>();
         headersSubject.put("Subject", "test (fwd)");
@@ -107,23 +120,23 @@ public class LuceneMailboxMessageSearchIndexTest {
         headersTestSubject.put("Cc", "test211 <te...@localhost>, test6 <te...@foobar>");
         
         uid1 = MessageUid.of(1);
-        SimpleMailboxMembership m = new SimpleMailboxMembership(new DefaultMessageId(), mailbox.getMailboxId(), uid1, 0, new Date(), 200, new Flags(Flag.ANSWERED), "My Body".getBytes(), headersSubject);
+        SimpleMailboxMembership m = new SimpleMailboxMembership(id1, mailbox.getMailboxId(), uid1, 0, new Date(), 200, new Flags(Flag.ANSWERED), "My Body".getBytes(), headersSubject);
         index.add(null, mailbox, m);
 
         uid2 = MessageUid.of(1);
-        SimpleMailboxMembership m2 = new SimpleMailboxMembership(new DefaultMessageId(), mailbox2.getMailboxId(), uid2, 0, new Date(), 20, new Flags(Flag.ANSWERED), "My Body".getBytes(), headersSubject);
+        SimpleMailboxMembership m2 = new SimpleMailboxMembership(id2, mailbox2.getMailboxId(), uid2, 0, new Date(), 20, new Flags(Flag.ANSWERED), "My Body".getBytes(), headersSubject);
         index.add(null, mailbox2, m2);
         
         uid3 = MessageUid.of(2);
         Calendar cal = Calendar.getInstance();
         cal.set(1980, 2, 10);
-        SimpleMailboxMembership m3 = new SimpleMailboxMembership(new DefaultMessageId(), mailbox.getMailboxId(), uid3, 0, cal.getTime(), 20, new Flags(Flag.DELETED), "My Otherbody".getBytes(), headersTest);
+        SimpleMailboxMembership m3 = new SimpleMailboxMembership(id3, mailbox.getMailboxId(), uid3, 0, cal.getTime(), 20, new Flags(Flag.DELETED), "My Otherbody".getBytes(), headersTest);
         index.add(null, mailbox, m3);
         
         uid4 = MessageUid.of(3);
         Calendar cal2 = Calendar.getInstance();
         cal2.set(8000, 2, 10);
-        SimpleMailboxMembership m4 = new SimpleMailboxMembership(new DefaultMessageId(), mailbox.getMailboxId(), uid4, 0, cal2.getTime(), 20, new Flags(Flag.DELETED), "My Otherbody2".getBytes(), headersTestSubject);
+        SimpleMailboxMembership m4 = new SimpleMailboxMembership(id4, mailbox.getMailboxId(), uid4, 0, cal2.getTime(), 20, new Flags(Flag.DELETED), "My Otherbody2".getBytes(), headersTestSubject);
         index.add(null, mailbox, m4);
         
         uid5 = MessageUid.of(10);
@@ -136,7 +149,7 @@ public class LuceneMailboxMessageSearchIndexTest {
         builder.uid = uid5;
         builder.mailboxId = mailbox3.getMailboxId();
         
-        index.add(null, mailbox3, builder.build());
+        index.add(null, mailbox3, builder.build(id5));
 
         session = new MockMailboxSession("username");
     }
@@ -267,29 +280,44 @@ public class LuceneMailboxMessageSearchIndexTest {
     public void searchBodyInAllMailboxesShouldMatch() throws Exception {
         SearchQuery query = new SearchQuery();
         query.andCriteria(SearchQuery.bodyContains("My Body"));
-        Map<MailboxId, Collection<MessageUid>> result = index.search(session, MultimailboxesSearchQuery.from(query).build());
-        assertThat(result).hasSize(2);
-        assertThat(result.get(mailbox.id)).containsExactly(uid1);
-        assertThat(result.get(mailbox2.id)).containsExactly(uid2);
+
+        List<MessageId> result = index.search(session, MultimailboxesSearchQuery.from(query).build(), LIMIT);
+
+        assertThat(result).containsOnly(id1, id2);
     }
 
     @Test
     public void searchBodyInSpecificMailboxesShouldMatch() throws Exception {
         SearchQuery query = new SearchQuery();
         query.andCriteria(SearchQuery.bodyContains("My Body"));
-        Map<MailboxId, Collection<MessageUid>> result = index.search(session, 
-                MultimailboxesSearchQuery.from(query).inMailboxes(mailbox.id, mailbox3.id).build());
-        assertThat(result).hasSize(1);
-        assertThat(result.get(mailbox.id)).containsExactly(uid1);
-    }
 
+        List<MessageId> result = index.search(session,
+                MultimailboxesSearchQuery.from(query).inMailboxes(mailbox.id, mailbox3.id).build(),
+                LIMIT);
+
+        assertThat(result).containsOnly(id1);
+    }
 
     @Test
     public void searchAllShouldMatchAllUserEmails() throws Exception {
         SearchQuery query = new SearchQuery();
         query.andCriteria(SearchQuery.all());
-        Map<MailboxId, Collection<MessageUid>> result = index.search(session, MultimailboxesSearchQuery.from(query).build());
-        assertThat(result).hasSize(3);
+
+        List<MessageId> result = index.search(session, MultimailboxesSearchQuery.from(query).build(), LIMIT);
+
+        // The query is not limited to one mailbox and we have 5 indexed messages
+        assertThat(result).hasSize(5);
+    }
+
+    @Test
+    public void searchAllShouldLimitTheSize() throws Exception {
+        SearchQuery query = new SearchQuery();
+        query.andCriteria(SearchQuery.all());
+
+        int limit = 1;
+        List<MessageId> result = index.search(session, MultimailboxesSearchQuery.from(query).build(), limit);
+
+        assertThat(result).hasSize(limit);
     }
     
     @Test

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java
----------------------------------------------------------------------
diff --git a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java
index 0270e40..864a7a0 100644
--- a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java
+++ b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java
@@ -26,6 +26,7 @@ import org.apache.james.mailbox.inmemory.InMemoryId;
 import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
 import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory;
 import org.apache.james.mailbox.inmemory.InMemoryMessageId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.apache.james.mailbox.store.FakeAuthenticator;
 import org.apache.james.mailbox.store.JVMMailboxPathLocker;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
@@ -42,8 +43,9 @@ public class LuceneMessageSearchIndexTest extends AbstractMessageSearchIndexTest
 
     @Override
     protected void initializeMailboxManager() throws Exception {
+        TestMessageId.Factory messageIdFactory = new TestMessageId.Factory();
         MailboxSessionMapperFactory mapperFactory = new InMemoryMailboxSessionMapperFactory();
-        messageSearchIndex = new LuceneMessageSearchIndex(mapperFactory, new InMemoryId.Factory(), new RAMDirectory());
+        messageSearchIndex = new LuceneMessageSearchIndex(mapperFactory, new InMemoryId.Factory(), new RAMDirectory(), messageIdFactory);
         storeMailboxManager = new InMemoryMailboxManager(
             mapperFactory,
             new FakeAuthenticator(),
@@ -51,7 +53,7 @@ public class LuceneMessageSearchIndexTest extends AbstractMessageSearchIndexTest
             new UnionMailboxACLResolver(),
             new SimpleGroupMembershipResolver(),
             new MessageParser(),
-            new InMemoryMessageId.Factory());
+            messageIdFactory);
         storeMailboxManager.setMessageSearchIndex(messageSearchIndex);
         storeMailboxManager.init();
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
----------------------------------------------------------------------
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 8bee086..5f83734 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
@@ -20,12 +20,10 @@
 package org.apache.james.mailbox.store;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 
@@ -40,7 +38,6 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MailboxSession.SessionType;
 import org.apache.james.mailbox.MailboxSessionIdGenerator;
 import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.RequestAware;
 import org.apache.james.mailbox.StandardMailboxMetaDataComparator;
 import org.apache.james.mailbox.acl.GroupMembershipResolver;
@@ -85,6 +82,8 @@ import org.apache.james.mailbox.store.transaction.Mapper;
 import org.apache.james.mailbox.store.transaction.TransactionalMapper;
 import org.slf4j.Logger;
 
+import com.google.common.base.Optional;
+
 /**
  * This base class of an {@link MailboxManager} implementation provides a high-level api for writing your own
  * {@link MailboxManager} implementation. If you plan to write your own {@link MailboxManager} its most times so easiest
@@ -699,8 +698,8 @@ public class StoreMailboxManager implements MailboxManager {
     }
 
     @Override
-    public Map<MailboxId, Collection<MessageUid>> search(MultimailboxesSearchQuery expression, MailboxSession session) throws MailboxException {
-        return index.search(session, expression);
+    public List<MessageId> search(MultimailboxesSearchQuery expression, MailboxSession session, long limit) throws MailboxException {
+        return index.search(session, expression, limit);
     }
 
     public boolean belongsToNamespaceAndUser(MailboxPath base, Mailbox mailbox) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/LazyMessageSearchIndex.java
----------------------------------------------------------------------
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 a1c9275..07c6188 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
@@ -18,11 +18,9 @@
  ****************************************************************/
 package org.apache.james.mailbox.store.search;
 
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
@@ -31,6 +29,7 @@ import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.UnsupportedSearchException;
 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.MultimailboxesSearchQuery;
 import org.apache.james.mailbox.model.SearchQuery;
@@ -128,7 +127,7 @@ public class LazyMessageSearchIndex extends ListeningMessageSearchIndex {
     
 
     @Override
-    public Map<MailboxId, Collection<MessageUid>> search(MailboxSession session, MultimailboxesSearchQuery searchQuery) throws MailboxException {
+    public List<MessageId> search(MailboxSession session, MultimailboxesSearchQuery searchQuery, long limit) throws MailboxException {
         throw new UnsupportedSearchException();
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearchIndex.java
----------------------------------------------------------------------
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 6324898..3df16bd 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
@@ -19,21 +19,20 @@
 
 package org.apache.james.mailbox.store.search;
 
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Iterator;
-import java.util.Map;
+import java.util.List;
 
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.MailboxException;
 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.SearchQuery;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 
-
 /**
  * An index which can be used to search for MailboxMessage UID's that match a {@link SearchQuery}.
  * 
@@ -50,8 +49,32 @@ public interface MessageSearchIndex {
     /**
      * Return all uids of all {@link Mailbox}'s the current user has access to which match the {@link SearchQuery}
      */
-    Map<MailboxId, Collection<MessageUid>> search(MailboxSession session, MultimailboxesSearchQuery searchQuery) throws MailboxException;
+    List<MessageId> search(MailboxSession session, MultimailboxesSearchQuery searchQuery, long limit) throws MailboxException;
 
     EnumSet<MailboxManager.SearchCapabilities> getSupportedCapabilities();
 
+    class SearchResult {
+        private final MessageId messageId;
+        private final MailboxId mailboxId;
+        private final MessageUid messageUid;
+
+        public SearchResult(MessageId messageId, MailboxId mailboxId, MessageUid messageUid) {
+            this.messageId = messageId;
+            this.mailboxId = mailboxId;
+            this.messageUid = messageUid;
+        }
+
+        public MessageId getMessageId() {
+            return messageId;
+        }
+
+        public MailboxId getMailboxId() {
+            return mailboxId;
+        }
+
+        public MessageUid getMessageUid() {
+            return messageUid;
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
index f90c144..c2126f1 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
@@ -29,7 +29,6 @@ import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Collection;
 import java.util.Date;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -70,12 +69,14 @@ import org.apache.james.mime4j.message.DefaultMessageWriter;
 import org.apache.james.mime4j.message.HeaderImpl;
 import org.apache.james.mime4j.utils.search.MessageMatcher;
 
+import com.google.common.base.Function;
+import com.google.common.collect.FluentIterable;
 import com.google.common.collect.Lists;
 
 /**
  * Utility methods to help perform search operations.
  */
-public class MessageSearches implements Iterable<MessageUid> {
+public class MessageSearches implements Iterable<SimpleMessageSearchIndex.SearchResult> {
 
     private Iterator<MailboxMessage> messages;
     private SearchQuery query;
@@ -93,7 +94,7 @@ public class MessageSearches implements Iterable<MessageUid> {
     public MessageSearches() {
     }
 
-    private Set<MessageUid> search() {
+    private Set<SimpleMessageSearchIndex.SearchResult> search() {
         TreeSet<MailboxMessage> matched = new TreeSet<MailboxMessage>(CombinedComparator.create(query.getSorts()));
         while (messages.hasNext()) {
             MailboxMessage m = messages.next();
@@ -107,12 +108,16 @@ public class MessageSearches implements Iterable<MessageUid> {
                 }
             }
         }
-        Set<MessageUid> uids = new HashSet<MessageUid>();
-        Iterator<MailboxMessage> matchedIt = matched.iterator();
-        while (matchedIt.hasNext()) {
-            uids.add(matchedIt.next().getUid());
-        }
-        return uids;
+        return FluentIterable.from(matched)
+            .transform(new Function<MailboxMessage, SimpleMessageSearchIndex.SearchResult>() {
+                @Override
+                public SimpleMessageSearchIndex.SearchResult apply(MailboxMessage input) {
+                    return new SimpleMessageSearchIndex.SearchResult(
+                        input.getMessageId(),
+                        input.getMailboxId(),
+                        input.getUid());
+                }
+            }).toSet();
     }
 
     /**
@@ -631,7 +636,7 @@ public class MessageSearches implements Iterable<MessageUid> {
      * according to the SearchQuery
      * 
      */
-    public Iterator<MessageUid> iterator() {
+    public Iterator<SimpleMessageSearchIndex.SearchResult> iterator() {
         return search().iterator();
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
----------------------------------------------------------------------
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 18ea774..537164e 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
@@ -19,11 +19,9 @@
 package org.apache.james.mailbox.store.search;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -33,8 +31,8 @@ import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.MailboxException;
-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.MessageRange;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
 import org.apache.james.mailbox.model.SearchQuery;
@@ -49,13 +47,11 @@ import org.apache.james.mailbox.store.mail.MessageMapperFactory;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableMultimap.Builder;
-import com.google.common.collect.Multimap;
 
 /**
  * {@link MessageSearchIndex} which just fetch {@link MailboxMessage}'s from the {@link MessageMapper} and use {@link MessageSearcher}
@@ -102,25 +98,26 @@ public class SimpleMessageSearchIndex implements MessageSearchIndex {
 	}
     
     @Override
-    public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery query) throws MailboxException {
+    public Iterator<MessageUid> search(MailboxSession session, final Mailbox mailbox, SearchQuery query) throws MailboxException {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
-        return searchMultimap(session, ImmutableList.of(mailbox), query)
-                .get(mailbox.getMailboxId())
+        return FluentIterable.from(searchResults(session, ImmutableList.of(mailbox), query))
+                .filter(isInMailbox(mailbox))
+                .transform(toMessageUid())
                 .iterator();
     }
-    
-    private Multimap<MailboxId, MessageUid> searchMultimap(MailboxSession session, Iterable<Mailbox> mailboxes, SearchQuery query) throws MailboxException {
-        Builder<MailboxId, MessageUid> multimap = ImmutableMultimap.builder();
+
+    private List<SearchResult> searchResults(MailboxSession session, Iterable<Mailbox> mailboxes, SearchQuery query) throws MailboxException {
+        ImmutableList.Builder<SearchResult> builder = ImmutableList.builder();
         for (Mailbox mailbox: mailboxes) {
-            multimap.putAll(searchMultimap(session, mailbox, query));
+            builder.addAll(searchResults(session, mailbox, query));
         }
-        return multimap.build();
+        return builder.build();
 
     }
     
-    private Multimap<MailboxId, MessageUid> searchMultimap(MailboxSession session, Mailbox mailbox, SearchQuery query) throws MailboxException {
+    private List<SearchResult> searchResults(MailboxSession session, Mailbox mailbox, SearchQuery query) throws MailboxException {
         if (!isMatchingUser(session, mailbox)) {
-            return ImmutableMultimap.of();
+            return ImmutableList.of();
         }
         MessageMapper mapper = messageMapperFactory.getMessageMapper(session);
 
@@ -145,11 +142,7 @@ public class SimpleMessageSearchIndex implements MessageSearchIndex {
             	hitSet.add(m);
             }
         }
-        
-        // MessageSearches does the filtering for us
-        return ImmutableMultimap.<MailboxId, MessageUid>builder()
-                    .putAll(mailbox.getMailboxId(), ImmutableList.copyOf(new MessageSearches(hitSet.iterator(), query, session).iterator()))
-                    .build();
+        return ImmutableList.copyOf(new MessageSearches(hitSet.iterator(), query, session).iterator());
     }
 
     private boolean isMatchingUser(MailboxSession session, Mailbox mailbox) {
@@ -157,7 +150,7 @@ public class SimpleMessageSearchIndex implements MessageSearchIndex {
     }
 
     @Override
-    public Map<MailboxId, Collection<MessageUid>> search(MailboxSession session, final MultimailboxesSearchQuery searchQuery) throws MailboxException {
+    public List<MessageId> search(MailboxSession session, final MultimailboxesSearchQuery searchQuery, long limit) throws MailboxException {
         List<Mailbox> allUserMailboxes = mailboxMapperFactory.getMailboxMapper(session)
                 .findMailboxWithPathLike(new MailboxPath(session.getPersonalSpace(), session.getUser().getUserName(), WILDCARD));
         FluentIterable<Mailbox> filteredMailboxes = FluentIterable.from(allUserMailboxes).filter(new Predicate<Mailbox>() {
@@ -167,8 +160,7 @@ public class SimpleMessageSearchIndex implements MessageSearchIndex {
             }
         });
         if (searchQuery.getInMailboxes().isEmpty()) {
-            return searchMultimap(session, filteredMailboxes, searchQuery.getSearchQuery())
-                    .asMap();
+            return getAsMessageIds(searchResults(session, filteredMailboxes, searchQuery.getSearchQuery()), limit);
         }
         List<Mailbox> queriedMailboxes = new ArrayList<Mailbox>();
         for (Mailbox mailbox: filteredMailboxes) {
@@ -176,8 +168,41 @@ public class SimpleMessageSearchIndex implements MessageSearchIndex {
                 queriedMailboxes.add(mailbox);
             }
         }
-        return searchMultimap(session, queriedMailboxes, searchQuery.getSearchQuery())
-                .asMap();
+        return getAsMessageIds(searchResults(session, queriedMailboxes, searchQuery.getSearchQuery()), limit);
+    }
+
+    private List<MessageId> getAsMessageIds(List<SearchResult> temp, long limit) {
+        return FluentIterable.from(temp)
+            .transform(toMessageId())
+            .limit(Long.valueOf(limit).intValue())
+            .toList();
+    }
+
+    private Function<SearchResult, MessageId> toMessageId() {
+        return new Function<SearchResult, MessageId>() {
+            @Override
+            public MessageId apply(SearchResult input) {
+                return input.getMessageId();
+            }
+        };
+    }
+
+    private Function<SearchResult, MessageUid> toMessageUid() {
+        return new Function<SearchResult, MessageUid>() {
+            @Override
+            public MessageUid apply(SearchResult input) {
+                return input.getMessageUid();
+            }
+        };
+    }
+
+    private Predicate<SearchResult> isInMailbox(final Mailbox mailbox) {
+        return new Predicate<SearchResult>() {
+            @Override
+            public boolean apply(SearchResult input) {
+                return input.getMailboxId().equals(mailbox.getMailboxId());
+            }
+        };
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/299addd9/mailbox/store/src/test/java/org/apache/james/mailbox/store/MessageBuilder.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/MessageBuilder.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/MessageBuilder.java
index c1cee99..59ec33b 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/MessageBuilder.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/MessageBuilder.java
@@ -25,6 +25,7 @@ import java.util.Map;
 import javax.mail.Flags;
 
 import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
@@ -41,7 +42,11 @@ public class MessageBuilder {
     public int lineNumber = 0;
     
     public MailboxMessage build() throws Exception {
-        return new SimpleMailboxMembership(new DefaultMessageId(), mailboxId, uid, -1,  internalDate, size, flags, body, headers);
+        return build(new DefaultMessageId());
+    }
+
+    public MailboxMessage build(MessageId messageId) throws Exception {
+        return new SimpleMailboxMembership(messageId, mailboxId, uid, -1,  internalDate, size, flags, body, headers);
     }
     
     public void header(String field, String value) {


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org