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 bt...@apache.org on 2019/05/23 03:37:48 UTC

[james-project] branch master updated (7d7e177 -> cbdc3fa)

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

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


    from 7d7e177  JAMES-2775 Linshare should be stopped in integration tests
     new 861b27e  JAMES-2764 Copy of mailbox-elasticsearch module to a new mailbox-elasticsearch-v6 module
     new 6da448e  JAMES-2764 Update version of ES to 6.7.2 in the mailbox es module
     new 69f005e  JAMES-2764 Refactor case insensitive analyzer into a normalizer
     new b638a96  JAMES-2764 Migrate mailbox ES to ES6
     new b091de9  JAMES-2764 Migrate mailbox ES tests to ES6
     new fba0e41  JAMES-2764 small cleanup in mailbox ES6
     new 502ce23  JAMES-2764 setNestedPath is deprecated in ES6
     new 4db9e6b  JAMES-2764 Reordering correctly dependencies
     new 45b0d88  JAMES-2764 Fix checkstyle and minor stuff
     new 15ab767  JAMES-2764 Add bean contracts for es6 mailbox objects
     new df4677c  JAMES-2776 DomainsRoutes should not accept same source & destination
     new d44abb8  JAMES-2776 Correct get domain aliases documentation
     new 496f419  JAMES-2675 Rely on ScrollIterable when no limit is specified
     new cbdc3fa  Fix the modules list order in the server POM file

The 14 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../james/backends/es/v6/IndexCreationFactory.java |   18 +-
 .../james/backends/es/v6/NodeMappingFactory.java   |    1 +
 mailbox/elasticsearch-v6/pom.xml                   |  196 +++
 .../v6/ElasticSearchMailboxConfiguration.java      |  180 +++
 .../elasticsearch/v6/IndexAttachments.java}        |   10 +-
 .../v6/MailboxElasticSearchConstants.java}         |   17 +-
 .../elasticsearch/v6/MailboxIndexCreationUtil.java |   56 +
 .../elasticsearch/v6/MailboxMappingFactory.java    |  339 ++++++
 .../ElasticSearchListeningMessageSearchIndex.java  |  197 +++
 .../mailbox/elasticsearch/v6/json/EMailer.java}    |   49 +-
 .../mailbox/elasticsearch/v6/json/EMailers.java}   |   34 +-
 .../elasticsearch/v6/json/HeaderCollection.java    |  224 ++++
 .../elasticsearch/v6/json/IndexableMessage.java    |  471 ++++++++
 .../v6/json/JsonMessageConstants.java              |   77 ++
 .../v6/json/MessageToElasticSearchJson.java        |   86 ++
 .../elasticsearch/v6/json/MessageUpdateJson.java   |   79 ++
 .../mailbox/elasticsearch/v6/json/MimePart.java    |  303 +++++
 .../v6/json/MimePartContainerBuilder.java}         |   32 +-
 .../elasticsearch/v6/json/MimePartParser.java      |  129 ++
 .../v6/json/RootMimePartContainerBuilder.java      |   96 ++
 .../v6/json/SerializableMessage.java}              |    9 +-
 .../mailbox/elasticsearch/v6/json/Subjects.java}   |   32 +-
 .../elasticsearch/v6/query/CriterionConverter.java |  313 +++++
 .../v6/query/DateResolutionFormater.java           |   73 ++
 .../elasticsearch/v6/query/QueryConverter.java     |   79 ++
 .../elasticsearch/v6/query/SortConverter.java      |   82 ++
 .../v6/search/ElasticSearchSearcher.java           |  139 +++
 .../elasticsearch-v6}/src/reporting-site/site.xml  |    0
 .../v6/ElasticSearchIntegrationTest.java           |  217 ++++
 .../v6/ElasticSearchMailboxConfigurationTest.java  |  227 ++++
 ...asticSearchListeningMessageSearchIndexTest.java |  270 +++++
 .../elasticsearch/v6/json/EMailerTest.java}        |   14 +-
 .../elasticsearch/v6/json/EMailersTest.java}       |   54 +-
 .../mailbox/elasticsearch/v6/json/FieldImpl.java}  |   57 +-
 .../v6/json/HeaderCollectionTest.java              |  334 +++++
 .../v6/json/IndexableMessageTest.java              |  578 +++++++++
 .../v6/json/MessageToElasticSearchJsonTest.java    |  388 ++++++
 .../elasticsearch/v6/json/MimePartTest.java}       |   54 +-
 .../elasticsearch/v6/json/SubjectsTest.java}       |   54 +-
 .../v6/query/DateResolutionFormaterTest.java       |   99 ++
 .../elasticsearch/v6/query/SearchQueryTest.java    |   77 ++
 .../src/test/resources/eml/bodyMakeTikaToFail.eml  | 1272 ++++++++++++++++++++
 .../test/resources/eml/emailWith3Attachments.eml   |   50 +
 .../src/test/resources/eml/mailWithHeaders.eml     |    0
 .../src/test/resources/logback-test.xml            |    0
 .../elasticsearch/ElasticSearchQuotaSearcher.java  |   55 +-
 .../ElasticSearchQuotaSearcherTest.java            |   60 +
 .../java/org/apache/james/quota/search/Limit.java  |    4 +
 .../org/apache/james/quota/search/LimitTest.java   |   24 +-
 mailbox/pom.xml                                    |    1 +
 server/pom.xml                                     |    5 +-
 .../james/webadmin/routes/DomainsRoutes.java       |   13 +
 .../james/webadmin/service/DomainAliasService.java |    7 +
 .../james/webadmin/routes/DomainsRoutesTest.java   |   28 +
 src/site/markdown/server/manage-webadmin.md        |    4 +-
 55 files changed, 7063 insertions(+), 204 deletions(-)
 create mode 100644 mailbox/elasticsearch-v6/pom.xml
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java} (79%)
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java} (67%)
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
 copy mailbox/{plugin/quota-search/src/main/java/org/apache/james/quota/search/Limit.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java} (60%)
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java} (59%)
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java} (58%)
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java} (79%)
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java} (61%)
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
 create mode 100644 mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
 copy {server/queue/queue-jms => mailbox/elasticsearch-v6}/src/reporting-site/site.xml (100%)
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
 copy mailbox/{plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java => elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java} (79%)
 copy mailbox/{plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java => elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java} (50%)
 copy mailbox/{plugin/quota-search/src/main/java/org/apache/james/quota/search/Limit.java => elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java} (57%)
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java
 copy mailbox/{plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java => elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java} (57%)
 copy mailbox/{plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java => elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java} (53%)
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java
 create mode 100644 mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java
 create mode 100644 mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml
 create mode 100644 mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml
 copy mailbox/{elasticsearch => elasticsearch-v6}/src/test/resources/eml/mailWithHeaders.eml (100%)
 copy mailbox/{elasticsearch => elasticsearch-v6}/src/test/resources/logback-test.xml (100%)


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


[james-project] 13/14: JAMES-2675 Rely on ScrollIterable when no limit is specified

Posted by bt...@apache.org.
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 496f4194c92f618add6ee9527a7c792996d91ded
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue May 21 18:31:21 2019 +0700

    JAMES-2675 Rely on ScrollIterable when no limit is specified
    
    Otherwise ES default limit of 10 will be applied. A more optimized "search query"
    can be used when the limit is specified, avoiding opening a scroll-session.
---
 .../elasticsearch/ElasticSearchQuotaSearcher.java  | 55 +++++++++++++++-----
 .../ElasticSearchQuotaSearcherTest.java            | 60 ++++++++++++++++++++++
 .../java/org/apache/james/quota/search/Limit.java  |  4 ++
 .../org/apache/james/quota/search/LimitTest.java   | 24 ++++++---
 4 files changed, 123 insertions(+), 20 deletions(-)

diff --git a/mailbox/plugin/quota-search-elasticsearch-v6/src/main/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcher.java b/mailbox/plugin/quota-search-elasticsearch-v6/src/main/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcher.java
index b08c4d8..e3f6ab5 100644
--- a/mailbox/plugin/quota-search-elasticsearch-v6/src/main/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcher.java
+++ b/mailbox/plugin/quota-search-elasticsearch-v6/src/main/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcher.java
@@ -24,16 +24,19 @@ import static org.apache.james.quota.search.elasticsearch.json.JsonMessageConsta
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Stream;
 
 import org.apache.james.backends.es.v6.AliasName;
 import org.apache.james.backends.es.v6.NodeMappingFactory;
 import org.apache.james.backends.es.v6.ReadAliasName;
+import org.apache.james.backends.es.v6.search.ScrollIterable;
 import org.apache.james.core.User;
 import org.apache.james.quota.search.QuotaQuery;
 import org.apache.james.quota.search.QuotaSearcher;
 import org.elasticsearch.action.search.SearchRequest;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.search.SearchHit;
 import org.elasticsearch.search.builder.SearchSourceBuilder;
 import org.elasticsearch.search.sort.SortBuilders;
@@ -42,6 +45,8 @@ import org.elasticsearch.search.sort.SortOrder;
 import com.github.steveash.guavate.Guavate;
 
 public class ElasticSearchQuotaSearcher implements QuotaSearcher {
+    private static final TimeValue TIMEOUT = TimeValue.timeValueMinutes(1);
+
     private final RestHighLevelClient client;
     private final AliasName readAlias;
     private final QuotaQueryConverter quotaQueryConverter;
@@ -55,9 +60,7 @@ public class ElasticSearchQuotaSearcher implements QuotaSearcher {
     @Override
     public List<User> search(QuotaQuery query) {
         try {
-            return Arrays.stream(client.search(prepareSearch(query), RequestOptions.DEFAULT)
-                .getHits()
-                .getHits())
+            return searchHits(query)
                 .map(SearchHit::getId)
                 .map(User::fromUsername)
                 .collect(Guavate.toImmutableList());
@@ -66,19 +69,43 @@ public class ElasticSearchQuotaSearcher implements QuotaSearcher {
         }
     }
 
-    private SearchRequest prepareSearch(QuotaQuery query) {
-        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder()
-            .query(quotaQueryConverter.from(query))
-            .sort(SortBuilders.fieldSort(USER)
-                .order(SortOrder.ASC))
-            .from(query.getOffset().getValue());
+    private Stream<SearchHit> searchHits(QuotaQuery query) throws IOException {
+        if (query.getLimit().isLimited()) {
+            return executeSingleSearch(query);
+        } else {
+            return executeScrolledSearch(query);
+        }
+    }
 
-        query.getLimit()
-            .getValue()
-            .ifPresent(sourceBuilder::size);
+    private Stream<SearchHit> executeSingleSearch(QuotaQuery query) throws IOException {
+        SearchSourceBuilder searchSourceBuilder = searchSourceBuilder(query)
+            .from(query.getOffset().getValue());
+        query.getLimit().getValue()
+            .ifPresent(searchSourceBuilder::size);
 
-        return new SearchRequest(readAlias.getValue())
+        SearchRequest searchRequest = new SearchRequest(readAlias.getValue())
             .types(NodeMappingFactory.DEFAULT_MAPPING_NAME)
-            .source(sourceBuilder);
+            .source(searchSourceBuilder);
+
+        return Arrays.stream(client.search(searchRequest, RequestOptions.DEFAULT)
+            .getHits()
+            .getHits());
+    }
+
+    private Stream<SearchHit> executeScrolledSearch(QuotaQuery query) {
+        return new ScrollIterable(client,
+            new SearchRequest(readAlias.getValue())
+                .types(NodeMappingFactory.DEFAULT_MAPPING_NAME)
+                .source(searchSourceBuilder(query))
+            .scroll(TIMEOUT))
+            .stream()
+            .flatMap(searchResponse -> Arrays.stream(searchResponse.getHits().getHits()))
+            .skip(query.getOffset().getValue());
+    }
+
+    private SearchSourceBuilder searchSourceBuilder(QuotaQuery query) {
+        return new SearchSourceBuilder()
+            .query(quotaQueryConverter.from(query))
+            .sort(SortBuilders.fieldSort(USER).order(SortOrder.ASC));
     }
 }
\ No newline at end of file
diff --git a/mailbox/plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java b/mailbox/plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java
index 2d4b79e..8c41989 100644
--- a/mailbox/plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java
+++ b/mailbox/plugin/quota-search-elasticsearch-v6/src/test/java/org/apache/james/quota/search/elasticsearch/ElasticSearchQuotaSearcherTest.java
@@ -19,10 +19,70 @@
 
 package org.apache.james.quota.search.elasticsearch;
 
+import static org.apache.james.core.CoreFixture.Domains.SIMPSON_COM;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.stream.IntStream;
+
+import org.apache.james.core.User;
+import org.apache.james.core.quota.QuotaSize;
+import org.apache.james.quota.search.Limit;
+import org.apache.james.quota.search.Offset;
+import org.apache.james.quota.search.QuotaQuery;
+import org.apache.james.quota.search.QuotaSearchTestSystem;
 import org.apache.james.quota.search.QuotaSearcherContract;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 
 @ExtendWith(ElasticSearchQuotaSearchTestSystemExtension.class)
 class ElasticSearchQuotaSearcherTest implements QuotaSearcherContract {
+    @Test
+    void searchShouldNotBeLimitedByElasticSearchDefaultSearchLimit(QuotaSearchTestSystem testSystem) throws Exception {
+        int userCount = 11;
+        testSystem.getDomainList().addDomain(SIMPSON_COM);
+        testSystem.getMaxQuotaManager().setGlobalMaxStorage(QuotaSize.size(100));
+
+        IntStream.range(0, userCount)
+            .boxed()
+            .map(i -> User.fromLocalPartWithDomain("user" + i, SIMPSON_COM))
+            .forEach(user -> provisionUser(testSystem, user));
+        testSystem.await();
+
+        assertThat(
+            testSystem.getQuotaSearcher()
+                .search(QuotaQuery.builder()
+                    .withLimit(Limit.unlimited())
+                    .build()))
+            .hasSize(userCount);
+    }
+
+    @Test
+    void searchShouldNotBeLimitedByElasticSearchDefaultSearchLimitWhenUsingOffset(QuotaSearchTestSystem testSystem) throws Exception {
+        int userCount = 12;
+        testSystem.getDomainList().addDomain(SIMPSON_COM);
+        testSystem.getMaxQuotaManager().setGlobalMaxStorage(QuotaSize.size(100));
+
+        IntStream.range(0, userCount)
+            .boxed()
+            .map(i -> User.fromLocalPartWithDomain("user" + i, SIMPSON_COM))
+            .forEach(user -> provisionUser(testSystem, user));
+        testSystem.await();
+
+        assertThat(
+            testSystem.getQuotaSearcher()
+                .search(QuotaQuery.builder()
+                    .withLimit(Limit.unlimited())
+                    .withOffset(Offset.of(1))
+                    .build()))
+            .hasSize(userCount - 1);
+    }
 
+    private void provisionUser(QuotaSearchTestSystem testSystem, User user) {
+        try {
+            testSystem.getUsersRepository().addUser(user.asString(), PASSWORD);
+            appendMessage(testSystem, user, withSize(49));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/mailbox/plugin/quota-search/src/main/java/org/apache/james/quota/search/Limit.java b/mailbox/plugin/quota-search/src/main/java/org/apache/james/quota/search/Limit.java
index b62dd5e..0ba698b 100644
--- a/mailbox/plugin/quota-search/src/main/java/org/apache/james/quota/search/Limit.java
+++ b/mailbox/plugin/quota-search/src/main/java/org/apache/james/quota/search/Limit.java
@@ -45,6 +45,10 @@ public class Limit {
         return value;
     }
 
+    public boolean isLimited() {
+        return value.isPresent();
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof Limit) {
diff --git a/mailbox/plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java b/mailbox/plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java
index 9bd6e8e..4f565cb 100644
--- a/mailbox/plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java
+++ b/mailbox/plugin/quota-search/src/test/java/org/apache/james/quota/search/LimitTest.java
@@ -26,36 +26,48 @@ import org.junit.jupiter.api.Test;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
 
-public class LimitTest {
+class LimitTest {
     @Test
-    public void shouldMatchBeanContract() {
+    void shouldMatchBeanContract() {
         EqualsVerifier.forClass(Limit.class)
             .verify();
     }
 
     @Test
-    public void getValueShouldReturnEmptyWhenUnlimited() {
+    void getValueShouldReturnEmptyWhenUnlimited() {
         assertThat(Limit.unlimited()
             .getValue())
             .isEmpty();
     }
 
     @Test
-    public void getValueShouldReturnZeroWhenZero() {
+    void getValueShouldReturnZeroWhenZero() {
         assertThat(Limit.of(0)
             .getValue())
             .contains(0);
     }
 
     @Test
-    public void getValueShouldReturnSuppliedValue() {
+    void getValueShouldReturnSuppliedValue() {
         assertThat(Limit.of(3)
             .getValue())
             .contains(3);
     }
 
     @Test
-    public void ofShouldThrowOnNegativeValue() {
+    void isLimitedShouldBeTrueWhenAValueIsSpecified() {
+        assertThat(Limit.of(3).isLimited())
+            .isTrue();
+    }
+
+    @Test
+    void isLimitedShouldBeFalseWhenUnlimited() {
+        assertThat(Limit.unlimited().isLimited())
+            .isFalse();
+    }
+
+    @Test
+    void ofShouldThrowOnNegativeValue() {
         assertThatThrownBy(() -> Limit.of(-1))
             .isInstanceOf(IllegalArgumentException.class);
     }


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


[james-project] 03/14: JAMES-2764 Refactor case insensitive analyzer into a normalizer

Posted by bt...@apache.org.
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 69f005e64465338bf209b9ba619a398a96bfdc34
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Mon May 20 17:03:25 2019 +0700

    JAMES-2764 Refactor case insensitive analyzer into a normalizer
---
 .../org/apache/james/backends/es/v6/IndexCreationFactory.java    | 9 +++++++--
 .../java/org/apache/james/backends/es/v6/NodeMappingFactory.java | 1 +
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java b/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java
index 111bbcf..8c281d0 100644
--- a/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java
+++ b/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java
@@ -131,13 +131,18 @@ public class IndexCreationFactory {
                         .field("number_of_shards", nbShards)
                         .field("number_of_replicas", nbReplica)
                         .startObject("analysis")
-                            .startObject("analyzer")
+                            .startObject("normalizer")
                                 .startObject(CASE_INSENSITIVE)
-                                    .field("tokenizer", "keyword")
+                                    .field("type", "custom")
+                                    .startArray("char_filter")
+                                    .endArray()
                                     .startArray("filter")
                                         .value("lowercase")
+                                        .value("asciifolding")
                                     .endArray()
                                 .endObject()
+                            .endObject()
+                            .startObject("analyzer")
                                 .startObject(KEEP_MAIL_AND_URL)
                                     .field("tokenizer", "uax_url_email")
                                     .startArray("filter")
diff --git a/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/NodeMappingFactory.java b/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/NodeMappingFactory.java
index 237f7fa..5fc2df1 100644
--- a/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/NodeMappingFactory.java
+++ b/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/NodeMappingFactory.java
@@ -48,6 +48,7 @@ public class NodeMappingFactory {
     public static final String RAW = "raw";
     public static final String SPLIT_EMAIL = "splitEmail";
     public static final String ANALYZER = "analyzer";
+    public static final String NORMALIZER = "normalizer";
     public static final String SEARCH_ANALYZER = "search_analyzer";
     public static final String SNOWBALL = "snowball";
     public static final String IGNORE_ABOVE = "ignore_above";


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


[james-project] 12/14: JAMES-2776 Correct get domain aliases documentation

Posted by bt...@apache.org.
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 d44abb8cfef0f88c9c3ab3cd7f932d558f7cd6a0
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Tue May 21 14:06:06 2019 +0700

    JAMES-2776 Correct get domain aliases documentation
---
 src/site/markdown/server/manage-webadmin.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 3d8bb5b..efe53b7 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -205,7 +205,7 @@ Response codes:
 
  - 200: The domain aliases was successfully retrieved
  - 400: destination.domain.tld has an invalid syntax
- - 404: destination.domain.tld is not part of handled domains or does not have local domains as aliases.
+ - 404: destination.domain.tld is not part of handled domains and does not have local domains as aliases.
 
 ### Create an alias for a domain
 


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


[james-project] 11/14: JAMES-2776 DomainsRoutes should not accept same source & destination

Posted by bt...@apache.org.
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 df4677c36aa70e02fba61bf79433c1ba11cd583c
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Tue May 21 11:27:31 2019 +0700

    JAMES-2776 DomainsRoutes should not accept same source & destination
---
 .../james/webadmin/routes/DomainsRoutes.java       | 13 ++++++++++
 .../james/webadmin/service/DomainAliasService.java |  7 ++++++
 .../james/webadmin/routes/DomainsRoutesTest.java   | 28 ++++++++++++++++++++++
 src/site/markdown/server/manage-webadmin.md        |  2 ++
 4 files changed, 50 insertions(+)

diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainsRoutes.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainsRoutes.java
index de7300d..77ed8a6 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainsRoutes.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/routes/DomainsRoutes.java
@@ -35,6 +35,7 @@ import org.apache.james.core.Domain;
 import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.domainlist.api.DomainListException;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
+import org.apache.james.rrt.api.SameSourceAndDestinationException;
 import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.dto.DomainAliasResponse;
 import org.apache.james.webadmin.service.DomainAliasService;
@@ -303,6 +304,8 @@ public class DomainsRoutes implements Routes {
             return Responses.returnNoContent(response);
         } catch (DomainAliasService.DomainNotFound e) {
             throw domainNotFound(e.getDomain());
+        } catch (SameSourceAndDestinationException e) {
+            throw sameSourceAndDestination(sourceDomain);
         }
     }
 
@@ -315,6 +318,8 @@ public class DomainsRoutes implements Routes {
             return Responses.returnNoContent(response);
         } catch (DomainAliasService.DomainNotFound e) {
             throw domainNotFound(e.getDomain());
+        } catch (SameSourceAndDestinationException e) {
+            throw sameSourceAndDestination(sourceDomain);
         }
     }
 
@@ -333,4 +338,12 @@ public class DomainsRoutes implements Routes {
             .message("The following domain is not in the domain list and has no registered local aliases: " + domain.name())
             .haltError();
     }
+
+    private HaltException sameSourceAndDestination(Domain sameDomain) {
+        return ErrorResponder.builder()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .type(ErrorType.INVALID_ARGUMENT)
+            .message("Source domain and destination domain can not have same value(" + sameDomain.name() + ")")
+            .haltError();
+    }
 }
diff --git a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DomainAliasService.java b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DomainAliasService.java
index afbc093..bc8e10d 100644
--- a/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DomainAliasService.java
+++ b/server/protocols/webadmin/webadmin-data/src/main/java/org/apache/james/webadmin/service/DomainAliasService.java
@@ -26,6 +26,7 @@ import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.domainlist.api.DomainListException;
 import org.apache.james.rrt.api.RecipientRewriteTable;
 import org.apache.james.rrt.api.RecipientRewriteTableException;
+import org.apache.james.rrt.api.SameSourceAndDestinationException;
 import org.apache.james.rrt.lib.Mapping;
 import org.apache.james.rrt.lib.MappingSource;
 import org.apache.james.webadmin.dto.DomainAliasResponse;
@@ -92,7 +93,13 @@ public class DomainAliasService {
             throw new DomainNotFound(sourceDomain);
         }
 
+        checkSameSourceAndDestination(sourceDomain, destinationDomain);
         operation.perform(MappingSource.fromDomain(sourceDomain), Mapping.domain(destinationDomain));
     }
 
+    private void checkSameSourceAndDestination(Domain source, Domain destination) throws RecipientRewriteTableException {
+        if (source.equals(destination)) {
+            throw new SameSourceAndDestinationException("Source and destination domain can't be the same!");
+        }
+    }
 }
diff --git a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainsRoutesTest.java b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainsRoutesTest.java
index 01dbe7e..e68ddc3 100644
--- a/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainsRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-data/src/test/java/org/apache/james/webadmin/routes/DomainsRoutesTest.java
@@ -398,6 +398,20 @@ class DomainsRoutesTest {
             }
 
             @Test
+            void putShouldReturnBadRequestWhenSourceAndDestinationAreTheSame() {
+                with().put(DOMAIN);
+
+                when()
+                    .put(DOMAIN + "/aliases/" + DOMAIN)
+                .then()
+                    .contentType(ContentType.JSON)
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
+                    .body("type", is("InvalidArgument"))
+                    .body("message", is("Source domain and destination domain can not have same value(" + DOMAIN + ")"));
+            }
+
+            @Test
             void putShouldNotFailOnExternalDomainAlias() {
                 with().put(DOMAIN);
 
@@ -510,6 +524,20 @@ class DomainsRoutesTest {
             }
 
             @Test
+            void deleteShouldReturnBadRequestWhenSourceAndDestinationAreTheSame() {
+                with().put(DOMAIN);
+
+                when()
+                    .delete(DOMAIN + "/aliases/" + DOMAIN)
+                .then()
+                    .contentType(ContentType.JSON)
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .body("statusCode", is(HttpStatus.BAD_REQUEST_400))
+                    .body("type", is("InvalidArgument"))
+                    .body("message", is("Source domain and destination domain can not have same value(" + DOMAIN + ")"));
+            }
+
+            @Test
             void deleteSourceDomainShouldRemoveTheCorrespondingAlias() {
                 with().put(ALIAS_DOMAIN_2);
                 with().put(ALIAS_DOMAIN);
diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 5217042..3d8bb5b 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -223,6 +223,7 @@ Response codes:
 
  - 204: The redirection now exists
  - 400: source.domain.tld or destination.domain.tld have an invalid syntax
+ - 400: source domain and destination domain are the same
  - 404: source.domain.tld are not part of handled domains.
 
 ### Delete an alias for a domain
@@ -242,6 +243,7 @@ Response codes:
 
  - 204: The redirection now exists
  - 400: source.domain.tld or destination.domain.tld have an invalid syntax
+ - 400: source domain and destination domain are the same
  - 404: source.domain.tld are not part of handled domains.
 
 ## Administrating users


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


[james-project] 14/14: Fix the modules list order in the server POM file

Posted by bt...@apache.org.
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 cbdc3fa93a6025e8565c66729f57508b9a11d13d
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed May 22 14:22:30 2019 +0700

    Fix the modules list order in the server POM file
---
 server/pom.xml | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/server/pom.xml b/server/pom.xml
index 4ce772a..07e2ae2 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -72,15 +72,14 @@
         <module>dns-service/dnsservice-test</module>
 
         <module>mailet/integration-testing</module>
-
         <module>mailet/mailetcontainer-api</module>
         <module>mailet/mailetcontainer-camel</module>
         <module>mailet/mailets</module>
 
         <module>mailrepository/deleted-messages-vault-repository</module>
         <module>mailrepository/mailrepository-api</module>
-        <module>mailrepository/mailrepository-memory</module>
         <module>mailrepository/mailrepository-cassandra</module>
+        <module>mailrepository/mailrepository-memory</module>
 
         <module>protocols/fetchmail</module>
         <module>protocols/jmap</module>
@@ -92,8 +91,8 @@
         <module>protocols/protocols-managesieve</module>
         <module>protocols/protocols-pop3</module>
         <module>protocols/protocols-smtp</module>
-        <module>protocols/webadmin-integration-test</module>
         <module>protocols/webadmin</module>
+        <module>protocols/webadmin-integration-test</module>
 
         <module>queue/queue-activemq</module>
         <module>queue/queue-api</module>


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


[james-project] 01/14: JAMES-2764 Copy of mailbox-elasticsearch module to a new mailbox-elasticsearch-v6 module

Posted by bt...@apache.org.
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 861b27ee70b0d79ccdcb8415dcc9715d1d60ba27
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Thu May 16 15:45:03 2019 +0700

    JAMES-2764 Copy of mailbox-elasticsearch module to a new mailbox-elasticsearch-v6 module
---
 mailbox/elasticsearch-v6/pom.xml                   |  203 ++++
 .../v6/ElasticSearchMailboxConfiguration.java      |  213 ++++
 .../mailbox/elasticsearch/v6/IndexAttachments.java |   24 +
 .../v6/MailboxElasticSearchConstants.java          |   37 +
 .../elasticsearch/v6/MailboxIndexCreationUtil.java |   56 +
 .../elasticsearch/v6/MailboxMappingFactory.java    |  365 ++++++
 .../ElasticSearchListeningMessageSearchIndex.java  |  196 +++
 .../mailbox/elasticsearch/v6/json/EMailer.java     |   75 ++
 .../mailbox/elasticsearch/v6/json/EMailers.java    |   52 +
 .../elasticsearch/v6/json/HeaderCollection.java    |  224 ++++
 .../elasticsearch/v6/json/IndexableMessage.java    |  471 ++++++++
 .../v6/json/JsonMessageConstants.java              |   83 ++
 .../v6/json/MessageToElasticSearchJson.java        |   86 ++
 .../elasticsearch/v6/json/MessageUpdateJson.java   |   79 ++
 .../mailbox/elasticsearch/v6/json/MimePart.java    |  303 +++++
 .../v6/json/MimePartContainerBuilder.java          |   50 +
 .../elasticsearch/v6/json/MimePartParser.java      |  129 ++
 .../v6/json/RootMimePartContainerBuilder.java      |   96 ++
 .../elasticsearch/v6/json/Serializable.java        |   25 +
 .../mailbox/elasticsearch/v6/json/Subjects.java    |   50 +
 .../elasticsearch/v6/query/CriterionConverter.java |  310 +++++
 .../v6/query/DateResolutionFormater.java           |   73 ++
 .../elasticsearch/v6/query/QueryConverter.java     |   79 ++
 .../elasticsearch/v6/query/SortConverter.java      |   81 ++
 .../v6/search/ElasticSearchSearcher.java           |  138 +++
 .../elasticsearch-v6/src/reporting-site/site.xml   |   29 +
 .../v6/ElasticSearchIntegrationTest.java           |  223 ++++
 .../v6/ElasticSearchMailboxConfigurationTest.java  |  219 ++++
 ...asticSearchListeningMessageSearchIndexTest.java |  269 +++++
 .../elasticsearch/v6/json/EMailersTest.java        |   66 +
 .../mailbox/elasticsearch/v6/json/FieldImpl.java   |   65 +
 .../v6/json/HeaderCollectionTest.java              |  334 +++++
 .../v6/json/IndexableMessageTest.java              |  578 +++++++++
 .../v6/json/MessageToElasticSearchJsonTest.java    |  388 ++++++
 .../elasticsearch/v6/json/MimePartTest.java        |   50 +
 .../elasticsearch/v6/json/SubjectsTest.java        |   66 +
 .../v6/query/DateResolutionFormaterTest.java       |   99 ++
 .../elasticsearch/v6/query/SearchQueryTest.java    |   77 ++
 .../src/test/resources/eml/bodyMakeTikaToFail.eml  | 1272 ++++++++++++++++++++
 .../test/resources/eml/emailWith3Attachments.eml   |   50 +
 .../src/test/resources/eml/mailWithHeaders.eml     |   14 +
 .../src/test/resources/logback-test.xml            |   24 +
 mailbox/pom.xml                                    |    1 +
 43 files changed, 7322 insertions(+)

diff --git a/mailbox/elasticsearch-v6/pom.xml b/mailbox/elasticsearch-v6/pom.xml
new file mode 100644
index 0000000..6bf0500
--- /dev/null
+++ b/mailbox/elasticsearch-v6/pom.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>apache-james-mailbox</artifactId>
+        <version>3.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>apache-james-mailbox-elasticsearch-v6</artifactId>
+    <name>Apache James :: Mailbox :: ElasticSearch :: v6</name>
+    <description>Apache James Mailbox IMAP search implementation using ElasticSearch v6</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-backends-es</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-backends-es</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-event-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-store</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-store</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-tika</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-tika</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-util</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>james-server-testing</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-jdk8</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.steveash.guavate</groupId>
+            <artifactId>guavate</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.jayway.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.mail</groupId>
+            <artifactId>javax.mail</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-assertj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>2.2.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.elasticsearch</groupId>
+            <artifactId>elasticsearch</artifactId>
+            <version>2.2.1</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <reuseForks>true</reuseForks>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
new file mode 100644
index 0000000..6ff086c
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
@@ -0,0 +1,213 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.james.backends.es.IndexName;
+import org.apache.james.backends.es.ReadAliasName;
+import org.apache.james.backends.es.WriteAliasName;
+import org.apache.james.util.OptionalUtils;
+
+public class ElasticSearchMailboxConfiguration {
+
+    public static class Builder {
+        private Optional<IndexName> indexMailboxName;
+        private Optional<ReadAliasName> readAliasMailboxName;
+        private Optional<WriteAliasName> writeAliasMailboxName;
+        private Optional<IndexAttachments> indexAttachment;
+
+        public Builder() {
+            indexMailboxName = Optional.empty();
+            readAliasMailboxName = Optional.empty();
+            writeAliasMailboxName = Optional.empty();
+            indexAttachment = Optional.empty();
+        }
+
+        public Builder indexMailboxName(IndexName indexMailboxName) {
+            return indexMailboxName(Optional.of(indexMailboxName));
+        }
+
+        public Builder indexMailboxName(Optional<IndexName> indexMailboxName) {
+            this.indexMailboxName = indexMailboxName;
+            return this;
+        }
+
+        public Builder readAliasMailboxName(ReadAliasName readAliasMailboxName) {
+            return readAliasMailboxName(Optional.of(readAliasMailboxName));
+        }
+
+        public Builder readAliasMailboxName(Optional<ReadAliasName> readAliasMailboxName) {
+            this.readAliasMailboxName = readAliasMailboxName;
+            return this;
+        }
+
+        public Builder writeAliasMailboxName(WriteAliasName writeAliasMailboxName) {
+            return writeAliasMailboxName(Optional.of(writeAliasMailboxName));
+        }
+
+        public Builder writeAliasMailboxName(Optional<WriteAliasName> writeAliasMailboxName) {
+            this.writeAliasMailboxName = writeAliasMailboxName;
+            return this;
+        }
+
+
+        public Builder indexAttachment(IndexAttachments indexAttachment) {
+            this.indexAttachment = Optional.of(indexAttachment);
+            return this;
+        }
+
+
+
+        public ElasticSearchMailboxConfiguration build() {
+            return new ElasticSearchMailboxConfiguration(
+                indexMailboxName.orElse(MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX),
+                readAliasMailboxName.orElse(MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS),
+                writeAliasMailboxName.orElse(MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS),
+                indexAttachment.orElse(IndexAttachments.YES));
+        }
+    }
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final String ELASTICSEARCH_HOSTS = "elasticsearch.hosts";
+    public static final String ELASTICSEARCH_MASTER_HOST = "elasticsearch.masterHost";
+    public static final String ELASTICSEARCH_PORT = "elasticsearch.port";
+    public static final String ELASTICSEARCH_INDEX_NAME = "elasticsearch.index.name";
+    public static final String ELASTICSEARCH_INDEX_MAILBOX_NAME = "elasticsearch.index.mailbox.name";
+    public static final String ELASTICSEARCH_NB_REPLICA = "elasticsearch.nb.replica";
+    public static final String ELASTICSEARCH_NB_SHARDS = "elasticsearch.nb.shards";
+    public static final String ELASTICSEARCH_ALIAS_READ_NAME = "elasticsearch.alias.read.name";
+    public static final String ELASTICSEARCH_ALIAS_WRITE_NAME = "elasticsearch.alias.write.name";
+    public static final String ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME = "elasticsearch.alias.read.mailbox.name";
+    public static final String ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME = "elasticsearch.alias.write.mailbox.name";
+    public static final String ELASTICSEARCH_INDEX_QUOTA_RATIO_NAME = "elasticsearch.index.quota.ratio.name";
+    public static final String ELASTICSEARCH_ALIAS_READ_QUOTA_RATIO_NAME = "elasticsearch.alias.read.quota.ratio.name";
+    public static final String ELASTICSEARCH_ALIAS_WRITE_QUOTA_RATIO_NAME = "elasticsearch.alias.write.quota.ratio.name";
+    public static final String ELASTICSEARCH_RETRY_CONNECTION_MIN_DELAY = "elasticsearch.retryConnection.minDelay";
+    public static final String ELASTICSEARCH_RETRY_CONNECTION_MAX_RETRIES = "elasticsearch.retryConnection.maxRetries";
+    public static final String ELASTICSEARCH_INDEX_ATTACHMENTS = "elasticsearch.indexAttachments";
+
+    public static final int DEFAULT_CONNECTION_MAX_RETRIES = 7;
+    public static final int DEFAULT_CONNECTION_MIN_DELAY = 3000;
+    public static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
+    public static final int DEFAULT_NB_SHARDS = 5;
+    public static final int DEFAULT_NB_REPLICA = 1;
+    public static final int DEFAULT_PORT = 9300;
+    public static final Optional<Integer> DEFAULT_PORT_AS_OPTIONAL = Optional.of(DEFAULT_PORT);
+
+    public static final ElasticSearchMailboxConfiguration DEFAULT_CONFIGURATION = builder().build();
+
+    public static ElasticSearchMailboxConfiguration fromProperties(Configuration configuration) {
+        return builder()
+            .indexMailboxName(computeMailboxIndexName(configuration))
+            .readAliasMailboxName(computeMailboxReadAlias(configuration))
+            .writeAliasMailboxName(computeMailboxWriteAlias(configuration))
+            .indexAttachment(provideIndexAttachments(configuration))
+            .build();
+    }
+
+    public static Optional<IndexName> computeMailboxIndexName(Configuration configuration) {
+        return OptionalUtils.or(
+            Optional.ofNullable(configuration.getString(ELASTICSEARCH_INDEX_MAILBOX_NAME))
+                .map(IndexName::new),
+            Optional.ofNullable(configuration.getString(ELASTICSEARCH_INDEX_NAME))
+                .map(IndexName::new));
+    }
+
+    public static Optional<WriteAliasName> computeMailboxWriteAlias(Configuration configuration) {
+        return OptionalUtils.or(
+            Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME))
+                .map(WriteAliasName::new),
+            Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_WRITE_NAME))
+                .map(WriteAliasName::new));
+    }
+
+    public static Optional<ReadAliasName> computeMailboxReadAlias(Configuration configuration) {
+        return OptionalUtils.or(
+            Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME))
+                .map(ReadAliasName::new),
+            Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_READ_NAME))
+                .map(ReadAliasName::new));
+    }
+
+
+    private static IndexAttachments provideIndexAttachments(Configuration configuration) {
+        if (configuration.getBoolean(ELASTICSEARCH_INDEX_ATTACHMENTS, DEFAULT_INDEX_ATTACHMENTS)) {
+            return IndexAttachments.YES;
+        }
+        return IndexAttachments.NO;
+    }
+
+
+
+
+    private final IndexName indexMailboxName;
+    private final ReadAliasName readAliasMailboxName;
+    private final WriteAliasName writeAliasMailboxName;
+    private final IndexAttachments indexAttachment;
+
+    private ElasticSearchMailboxConfiguration(IndexName indexMailboxName, ReadAliasName readAliasMailboxName,
+                                              WriteAliasName writeAliasMailboxName, IndexAttachments indexAttachment) {
+        this.indexMailboxName = indexMailboxName;
+        this.readAliasMailboxName = readAliasMailboxName;
+        this.writeAliasMailboxName = writeAliasMailboxName;
+        this.indexAttachment = indexAttachment;
+    }
+
+
+    public IndexName getIndexMailboxName() {
+        return indexMailboxName;
+    }
+
+    public ReadAliasName getReadAliasMailboxName() {
+        return readAliasMailboxName;
+    }
+
+    public WriteAliasName getWriteAliasMailboxName() {
+        return writeAliasMailboxName;
+    }
+
+    public IndexAttachments getIndexAttachment() {
+        return indexAttachment;
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        if (o instanceof ElasticSearchMailboxConfiguration) {
+            ElasticSearchMailboxConfiguration that = (ElasticSearchMailboxConfiguration) o;
+
+            return Objects.equals(this.indexAttachment, that.indexAttachment)
+                && Objects.equals(this.indexMailboxName, that.indexMailboxName)
+                && Objects.equals(this.readAliasMailboxName, that.readAliasMailboxName)
+                && Objects.equals(this.writeAliasMailboxName, that.writeAliasMailboxName);
+        }
+        return false;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(indexMailboxName, readAliasMailboxName, writeAliasMailboxName, indexAttachment, writeAliasMailboxName);
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java
new file mode 100644
index 0000000..f827bd8
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/IndexAttachments.java
@@ -0,0 +1,24 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+public enum IndexAttachments {
+    NO, YES
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
new file mode 100644
index 0000000..301127e
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
@@ -0,0 +1,37 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+import org.apache.james.backends.es.IndexName;
+import org.apache.james.backends.es.ReadAliasName;
+import org.apache.james.backends.es.TypeName;
+import org.apache.james.backends.es.WriteAliasName;
+
+public interface MailboxElasticSearchConstants {
+
+    interface InjectionNames {
+        String MAILBOX = "mailbox";
+    }
+
+    WriteAliasName DEFAULT_MAILBOX_WRITE_ALIAS = new WriteAliasName("mailboxWriteAlias");
+    ReadAliasName DEFAULT_MAILBOX_READ_ALIAS = new ReadAliasName("mailboxReadAlias");
+    IndexName DEFAULT_MAILBOX_INDEX = new IndexName("mailbox_v1");
+    TypeName MESSAGE_TYPE = new TypeName("message");
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
new file mode 100644
index 0000000..4d320e4
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
@@ -0,0 +1,56 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+import org.apache.james.backends.es.ElasticSearchConfiguration;
+import org.apache.james.backends.es.IndexCreationFactory;
+import org.apache.james.backends.es.IndexName;
+import org.apache.james.backends.es.NodeMappingFactory;
+import org.apache.james.backends.es.ReadAliasName;
+import org.apache.james.backends.es.WriteAliasName;
+import org.elasticsearch.client.Client;
+
+public class MailboxIndexCreationUtil {
+
+    public static Client prepareClient(Client client,
+                                       ReadAliasName readAlias,
+                                       WriteAliasName writeAlias,
+                                       IndexName indexName,
+                                       ElasticSearchConfiguration configuration) {
+
+        return NodeMappingFactory.applyMapping(
+            new IndexCreationFactory(configuration)
+                .useIndex(indexName)
+                .addAlias(readAlias)
+                .addAlias(writeAlias)
+                .createIndexAndAliases(client),
+            indexName,
+            MailboxElasticSearchConstants.MESSAGE_TYPE,
+            MailboxMappingFactory.getMappingContent());
+    }
+
+    public static Client prepareDefaultClient(Client client, ElasticSearchConfiguration configuration) {
+        return prepareClient(client,
+            MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
+            MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
+            MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX,
+                configuration);
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
new file mode 100644
index 0000000..2ecd6d3
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
@@ -0,0 +1,365 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+import static org.apache.james.backends.es.IndexCreationFactory.CASE_INSENSITIVE;
+import static org.apache.james.backends.es.IndexCreationFactory.KEEP_MAIL_AND_URL;
+import static org.apache.james.backends.es.IndexCreationFactory.SNOWBALL_KEEP_MAIL_AND_URL;
+import static org.apache.james.backends.es.NodeMappingFactory.ANALYZER;
+import static org.apache.james.backends.es.NodeMappingFactory.BOOLEAN;
+import static org.apache.james.backends.es.NodeMappingFactory.FIELDS;
+import static org.apache.james.backends.es.NodeMappingFactory.FORMAT;
+import static org.apache.james.backends.es.NodeMappingFactory.IGNORE_ABOVE;
+import static org.apache.james.backends.es.NodeMappingFactory.INDEX;
+import static org.apache.james.backends.es.NodeMappingFactory.LONG;
+import static org.apache.james.backends.es.NodeMappingFactory.NESTED;
+import static org.apache.james.backends.es.NodeMappingFactory.NOT_ANALYZED;
+import static org.apache.james.backends.es.NodeMappingFactory.PROPERTIES;
+import static org.apache.james.backends.es.NodeMappingFactory.RAW;
+import static org.apache.james.backends.es.NodeMappingFactory.SEARCH_ANALYZER;
+import static org.apache.james.backends.es.NodeMappingFactory.SNOWBALL;
+import static org.apache.james.backends.es.NodeMappingFactory.SPLIT_EMAIL;
+import static org.apache.james.backends.es.NodeMappingFactory.STRING;
+import static org.apache.james.backends.es.NodeMappingFactory.TYPE;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.BCC;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.CC;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.DATE;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.FROM;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.HAS_ATTACHMENT;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.HTML_BODY;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_ANSWERED;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_DELETED;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_DRAFT;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_FLAGGED;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_RECENT;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.IS_UNREAD;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MAILBOX_ID;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MEDIA_TYPE;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MESSAGE_ID;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MIME_MESSAGE_ID;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.MODSEQ;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SENT_DATE;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SIZE;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SUBJECT;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.SUBTYPE;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.TEXT;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.TEXT_BODY;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.TO;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.UID;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.USERS;
+import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.USER_FLAGS;
+import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
+
+import java.io.IOException;
+
+import org.apache.james.backends.es.NodeMappingFactory;
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.EMailer;
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.Property;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+public class MailboxMappingFactory {
+
+    private static final int MAXIMUM_TERM_LENGTH = 4096;
+    private static final String STANDARD = "standard";
+
+    public static XContentBuilder getMappingContent() {
+        try {
+            return jsonBuilder()
+                .startObject()
+
+                    .startObject(MailboxElasticSearchConstants.MESSAGE_TYPE.getValue())
+                        .startObject(PROPERTIES)
+
+                            .startObject(MESSAGE_ID)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(UID)
+                                .field(TYPE, LONG)
+                            .endObject()
+
+                            .startObject(MODSEQ)
+                                .field(TYPE, LONG)
+                            .endObject()
+
+                            .startObject(SIZE)
+                                .field(TYPE, LONG)
+                            .endObject()
+
+                            .startObject(IS_ANSWERED)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(IS_DELETED)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(IS_DRAFT)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(IS_FLAGGED)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(IS_RECENT)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(IS_UNREAD)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(DATE)
+                                .field(TYPE, NodeMappingFactory.DATE)
+                                .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
+                            .endObject()
+
+                            .startObject(SENT_DATE)
+                                .field(TYPE, NodeMappingFactory.DATE)
+                                .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
+                            .endObject()
+
+                            .startObject(MEDIA_TYPE)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(SUBTYPE)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(USER_FLAGS)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(FROM)
+                                .field(TYPE, NESTED)
+                                .startObject(PROPERTIES)
+                                    .startObject(EMailer.NAME)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                    .startObject(EMailer.ADDRESS)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, STANDARD)
+                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(SUBJECT)
+                                .field(TYPE, STRING)
+                                .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                .startObject(FIELDS)
+                                    .startObject(RAW)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, CASE_INSENSITIVE)
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(TO)
+                                .field(TYPE, NESTED)
+                                .startObject(PROPERTIES)
+                                    .startObject(EMailer.NAME)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                    .startObject(EMailer.ADDRESS)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, STANDARD)
+                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(CC)
+                                .field(TYPE, NESTED)
+                                .startObject(PROPERTIES)
+                                    .startObject(EMailer.NAME)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                    .startObject(EMailer.ADDRESS)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, STANDARD)
+                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                            .field(TYPE, STRING)
+                                            .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(BCC)
+                                .field(TYPE, NESTED)
+                                .startObject(PROPERTIES)
+                                    .startObject(EMailer.NAME)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                    .startObject(EMailer.ADDRESS)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, STANDARD)
+                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                        .startObject(FIELDS)
+                                            .startObject(RAW)
+                                                .field(TYPE, STRING)
+                                                .field(ANALYZER, CASE_INSENSITIVE)
+                                            .endObject()
+                                        .endObject()
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(MAILBOX_ID)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(MIME_MESSAGE_ID)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(USERS)
+                                .field(TYPE, STRING)
+                                .field(INDEX, NOT_ANALYZED)
+                            .endObject()
+
+                            .startObject(PROPERTIES)
+                                .field(TYPE, NESTED)
+                                .startObject(PROPERTIES)
+                                    .startObject(Property.NAMESPACE)
+                                        .field(TYPE, STRING)
+                                        .field(INDEX, NOT_ANALYZED)
+                                    .endObject()
+                                    .startObject(Property.NAME)
+                                        .field(TYPE, STRING)
+                                        .field(INDEX, NOT_ANALYZED)
+                                    .endObject()
+                                    .startObject(Property.VALUE)
+                                        .field(TYPE, STRING)
+                                        .field(INDEX, NOT_ANALYZED)
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(TEXT_BODY)
+                                .field(TYPE, STRING)
+                                .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                .startObject(FIELDS)
+                                    .startObject(SPLIT_EMAIL)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, STANDARD)
+                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                    .endObject()
+                                    .startObject(RAW)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, CASE_INSENSITIVE)
+                                        .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(HTML_BODY)
+                                .field(TYPE, STRING)
+                                .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                .startObject(FIELDS)
+                                    .startObject(SPLIT_EMAIL)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, STANDARD)
+                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                    .endObject()
+                                    .startObject(RAW)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, CASE_INSENSITIVE)
+                                        .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+
+                            .startObject(HAS_ATTACHMENT)
+                                .field(TYPE, BOOLEAN)
+                            .endObject()
+
+                            .startObject(TEXT)
+                                .field(TYPE, STRING)
+                                .field(ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
+                                .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
+                                .startObject(FIELDS)
+                                    .startObject(SPLIT_EMAIL)
+                                        .field(TYPE, STRING)
+                                        .field(ANALYZER, SNOWBALL)
+                                        .field(SEARCH_ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
+                                    .endObject()
+                                .endObject()
+                            .endObject()
+                        .endObject()
+                    .endObject()
+                .endObject();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
new file mode 100644
index 0000000..8ee2204
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
@@ -0,0 +1,196 @@
+/****************************************************************
+ * 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.elasticsearch.v6.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.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.james.backends.es.ElasticSearchIndexer;
+import org.apache.james.backends.es.UpdatedRepresentation;
+import org.apache.james.mailbox.MailboxManager.MessageCapabilities;
+import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.elasticsearch.v6.MailboxElasticSearchConstants;
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
+import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
+import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
+import org.apache.james.mailbox.events.Group;
+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.SearchQuery;
+import org.apache.james.mailbox.model.UpdatedFlags;
+import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
+import org.apache.james.mailbox.store.SessionProvider;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.search.ListeningMessageSearchIndex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSearchIndex {
+    public static class ElasticSearchListeningMessageSearchIndexGroup extends Group {}
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchListeningMessageSearchIndex.class);
+    private static final String ID_SEPARATOR = ":";
+    private static final Group GROUP = new ElasticSearchListeningMessageSearchIndexGroup();
+
+    private final ElasticSearchIndexer elasticSearchIndexer;
+    private final ElasticSearchSearcher searcher;
+    private final MessageToElasticSearchJson messageToElasticSearchJson;
+
+    @Inject
+    public ElasticSearchListeningMessageSearchIndex(MailboxSessionMapperFactory factory,
+                                                    @Named(MailboxElasticSearchConstants.InjectionNames.MAILBOX) ElasticSearchIndexer indexer,
+                                                    ElasticSearchSearcher searcher, MessageToElasticSearchJson messageToElasticSearchJson,
+                                                    SessionProvider sessionProvider) {
+        super(factory, sessionProvider);
+        this.elasticSearchIndexer = indexer;
+        this.messageToElasticSearchJson = messageToElasticSearchJson;
+        this.searcher = searcher;
+    }
+
+    @Override
+    public Group getDefaultGroup() {
+        return GROUP;
+    }
+
+    @Override
+    public EnumSet<SearchCapabilities> getSupportedCapabilities(EnumSet<MessageCapabilities> messageCapabilities) {
+        return EnumSet.of(
+            SearchCapabilities.MultimailboxSearch,
+            SearchCapabilities.Text,
+            SearchCapabilities.FullText,
+            SearchCapabilities.Attachment,
+            SearchCapabilities.AttachmentFileName,
+            SearchCapabilities.PartialEmailMatch);
+    }
+    
+    @Override
+    public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) throws MailboxException {
+        Preconditions.checkArgument(session != null, "'session' is mandatory");
+        Optional<Long> noLimit = Optional.empty();
+        return searcher
+                .search(ImmutableList.of(mailbox.getMailboxId()), searchQuery, noLimit)
+                .map(SearchResult::getMessageUid)
+                .iterator();
+    }
+    
+    @Override
+    public List<MessageId> search(MailboxSession session, Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit)
+            throws MailboxException {
+        Preconditions.checkArgument(session != null, "'session' is mandatory");
+
+        if (mailboxIds.isEmpty()) {
+            return ImmutableList.of();
+        }
+
+        return searcher.search(mailboxIds, searchQuery, Optional.empty())
+            .peek(this::logIfNoMessageId)
+            .map(SearchResult::getMessageId)
+            .map(Optional::get)
+            .distinct()
+            .limit(limit)
+            .collect(Guavate.toImmutableList());
+    }
+
+    @Override
+    public void add(MailboxSession session, Mailbox mailbox, MailboxMessage message) throws JsonProcessingException {
+        LOGGER.info("Indexing mailbox {}-{} of user {} on message {}",
+            mailbox.getName(),
+            mailbox.getMailboxId(),
+            session.getUser().asString(),
+            message.getUid());
+
+        String jsonContent = generateIndexedJson(mailbox, message, session);
+        elasticSearchIndexer.index(indexIdFor(mailbox, message.getUid()), jsonContent);
+    }
+
+    private String generateIndexedJson(Mailbox mailbox, MailboxMessage message, MailboxSession session) throws JsonProcessingException {
+        try {
+            return messageToElasticSearchJson.convertToJson(message, ImmutableList.of(session.getUser()));
+        } catch (Exception e) {
+            LOGGER.warn("Indexing mailbox {}-{} of user {} on message {} without attachments ",
+                mailbox.getName(),
+                mailbox.getMailboxId().serialize(),
+                session.getUser().asString(),
+                message.getUid(),
+                e);
+            return messageToElasticSearchJson.convertToJsonWithoutAttachment(message, ImmutableList.of(session.getUser()));
+        }
+    }
+
+    @Override
+    public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) {
+            elasticSearchIndexer.delete(expungedUids.stream()
+                .map(uid ->  indexIdFor(mailbox, uid))
+                .collect(Guavate.toImmutableList()));
+    }
+
+    @Override
+    public void deleteAll(MailboxSession session, Mailbox mailbox) {
+            elasticSearchIndexer.deleteAllMatchingQuery(
+                termQuery(
+                    JsonMessageConstants.MAILBOX_ID,
+                    mailbox.getMailboxId().serialize()));
+    }
+
+    @Override
+    public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) {
+            elasticSearchIndexer.update(updatedFlagsList.stream()
+                .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
+                    updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
+                    .sneakyThrow())
+                .collect(Guavate.toImmutableList()));
+    }
+
+    private UpdatedRepresentation createUpdatedDocumentPartFromUpdatedFlags(Mailbox mailbox, UpdatedFlags updatedFlags) throws JsonProcessingException {
+            return new UpdatedRepresentation(
+                indexIdFor(mailbox, updatedFlags.getUid()),
+                    messageToElasticSearchJson.getUpdatedJsonMessagePart(
+                        updatedFlags.getNewFlags(),
+                        updatedFlags.getModSeq()));
+    }
+
+    private String indexIdFor(Mailbox mailbox, MessageUid uid) {
+        return String.join(ID_SEPARATOR, mailbox.getMailboxId().serialize(), String.valueOf(uid.asLong()));
+    }
+
+    private void logIfNoMessageId(SearchResult searchResult) {
+        if (!searchResult.getMessageId().isPresent()) {
+            LOGGER.error("No messageUid for {} in mailbox {}", searchResult.getMessageUid(), searchResult.getMailboxId());
+        }
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
new file mode 100644
index 0000000..83ff556
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
@@ -0,0 +1,75 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Joiner;
+import com.google.common.base.MoreObjects;
+
+public class EMailer implements Serializable {
+
+    private final String name;
+    private final String address;
+
+    public EMailer(String name, String address) {
+        this.name = name;
+        this.address = address;
+    }
+
+    @JsonProperty(JsonMessageConstants.EMailer.NAME)
+    public String getName() {
+        return name;
+    }
+
+    @JsonProperty(JsonMessageConstants.EMailer.ADDRESS)
+    public String getAddress() {
+        return address;
+    }
+
+    @Override
+    public String serialize() {
+        return Joiner.on(" ").join(name, address);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof EMailer) {
+            EMailer otherEMailer = (EMailer) o;
+            return Objects.equals(name, otherEMailer.name)
+                && Objects.equals(address, otherEMailer.address);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, address);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("name", name)
+            .add("address", address)
+            .toString();
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
new file mode 100644
index 0000000..e5e8e65
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
@@ -0,0 +1,52 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.google.common.base.Preconditions;
+
+public class EMailers implements Serializable {
+
+    public static EMailers from(Set<EMailer> emailers) {
+        Preconditions.checkNotNull(emailers, "'emailers' is mandatory");
+        return new EMailers(emailers);
+    }
+
+    private final Set<EMailer> emailers;
+
+    private EMailers(Set<EMailer> emailers) {
+        this.emailers = emailers;
+    }
+
+    @JsonValue
+    public Set<EMailer> getEmailers() {
+        return emailers;
+    }
+
+    @Override
+    public String serialize() {
+        return emailers.stream()
+            .map(EMailer::serialize)
+            .collect(Collectors.joining(" "));
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java
new file mode 100644
index 0000000..83f2b52
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollection.java
@@ -0,0 +1,224 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.time.ZonedDateTime;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.james.mailbox.store.search.SearchUtil;
+import org.apache.james.mailbox.store.search.comparator.SentDateComparator;
+import org.apache.james.mime4j.dom.address.Address;
+import org.apache.james.mime4j.dom.address.Group;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.field.address.LenientAddressParser;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.util.MimeUtil;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+
+public class HeaderCollection {
+
+    public static class Builder {
+
+        private final Set<EMailer> toAddressSet;
+        private final Set<EMailer> fromAddressSet;
+        private final Set<EMailer> ccAddressSet;
+        private final Set<EMailer> bccAddressSet;
+        private final Set<EMailer> replyToAddressSet;
+        private final Set<String> subjectSet;
+        private final Multimap<String, String> headers;
+        private Optional<ZonedDateTime> sentDate;
+        private Optional<String> messageID;
+
+        private Builder() {
+            toAddressSet = new HashSet<>();
+            fromAddressSet = new HashSet<>();
+            ccAddressSet = new HashSet<>();
+            bccAddressSet = new HashSet<>();
+            replyToAddressSet = new HashSet<>();
+            subjectSet = new HashSet<>();
+            headers = ArrayListMultimap.create();
+            sentDate = Optional.empty();
+            messageID = Optional.empty();
+        }
+
+        public Builder add(Field field) {
+            Preconditions.checkNotNull(field);
+            String headerName = field.getName().toLowerCase(Locale.US);
+            String rawHeaderValue = field.getBody();
+            String sanitizedValue = MimeUtil.unscrambleHeaderValue(rawHeaderValue);
+
+            if (!headerName.contains(".")) {
+                headers.put(headerName, sanitizedValue);
+            }
+            handleSpecificHeader(headerName, sanitizedValue, rawHeaderValue);
+            return this;
+        }
+
+        public HeaderCollection build() {
+            return new HeaderCollection(
+                ImmutableSet.copyOf(toAddressSet),
+                ImmutableSet.copyOf(fromAddressSet),
+                ImmutableSet.copyOf(ccAddressSet),
+                ImmutableSet.copyOf(bccAddressSet),
+                ImmutableSet.copyOf(replyToAddressSet),
+                ImmutableSet.copyOf(subjectSet),
+                ImmutableMultimap.copyOf(headers),
+                sentDate, messageID);
+        }
+
+        private void handleSpecificHeader(String headerName, String headerValue, String rawHeaderValue) {
+            switch (headerName) {
+                case TO:
+                case FROM:
+                case CC:
+                case BCC:
+                case REPLY_TO:
+                    manageAddressField(headerName, rawHeaderValue);
+                    break;
+                case SUBJECT:
+                    subjectSet.add(headerValue);
+                    break;
+                case DATE:
+                    sentDate = SentDateComparator.toISODate(headerValue);
+                    break;
+                case MESSAGE_ID:
+                    messageID = Optional.ofNullable(headerValue);
+                    break;
+            }
+        }
+
+        private void manageAddressField(String headerName, String rawHeaderValue) {
+            LenientAddressParser.DEFAULT
+                .parseAddressList(rawHeaderValue)
+                .stream()
+                .flatMap(this::convertAddressToMailboxStream)
+                .map((mailbox) -> new EMailer(SearchUtil.getDisplayAddress(mailbox), mailbox.getAddress()))
+                .collect(Collectors.toCollection(() -> getAddressSet(headerName)));
+        }
+
+        private Stream<Mailbox> convertAddressToMailboxStream(Address address) {
+            if (address instanceof Mailbox) {
+                return Stream.of((Mailbox) address);
+            } else if (address instanceof Group) {
+                return ((Group) address).getMailboxes().stream();
+            }
+            return Stream.empty();
+        }
+
+        private Set<EMailer> getAddressSet(String headerName) {
+            switch (headerName) {
+                case TO:
+                    return toAddressSet;
+                case FROM:
+                    return fromAddressSet;
+                case CC:
+                    return ccAddressSet;
+                case BCC:
+                    return bccAddressSet;
+                case REPLY_TO:
+                    return replyToAddressSet;
+            }
+            throw new RuntimeException(headerName + " is not a address header name");
+        }
+    }
+
+    public static final String TO = "to";
+    public static final String FROM = "from";
+    public static final String CC = "cc";
+    public static final String BCC = "bcc";
+    public static final String REPLY_TO = "reply-to";
+    public static final String SUBJECT = "subject";
+    public static final String DATE = "date";
+    public static final String MESSAGE_ID = "message-id";
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    private final ImmutableSet<EMailer> toAddressSet;
+    private final ImmutableSet<EMailer> fromAddressSet;
+    private final ImmutableSet<EMailer> ccAddressSet;
+    private final ImmutableSet<EMailer> bccAddressSet;
+    private final ImmutableSet<EMailer> replyToAddressSet;
+    private final ImmutableSet<String> subjectSet;
+    private final ImmutableMultimap<String, String> headers;
+    private final Optional<ZonedDateTime> sentDate;
+    private final Optional<String> messageID;
+
+    private HeaderCollection(ImmutableSet<EMailer> toAddressSet, ImmutableSet<EMailer> fromAddressSet,
+                             ImmutableSet<EMailer> ccAddressSet, ImmutableSet<EMailer> bccAddressSet, ImmutableSet<EMailer> replyToAddressSet, ImmutableSet<String> subjectSet,
+                             ImmutableMultimap<String, String> headers, Optional<ZonedDateTime> sentDate, Optional<String> messageID) {
+        this.toAddressSet = toAddressSet;
+        this.fromAddressSet = fromAddressSet;
+        this.ccAddressSet = ccAddressSet;
+        this.bccAddressSet = bccAddressSet;
+        this.replyToAddressSet = replyToAddressSet;
+        this.subjectSet = subjectSet;
+        this.headers = headers;
+        this.sentDate = sentDate;
+        this.messageID = messageID;
+    }
+
+    public Set<EMailer> getToAddressSet() {
+        return toAddressSet;
+    }
+
+    public Set<EMailer> getFromAddressSet() {
+        return fromAddressSet;
+    }
+
+    public Set<EMailer> getCcAddressSet() {
+        return ccAddressSet;
+    }
+
+    public Set<EMailer> getBccAddressSet() {
+        return bccAddressSet;
+    }
+
+    public Set<EMailer> getReplyToAddressSet() {
+        return replyToAddressSet;
+    }
+
+    public Set<String> getSubjectSet() {
+        return subjectSet;
+    }
+
+    public Optional<ZonedDateTime> getSentDate() {
+        return sentDate;
+    }
+
+    public Multimap<String, String> getHeaders() {
+        return headers;
+    }
+
+    public Optional<String> getMessageID() {
+        return messageID;
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java
new file mode 100644
index 0000000..221aa91
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessage.java
@@ -0,0 +1,471 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
+import org.apache.james.mailbox.elasticsearch.v6.query.DateResolutionFormater;
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.Property;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
+import org.apache.james.mailbox.store.search.SearchUtil;
+import org.apache.james.mime4j.MimeException;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+
+public class IndexableMessage {
+
+    public static class Builder {
+        private static ZonedDateTime getSanitizedInternalDate(MailboxMessage message, ZoneId zoneId) {
+            if (message.getInternalDate() == null) {
+                return ZonedDateTime.now();
+            }
+            return ZonedDateTime.ofInstant(
+                    Instant.ofEpochMilli(message.getInternalDate().getTime()),
+                    zoneId);
+        }
+        
+        private IndexAttachments indexAttachments;
+        private MailboxMessage message;
+        private TextExtractor textExtractor;
+        private List<User> users;
+
+        private ZoneId zoneId;
+
+        private Builder() {
+        }
+
+        public IndexableMessage build() {
+            Preconditions.checkNotNull(message.getMailboxId());
+            Preconditions.checkNotNull(users);
+            Preconditions.checkNotNull(textExtractor);
+            Preconditions.checkNotNull(indexAttachments);
+            Preconditions.checkNotNull(zoneId);
+            Preconditions.checkState(!users.isEmpty());
+
+            try {
+                return instanciateIndexedMessage();
+            } catch (IOException | MimeException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public Builder extractor(TextExtractor textExtractor) {
+            this.textExtractor = textExtractor;
+            return this;
+        }
+
+        public Builder indexAttachments(IndexAttachments indexAttachments) {
+            this.indexAttachments = indexAttachments;
+            return this;
+        }
+
+        public Builder message(MailboxMessage message) {
+            this.message = message;
+            return this;
+        }
+
+        public Builder users(List<User> users) {
+            this.users = users;
+            return this;
+        }
+
+        public Builder zoneId(ZoneId zoneId) {
+            this.zoneId = zoneId;
+            return this;
+        }
+
+        private boolean computeHasAttachment(MailboxMessage message) {
+            return message.getProperties()
+                    .stream()
+                    .anyMatch(property -> property.equals(HAS_ATTACHMENT_PROPERTY));
+        }
+
+        private IndexableMessage instanciateIndexedMessage() throws IOException, MimeException {
+            String messageId = SearchUtil.getSerializedMessageIdIfSupportedByUnderlyingStorageOrNull(message);
+            MimePart parsingResult = new MimePartParser(message, textExtractor).parse();
+
+            List<String> stringifiedUsers = users.stream()
+                    .map(User::asString)
+                    .collect(Guavate.toImmutableList());
+
+            Optional<String> bodyText = parsingResult.locateFirstTextBody();
+            Optional<String> bodyHtml = parsingResult.locateFirstHtmlBody();
+
+            boolean hasAttachment = computeHasAttachment(message);
+            List<MimePart> attachments = setFlattenedAttachments(parsingResult, indexAttachments);
+
+            HeaderCollection headerCollection = parsingResult.getHeaderCollection();
+            ZonedDateTime internalDate = getSanitizedInternalDate(message, zoneId);
+
+            Multimap<String, String> headers = headerCollection.getHeaders();
+            Subjects subjects = Subjects.from(headerCollection.getSubjectSet());
+            EMailers from = EMailers.from(headerCollection.getFromAddressSet());
+            EMailers to = EMailers.from(headerCollection.getToAddressSet());
+            EMailers replyTo = EMailers.from(headerCollection.getReplyToAddressSet());
+            EMailers cc = EMailers.from(headerCollection.getCcAddressSet());
+            EMailers bcc = EMailers.from(headerCollection.getBccAddressSet());
+            String sentDate = DateResolutionFormater.DATE_TIME_FOMATTER.format(headerCollection.getSentDate().orElse(internalDate));
+            Optional<String> mimeMessageID = headerCollection.getMessageID();
+
+            String text = Stream.of(from.serialize(),
+                        to.serialize(),
+                        cc.serialize(),
+                        bcc.serialize(),
+                        subjects.serialize(),
+                        bodyText.orElse(null),
+                        bodyHtml.orElse(null))
+                    .filter(str -> !Strings.isNullOrEmpty(str))
+                    .collect(Collectors.joining(" "));
+
+            long uid = message.getUid().asLong();
+            String mailboxId = message.getMailboxId().serialize();
+            long modSeq = message.getModSeq();
+            long size = message.getFullContentOctets();
+            String date = DateResolutionFormater.DATE_TIME_FOMATTER.format(getSanitizedInternalDate(message, zoneId));
+            String mediaType = message.getMediaType();
+            String subType = message.getSubType();
+            boolean isAnswered = message.isAnswered();
+            boolean isDeleted = message.isDeleted();
+            boolean isDraft = message.isDraft();
+            boolean isFlagged = message.isFlagged();
+            boolean isRecent = message.isRecent();
+            boolean isUnRead = !message.isSeen();
+            String[] userFlags = message.createFlags().getUserFlags();
+            List<Property> properties = message.getProperties();
+
+            return new IndexableMessage(
+                    attachments,
+                    bcc,
+                    bodyHtml,
+                    bodyText,
+                    cc,
+                    date,
+                    from,
+                    hasAttachment,
+                    headers,
+                    isAnswered,
+                    isDeleted,
+                    isDraft,
+                    isFlagged,
+                    isRecent,
+                    isUnRead,
+                    mailboxId,
+                    mediaType,
+                    messageId,
+                    modSeq,
+                    properties,
+                    replyTo,
+                    sentDate,
+                    size,
+                    subjects,
+                    subType,
+                    text,
+                    to,
+                    uid,
+                    userFlags,
+                    stringifiedUsers,
+                    mimeMessageID);
+        }
+
+        private List<MimePart> setFlattenedAttachments(MimePart parsingResult, IndexAttachments indexAttachments) {
+            if (IndexAttachments.YES.equals(indexAttachments)) {
+                return parsingResult.getAttachmentsStream()
+                    .collect(Guavate.toImmutableList());
+            } else {
+                return ImmutableList.of();
+            }
+        }
+    }
+
+    public static final SimpleProperty HAS_ATTACHMENT_PROPERTY = new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "true");
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    private final List<MimePart> attachments;
+    private final EMailers bcc;
+    private final Optional<String> bodyHtml;
+    private final Optional<String> bodyText;
+    private final EMailers cc;
+    private final String date;
+    private final EMailers from;
+    private final boolean hasAttachment;
+    private final Multimap<String, String> headers;
+    private final boolean isAnswered;
+    private final boolean isDeleted;
+    private final boolean isDraft;
+    private final boolean isFlagged;
+    private final boolean isRecent;
+    private final boolean isUnRead;
+    private final String mailboxId;
+    private final String mediaType;
+    private final String messageId;
+    private final long modSeq;
+    private final List<Property> properties;
+    private final EMailers replyTo;
+    private final String sentDate;
+    private final long size;
+    private final Subjects subjects;
+    private final String subType;
+    private final String text;
+    private final EMailers to;
+    private final long uid;
+    private final String[] userFlags;
+    private final List<String> users;
+    private final Optional<String> mimeMessageID;
+
+    private IndexableMessage(
+            List<MimePart> attachments,
+            EMailers bcc,
+            Optional<String> bodyHtml,
+            Optional<String> bodyText,
+            EMailers cc,
+            String date,
+            EMailers from,
+            boolean hasAttachment,
+            Multimap<String, String> headers,
+            boolean isAnswered,
+            boolean isDeleted,
+            boolean isDraft,
+            boolean isFlagged,
+            boolean isRecent,
+            boolean isUnRead,
+            String mailboxId,
+            String mediaType,
+            String messageId,
+            long modSeq,
+            List<Property> properties,
+            EMailers replyTo,
+            String sentDate,
+            long size,
+            Subjects subjects,
+            String subType,
+            String text,
+            EMailers to,
+            long uid,
+            String[] userFlags,
+            List<String> users,
+            Optional<String> mimeMessageID) {
+        this.attachments = attachments;
+        this.bcc = bcc;
+        this.bodyHtml = bodyHtml;
+        this.bodyText = bodyText;
+        this.cc = cc;
+        this.date = date;
+        this.from = from;
+        this.hasAttachment = hasAttachment;
+        this.headers = headers;
+        this.isAnswered = isAnswered;
+        this.isDeleted = isDeleted;
+        this.isDraft = isDraft;
+        this.isFlagged = isFlagged;
+        this.isRecent = isRecent;
+        this.isUnRead = isUnRead;
+        this.mailboxId = mailboxId;
+        this.mediaType = mediaType;
+        this.messageId = messageId;
+        this.modSeq = modSeq;
+        this.properties = properties;
+        this.replyTo = replyTo;
+        this.sentDate = sentDate;
+        this.size = size;
+        this.subjects = subjects;
+        this.subType = subType;
+        this.text = text;
+        this.to = to;
+        this.uid = uid;
+        this.userFlags = userFlags;
+        this.users = users;
+        this.mimeMessageID = mimeMessageID;
+    }
+
+    @JsonProperty(JsonMessageConstants.ATTACHMENTS)
+    public List<MimePart> getAttachments() {
+        return attachments;
+    }
+    
+    @JsonProperty(JsonMessageConstants.BCC)
+    public EMailers getBcc() {
+        return bcc;
+    }
+    
+    @JsonProperty(JsonMessageConstants.HTML_BODY)
+    public Optional<String> getBodyHtml() {
+        return bodyHtml;
+    }
+
+    @JsonProperty(JsonMessageConstants.TEXT_BODY)
+    public Optional<String> getBodyText() {
+        return bodyText;
+    }
+
+    @JsonProperty(JsonMessageConstants.CC)
+    public EMailers getCc() {
+        return cc;
+    }
+
+    @JsonProperty(JsonMessageConstants.DATE)
+    public String getDate() {
+        return date;
+    }
+
+    @JsonProperty(JsonMessageConstants.FROM)
+    public EMailers getFrom() {
+        return from;
+    }
+
+    @JsonProperty(JsonMessageConstants.HAS_ATTACHMENT)
+    public boolean getHasAttachment() {
+        return hasAttachment;
+    }
+
+    @JsonProperty(JsonMessageConstants.HEADERS)
+    public Multimap<String, String> getHeaders() {
+        return headers;
+    }
+
+    @JsonProperty(JsonMessageConstants.MAILBOX_ID)
+    public String getMailboxId() {
+        return mailboxId;
+    }
+
+    @JsonProperty(JsonMessageConstants.MEDIA_TYPE)
+    public String getMediaType() {
+        return mediaType;
+    }
+
+    @JsonProperty(JsonMessageConstants.MESSAGE_ID)
+    public String getMessageId() {
+        return messageId;
+    }
+
+    @JsonProperty(JsonMessageConstants.MODSEQ)
+    public long getModSeq() {
+        return modSeq;
+    }
+
+    @JsonProperty(JsonMessageConstants.PROPERTIES)
+    public List<Property> getProperties() {
+        return properties;
+    }
+
+    @JsonProperty(JsonMessageConstants.REPLY_TO)
+    public EMailers getReplyTo() {
+        return replyTo;
+    }
+
+    @JsonProperty(JsonMessageConstants.SENT_DATE)
+    public String getSentDate() {
+        return sentDate;
+    }
+
+    @JsonProperty(JsonMessageConstants.SIZE)
+    public long getSize() {
+        return size;
+    }
+
+    @JsonProperty(JsonMessageConstants.SUBJECT)
+    public Subjects getSubjects() {
+        return subjects;
+    }
+
+    @JsonProperty(JsonMessageConstants.SUBTYPE)
+    public String getSubType() {
+        return subType;
+    }
+
+    @JsonProperty(JsonMessageConstants.TEXT)
+    public String getText() {
+        return text;
+    }
+
+    @JsonProperty(JsonMessageConstants.TO)
+    public EMailers getTo() {
+        return to;
+    }
+
+    @JsonProperty(JsonMessageConstants.UID)
+    public Long getUid() {
+        return uid;
+    }
+
+    @JsonProperty(JsonMessageConstants.USER_FLAGS)
+    public String[] getUserFlags() {
+        return userFlags;
+    }
+
+    @JsonProperty(JsonMessageConstants.USERS)
+    public List<String> getUsers() {
+        return users;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_ANSWERED)
+    public boolean isAnswered() {
+        return isAnswered;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_DELETED)
+    public boolean isDeleted() {
+        return isDeleted;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_DRAFT)
+    public boolean isDraft() {
+        return isDraft;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_FLAGGED)
+    public boolean isFlagged() {
+        return isFlagged;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_RECENT)
+    public boolean isRecent() {
+        return isRecent;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_UNREAD)
+    public boolean isUnRead() {
+        return isUnRead;
+    }
+
+    @JsonProperty(JsonMessageConstants.MIME_MESSAGE_ID)
+    public Optional<String> getMimeMessageID() {
+        return mimeMessageID;
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
new file mode 100644
index 0000000..e747c9f
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+public interface JsonMessageConstants {
+
+    /*
+    Properties defined by JMAP
+     */
+    String MESSAGE_ID = "messageId";
+    String UID = "uid";
+    String MAILBOX_ID = "mailboxId";
+    String USERS = "users";
+    String IS_UNREAD = "isUnread";
+    String IS_FLAGGED = "isFlagged";
+    String IS_ANSWERED = "isAnswered";
+    String IS_DRAFT = "isDraft";
+    String HEADERS = "headers";
+    String FROM = "from";
+    String TO = "to";
+    String CC = "cc";
+    String BCC = "bcc";
+    String REPLY_TO = "replyTo";
+    String SUBJECT = "subject";
+    String DATE = "date";
+    String SIZE = "size";
+    String TEXT_BODY = "textBody";
+    String HTML_BODY = "htmlBody";
+    String SENT_DATE = "sentDate";
+    String ATTACHMENTS = "attachments";
+    String TEXT = "text";
+    String MIME_MESSAGE_ID = "mimeMessageID";
+
+    /*
+    James properties we can easily get
+     */
+    String PROPERTIES = "properties";
+    String MODSEQ = "modSeq";
+    String USER_FLAGS = "userFlags";
+    String IS_RECENT = "isRecent";
+    String IS_DELETED = "isDeleted";
+    String MEDIA_TYPE = "mediaType";
+    String SUBTYPE = "subtype";
+    String HAS_ATTACHMENT = "hasAttachment";
+
+    interface EMailer {
+        String NAME = "name";
+        String ADDRESS = "address";
+    }
+
+    interface Attachment {
+        String TEXT_CONTENT = "textContent";
+        String MEDIA_TYPE = "mediaType";
+        String SUBTYPE = "subtype";
+        String CONTENT_DISPOSITION = "contentDisposition";
+        String FILENAME = "fileName";
+        String FILE_EXTENSION = "fileExtension";
+    }
+
+    interface Property {
+        String NAMESPACE = "namespace";
+        String NAME = "name";
+        String VALUE = "value";
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java
new file mode 100644
index 0000000..95d9c5d
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJson.java
@@ -0,0 +1,86 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.time.ZoneId;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.mail.Flags;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.guava.GuavaModule;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.google.common.base.Preconditions;
+
+public class MessageToElasticSearchJson {
+
+    private final ObjectMapper mapper;
+    private final TextExtractor textExtractor;
+    private final ZoneId zoneId;
+    private final IndexAttachments indexAttachments;
+
+    public MessageToElasticSearchJson(TextExtractor textExtractor, ZoneId zoneId, IndexAttachments indexAttachments) {
+        this.textExtractor = textExtractor;
+        this.zoneId = zoneId;
+        this.indexAttachments = indexAttachments;
+        this.mapper = new ObjectMapper();
+        this.mapper.registerModule(new GuavaModule());
+        this.mapper.registerModule(new Jdk8Module());
+    }
+
+    @Inject
+    public MessageToElasticSearchJson(TextExtractor textExtractor, IndexAttachments indexAttachments) {
+        this(textExtractor, ZoneId.systemDefault(), indexAttachments);
+    }
+
+    public String convertToJson(MailboxMessage message, List<User> users) throws JsonProcessingException {
+        Preconditions.checkNotNull(message);
+
+        return mapper.writeValueAsString(IndexableMessage.builder()
+                .message(message)
+                .users(users)
+                .extractor(textExtractor)
+                .zoneId(zoneId)
+                .indexAttachments(indexAttachments)
+                .build());
+    }
+
+    public String convertToJsonWithoutAttachment(MailboxMessage message, List<User> users) throws JsonProcessingException {
+        return mapper.writeValueAsString(IndexableMessage.builder()
+                .message(message)
+                .users(users)
+                .extractor(textExtractor)
+                .zoneId(zoneId)
+                .indexAttachments(IndexAttachments.NO)
+                .build());
+    }
+
+    public String getUpdatedJsonMessagePart(Flags flags, long modSeq) throws JsonProcessingException {
+        Preconditions.checkNotNull(flags);
+        return mapper.writeValueAsString(new MessageUpdateJson(flags, modSeq));
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
new file mode 100644
index 0000000..f8b2510
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
@@ -0,0 +1,79 @@
+
+
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import javax.mail.Flags;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MessageUpdateJson {
+
+    private final Flags flags;
+    private final long modSeq;
+
+    public MessageUpdateJson(Flags flags, long modSeq) {
+        this.flags = flags;
+        this.modSeq = modSeq;
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_ANSWERED)
+    public boolean isAnswered() {
+        return flags.contains(Flags.Flag.ANSWERED);
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_DELETED)
+    public boolean isDeleted() {
+        return flags.contains(Flags.Flag.DELETED);
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_DRAFT)
+    public boolean isDraft() {
+        return flags.contains(Flags.Flag.DRAFT);
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_FLAGGED)
+    public boolean isFlagged() {
+        return flags.contains(Flags.Flag.FLAGGED);
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_RECENT)
+    public boolean isRecent() {
+        return flags.contains(Flags.Flag.RECENT);
+    }
+
+    @JsonProperty(JsonMessageConstants.IS_UNREAD)
+    public boolean isUnRead() {
+        return ! flags.contains(Flags.Flag.SEEN);
+    }
+
+
+    @JsonProperty(JsonMessageConstants.USER_FLAGS)
+    public String[] getUserFlags() {
+        return flags.getUserFlags();
+    }
+
+    @JsonProperty(JsonMessageConstants.MODSEQ)
+    public long getModSeq() {
+        return modSeq;
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java
new file mode 100644
index 0000000..3d700cb
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePart.java
@@ -0,0 +1,303 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mailbox.extractor.ParsedContent;
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
+import org.apache.james.mime4j.stream.Field;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+public class MimePart {
+
+    public static class Builder implements MimePartContainerBuilder {
+
+        private final HeaderCollection.Builder headerCollectionBuilder;
+        private Optional<InputStream> bodyContent;
+        private final List<MimePart> children;
+        private Optional<String> mediaType;
+        private Optional<String> subType;
+        private Optional<String> fileName;
+        private Optional<String> fileExtension;
+        private Optional<String> contentDisposition;
+        private Optional<Charset> charset;
+        private TextExtractor textExtractor;
+
+        private Builder() {
+            children = Lists.newArrayList();
+            headerCollectionBuilder = HeaderCollection.builder();
+            this.bodyContent = Optional.empty();
+            this.mediaType = Optional.empty();
+            this.subType = Optional.empty();
+            this.fileName = Optional.empty();
+            this.fileExtension = Optional.empty();
+            this.contentDisposition = Optional.empty();
+            this.charset = Optional.empty();
+            this.textExtractor = new DefaultTextExtractor();
+        }
+
+        @Override
+        public Builder addToHeaders(Field field) {
+            headerCollectionBuilder.add(field);
+            return this;
+        }
+
+        @Override
+        public Builder addBodyContent(InputStream bodyContent) {
+            this.bodyContent = Optional.of(bodyContent);
+            return this;
+        }
+
+        @Override
+        public Builder addChild(MimePart mimePart) {
+            children.add(mimePart);
+            return this;
+        }
+
+        @Override
+        public Builder addFileName(String fileName) {
+            this.fileName = Optional.ofNullable(fileName);
+            this.fileExtension = this.fileName.map(FilenameUtils::getExtension);
+            return this;
+        }
+
+        @Override
+        public Builder addMediaType(String mediaType) {
+            this.mediaType = Optional.ofNullable(mediaType);
+            return this;
+        }
+
+        @Override
+        public Builder addSubType(String subType) {
+            this.subType = Optional.ofNullable(subType);
+            return this;
+        }
+
+        @Override
+        public Builder addContentDisposition(String contentDisposition) {
+            this.contentDisposition = Optional.ofNullable(contentDisposition);
+            return this;
+        }
+
+        @Override
+        public MimePartContainerBuilder using(TextExtractor textExtractor) {
+            Preconditions.checkArgument(textExtractor != null, "Provided text extractor should not be null");
+            this.textExtractor = textExtractor;
+            return this;
+        }
+
+        @Override
+        public MimePartContainerBuilder charset(Charset charset) {
+            this.charset = Optional.of(charset);
+            return this;
+        }
+
+        @Override
+        public MimePart build() {
+            Optional<ParsedContent> parsedContent = parseContent(textExtractor);
+            return new MimePart(
+                headerCollectionBuilder.build(),
+                parsedContent.flatMap(ParsedContent::getTextualContent),
+                mediaType,
+                subType,
+                fileName,
+                fileExtension,
+                contentDisposition,
+                children);
+        }
+
+        private Optional<ParsedContent> parseContent(TextExtractor textExtractor) {
+            if (bodyContent.isPresent()) {
+                try {
+                    return Optional.of(extractText(textExtractor, bodyContent.get()));
+                } catch (Throwable e) {
+                    LOGGER.warn("Failed parsing attachment", e);
+                }
+            }
+            return Optional.empty();
+        }
+
+        private ParsedContent extractText(TextExtractor textExtractor, InputStream bodyContent) throws Exception {
+            if (isTextBody()) {
+                return new ParsedContent(
+                        Optional.ofNullable(IOUtils.toString(bodyContent, charset.orElse(StandardCharsets.UTF_8))),
+                        ImmutableMap.of());
+            }
+            return textExtractor.extractContent(
+                bodyContent,
+                computeContentType().orElse(null));
+        }
+
+        private Boolean isTextBody() {
+            return mediaType.map("text"::equals).orElse(false);
+        }
+
+        private Optional<String> computeContentType() {
+            if (mediaType.isPresent() && subType.isPresent()) {
+                return Optional.of(mediaType.get() + "/" + subType.get());
+            } else {
+                return Optional.empty();
+            }
+        }
+
+    }
+    
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MimePart.class);
+
+    private final HeaderCollection headerCollection;
+    private final Optional<String> bodyTextContent;
+    private final Optional<String> mediaType;
+    private final Optional<String> subType;
+    private final Optional<String> fileName;
+    private final Optional<String> fileExtension;
+    private final Optional<String> contentDisposition;
+    private final List<MimePart> attachments;
+
+    private MimePart(HeaderCollection headerCollection, Optional<String> bodyTextContent, Optional<String> mediaType,
+                    Optional<String> subType, Optional<String> fileName, Optional<String> fileExtension,
+                    Optional<String> contentDisposition, List<MimePart> attachments) {
+        this.headerCollection = headerCollection;
+        this.mediaType = mediaType;
+        this.subType = subType;
+        this.fileName = fileName;
+        this.fileExtension = fileExtension;
+        this.contentDisposition = contentDisposition;
+        this.attachments = attachments;
+        this.bodyTextContent = bodyTextContent;
+    }
+
+    @JsonIgnore
+    public List<MimePart> getAttachments() {
+        return attachments;
+    }
+
+    @JsonIgnore
+    public HeaderCollection getHeaderCollection() {
+        return headerCollection;
+    }
+
+    @JsonProperty(JsonMessageConstants.HEADERS)
+    public Multimap<String, String> getHeaders() {
+        return headerCollection.getHeaders();
+    }
+
+    @JsonProperty(JsonMessageConstants.Attachment.FILENAME)
+    public Optional<String> getFileName() {
+        return fileName;
+    }
+
+    @JsonProperty(JsonMessageConstants.Attachment.FILE_EXTENSION)
+    public Optional<String> getFileExtension() {
+        return fileExtension;
+    }
+
+    @JsonProperty(JsonMessageConstants.Attachment.MEDIA_TYPE)
+    public Optional<String> getMediaType() {
+        return mediaType;
+    }
+
+    @JsonProperty(JsonMessageConstants.Attachment.SUBTYPE)
+    public Optional<String> getSubType() {
+        return subType;
+    }
+
+    @JsonProperty(JsonMessageConstants.Attachment.CONTENT_DISPOSITION)
+    public Optional<String> getContentDisposition() {
+        return contentDisposition;
+    }
+
+    @JsonProperty(JsonMessageConstants.Attachment.TEXT_CONTENT)
+    public Optional<String> getTextualBody() {
+        return bodyTextContent;
+    }
+
+    @JsonIgnore
+    public Optional<String> locateFirstTextBody() {
+        return firstBody(textAttachments()
+                .filter(this::isPlainSubType));
+    }
+
+    @JsonIgnore
+    public Optional<String> locateFirstHtmlBody() {
+        return firstBody(textAttachments()
+                .filter(this::isHtmlSubType));
+    }
+
+    private Optional<String> firstBody(Stream<MimePart> mimeParts) {
+        return mimeParts
+                .map((mimePart) -> mimePart.bodyTextContent)
+                .filter(Optional::isPresent)
+                .map(Optional::get)
+                .findFirst();
+    }
+
+    private Stream<MimePart> textAttachments() {
+        return Stream.concat(
+                    Stream.of(this),
+                    attachments.stream())
+                .filter(this::isTextMediaType);
+    }
+
+    private boolean isTextMediaType(MimePart mimePart) {
+        return mimePart.getMediaType()
+                .filter("text"::equals)
+                .isPresent();
+    }
+
+    private boolean isPlainSubType(MimePart mimePart) {
+        return mimePart.getSubType()
+                .filter("plain"::equals)
+                .isPresent();
+    }
+
+    private boolean isHtmlSubType(MimePart mimePart) {
+        return mimePart.getSubType()
+                .filter("html"::equals)
+                .isPresent();
+    }
+
+    @JsonIgnore
+    public Stream<MimePart> getAttachmentsStream() {
+        return attachments.stream()
+                .flatMap((mimePart) -> Stream.concat(Stream.of(mimePart), mimePart.getAttachmentsStream()));
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java
new file mode 100644
index 0000000..5a12008
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartContainerBuilder.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mime4j.stream.Field;
+
+public interface MimePartContainerBuilder {
+
+    MimePart build();
+
+    MimePartContainerBuilder using(TextExtractor textExtractor);
+
+    MimePartContainerBuilder addToHeaders(Field field);
+
+    MimePartContainerBuilder addBodyContent(InputStream bodyContent);
+
+    MimePartContainerBuilder addChild(MimePart mimePart);
+
+    MimePartContainerBuilder addFileName(String fileName);
+
+    MimePartContainerBuilder charset(Charset charset);
+
+    MimePartContainerBuilder addMediaType(String mediaType);
+
+    MimePartContainerBuilder addSubType(String subType);
+
+    MimePartContainerBuilder addContentDisposition(String contentDisposition);
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java
new file mode 100644
index 0000000..0f2a8ff
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartParser.java
@@ -0,0 +1,129 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.Optional;
+
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mailbox.store.mail.model.Message;
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.message.DefaultBodyDescriptorBuilder;
+import org.apache.james.mime4j.message.MaximalBodyDescriptor;
+import org.apache.james.mime4j.stream.EntityState;
+import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.mime4j.stream.MimeTokenStream;
+
+import com.google.common.base.Preconditions;
+
+public class MimePartParser {
+
+    private final Message message;
+    private final TextExtractor textExtractor;
+    private final MimeTokenStream stream;
+    private final Deque<MimePartContainerBuilder> builderStack;
+    private MimePart result;
+    private MimePartContainerBuilder currentlyBuildMimePart;
+
+    public MimePartParser(Message message, TextExtractor textExtractor) {
+        this.message = message;
+        this.textExtractor = textExtractor;
+        this.builderStack = new LinkedList<>();
+        this.currentlyBuildMimePart = new RootMimePartContainerBuilder();
+        this.stream = new MimeTokenStream(
+            MimeConfig.PERMISSIVE,
+            new DefaultBodyDescriptorBuilder());
+    }
+
+    public MimePart parse() throws IOException, MimeException {
+        stream.parse(message.getFullContent());
+        for (EntityState state = stream.getState(); state != EntityState.T_END_OF_STREAM; state = stream.next()) {
+            processMimePart(stream, state);
+        }
+        return result;
+    }
+
+    private void processMimePart(MimeTokenStream stream, EntityState state) {
+        switch (state) {
+            case T_START_MULTIPART:
+            case T_START_MESSAGE:
+                stackCurrent();
+                break;
+            case T_START_HEADER:
+                currentlyBuildMimePart = MimePart.builder();
+                break;
+            case T_FIELD:
+                currentlyBuildMimePart.addToHeaders(stream.getField());
+                break;
+            case T_BODY:
+                manageBodyExtraction(stream);
+                closeMimePart();
+                break;
+            case T_END_MULTIPART:
+            case T_END_MESSAGE:
+                unstackToCurrent();
+                closeMimePart();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void stackCurrent() {
+        builderStack.push(currentlyBuildMimePart);
+        currentlyBuildMimePart = null;
+    }
+
+    private void unstackToCurrent() {
+        currentlyBuildMimePart = builderStack.pop();
+    }
+    
+    private void closeMimePart() {
+        MimePart bodyMimePart = currentlyBuildMimePart.using(textExtractor).build();
+        if (!builderStack.isEmpty()) {
+            builderStack.peek().addChild(bodyMimePart);
+        } else {
+            Preconditions.checkState(result == null);
+            result = bodyMimePart;
+        }
+    }
+
+    private void manageBodyExtraction(MimeTokenStream stream) {
+        extractMimePartBodyDescription(stream);
+        currentlyBuildMimePart.addBodyContent(stream.getDecodedInputStream());
+    }
+
+    private void extractMimePartBodyDescription(MimeTokenStream stream) {
+        MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) stream.getBodyDescriptor();
+
+        currentlyBuildMimePart.addMediaType(descriptor.getMediaType())
+            .addSubType(descriptor.getSubType())
+            .addContentDisposition(descriptor.getContentDispositionType())
+            .addFileName(descriptor.getContentDispositionFilename());
+
+        Optional.ofNullable(descriptor.getCharset())
+            .map(Charset::forName)
+            .ifPresent(currentlyBuildMimePart::charset);
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java
new file mode 100644
index 0000000..415d96f
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/RootMimePartContainerBuilder.java
@@ -0,0 +1,96 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mime4j.stream.Field;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RootMimePartContainerBuilder implements MimePartContainerBuilder {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RootMimePartContainerBuilder.class);
+
+    private MimePart rootMimePart;
+
+    @Override
+    public MimePart build() {
+        return rootMimePart;
+    }
+
+    @Override public MimePartContainerBuilder using(TextExtractor textExtractor) {
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addToHeaders(Field field) {
+        LOGGER.warn("Trying to add headers to the Root MimePart container");
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addBodyContent(InputStream bodyContent) {
+        LOGGER.warn("Trying to add body content to the Root MimePart container");
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addChild(MimePart mimePart) {
+        if (rootMimePart == null) {
+            rootMimePart = mimePart;
+        } else {
+            LOGGER.warn("Trying to add several children to the Root MimePart container");
+        }
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addFileName(String fileName) {
+        LOGGER.warn("Trying to add fineName to the Root MimePart container");
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addMediaType(String mediaType) {
+        LOGGER.warn("Trying to add media type to the Root MimePart container");
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addSubType(String subType) {
+        LOGGER.warn("Trying to add sub type to the Root MimePart container");
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder addContentDisposition(String contentDisposition) {
+        LOGGER.warn("Trying to add content disposition to the Root MimePart container");
+        return this;
+    }
+
+    @Override
+    public MimePartContainerBuilder charset(Charset charset) {
+        LOGGER.warn("Trying to add content charset to the Root MimePart container");
+        return this;
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Serializable.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Serializable.java
new file mode 100644
index 0000000..5ad6334
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Serializable.java
@@ -0,0 +1,25 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+public interface Serializable {
+
+    String serialize();
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
new file mode 100644
index 0000000..d962932
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+
+public class Subjects implements Serializable {
+
+    public static Subjects from(Set<String> subjects) {
+        Preconditions.checkNotNull(subjects, "'subjects' is mandatory");
+        return new Subjects(subjects);
+    }
+
+    private final Set<String> subjects;
+
+    private Subjects(Set<String> subjects) {
+        this.subjects = subjects;
+    }
+
+    @JsonValue
+    public Set<String> getSubjects() {
+        return subjects;
+    }
+
+    @Override
+    public String serialize() {
+        return Joiner.on(" ").join(subjects);
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
new file mode 100644
index 0000000..19612cc
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
@@ -0,0 +1,310 @@
+/****************************************************************
+ * 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.elasticsearch.v6.query;
+
+import static org.apache.james.backends.es.NodeMappingFactory.RAW;
+import static org.apache.james.backends.es.NodeMappingFactory.SPLIT_EMAIL;
+import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
+import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
+import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
+import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
+import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
+import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
+import static org.elasticsearch.index.query.QueryBuilders.termQuery;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Stream;
+
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.elasticsearch.v6.json.HeaderCollection;
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.model.SearchQuery.Criterion;
+import org.apache.james.mailbox.model.SearchQuery.HeaderOperator;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+
+public class CriterionConverter {
+
+    private final Map<Class<?>, Function<SearchQuery.Criterion, QueryBuilder>> criterionConverterMap;
+    private final Map<Class<?>, BiFunction<String, SearchQuery.HeaderOperator, QueryBuilder>> headerOperatorConverterMap;
+
+    public CriterionConverter() {
+        criterionConverterMap = new HashMap<>();
+        headerOperatorConverterMap = new HashMap<>();
+        
+        registerCriterionConverters();
+        registerHeaderOperatorConverters();
+    }
+
+    private void registerCriterionConverters() {
+        registerCriterionConverter(SearchQuery.FlagCriterion.class, this::convertFlag);
+        registerCriterionConverter(SearchQuery.UidCriterion.class, this::convertUid);
+        registerCriterionConverter(SearchQuery.ConjunctionCriterion.class, this::convertConjunction);
+        registerCriterionConverter(SearchQuery.HeaderCriterion.class, this::convertHeader);
+        registerCriterionConverter(SearchQuery.TextCriterion.class, this::convertTextCriterion);
+        registerCriterionConverter(SearchQuery.CustomFlagCriterion.class, this::convertCustomFlagCriterion);
+        
+        registerCriterionConverter(SearchQuery.AllCriterion.class,
+            criterion -> matchAllQuery());
+        
+        registerCriterionConverter(SearchQuery.ModSeqCriterion.class,
+            criterion -> createNumericFilter(JsonMessageConstants.MODSEQ, criterion.getOperator()));
+        
+        registerCriterionConverter(SearchQuery.SizeCriterion.class,
+            criterion -> createNumericFilter(JsonMessageConstants.SIZE, criterion.getOperator()));
+
+        registerCriterionConverter(SearchQuery.InternalDateCriterion.class,
+            criterion -> dateRangeFilter(JsonMessageConstants.DATE, criterion.getOperator()));
+
+        registerCriterionConverter(SearchQuery.AttachmentCriterion.class, this::convertAttachmentCriterion);
+        registerCriterionConverter(SearchQuery.MimeMessageIDCriterion.class, this::convertMimeMessageIDCriterion);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private <T extends Criterion> void registerCriterionConverter(Class<T> type, Function<T, QueryBuilder> f) {
+        criterionConverterMap.put(type, (Function<Criterion, QueryBuilder>) f);
+    }
+    
+    private void registerHeaderOperatorConverters() {
+
+        registerHeaderOperatorConverter(
+            SearchQuery.ExistsOperator.class,
+            (headerName, operator) ->
+                existsQuery(JsonMessageConstants.HEADERS + "." + headerName));
+        
+        registerHeaderOperatorConverter(
+            SearchQuery.AddressOperator.class,
+            (headerName, operator) -> manageAddressFields(headerName, operator.getAddress()));
+        
+        registerHeaderOperatorConverter(
+            SearchQuery.DateOperator.class,
+            (headerName, operator) -> dateRangeFilter(JsonMessageConstants.SENT_DATE, operator));
+        
+        registerHeaderOperatorConverter(
+            SearchQuery.ContainsOperator.class,
+            (headerName, operator) -> matchQuery(JsonMessageConstants.HEADERS + "." + headerName,
+                    operator.getValue()));
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends HeaderOperator> void registerHeaderOperatorConverter(Class<T> type, BiFunction<String, T, QueryBuilder> f) {
+        headerOperatorConverterMap.put(type, (BiFunction<String, HeaderOperator, QueryBuilder>) f);
+    }
+
+    public QueryBuilder convertCriterion(SearchQuery.Criterion criterion) {
+        return criterionConverterMap.get(criterion.getClass()).apply(criterion);
+    }
+
+    private QueryBuilder convertAttachmentCriterion(SearchQuery.AttachmentCriterion criterion) {
+        return termQuery(JsonMessageConstants.HAS_ATTACHMENT, criterion.getOperator().isSet());
+    }
+
+    private QueryBuilder convertMimeMessageIDCriterion(SearchQuery.MimeMessageIDCriterion criterion) {
+        return termQuery(JsonMessageConstants.MIME_MESSAGE_ID, criterion.getMessageID());
+    }
+
+    private QueryBuilder convertCustomFlagCriterion(SearchQuery.CustomFlagCriterion criterion) {
+        QueryBuilder termQueryBuilder = termQuery(JsonMessageConstants.USER_FLAGS, criterion.getFlag());
+        if (criterion.getOperator().isSet()) {
+            return termQueryBuilder;
+        } else {
+            return boolQuery().mustNot(termQueryBuilder);
+        }
+    }
+
+    private QueryBuilder convertTextCriterion(SearchQuery.TextCriterion textCriterion) {
+        switch (textCriterion.getType()) {
+        case BODY:
+            return boolQuery()
+                    .should(matchQuery(JsonMessageConstants.TEXT_BODY, textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.TEXT_BODY + "." + SPLIT_EMAIL,
+                        textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.HTML_BODY + "." + SPLIT_EMAIL,
+                        textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.HTML_BODY, textCriterion.getOperator().getValue()));
+        case TEXT:
+            return boolQuery()
+                    .should(matchQuery(JsonMessageConstants.TEXT, textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.TEXT + "." + SPLIT_EMAIL,
+                        textCriterion.getOperator().getValue()));
+        case FULL:
+            return boolQuery()
+                    .should(matchQuery(JsonMessageConstants.TEXT_BODY, textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.TEXT_BODY + "." + SPLIT_EMAIL,
+                        textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.HTML_BODY + "." + SPLIT_EMAIL,
+                        textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.HTML_BODY, textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.HTML_BODY, textCriterion.getOperator().getValue()))
+                    .should(matchQuery(JsonMessageConstants.ATTACHMENTS + "." + JsonMessageConstants.Attachment.TEXT_CONTENT,
+                        textCriterion.getOperator().getValue()));
+        case ATTACHMENTS:
+            return boolQuery()
+                    .should(matchQuery(JsonMessageConstants.ATTACHMENTS + "." + JsonMessageConstants.Attachment.TEXT_CONTENT,
+                        textCriterion.getOperator().getValue()));
+        case ATTACHMENT_FILE_NAME:
+            return boolQuery()
+                .should(termQuery(JsonMessageConstants.ATTACHMENTS + "." + JsonMessageConstants.Attachment.FILENAME,
+                    textCriterion.getOperator().getValue()));
+        }
+        throw new RuntimeException("Unknown SCOPE for text criterion");
+    }
+
+    private QueryBuilder dateRangeFilter(String field, SearchQuery.DateOperator dateOperator) {
+        return boolQuery().filter(
+            convertDateOperator(field,
+                dateOperator.getType(),
+                DateResolutionFormater.DATE_TIME_FOMATTER.format(
+                    DateResolutionFormater.computeLowerDate(
+                        DateResolutionFormater.convertDateToZonedDateTime(dateOperator.getDate()),
+                        dateOperator.getDateResultion())),
+                DateResolutionFormater.DATE_TIME_FOMATTER.format(
+                    DateResolutionFormater.computeUpperDate(
+                        DateResolutionFormater.convertDateToZonedDateTime(dateOperator.getDate()),
+                        dateOperator.getDateResultion()))));
+    }
+
+    private BoolQueryBuilder convertConjunction(SearchQuery.ConjunctionCriterion criterion) {
+        return convertToBoolQuery(criterion.getCriteria().stream().map(this::convertCriterion),
+            convertConjunctionType(criterion.getType()));
+    }
+
+    private BiFunction<BoolQueryBuilder, QueryBuilder, BoolQueryBuilder> convertConjunctionType(SearchQuery.Conjunction type) {
+        switch (type) {
+            case AND:
+                return BoolQueryBuilder::must;
+            case OR:
+                return BoolQueryBuilder::should;
+            case NOR:
+                return BoolQueryBuilder::mustNot;
+            default:
+                throw new RuntimeException("Unexpected conjunction criteria " + type);
+        }
+    }
+
+    private BoolQueryBuilder convertToBoolQuery(Stream<QueryBuilder> stream, BiFunction<BoolQueryBuilder, QueryBuilder, BoolQueryBuilder> addCriterionToBoolQuery) {
+        return stream.collect(Collector.of(QueryBuilders::boolQuery,
+                addCriterionToBoolQuery::apply,
+                addCriterionToBoolQuery::apply));
+    }
+
+    private QueryBuilder convertFlag(SearchQuery.FlagCriterion flagCriterion) {
+        SearchQuery.BooleanOperator operator = flagCriterion.getOperator();
+        Flags.Flag flag = flagCriterion.getFlag();
+        if (flag.equals(Flags.Flag.DELETED)) {
+            return boolQuery().filter(termQuery(JsonMessageConstants.IS_DELETED, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.ANSWERED)) {
+            return boolQuery().filter(termQuery(JsonMessageConstants.IS_ANSWERED, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.DRAFT)) {
+            return boolQuery().filter(termQuery(JsonMessageConstants.IS_DRAFT, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.SEEN)) {
+            return boolQuery().filter(termQuery(JsonMessageConstants.IS_UNREAD, !operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.RECENT)) {
+            return boolQuery().filter(termQuery(JsonMessageConstants.IS_RECENT, operator.isSet()));
+        }
+        if (flag.equals(Flags.Flag.FLAGGED)) {
+            return boolQuery().filter(termQuery(JsonMessageConstants.IS_FLAGGED, operator.isSet()));
+        }
+        throw new RuntimeException("Unknown flag used in Flag search criterion");
+    }
+
+    private QueryBuilder createNumericFilter(String fieldName, SearchQuery.NumericOperator operator) {
+        switch (operator.getType()) {
+        case EQUALS:
+            return boolQuery().filter(rangeQuery(fieldName).gte(operator.getValue()).lte(operator.getValue()));
+        case GREATER_THAN:
+            return boolQuery().filter(rangeQuery(fieldName).gte(operator.getValue()));
+        case LESS_THAN:
+            return boolQuery().filter(rangeQuery(fieldName).lte(operator.getValue()));
+        default:
+            throw new RuntimeException("A non existing numeric operator was triggered");
+        }
+    }
+
+    private BoolQueryBuilder convertUid(SearchQuery.UidCriterion uidCriterion) {
+        if (uidCriterion.getOperator().getRange().length == 0) {
+            return boolQuery();
+        }
+        return boolQuery().filter(
+            convertToBoolQuery(
+                Arrays.stream(uidCriterion.getOperator().getRange())
+                    .map(this::uidRangeFilter), BoolQueryBuilder::should));
+    }
+
+    private QueryBuilder uidRangeFilter(SearchQuery.UidRange numericRange) {
+        return rangeQuery(JsonMessageConstants.UID)
+                .lte(numericRange.getHighValue().asLong())
+                .gte(numericRange.getLowValue().asLong());
+    }
+
+    private QueryBuilder convertHeader(SearchQuery.HeaderCriterion headerCriterion) {
+        return headerOperatorConverterMap.get(headerCriterion.getOperator().getClass())
+            .apply(
+                headerCriterion.getHeaderName().toLowerCase(Locale.US),
+                headerCriterion.getOperator());
+    }
+
+    private QueryBuilder manageAddressFields(String headerName, String value) {
+        return nestedQuery(getFieldNameFromHeaderName(headerName), boolQuery()
+            .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.NAME, value))
+            .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS, value))
+            .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS + "." + RAW, value)));
+    }
+
+    private String getFieldNameFromHeaderName(String headerName) {
+        switch (headerName.toLowerCase(Locale.US)) {
+        case HeaderCollection.TO:
+            return JsonMessageConstants.TO;
+        case HeaderCollection.CC:
+            return JsonMessageConstants.CC;
+        case HeaderCollection.BCC:
+            return JsonMessageConstants.BCC;
+        case HeaderCollection.FROM:
+            return JsonMessageConstants.FROM;
+        }
+        throw new RuntimeException("Header not recognized as Addess Header : " + headerName);
+    }
+
+    private QueryBuilder convertDateOperator(String field, SearchQuery.DateComparator dateComparator, String lowDateString, String upDateString) {
+        switch (dateComparator) {
+        case BEFORE:
+            return rangeQuery(field).lte(upDateString);
+        case AFTER:
+            return rangeQuery(field).gte(lowDateString);
+        case ON:
+            return rangeQuery(field).lte(upDateString).gte(lowDateString);
+        }
+        throw new RuntimeException("Unknown date operator");
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java
new file mode 100644
index 0000000..731b564
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormater.java
@@ -0,0 +1,73 @@
+/****************************************************************
+ * 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.elasticsearch.v6.query;
+
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.Date;
+
+import org.apache.james.mailbox.model.SearchQuery;
+
+public class DateResolutionFormater {
+
+    public static DateTimeFormatter DATE_TIME_FOMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");
+
+    public static ZonedDateTime computeUpperDate(ZonedDateTime date, SearchQuery.DateResolution resolution) {
+        return computeLowerDate(date, resolution).plus(1, convertDateResolutionField(resolution));
+    }
+
+    public static ZonedDateTime computeLowerDate(ZonedDateTime date, SearchQuery.DateResolution resolution) {
+        switch (resolution) {
+            case Year:
+                return date.truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
+            case Month:
+                return date.truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1);
+            default:
+                return date.truncatedTo(convertDateResolutionField(resolution));
+        }
+    }
+
+    private static TemporalUnit convertDateResolutionField(SearchQuery.DateResolution resolution) {
+        switch (resolution) {
+            case Year:
+                return ChronoUnit.YEARS;
+            case Month:
+                return ChronoUnit.MONTHS;
+            case Day:
+                return ChronoUnit.DAYS;
+            case Hour:
+                return ChronoUnit.HOURS;
+            case Minute:
+                return ChronoUnit.MINUTES;
+            case Second:
+                return ChronoUnit.SECONDS;
+            default:
+                throw new RuntimeException("Unknown Date resolution used");
+        }
+    }
+
+    public static ZonedDateTime convertDateToZonedDateTime(Date date) {
+        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
+    }
+}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java
new file mode 100644
index 0000000..c06239b
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/QueryConverter.java
@@ -0,0 +1,79 @@
+/****************************************************************
+ * 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.elasticsearch.v6.query;
+
+import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
+import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.elasticsearch.index.query.BoolQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableList;
+
+public class QueryConverter {
+
+
+    private final CriterionConverter criterionConverter;
+
+    @Inject
+    public QueryConverter(CriterionConverter criterionConverter) {
+        this.criterionConverter = criterionConverter;
+    }
+
+    public QueryBuilder from(Collection<MailboxId> mailboxIds, SearchQuery query) {
+        BoolQueryBuilder boolQueryBuilder = boolQuery()
+            .must(generateQueryBuilder(query));
+
+        mailboxesQuery(mailboxIds).map(boolQueryBuilder::filter);
+        return boolQueryBuilder;
+    }
+
+    private QueryBuilder generateQueryBuilder(SearchQuery searchQuery) {
+        List<SearchQuery.Criterion> criteria = searchQuery.getCriterias();
+        if (criteria.isEmpty()) {
+            return criterionConverter.convertCriterion(SearchQuery.all());
+        } else if (criteria.size() == 1) {
+            return criterionConverter.convertCriterion(criteria.get(0));
+        } else {
+            return criterionConverter.convertCriterion(new SearchQuery.ConjunctionCriterion(SearchQuery.Conjunction.AND, criteria));
+        }
+    }
+
+    private Optional<QueryBuilder> mailboxesQuery(Collection<MailboxId> mailboxIds) {
+        if (mailboxIds.isEmpty()) {
+            return Optional.empty();
+        }
+        ImmutableList<String> ids = mailboxIds.stream()
+                .map(MailboxId::serialize)
+                .collect(Guavate.toImmutableList());
+        return Optional.of(termsQuery(JsonMessageConstants.MAILBOX_ID, ids));
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
new file mode 100644
index 0000000..52a2624
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
@@ -0,0 +1,81 @@
+/****************************************************************
+ * 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.elasticsearch.v6.query;
+
+import org.apache.james.backends.es.NodeMappingFactory;
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortOrder;
+
+public class SortConverter {
+
+    private static final String MIN = "min";
+    private static final String PATH_SEPARATOR = ".";
+
+    public static FieldSortBuilder convertSort(SearchQuery.Sort sort) {
+        return getSortClause(sort.getSortClause())
+            .order(getOrder(sort))
+            .sortMode(MIN);
+    }
+
+    private static FieldSortBuilder getSortClause(SearchQuery.Sort.SortClause clause) {
+        switch (clause) {
+            case Arrival :
+                return SortBuilders.fieldSort(JsonMessageConstants.DATE);
+            case MailboxCc :
+                return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
+                    .setNestedPath(JsonMessageConstants.CC);
+            case MailboxFrom :
+                return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
+                    .setNestedPath(JsonMessageConstants.FROM);
+            case MailboxTo :
+                return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
+                    .setNestedPath(JsonMessageConstants.TO);
+            case BaseSubject :
+                return SortBuilders.fieldSort(JsonMessageConstants.SUBJECT + PATH_SEPARATOR + NodeMappingFactory.RAW);
+            case Size :
+                return SortBuilders.fieldSort(JsonMessageConstants.SIZE);
+            case SentDate :
+                return SortBuilders.fieldSort(JsonMessageConstants.SENT_DATE);
+            case Uid :
+                return SortBuilders.fieldSort(JsonMessageConstants.UID);
+            case DisplayFrom:
+                return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME + PATH_SEPARATOR + NodeMappingFactory.RAW)
+                    .setNestedPath(JsonMessageConstants.FROM);
+            case DisplayTo:
+                return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME + PATH_SEPARATOR + NodeMappingFactory.RAW)
+                    .setNestedPath(JsonMessageConstants.TO);
+            case Id:
+                return SortBuilders.fieldSort(JsonMessageConstants.MESSAGE_ID);
+            default:
+                throw new RuntimeException("Sort is not implemented");
+        }
+    }
+
+    private static SortOrder getOrder(SearchQuery.Sort sort) {
+        if (sort.isReverse()) {
+            return SortOrder.DESC;
+        } else {
+            return SortOrder.ASC;
+        }
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
new file mode 100644
index 0000000..195326e
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
@@ -0,0 +1,138 @@
+/****************************************************************
+ * 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.elasticsearch.v6.search;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.james.backends.es.AliasName;
+import org.apache.james.backends.es.ReadAliasName;
+import org.apache.james.backends.es.TypeName;
+import org.apache.james.backends.es.search.ScrollIterable;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
+import org.apache.james.mailbox.elasticsearch.v6.query.QueryConverter;
+import org.apache.james.mailbox.elasticsearch.v6.query.SortConverter;
+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.SearchQuery;
+import org.apache.james.mailbox.store.search.MessageSearchIndex;
+import org.apache.james.util.streams.Iterators;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.SearchHitField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ElasticSearchSearcher {
+
+    public static final int DEFAULT_SEARCH_SIZE = 100;
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchSearcher.class);
+    private static final TimeValue TIMEOUT = new TimeValue(60000);
+
+    private final Client client;
+    private final QueryConverter queryConverter;
+    private final int size;
+    private final MailboxId.Factory mailboxIdFactory;
+    private final MessageId.Factory messageIdFactory;
+    private final AliasName aliasName;
+    private final TypeName typeName;
+
+    public ElasticSearchSearcher(Client client, QueryConverter queryConverter, int size,
+                                 MailboxId.Factory mailboxIdFactory, MessageId.Factory messageIdFactory,
+                                 ReadAliasName aliasName, TypeName typeName) {
+        this.client = client;
+        this.queryConverter = queryConverter;
+        this.size = size;
+        this.mailboxIdFactory = mailboxIdFactory;
+        this.messageIdFactory = messageIdFactory;
+        this.aliasName = aliasName;
+        this.typeName = typeName;
+    }
+
+    public Stream<MessageSearchIndex.SearchResult> search(Collection<MailboxId> mailboxIds, SearchQuery query,
+                                                          Optional<Long> limit) throws MailboxException {
+        SearchRequestBuilder searchRequestBuilder = getSearchRequestBuilder(client, mailboxIds, query, limit);
+        Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, searchRequestBuilder).stream()
+            .flatMap(this::transformResponseToUidStream);
+
+        return limit.map(pairStream::limit)
+            .orElse(pairStream);
+    }
+
+    private SearchRequestBuilder getSearchRequestBuilder(Client client, Collection<MailboxId> users,
+                                                         SearchQuery query, Optional<Long> limit) {
+        return query.getSorts()
+            .stream()
+            .reduce(
+                client.prepareSearch(aliasName.getValue())
+                    .setTypes(typeName.getValue())
+                    .setScroll(TIMEOUT)
+                    .addFields(JsonMessageConstants.UID, JsonMessageConstants.MAILBOX_ID, JsonMessageConstants.MESSAGE_ID)
+                    .setQuery(queryConverter.from(users, query))
+                    .setSize(computeRequiredSize(limit)),
+                (searchBuilder, sort) -> searchBuilder.addSort(SortConverter.convertSort(sort)),
+                (partialResult1, partialResult2) -> partialResult1);
+    }
+
+    private int computeRequiredSize(Optional<Long> limit) {
+        return limit.map(value -> Math.min(value.intValue(), size))
+            .orElse(size);
+    }
+
+    private Stream<MessageSearchIndex.SearchResult> transformResponseToUidStream(SearchResponse searchResponse) {
+        return Iterators.toStream(searchResponse.getHits().iterator())
+            .map(this::extractContentFromHit)
+            .filter(Optional::isPresent)
+            .map(Optional::get);
+    }
+
+    private Optional<MessageSearchIndex.SearchResult> extractContentFromHit(SearchHit hit) {
+        SearchHitField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
+        SearchHitField uid = hit.field(JsonMessageConstants.UID);
+        Optional<SearchHitField> id = retrieveMessageIdField(hit);
+        if (mailboxId != null && uid != null) {
+            Number uidAsNumber = uid.getValue();
+            return Optional.of(
+                new MessageSearchIndex.SearchResult(
+                    id.map(field -> messageIdFactory.fromString(field.getValue())),
+                    mailboxIdFactory.fromString(mailboxId.getValue()),
+                    MessageUid.of(uidAsNumber.longValue())));
+        } else {
+            LOGGER.warn("Can not extract UID, MessageID and/or MailboxId for search result {}", hit.getId());
+            return Optional.empty();
+        }
+    }
+
+    private Optional<SearchHitField> retrieveMessageIdField(SearchHit hit) {
+        if (hit.fields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
+            return Optional.ofNullable(hit.field(JsonMessageConstants.MESSAGE_ID));
+        } else {
+            return Optional.empty();
+        }
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/reporting-site/site.xml b/mailbox/elasticsearch-v6/src/reporting-site/site.xml
new file mode 100644
index 0000000..d919164
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/reporting-site/site.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    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.    
+-->
+<project name="${project.name}">
+
+    <body>
+
+        <menu ref="parent" />
+        <menu ref="reports" />
+
+    </body>
+
+</project>
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
new file mode 100644
index 0000000..81a4293
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
@@ -0,0 +1,223 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.charset.StandardCharsets;
+import java.time.ZoneId;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.james.backends.es.DockerElasticSearchRule;
+import org.apache.james.backends.es.ElasticSearchConfiguration;
+import org.apache.james.backends.es.ElasticSearchIndexer;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MailboxSessionUtil;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.elasticsearch.v6.events.ElasticSearchListeningMessageSearchIndex;
+import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
+import org.apache.james.mailbox.elasticsearch.v6.query.CriterionConverter;
+import org.apache.james.mailbox.elasticsearch.v6.query.QueryConverter;
+import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
+import org.apache.james.mailbox.inmemory.InMemoryId;
+import org.apache.james.mailbox.inmemory.InMemoryMessageId;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.store.search.AbstractMessageSearchIndexTest;
+import org.apache.james.mailbox.tika.TikaConfiguration;
+import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
+import org.apache.james.mailbox.tika.TikaHttpClientImpl;
+import org.apache.james.mailbox.tika.TikaTextExtractor;
+import org.apache.james.metrics.api.NoopMetricFactory;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.util.concurrent.NamedThreadFactory;
+import org.elasticsearch.client.Client;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.google.common.base.Strings;
+
+public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest {
+
+    private static final int BATCH_SIZE = 1;
+    private static final int SEARCH_SIZE = 1;
+
+    @ClassRule
+    public static TikaContainerSingletonRule tika = TikaContainerSingletonRule.rule;
+
+    @Rule
+    public DockerElasticSearchRule elasticSearch = new DockerElasticSearchRule();
+    private TikaTextExtractor textExtractor;
+
+    @Override
+    public void setUp() throws Exception {
+        textExtractor = new TikaTextExtractor(new NoopMetricFactory(),
+            new TikaHttpClientImpl(TikaConfiguration.builder()
+                .host(tika.getIp())
+                .port(tika.getPort())
+                .timeoutInMillis(tika.getTimeoutInMillis())
+                .build()));
+        super.setUp();
+    }
+
+    @Override
+    protected void await() {
+        elasticSearch.awaitForElasticSearch();
+    }
+
+    @Override
+    protected void initializeMailboxManager() {
+        Client client = MailboxIndexCreationUtil.prepareDefaultClient(
+            elasticSearch.clientProvider().get(),
+            ElasticSearchConfiguration.builder()
+                .addHost(elasticSearch.getTcpHost())
+                .build());
+
+        InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();
+        ThreadFactory threadFactory = NamedThreadFactory.withClassName(getClass());
+
+        InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder()
+            .preProvisionnedFakeAuthenticator()
+            .fakeAuthorizator()
+            .inVmEventBus()
+            .defaultAnnotationLimits()
+            .defaultMessageParser()
+            .listeningSearchIndex(preInstanciationStage -> new ElasticSearchListeningMessageSearchIndex(
+                preInstanciationStage.getMapperFactory(),
+                new ElasticSearchIndexer(client,
+                    Executors.newSingleThreadExecutor(threadFactory),
+                    MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
+                    MailboxElasticSearchConstants.MESSAGE_TYPE,
+                    BATCH_SIZE),
+                new ElasticSearchSearcher(client, new QueryConverter(new CriterionConverter()), SEARCH_SIZE,
+                    new InMemoryId.Factory(), messageIdFactory,
+                    MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
+                    MailboxElasticSearchConstants.MESSAGE_TYPE),
+                new MessageToElasticSearchJson(textExtractor, ZoneId.of("Europe/Paris"), IndexAttachments.YES),
+                preInstanciationStage.getSessionProvider()))
+            .noPreDeletionHooks()
+            .storeQuotaManager()
+            .build();
+
+        storeMailboxManager = resources.getMailboxManager();
+        messageIdManager = resources.getMessageIdManager();
+        messageSearchIndex = resources.getSearchIndex();
+    }
+
+    @Test
+    public void termsBetweenElasticSearchAndLuceneLimitDueTuNonAsciiCharsShouldBeTruncated() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        String recipient = "benwa@linagora.com";
+        ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
+            Message.Builder.of()
+                .setTo(recipient)
+                .setBody(Strings.repeat("0à2345678é", 3200), StandardCharsets.UTF_8)),
+            session);
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(new SearchQuery(SearchQuery.address(SearchQuery.AddressType.To, recipient)), session))
+            .containsExactly(composedMessageId.getUid());
+    }
+
+    @Test
+    public void tooLongTermsShouldNotMakeIndexingFail() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        String recipient = "benwa@linagora.com";
+        ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
+            Message.Builder.of()
+                .setTo(recipient)
+                .setBody(Strings.repeat("0123456789", 3300), StandardCharsets.UTF_8)),
+            session);
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(new SearchQuery(SearchQuery.address(SearchQuery.AddressType.To, recipient)), session))
+            .containsExactly(composedMessageId.getUid());
+    }
+
+    @Test
+    public void fieldsExceedingLuceneLimitShouldNotBeIgnored() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        String recipient = "benwa@linagora.com";
+        ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
+            Message.Builder.of()
+                .setTo(recipient)
+                .setBody(Strings.repeat("0123456789 ", 5000), StandardCharsets.UTF_8)),
+            session);
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(new SearchQuery(SearchQuery.bodyContains("0123456789")), session))
+            .containsExactly(composedMessageId.getUid());
+    }
+
+    @Test
+    public void fieldsWithTooLongTermShouldStillBeIndexed() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        String recipient = "benwa@linagora.com";
+        ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
+            Message.Builder.of()
+                .setTo(recipient)
+                .setBody(Strings.repeat("0123456789 ", 5000) + " matchMe", StandardCharsets.UTF_8)),
+            session);
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(new SearchQuery(SearchQuery.bodyContains("matchMe")), session))
+            .containsExactly(composedMessageId.getUid());
+    }
+
+    @Test
+    public void reasonableLongTermShouldNotBeIgnored() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        String recipient = "benwa@linagora.com";
+        String reasonableLongTerm = "dichlorodiphényltrichloroéthane";
+        ComposedMessageId composedMessageId = messageManager.appendMessage(MessageManager.AppendCommand.from(
+            Message.Builder.of()
+                .setTo(recipient)
+                .setBody(reasonableLongTerm, StandardCharsets.UTF_8)),
+            session);
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(new SearchQuery(SearchQuery.bodyContains(reasonableLongTerm)), session))
+            .containsExactly(composedMessageId.getUid());
+    }
+}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
new file mode 100644
index 0000000..64a1cd0
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
@@ -0,0 +1,219 @@
+/****************************************************************
+ * 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.elasticsearch.v6;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.james.backends.es.IndexName;
+import org.apache.james.backends.es.ReadAliasName;
+import org.apache.james.backends.es.WriteAliasName;
+import org.junit.Test;
+
+public class ElasticSearchMailboxConfigurationTest {
+    @Test
+    public void getIndexMailboxNameShouldReturnOldConfiguredValue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.index.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexMailboxName())
+            .isEqualTo(new IndexName(name));
+    }
+
+    @Test
+    public void getIndexMailboxNameShouldReturnNewConfiguredValueWhenBoth() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.index.name", "other");
+        configuration.addProperty("elasticsearch.index.mailbox.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexMailboxName())
+            .isEqualTo(new IndexName(name));
+    }
+
+    @Test
+    public void getIndexMailboxNameShouldReturnConfiguredValue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.index.mailbox.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexMailboxName())
+            .isEqualTo(new IndexName(name));
+    }
+
+    @Test
+    public void getIndexMailboxNameShouldReturnDefaultValueWhenMissing() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexMailboxName())
+            .isEqualTo(MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX);
+    }
+
+    @Test
+    public void getReadAliasMailboxNameShouldReturnOldConfiguredValue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.alias.read.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
+            .isEqualTo(new ReadAliasName(name));
+    }
+
+    @Test
+    public void getReadAliasMailboxNameShouldReturnConfiguredValue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.alias.read.mailbox.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
+            .isEqualTo(new ReadAliasName(name));
+    }
+
+    @Test
+    public void getReadAliasMailboxNameShouldReturnNewConfiguredValueWhenBoth() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.alias.read.mailbox.name", name);
+        configuration.addProperty("elasticsearch.alias.read.name", "other");
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
+            .isEqualTo(new ReadAliasName(name));
+    }
+
+    @Test
+    public void getReadAliasMailboxNameShouldReturnDefaultValueWhenMissing() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getReadAliasMailboxName())
+            .isEqualTo(MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS);
+    }
+
+    @Test
+    public void getWriteAliasMailboxNameShouldReturnOldConfiguredValue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.alias.write.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
+            .isEqualTo(new WriteAliasName(name));
+    }
+
+    @Test
+    public void getWriteAliasMailboxNameShouldReturnConfiguredValue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.alias.write.mailbox.name", name);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
+            .isEqualTo(new WriteAliasName(name));
+    }
+
+    @Test
+    public void getWriteAliasMailboxNameShouldReturnNewConfiguredValueWhenBoth() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        String name = "name";
+        configuration.addProperty("elasticsearch.alias.write.mailbox.name", name);
+        configuration.addProperty("elasticsearch.alias.write.name", "other");
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
+            .isEqualTo(new WriteAliasName(name));
+    }
+
+    @Test
+    public void getWriteAliasMailboxNameShouldReturnDefaultValueWhenMissing() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getWriteAliasMailboxName())
+            .isEqualTo(MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS);
+    }
+
+    @Test
+    public void getIndexAttachmentShouldReturnConfiguredValueWhenTrue() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("elasticsearch.indexAttachments", true);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexAttachment())
+            .isEqualTo(IndexAttachments.YES);
+    }
+
+    @Test
+    public void getIndexAttachmentShouldReturnConfiguredValueWhenFalse() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("elasticsearch.indexAttachments", false);
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexAttachment())
+            .isEqualTo(IndexAttachments.NO);
+    }
+
+    @Test
+    public void getIndexAttachmentShouldReturnDefaultValueWhenMissing() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("elasticsearch.hosts", "127.0.0.1");
+
+        ElasticSearchMailboxConfiguration elasticSearchConfiguration = ElasticSearchMailboxConfiguration.fromProperties(configuration);
+
+        assertThat(elasticSearchConfiguration.getIndexAttachment())
+            .isEqualTo(IndexAttachments.YES);
+    }
+
+}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
new file mode 100644
index 0000000..e17f6ca
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
@@ -0,0 +1,269 @@
+/****************************************************************
+ * 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.elasticsearch.v6.events;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.refEq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+import java.util.Optional;
+
+import javax.mail.Flags;
+
+import org.apache.james.backends.es.ElasticSearchIndexer;
+import org.apache.james.backends.es.UpdatedRepresentation;
+import org.apache.james.core.User;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MailboxSessionUtil;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
+import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
+import org.apache.james.mailbox.events.Group;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.UpdatedFlags;
+import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
+import org.apache.james.mailbox.store.SessionProvider;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.elasticsearch.ElasticsearchException;
+import org.elasticsearch.action.bulk.BulkResponse;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class ElasticSearchListeningMessageSearchIndexTest {
+    private static final long MODSEQ = 18L;
+    private static final MessageUid MESSAGE_UID = MessageUid.of(1);
+    private static final TestId MAILBOX_ID = TestId.of(12);
+    private static final String ELASTIC_SEARCH_ID = "12:1";
+    private static final String EXPECTED_JSON_CONTENT = "json content";
+    private static final String USERNAME = "username";
+
+    private ElasticSearchIndexer elasticSearchIndexer;
+    private MessageToElasticSearchJson messageToElasticSearchJson;
+    private ElasticSearchListeningMessageSearchIndex testee;
+    private MailboxSession session;
+    private List<User> users;
+    private Mailbox mailbox;
+
+    @Before
+    public void setup() {
+        MailboxSessionMapperFactory mapperFactory = mock(MailboxSessionMapperFactory.class);
+        messageToElasticSearchJson = mock(MessageToElasticSearchJson.class);
+        ElasticSearchSearcher elasticSearchSearcher = mock(ElasticSearchSearcher.class);
+        SessionProvider mockSessionProvider = mock(SessionProvider.class);
+
+        elasticSearchIndexer = mock(ElasticSearchIndexer.class);
+        
+        testee = new ElasticSearchListeningMessageSearchIndex(mapperFactory, elasticSearchIndexer, elasticSearchSearcher,
+            messageToElasticSearchJson, mockSessionProvider);
+        session = MailboxSessionUtil.create(USERNAME);
+        users = ImmutableList.of(User.fromUsername(USERNAME));
+
+        mailbox = mock(Mailbox.class);
+        when(mailbox.getMailboxId()).thenReturn(MAILBOX_ID);
+    }
+
+    @Test
+    public void deserializeElasticSearchListeningMessageSearchIndexGroup() throws Exception {
+        assertThat(Group.deserialize("org.apache.james.mailbox.elasticsearch.v6.events.ElasticSearchListeningMessageSearchIndex$ElasticSearchListeningMessageSearchIndexGroup"))
+            .isEqualTo(new ElasticSearchListeningMessageSearchIndex.ElasticSearchListeningMessageSearchIndexGroup());
+    }
+    
+    @Test
+    public void addShouldIndex() throws Exception {
+        //Given
+        MailboxMessage message = mockedMessage(MESSAGE_UID);
+        
+        when(messageToElasticSearchJson.convertToJson(eq(message), eq(users)))
+            .thenReturn(EXPECTED_JSON_CONTENT);
+        
+        //When
+        testee.add(session, mailbox, message);
+        
+        //Then
+        verify(elasticSearchIndexer).index(eq(ELASTIC_SEARCH_ID), eq(EXPECTED_JSON_CONTENT));
+    }
+
+    @Test
+    public void addShouldIndexEmailBodyWhenNotIndexableAttachment() throws Exception {
+        //Given
+        MailboxMessage message = mockedMessage(MESSAGE_UID);
+        
+        when(messageToElasticSearchJson.convertToJson(eq(message), eq(users)))
+            .thenThrow(JsonProcessingException.class);
+        
+        when(messageToElasticSearchJson.convertToJsonWithoutAttachment(eq(message), eq(users)))
+            .thenReturn(EXPECTED_JSON_CONTENT);
+        
+        //When
+        testee.add(session, mailbox, message);
+        
+        //Then
+        verify(elasticSearchIndexer).index(eq(ELASTIC_SEARCH_ID), eq(EXPECTED_JSON_CONTENT));
+    }
+
+    private MailboxMessage mockedMessage(MessageUid uid) {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.getUid()).thenReturn(uid);
+        return message;
+    }
+
+    @Test
+    public void addShouldPropagateExceptionWhenExceptionOccurs() throws Exception {
+        //Given
+        MailboxMessage message = mockedMessage(MESSAGE_UID);
+        
+        when(messageToElasticSearchJson.convertToJson(eq(message), eq(users)))
+            .thenThrow(JsonProcessingException.class);
+
+        // When
+        JsonGenerator jsonGenerator = null;
+        when(messageToElasticSearchJson.convertToJsonWithoutAttachment(eq(message), eq(users)))
+            .thenThrow(new JsonGenerationException("expected error", jsonGenerator));
+        
+        //Then
+        assertThatThrownBy(() -> testee.add(session, mailbox, message)).isInstanceOf(JsonGenerationException.class);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void deleteShouldWork() {
+        //Given
+        BulkResponse expectedBulkResponse = mock(BulkResponse.class);
+        when(elasticSearchIndexer.delete(any(List.class)))
+            .thenReturn(Optional.of(expectedBulkResponse));
+
+        //When
+        testee.delete(session, mailbox, Lists.newArrayList(MESSAGE_UID));
+
+        //Then
+        verify(elasticSearchIndexer).delete(eq(Lists.newArrayList(ELASTIC_SEARCH_ID)));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void deleteShouldWorkWhenMultipleMessageIds() {
+        //Given
+        MessageUid messageId2 = MessageUid.of(2);
+        MessageUid messageId3 = MessageUid.of(3);
+        MessageUid messageId4 = MessageUid.of(4);
+        MessageUid messageId5 = MessageUid.of(5);
+
+        BulkResponse expectedBulkResponse = mock(BulkResponse.class);
+        when(elasticSearchIndexer.delete(any(List.class)))
+            .thenReturn(Optional.of(expectedBulkResponse));
+        
+        //When
+        testee.delete(session, mailbox, Lists.newArrayList(MESSAGE_UID, messageId2, messageId3, messageId4, messageId5));
+        
+        //Then
+        verify(elasticSearchIndexer).delete(eq(Lists.newArrayList(ELASTIC_SEARCH_ID, "12:2", "12:3", "12:4", "12:5")));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void deleteShouldPropagateExceptionWhenExceptionOccurs() {
+        //Given
+        when(elasticSearchIndexer.delete(any(List.class)))
+            .thenThrow(new ElasticsearchException(""));
+
+        // Then
+        assertThatThrownBy(() -> testee.delete(session, mailbox, Lists.newArrayList(MESSAGE_UID)))
+            .isInstanceOf(ElasticsearchException.class);
+    }
+
+    @Test
+    public void updateShouldWork() throws Exception {
+        //Given
+        Flags flags = new Flags();
+
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .modSeq(MODSEQ)
+            .oldFlags(flags)
+            .newFlags(flags)
+            .build();
+
+        when(messageToElasticSearchJson.getUpdatedJsonMessagePart(any(Flags.class), any(Long.class)))
+            .thenReturn("json updated content");
+        
+        //When
+        testee.update(session, mailbox, Lists.newArrayList(updatedFlags));
+        
+        //Then
+        ImmutableList<UpdatedRepresentation> expectedUpdatedRepresentations = ImmutableList.of(new UpdatedRepresentation(ELASTIC_SEARCH_ID, "json updated content"));
+        verify(elasticSearchIndexer).update(expectedUpdatedRepresentations);
+    }
+
+    @Test
+    public void updateShouldPropagateExceptionWhenExceptionOccurs() throws Exception {
+        //Given
+        Flags flags = new Flags();
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .modSeq(MODSEQ)
+            .oldFlags(flags)
+            .newFlags(flags)
+            .build();
+        when(messageToElasticSearchJson.getUpdatedJsonMessagePart(any(), anyLong())).thenReturn("update doc");
+
+        //When
+        when(elasticSearchIndexer.update(any())).thenThrow(new ElasticsearchException(""));
+        
+        //Then
+        assertThatThrownBy(() -> testee.update(session, mailbox, Lists.newArrayList(updatedFlags))).isInstanceOf(ElasticsearchException.class);
+    }
+
+    @Test
+    public void deleteAllShouldWork() {
+        //Given
+        testee.deleteAll(session, mailbox);
+        
+        //Then
+        QueryBuilder expectedQueryBuilder = QueryBuilders.termQuery("mailboxId", "12");
+        verify(elasticSearchIndexer).deleteAllMatchingQuery(refEq(expectedQueryBuilder));
+    }
+
+    @Test
+    public void deleteAllShouldNotPropagateExceptionWhenExceptionOccurs() {
+        //Given
+        doThrow(RuntimeException.class)
+            .when(elasticSearchIndexer).deleteAllMatchingQuery(any());
+
+        //Then
+        assertThatThrownBy(() -> testee.deleteAll(session, mailbox)).isInstanceOf(RuntimeException.class);
+    }
+
+}
\ No newline at end of file
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java
new file mode 100644
index 0000000..c89a0ed
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailersTest.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+
+public class EMailersTest {
+
+    @Test
+    public void fromShouldThrowWhenSetIsNull() {
+        assertThatThrownBy(() -> EMailers.from(null))
+            .isInstanceOf(NullPointerException.class)
+            .hasMessage("'emailers' is mandatory");
+    }
+
+    @Test
+    public void serializeShouldReturnEmptyWhenEmptySet() {
+        EMailers eMailers = EMailers.from(ImmutableSet.of());
+
+        assertThat(eMailers.serialize()).isEmpty();
+    }
+
+    @Test
+    public void serializeShouldNotJoinWhenOneElement() {
+        EMailer emailer = new EMailer("name", "address");
+        EMailers eMailers = EMailers.from(ImmutableSet.of(emailer));
+
+        assertThat(eMailers.serialize()).isEqualTo(emailer.serialize());
+    }
+
+    @Test
+    public void serializeShouldJoinWhenMultipleElements() {
+        EMailer emailer = new EMailer("name", "address");
+        EMailer emailer2 = new EMailer("name2", "address2");
+        EMailer emailer3 = new EMailer("name3", "address3");
+
+        String expected = Joiner.on(" ").join(emailer.serialize(), emailer2.serialize(), emailer3.serialize());
+
+        EMailers eMailers = EMailers.from(ImmutableSet.of(emailer, emailer2, emailer3));
+
+        assertThat(eMailers.serialize()).isEqualTo(expected);
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java
new file mode 100644
index 0000000..0525330
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/FieldImpl.java
@@ -0,0 +1,65 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import java.util.Objects;
+
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.util.ByteSequence;
+
+public class FieldImpl implements Field {
+    private final String name;
+    private final String body;
+
+    public FieldImpl(String name, String body) {
+        this.name = name;
+        this.body = body;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getBody() {
+        return body;
+    }
+
+    @Override
+    public ByteSequence getRaw() {
+        return null;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, body);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof  FieldImpl) {
+            FieldImpl otherField = (FieldImpl) o;
+            return Objects.equals(name, otherField.name)
+                && Objects.equals(body, otherField.body);
+        }
+        return false;
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java
new file mode 100644
index 0000000..649c61c
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/HeaderCollectionTest.java
@@ -0,0 +1,334 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.time.format.DateTimeFormatter;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+class HeaderCollectionTest {
+
+    static class UTF8FromHeaderTestSource implements ArgumentsProvider {
+
+        @Override
+        public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+            return Stream.of(
+                Arguments.of("=?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= <fm...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>", "Frédéric MARTIN"),
+                Arguments.of("=?UTF-8?Q?=C3=9Csteli=C4=9Fhan_Ma=C5=9Frapa?= <us...@domain.tld>", "ÃœsteliÄŸhan MaÅŸrapa"),
+                Arguments.of("=?UTF-8?Q?Ke=C5=9Ffet_Turizm?= <ke...@domain.tld>", "KeÅŸfet Turizm"),
+                Arguments.of("=?UTF-8?Q?MODAL=C4=B0F?= <mo...@domain.tld>", "MODALÄ°F"));
+        }
+    }
+
+    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
+
+    @Test
+    void simpleValueAddressHeaderShouldBeAddedToTheAddressSet() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("To", "ben.tellier@linagora.com"))
+            .build();
+
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(new EMailer("ben.tellier@linagora.com", "ben.tellier@linagora.com"));
+    }
+
+    @Test
+    void comaSeparatedAddressShouldBeBothAddedToTheAddressSet() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("To", "ben.tellier@linagora.com, btellier@minet.net"))
+            .build();
+
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(
+                new EMailer("ben.tellier@linagora.com", "ben.tellier@linagora.com"),
+                new EMailer("btellier@minet.net", "btellier@minet.net"));
+    }
+
+    @Test
+    void addressesOfTwoFieldsHavingTheSameNameShouldBeMerged() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("To", "ben.tellier@linagora.com"))
+            .add(new FieldImpl("To", "ben.tellier@linagora.com, btellier@minet.net"))
+            .build();
+
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(
+                new EMailer("ben.tellier@linagora.com", "ben.tellier@linagora.com"),
+                new EMailer("btellier@minet.net", "btellier@minet.net"));
+    }
+
+    @Test
+    void displayNamesShouldBeRetreived() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("To", "Christophe Hamerling <ch...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
+    }
+
+    @ParameterizedTest
+    @ArgumentsSource(UTF8FromHeaderTestSource.class)
+    void displayNamesShouldBeRetrievedWhenEncodedWord(String encodedFromHeader, String nameOfFromAddress) {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("From", encodedFromHeader))
+            .build();
+
+        assertThat(headerCollection.getFromAddressSet())
+            .extracting(EMailer::getName)
+            .contains(nameOfFromAddress);
+    }
+
+    @Test
+    void getHeadersShouldDecodeValues() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("From", "=?UTF-8?B?RnLDqWTDqXJpYyBNQVJUSU4=?= <fm...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getHeaders().get("from"))
+            .containsExactly("Frédéric MARTIN <fm...@linagora.com>, Graham CROSMARIE <gc...@linagora.com>");
+    }
+
+    @Test
+    void getHeadersShouldIgnoreHeadersWithDots() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("a.b.c", "value"))
+            .build();
+
+        assertThat(headerCollection.getHeaders().get("a.b.c"))
+            .isEmpty();
+    }
+
+    @Test
+    void addressWithTwoDisplayNamesOnTheSameFieldShouldBeRetrieved() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>, Graham CROSMARIE <gr...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getFromAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"),
+                new EMailer("Graham CROSMARIE", "grah.crosmarie@linagora.com"));
+    }
+
+    @Test
+    void foldedFromHeaderShouldBeSupported() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>,\r\n" +
+                " Graham CROSMARIE <gr...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getFromAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"),
+                new EMailer("Graham CROSMARIE", "grah.crosmarie@linagora.com"));
+    }
+
+    @Test
+    void foldedHeaderShouldBeSupported() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("From", "Christophe Hamerling <ch...@linagora.com>,\r\n" +
+                " Graham CROSMARIE <gr...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getHeaders().get("from"))
+            .containsOnly("Christophe Hamerling <ch...@linagora.com>, Graham CROSMARIE <gr...@linagora.com>");
+    }
+
+    @Test
+    void mixingAddressWithDisplayNamesWithOthersShouldBeAllowed() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("To", "Christophe Hamerling <ch...@linagora.com>, grah.crosmarie@linagora.com"))
+            .build();
+
+        assertThat(headerCollection.getToAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"),
+                new EMailer("grah.crosmarie@linagora.com", "grah.crosmarie@linagora.com"));
+    }
+
+    @Test
+    void displayNamesShouldBeRetreivedOnCc() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Cc", "Christophe Hamerling <ch...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getCcAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
+    }
+
+    @Test
+    void displayNamesShouldBeRetreivedOnReplyTo() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Reply-To", "Christophe Hamerling <ch...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getReplyToAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
+    }
+
+    @Test
+    void displayNamesShouldBeRetreivedOnBcc() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Bcc", "Christophe Hamerling <ch...@linagora.com>"))
+            .build();
+
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Christophe Hamerling", "chri.hamerling@linagora.com"));
+    }
+
+    @Test
+    void headerContaingNoAddressShouldBeConsideredBothAsNameAndAddress() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Bcc", "Not an address"))
+            .build();
+
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Not an address", "Not an address"));
+    }
+
+    @Test
+    void unclosedAddressSubpartShouldBeWellHandled() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Bcc", "Mickey <tricky@mouse.com"))
+            .build();
+
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Mickey", "tricky@mouse.com"));
+    }
+
+    @Test
+    void notComaSeparatedAddressSubpartShouldBeWellHandled() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Bcc", "Mickey <tr...@mouse.com> Miny<he...@polo.com>"))
+            .build();
+
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Mickey", "tricky@mouse.com"),
+                new EMailer("Miny", "hello@polo.com"));
+    }
+
+    @Test
+    void notSeparatedAddressSubpartShouldBeWellHandled() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Bcc", "Mickey <tr...@polo.com>"))
+            .build();
+
+        assertThat(headerCollection.getBccAddressSet())
+            .containsOnly(new EMailer("Mickey", "tricky@mouse.com"),
+                new EMailer("Miny", "hello@polo.com"));
+    }
+
+    @Test
+    void dateShouldBeRetreived() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Date", "Thu, 4 Jun 2015 06:08:41 +0200"))
+            .build();
+
+        assertThat(DATE_TIME_FORMATTER.format(headerCollection.getSentDate().get()))
+            .isEqualTo("2015/06/04 06:08:41");
+    }
+
+    @Test
+    void partialYearShouldBeCompleted() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Date", "Thu, 4 Jun 15 06:08:41 +0200"))
+            .build();
+
+        assertThat(DATE_TIME_FORMATTER.format(headerCollection.getSentDate().get()))
+            .isEqualTo("2015/06/04 06:08:41");
+    }
+
+    @Test
+    void nonStandardDatesShouldBeRetreived() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Date", "Thu, 4 Jun 2015 06:08:41 +0200 (UTC)"))
+            .build();
+
+        assertThat(DATE_TIME_FORMATTER.format(headerCollection.getSentDate().get()))
+            .isEqualTo("2015/06/04 06:08:41");
+    }
+
+    @Test
+    void dateShouldBeAbsentOnInvalidHeader() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Date", "Not a date"))
+            .build();
+
+        assertThat(headerCollection.getSentDate().isPresent())
+            .isFalse();
+    }
+
+    @Test
+    void subjectsShouldBeWellRetrieved() {
+        String subject = "A fantastic ElasticSearch module will be available soon for JAMES";
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Subject", subject))
+            .build();
+
+        assertThat(headerCollection.getSubjectSet()).containsOnly("A fantastic ElasticSearch module will be available soon for JAMES");
+    }
+
+    @Test
+    void getMessageIDShouldReturnMessageIdValue() {
+        String messageID = "<ab...@123>";
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Message-ID", messageID))
+            .build();
+
+        assertThat(headerCollection.getMessageID())
+            .contains(messageID);
+    }
+
+    @Test
+    void getMessageIDShouldReturnLatestEncounteredMessageIdValue() {
+        String messageID = "<ab...@123>";
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Message-ID", "<ot...@toto.com>"))
+            .add(new FieldImpl("Message-ID", messageID))
+            .build();
+
+        assertThat(headerCollection.getMessageID())
+            .contains(messageID);
+    }
+
+    @Test
+    void getMessageIDShouldReturnEmptyWhenNoMessageId() {
+        HeaderCollection headerCollection = HeaderCollection.builder()
+            .add(new FieldImpl("Other", "value"))
+            .build();
+
+        assertThat(headerCollection.getMessageID())
+            .isEmpty();
+    }
+
+    @Test
+    void nullFieldShouldThrow() {
+        assertThatThrownBy(() -> HeaderCollection.builder().add(null).build())
+            .isInstanceOf(NullPointerException.class);
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java
new file mode 100644
index 0000000..c9e2e19
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/IndexableMessageTest.java
@@ -0,0 +1,578 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.time.ZoneId;
+import java.util.Optional;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
+import org.apache.james.mailbox.extractor.ParsedContent;
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mailbox.inmemory.InMemoryMessageId;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleProperty;
+import org.apache.james.mailbox.tika.TikaConfiguration;
+import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
+import org.apache.james.mailbox.tika.TikaHttpClientImpl;
+import org.apache.james.mailbox.tika.TikaTextExtractor;
+import org.apache.james.metrics.api.NoopMetricFactory;
+import org.assertj.core.api.iterable.Extractor;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class IndexableMessageTest {
+    private static final MessageUid MESSAGE_UID = MessageUid.of(154);
+
+    @ClassRule
+    public static TikaContainerSingletonRule tika = TikaContainerSingletonRule.rule;
+
+    private TikaTextExtractor textExtractor;
+
+    @Before
+    public void setUp() throws Exception {
+        textExtractor = new TikaTextExtractor(new NoopMetricFactory(), new TikaHttpClientImpl(TikaConfiguration.builder()
+                .host(tika.getIp())
+                .port(tika.getPort())
+                .timeoutInMillis(tika.getTimeoutInMillis())
+                .build()));
+    }
+
+    @Test
+    public void textShouldBeEmptyWhenNoMatchingHeaders() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEmpty();
+    }
+
+    @Test
+    public void textShouldContainsFromWhenFrom() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("From: First user <us...@james.org>\nFrom: Second user <us...@james.org>".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("Second user user2@james.org First user user@james.org");
+    }
+
+    @Test
+    public void textShouldContainsToWhenTo() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("To: First to <us...@james.org>\nTo: Second to <us...@james.org>".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("First to user@james.org Second to user2@james.org");
+    }
+
+    @Test
+    public void textShouldContainsCcWhenCc() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("Cc: First cc <us...@james.org>\nCc: Second cc <us...@james.org>".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("First cc user@james.org Second cc user2@james.org");
+    }
+
+    @Test
+    public void textShouldContainsBccWhenBcc() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("Bcc: First bcc <us...@james.org>\nBcc: Second bcc <us...@james.org>".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("Second bcc user2@james.org First bcc user@james.org");
+    }
+
+    @Test
+    public void textShouldContainsSubjectsWhenSubjects() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("Subject: subject1\nSubject: subject2".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("subject1 subject2");
+    }
+
+    @Test
+    public void textShouldContainsBodyWhenBody() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(new ByteArrayInputStream("\nMy body".getBytes()));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("My body");
+    }
+
+    @Test
+    public void textShouldContainsAllFieldsWhenAllSet() throws Exception {
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        assertThat(indexableMessage.getText()).isEqualTo("Ad Min admin@opush.test " +
+                "a@test a@test B b@test " + 
+                "c@test c@test " +
+                "dD d@test " + 
+                "my subject " + 
+                "Mail content\n" +
+                "\n" +
+                "-- \n" + 
+                "Ad Min\n");
+    }
+
+    @Test
+    public void hasAttachmentsShouldReturnTrueWhenPropertyIsPresentAndTrue() throws IOException {
+        //Given
+        MailboxMessage  mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+        when(mailboxMessage.getProperties()).thenReturn(ImmutableList.of(IndexableMessage.HAS_ATTACHMENT_PROPERTY));
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.YES)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getHasAttachment()).isTrue();
+    }
+
+    @Test
+    public void hasAttachmentsShouldReturnFalseWhenPropertyIsPresentButFalse() throws IOException {
+        //Given
+        MailboxMessage  mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+        when(mailboxMessage.getProperties())
+            .thenReturn(ImmutableList.of(new SimpleProperty(PropertyBuilder.JAMES_INTERNALS, PropertyBuilder.HAS_ATTACHMENT, "false")));
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getHasAttachment()).isFalse();
+    }
+
+    @Test
+    public void hasAttachmentsShouldReturnFalseWhenPropertyIsAbsent() throws IOException {
+        //Given
+        MailboxMessage  mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+        when(mailboxMessage.getProperties())
+            .thenReturn(ImmutableList.of());
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getHasAttachment()).isFalse();
+    }
+
+    @Test
+    public void attachmentsShouldNotBeenIndexedWhenAsked() throws Exception {
+        //Given
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/mailWithHeaders.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.NO)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getAttachments()).isEmpty();
+    }
+
+    @Test
+    public void attachmentsShouldBeenIndexedWhenAsked() throws Exception {
+        //Given
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/emailWith3Attachments.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(new DefaultTextExtractor())
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.YES)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getAttachments()).isNotEmpty();
+    }
+
+    @Test
+    public void otherAttachmentsShouldBeenIndexedWhenOneOfThemCannotBeParsed() throws Exception {
+        //Given
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/emailWith3Attachments.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        TextExtractor textExtractor = mock(TextExtractor.class);
+        when(textExtractor.extractContent(any(), any()))
+            .thenReturn(new ParsedContent(Optional.of("first attachment content"), ImmutableMap.of()))
+            .thenThrow(new RuntimeException("second cannot be parsed"))
+            .thenReturn(new ParsedContent(Optional.of("third attachment content"), ImmutableMap.of()));
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(textExtractor)
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.YES)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getAttachments())
+            .extracting(new TextualBodyExtractor())
+            .contains("first attachment content", TextualBodyExtractor.NO_TEXTUAL_BODY, "third attachment content");
+    }
+
+    private static class TextualBodyExtractor implements Extractor<MimePart, String> {
+
+        public static final String NO_TEXTUAL_BODY = "The textual body is not present";
+
+        @Override
+        public String extract(MimePart input) {
+            return input.getTextualBody().orElse(NO_TEXTUAL_BODY);
+        }
+    }
+
+    @Test
+    public void messageShouldBeIndexedEvenIfTikaParserThrowsAnError() throws Exception {
+        //Given
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(InMemoryMessageId.of(42));
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/bodyMakeTikaToFail.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        // When
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(textExtractor)
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.YES)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getText()).contains("subject should be parsed");
+    }
+
+    @Test
+    public void shouldHandleCorrectlyMessageIdHavingSerializeMethodThatReturnNull() throws Exception {
+       MessageId invalidMessageIdThatReturnNull = mock(MessageId.class);
+       when(invalidMessageIdThatReturnNull.serialize())
+           .thenReturn(null);
+       
+        // When
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(invalidMessageIdThatReturnNull);
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/bodyMakeTikaToFail.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(textExtractor)
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.YES)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getMessageId()).isNull();
+    }
+
+    @Test
+    public void shouldHandleCorrectlyNullMessageId() throws Exception {
+       
+        // When
+        MailboxMessage mailboxMessage = mock(MailboxMessage.class);
+        TestId mailboxId = TestId.of(1);
+        when(mailboxMessage.getMailboxId())
+            .thenReturn(mailboxId);
+        when(mailboxMessage.getMessageId())
+            .thenReturn(null);
+        when(mailboxMessage.getFullContent())
+            .thenReturn(ClassLoader.getSystemResourceAsStream("eml/bodyMakeTikaToFail.eml"));
+        when(mailboxMessage.createFlags())
+            .thenReturn(new Flags());
+        when(mailboxMessage.getUid())
+            .thenReturn(MESSAGE_UID);
+
+        IndexableMessage indexableMessage = IndexableMessage.builder()
+                .message(mailboxMessage)
+                .users(ImmutableList.of(User.fromUsername("username")))
+                .extractor(textExtractor)
+                .zoneId(ZoneId.of("Europe/Paris"))
+                .indexAttachments(IndexAttachments.YES)
+                .build();
+
+        // Then
+        assertThat(indexableMessage.getMessageId()).isNull();
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java
new file mode 100644
index 0000000..9c840be
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageToElasticSearchJsonTest.java
@@ -0,0 +1,388 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER;
+import static net.javacrumbs.jsonunit.core.Option.IGNORING_VALUES;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.time.ZoneId;
+import java.util.Date;
+
+import javax.mail.Flags;
+import javax.mail.util.SharedByteArrayInputStream;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.FlagsBuilder;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.elasticsearch.v6.IndexAttachments;
+import org.apache.james.mailbox.extractor.TextExtractor;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
+import org.apache.james.mailbox.tika.TikaConfiguration;
+import org.apache.james.mailbox.tika.TikaContainerSingletonRule;
+import org.apache.james.mailbox.tika.TikaHttpClientImpl;
+import org.apache.james.mailbox.tika.TikaTextExtractor;
+import org.apache.james.metrics.api.NoopMetricFactory;
+import org.apache.james.util.ClassLoaderUtils;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+public class MessageToElasticSearchJsonTest {
+    private static final int SIZE = 25;
+    private static final int BODY_START_OCTET = 100;
+    private static final TestId MAILBOX_ID = TestId.of(18L);
+    private static final MessageId MESSAGE_ID = TestMessageId.of(184L);
+    private static final long MOD_SEQ = 42L;
+    private static final MessageUid UID = MessageUid.of(25);
+    private static final String USERNAME = "username";
+    private static final User USER = User.fromUsername(USERNAME);
+
+    private TextExtractor textExtractor;
+
+    private Date date;
+    private PropertyBuilder propertyBuilder;
+
+    @ClassRule
+    public static TikaContainerSingletonRule tika = TikaContainerSingletonRule.rule;
+
+    @Before
+    public void setUp() throws Exception {
+        textExtractor = new TikaTextExtractor(new NoopMetricFactory(), new TikaHttpClientImpl(TikaConfiguration.builder()
+                .host(tika.getIp())
+                .port(tika.getPort())
+                .timeoutInMillis(tika.getTimeoutInMillis())
+                .build()));
+        // 2015/06/07 00:00:00 0200 (Paris time zone)
+        date = new Date(1433628000000L);
+        propertyBuilder = new PropertyBuilder();
+        propertyBuilder.setMediaType("plain");
+        propertyBuilder.setSubType("text");
+        propertyBuilder.setTextualLineCount(18L);
+        propertyBuilder.setContentDescription("An e-mail");
+    }
+
+    @Test
+    public void convertToJsonShouldThrowWhenNoUser() {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+                new DefaultTextExtractor(),
+                ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage spamMail = new SimpleMailboxMessage(MESSAGE_ID,
+                date,
+                SIZE,
+                BODY_START_OCTET,
+                new SharedByteArrayInputStream("message".getBytes(StandardCharsets.UTF_8)),
+                new Flags(),
+                propertyBuilder,
+                MAILBOX_ID);
+        ImmutableList<User> users = ImmutableList.of();
+
+        assertThatThrownBy(() -> messageToElasticSearchJson.convertToJson(spamMail, users))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    public void spamEmailShouldBeWellConvertedToJson() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage spamMail = new SimpleMailboxMessage(MESSAGE_ID,
+                date,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/spamMail.eml"),
+                new Flags(),
+                propertyBuilder,
+                MAILBOX_ID);
+        spamMail.setUid(UID);
+        spamMail.setModSeq(MOD_SEQ);
+        assertThatJson(messageToElasticSearchJson.convertToJson(spamMail, ImmutableList.of(USER)))
+            .when(IGNORING_ARRAY_ORDER)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/spamMail.json"));
+    }
+
+    @Test
+    public void htmlEmailShouldBeWellConvertedToJson() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage htmlMail = new SimpleMailboxMessage(MESSAGE_ID,
+                date,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/htmlMail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("social", "pocket-money").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        htmlMail.setModSeq(MOD_SEQ);
+        htmlMail.setUid(UID);
+        assertThatJson(messageToElasticSearchJson.convertToJson(htmlMail, ImmutableList.of(USER)))
+            .when(IGNORING_ARRAY_ORDER)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/htmlMail.json"));
+    }
+
+    @Test
+    public void pgpSignedEmailShouldBeWellConvertedToJson() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage pgpSignedMail = new SimpleMailboxMessage(MESSAGE_ID,
+                date,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/pgpSignedMail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        pgpSignedMail.setModSeq(MOD_SEQ);
+        pgpSignedMail.setUid(UID);
+        assertThatJson(messageToElasticSearchJson.convertToJson(pgpSignedMail, ImmutableList.of(USER)))
+            .when(IGNORING_ARRAY_ORDER)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/pgpSignedMail.json"));
+    }
+
+    @Test
+    public void simpleEmailShouldBeWellConvertedToJson() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage mail = new SimpleMailboxMessage(MESSAGE_ID,
+                date,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/mail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        mail.setModSeq(MOD_SEQ);
+        mail.setUid(UID);
+        assertThatJson(messageToElasticSearchJson.convertToJson(mail,
+                ImmutableList.of(User.fromUsername("user1"), User.fromUsername("user2"))))
+            .when(IGNORING_ARRAY_ORDER).when(IGNORING_VALUES)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/mail.json"));
+    }
+
+    @Test
+    public void recursiveEmailShouldBeWellConvertedToJson() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage recursiveMail = new SimpleMailboxMessage(MESSAGE_ID, 
+                date,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        recursiveMail.setModSeq(MOD_SEQ);
+        recursiveMail.setUid(UID);
+        assertThatJson(messageToElasticSearchJson.convertToJson(recursiveMail, ImmutableList.of(USER)))
+            .when(IGNORING_ARRAY_ORDER).when(IGNORING_VALUES)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMail.json"));
+    }
+
+    @Test
+    public void emailWithNoInternalDateShouldUseNowDate() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage mailWithNoInternalDate = new SimpleMailboxMessage(MESSAGE_ID,
+                null,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        mailWithNoInternalDate.setModSeq(MOD_SEQ);
+        mailWithNoInternalDate.setUid(UID);
+        assertThatJson(messageToElasticSearchJson.convertToJson(mailWithNoInternalDate, ImmutableList.of(USER)))
+            .when(IGNORING_ARRAY_ORDER)
+            .when(IGNORING_VALUES)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMail.json"));
+    }
+
+    @Test
+    public void emailWithAttachmentsShouldConvertAttachmentsWhenIndexAttachmentsIsTrue() throws IOException {
+        // Given
+        MailboxMessage mailWithNoInternalDate = new SimpleMailboxMessage(MESSAGE_ID,
+                null,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        mailWithNoInternalDate.setModSeq(MOD_SEQ);
+        mailWithNoInternalDate.setUid(UID);
+
+        // When
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"),
+            IndexAttachments.YES);
+        String convertToJson = messageToElasticSearchJson.convertToJson(mailWithNoInternalDate, ImmutableList.of(USER));
+
+        // Then
+        assertThatJson(convertToJson)
+            .when(IGNORING_ARRAY_ORDER)
+            .when(IGNORING_VALUES)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMail.json"));
+    }
+
+    @Test
+    public void emailWithAttachmentsShouldNotConvertAttachmentsWhenIndexAttachmentsIsFalse() throws IOException {
+        // Given
+        MailboxMessage mailWithNoInternalDate = new SimpleMailboxMessage(MESSAGE_ID,
+                null,
+                SIZE,
+                BODY_START_OCTET,
+                ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
+                new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+                propertyBuilder,
+                MAILBOX_ID);
+        mailWithNoInternalDate.setModSeq(MOD_SEQ);
+        mailWithNoInternalDate.setUid(UID);
+
+        // When
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"),
+            IndexAttachments.NO);
+        String convertToJson = messageToElasticSearchJson.convertToJson(mailWithNoInternalDate, ImmutableList.of(USER));
+
+        // Then
+        assertThatJson(convertToJson)
+            .when(IGNORING_ARRAY_ORDER)
+            .when(IGNORING_VALUES)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/recursiveMailWithoutAttachments.json"));
+    }
+
+    @Test
+    public void emailWithNoMailboxIdShouldThrow() {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"), IndexAttachments.YES);
+        MailboxMessage mailWithNoMailboxId = new SimpleMailboxMessage(MESSAGE_ID, date,
+            SIZE,
+            BODY_START_OCTET,
+            ClassLoaderUtils.getSystemResourceAsSharedStream("eml/recursiveMail.eml"),
+            new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+            propertyBuilder,
+            null);
+        mailWithNoMailboxId.setModSeq(MOD_SEQ);
+        mailWithNoMailboxId.setUid(UID);
+
+        assertThatThrownBy(() ->
+            messageToElasticSearchJson.convertToJson(mailWithNoMailboxId, ImmutableList.of(USER)))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void getUpdatedJsonMessagePartShouldBehaveWellOnEmptyFlags() throws Exception {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"),
+            IndexAttachments.YES);
+        assertThatJson(messageToElasticSearchJson.getUpdatedJsonMessagePart(new Flags(), MOD_SEQ))
+            .isEqualTo("{\"modSeq\":42,\"isAnswered\":false,\"isDeleted\":false,\"isDraft\":false,\"isFlagged\":false,\"isRecent\":false,\"userFlags\":[],\"isUnread\":true}");
+    }
+
+    @Test
+    public void getUpdatedJsonMessagePartShouldBehaveWellOnNonEmptyFlags() throws Exception {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"),
+            IndexAttachments.YES);
+        assertThatJson(messageToElasticSearchJson.getUpdatedJsonMessagePart(new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.FLAGGED).add("user").build(), MOD_SEQ))
+            .isEqualTo("{\"modSeq\":42,\"isAnswered\":false,\"isDeleted\":true,\"isDraft\":false,\"isFlagged\":true,\"isRecent\":false,\"userFlags\":[\"user\"],\"isUnread\":true}");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void getUpdatedJsonMessagePartShouldThrowIfFlagsIsNull() throws Exception {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            new DefaultTextExtractor(),
+            ZoneId.of("Europe/Paris"),
+            IndexAttachments.YES);
+        messageToElasticSearchJson.getUpdatedJsonMessagePart(null, MOD_SEQ);
+    }
+
+    @Test
+    public void spamEmailShouldBeWellConvertedToJsonWithApacheTika() throws IOException {
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+            textExtractor,
+            ZoneId.of("Europe/Paris"),
+            IndexAttachments.YES);
+        MailboxMessage spamMail = new SimpleMailboxMessage(MESSAGE_ID, date,
+            SIZE,
+            BODY_START_OCTET,
+            ClassLoaderUtils.getSystemResourceAsSharedStream("eml/nonTextual.eml"),
+            new Flags(),
+            propertyBuilder,
+            MAILBOX_ID);
+        spamMail.setUid(UID);
+        spamMail.setModSeq(MOD_SEQ);
+
+        assertThatJson(messageToElasticSearchJson.convertToJson(spamMail, ImmutableList.of(USER)))
+            .when(IGNORING_ARRAY_ORDER)
+            .isEqualTo(
+                ClassLoaderUtils.getSystemResourceAsString("eml/nonTextual.json", StandardCharsets.UTF_8));
+    }
+
+    @Test
+    public void convertToJsonWithoutAttachmentShouldConvertEmailBoby() throws IOException {
+        // Given
+        MailboxMessage message = new SimpleMailboxMessage(MESSAGE_ID,
+            null,
+            SIZE,
+            BODY_START_OCTET,
+            ClassLoaderUtils.getSystemResourceAsSharedStream("eml/emailWithNonIndexableAttachment.eml"),
+            new FlagsBuilder().add(Flags.Flag.DELETED, Flags.Flag.SEEN).add("debian", "security").build(),
+            propertyBuilder,
+            MAILBOX_ID);
+        message.setModSeq(MOD_SEQ);
+        message.setUid(UID);
+
+        // When
+        MessageToElasticSearchJson messageToElasticSearchJson = new MessageToElasticSearchJson(
+                new DefaultTextExtractor(),
+                ZoneId.of("Europe/Paris"),
+                IndexAttachments.NO);
+        String convertToJsonWithoutAttachment = messageToElasticSearchJson.convertToJsonWithoutAttachment(message, ImmutableList.of(USER));
+
+        // Then
+        assertThatJson(convertToJsonWithoutAttachment)
+            .when(IGNORING_ARRAY_ORDER)
+            .when(IGNORING_VALUES)
+            .isEqualTo(ClassLoaderUtils.getSystemResourceAsString("eml/emailWithNonIndexableAttachmentWithoutAttachment.json"));
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java
new file mode 100644
index 0000000..89daa82
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/MimePartTest.java
@@ -0,0 +1,50 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.junit.Test;
+
+public class MimePartTest {
+
+    @Test
+    public void buildShouldWorkWhenTextualContentFromParserIsEmpty() {
+        MimePart.builder()
+            .addBodyContent(new ByteArrayInputStream(new byte[] {}))
+            .addMediaType("text")
+            .addSubType("plain")
+            .build();
+    }
+
+    @Test
+    public void buildShouldWorkWhenTextualContentFromParserIsNonEmpty() {
+        String body = "text";
+        MimePart mimePart = MimePart.builder()
+            .addBodyContent(new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)))
+            .addMediaType("text")
+            .addSubType("plain")
+            .build();
+        
+        assertThat(mimePart.getTextualBody()).contains(body);
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java
new file mode 100644
index 0000000..38f6581
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/SubjectsTest.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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.elasticsearch.v6.json;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.Test;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+
+public class SubjectsTest {
+
+    @Test
+    public void fromShouldThrowWhenSetIsNull() {
+        assertThatThrownBy(() -> Subjects.from(null))
+            .isInstanceOf(NullPointerException.class)
+            .hasMessage("'subjects' is mandatory");
+    }
+
+    @Test
+    public void serializeShouldReturnEmptyWhenEmptySet() {
+        Subjects subjects = Subjects.from(ImmutableSet.of());
+
+        assertThat(subjects.serialize()).isEmpty();
+    }
+
+    @Test
+    public void serializeShouldNotJoinWhenOneElement() {
+        String expected = "subject";
+        Subjects subjects = Subjects.from(ImmutableSet.of(expected));
+
+        assertThat(subjects.serialize()).isEqualTo(expected);
+    }
+
+    @Test
+    public void serializeShouldJoinWhenMultipleElements() {
+        String subject = "subject";
+        String subject2 = "subject2";
+        String subject3 = "subject3";
+
+        String expected = Joiner.on(" ").join(subject, subject2, subject3);
+
+        Subjects subjects = Subjects.from(ImmutableSet.of(subject, subject2, subject3));
+
+        assertThat(subjects.serialize()).isEqualTo(expected);
+    }
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java
new file mode 100644
index 0000000..db48dad
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/DateResolutionFormaterTest.java
@@ -0,0 +1,99 @@
+/****************************************************************
+ * 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.elasticsearch.v6.query;
+
+import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.text.ParseException;
+import java.time.ZonedDateTime;
+
+import org.apache.james.mailbox.model.SearchQuery;
+import org.junit.Test;
+
+
+public class DateResolutionFormaterTest {
+
+    private final String dateString = "2014-01-02T15:15:15Z";
+
+    @Test
+    public void calculateUpperDateShouldReturnDateUpToTheNextMinuteUsingMinuteUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Minute)
+            )
+        ).isEqualTo("2014-01-02T15:16:00Z");
+    }
+
+    @Test
+    public void calculateUpperDateShouldReturnDateUpToTheNextHourUsingHourUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Hour)
+            )
+        ).isEqualTo("2014-01-02T16:00:00Z");
+    }
+
+    @Test
+    public void calculateUpperDateShouldReturnDateUpToTheNextMonthUsingMonthUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Month)
+            )
+        ).isEqualTo("2014-02-01T00:00:00Z");
+    }
+
+    @Test
+    public void calculateUpperDateShouldReturnDateUpToTheNextYearUsingYearUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Year)
+            )
+        ).isEqualTo("2015-01-01T00:00:00Z");
+    }
+
+    @Test
+    public void calculateUpperDateShouldReturnDateUpToTheNextDayUsingDayUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeUpperDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Day)
+            )
+        ).isEqualTo("2014-01-03T00:00:00Z");
+    }
+
+    @Test
+    public void calculateLowerDateShouldReturnDateUpToThePreviousMinuteUsingMinuteUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeLowerDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Minute)
+            )
+        ).isEqualTo("2014-01-02T15:15:00Z");
+    }
+
+    @Test
+    public void calculateLowerDateShouldReturnDateUpToThePreviousDayUsingDayUnit() throws ParseException {
+        assertThat(
+            ISO_OFFSET_DATE_TIME.format(
+                DateResolutionFormater.computeLowerDate(ZonedDateTime.parse(dateString, ISO_OFFSET_DATE_TIME), SearchQuery.DateResolution.Day)
+            )
+        ).isEqualTo("2014-01-02T00:00:00Z");
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java
new file mode 100644
index 0000000..bfa4245
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/query/SearchQueryTest.java
@@ -0,0 +1,77 @@
+/****************************************************************
+ * 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.elasticsearch.v6.query;
+
+import java.util.Date;
+
+import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.model.SearchQuery.DateResolution;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class SearchQueryTest {
+
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    @Test
+    public void sentDateOnShouldThrowOnNullDate() {
+        expectedException.expect(NullPointerException.class);
+
+        SearchQuery.sentDateOn(null, DateResolution.Day);
+    }
+
+    @Test
+    public void sentDateOnShouldThrowOnNullResolution() {
+        expectedException.expect(NullPointerException.class);
+
+        SearchQuery.sentDateOn(new Date(), null);
+    }
+
+    @Test
+    public void sentDateAfterShouldThrowOnNullDate() {
+        expectedException.expect(NullPointerException.class);
+
+        SearchQuery.sentDateAfter(null, DateResolution.Day);
+    }
+
+    @Test
+    public void sentDateAfterShouldThrowOnNullResolution() {
+        expectedException.expect(NullPointerException.class);
+
+        SearchQuery.sentDateAfter(new Date(), null);
+    }
+
+    @Test
+    public void sentDateBeforeShouldThrowOnNullDate() {
+        expectedException.expect(NullPointerException.class);
+
+        SearchQuery.sentDateBefore(null, DateResolution.Day);
+    }
+
+    @Test
+    public void sentDateBeforeShouldThrowOnNullResolution() {
+        expectedException.expect(NullPointerException.class);
+
+        SearchQuery.sentDateOn(new Date(), null);
+    }
+
+}
diff --git a/mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml b/mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml
new file mode 100644
index 0000000..e4e7ede
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/resources/eml/bodyMakeTikaToFail.eml
@@ -0,0 +1,1272 @@
+Date: Mon, 30 Jan 2017 11:51:35 +0100
+From: Raphael OUAZANA <ra...@linagora.com>
+To: OUAZANA Raphael <ra...@linagora.com>
+Subject: subject should be parsed
+Message-ID: <25...@linagora.com>
+X-Sender: raph.ouazana@linagora.com
+User-Agent: Roundcube Webmail/1.1.4
+
+Return-Path: <ip...@obm.lng.org>
+Received: from lenny.obm.lng.org (localhost [127.0.0.1])
+	 by lenny.obm.lng.org (Cyrus v2.3.14-Debian-2.3.14-2) with LMTPA;
+	 Wed, 21 Mar 2012 14:51:59 +0100
+X-Sieve: CMU Sieve 2.3
+Received: from [192.168.2.48] (unknown [192.168.2.48])
+	by lenny.obm.lng.org (Postfix) with ESMTP id E495D32929
+	for <us...@obm.lng.org>; Wed, 21 Mar 2012 14:51:58 +0100 (CET)
+Message-ID: <4F...@obm.lng.org>
+Date: Wed, 21 Mar 2012 14:52:14 +0100
+ From: iphone <ip...@obm.lng.org>
+User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; 
+rv:1.9.2.28) Gecko/20120306 Lightning/1.0b2 Thunderbird/3.1.20
+MIME-Version: 1.0
+To: usera <us...@obm.lng.org>
+Subject: Fwd: Re: email with sign
+Content-Type: multipart/mixed;
+  boundary="------------080809000000030101030405"
+
+This is a multi-part message in MIME format.
+--------------080809000000030101030405
+Content-Type: text/plain; charset=ISO-8859-1; format=flowed
+Content-Transfer-Encoding: 7bit
+
+new email text part
+
+--------------080809000000030101030405
+Content-Type: message/rfc822;
+  name="Re: email with sign.eml"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: attachment;
+  filename="Re: email with sign.eml"
+
+Return-Path: <us...@obm.lng.org>
+Received: from lenny.obm.lng.org (localhost [127.0.0.1])
+	 by lenny.obm.lng.org (Cyrus v2.3.14-Debian-2.3.14-2) with LMTPA;
+	 Wed, 21 Mar 2012 14:19:29 +0100
+X-Sieve: CMU Sieve 2.3
+Received: from [192.168.2.48] (unknown [192.168.2.48])
+	by lenny.obm.lng.org (Postfix) with ESMTP id 47EC832D51
+	for <ip...@obm.lng.org>; Wed, 21 Mar 2012 14:19:29 +0100 (CET)
+Message-ID: <4F...@obm.lng.org>
+Date: Wed, 21 Mar 2012 14:19:44 +0100
+ From: usera <us...@obm.lng.org>
+User-Agent: Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; 
+rv:1.9.2.28) Gecko/20120306 Thunderbird/3.1.20
+MIME-Version: 1.0
+To: iphone <ip...@obm.lng.org>
+Subject: Re: email with sign
+References: <4F...@obm.lng.org>
+In-Reply-To: <4F...@obm.lng.org>
+Content-Type: multipart/alternative;
+  boundary="------------050702060806040107070701"
+
+This is a multi-part message in MIME format.
+--------------050702060806040107070701
+Content-Type: text/plain; charset=ISO-8859-1; format=flowed
+Content-Transfer-Encoding: 7bit
+
+On 03/21/2012 01:59 PM, iphone wrote:
+> email with sign text part
+> --
+new email text part
+
+--------------050702060806040107070701
+Content-Type: multipart/related;
+  boundary="------------090109030206070103090500"
+
+
+--------------090109030206070103090500
+Content-Type: text/html; charset=ISO-8859-1
+Content-Transfer-Encoding: 7bit
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+   <head>
+     <meta content="text/html; charset=ISO-8859-1"
+       http-equiv="Content-Type">
+   </head>
+   <body text="#000000" bgcolor="#ffffff">
+     On 03/21/2012 01:59 PM, iphone wrote:
+     <blockquote cite="mid:4F69D0C6.501@obm.lng.org" type="cite">
+       <meta http-equiv="content-type" content="text/html;
+         charset=ISO-8859-1">
+       email with sign text part<br>
+       <div class="moz-signature">-- <br>
+         <img src="cid:part1.05020706.03070506@obm.lng.org" 
+border="0"></div>
+     </blockquote>
+     new email text part<br>
+   </body>
+
+</html>
+
+--------------090109030206070103090500
+Content-Type: image/jpeg
+Content-Transfer-Encoding: base64
+Content-ID: <pa...@obm.lng.org>
+
+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
+RvvR7cp9+nyYupWotquN7t17lQ1W/iOqfiKi4Sl3iAJbUmI2CNhIVz3Yykxd66HItw0joyzq
+lWSbK0tavBjTt3sSJ8Z5BaZQtzgKWNjmWEKSoKO3KgdwJoC46n1W1brNapdnipvUm9SG49qa
+bfDbchS21OhRcwdqA22tZUAThPIE4FQrfSXEtUqRa9awFWW7tPstNxoXGuKJXGbeW0WS20Fq
+3CM+MFtJy2Rg5TmTvGjus6XsdsttyFunWBbLttltxG9jbjbSmubKNqNim1uIKE7QAo7duBiM
+hdH85zVNu1VfNQNT7zGuCJTy2IHAZUy3Elx22G0FxRQEmY45uUpZJJHIEbQPub0raPtUORIv
+dzTH6uuaXjFiyZKGWo0l1hTjiksjh5UypPjAArCkoU4AFK+k9KmkWHZEa43EMyY7zyXkxosl
+9tlpEuRGDriwyA2nfGcClKwhBB8dSSlaud9IXRzrONGv9o0il+ajVMGdEnyVR44ZaTImzZKA
+SuQlbezrriVKS27vHclBxi4o6KNto1zA8PZ7V22XA39T/wA148u4yN+N/j7fCO3Hi54Wcjdh
+IFsh6005L1IrT0ec6qeHHGk5iPJZccbBLjaHikNLWkA7kJUVDarIGDjLqDVmn7BPhQLtcBHk
+zXWmmGw0tZUp15DLYO0EJ3OOJSCrA7z3JURUtNdFMGxdIb+qY5sRS5NlzgrwCz4RLskrK0qm
+klZbBcXtCUpVghJUUjB+ekLRGor/AKvdl2i5QrexJYta1SZUMyktOW6c5KQ3ww80r9Ip1GVA
+nxWlDkVJUAL/AGy5Qrm285CfDoYfcjujaUlDiFFKkkEAjmP3ggjIINbdVvRNpm2+dqefNbDJ
+u95VLaZ3BWxtDDMdJ5EjxwxxMeTfzwcirJQClKUApSlAKUpQClKUApSlAKUpQClKUApSlAKU
+pQClKUApSlAKUpQClKUApSlAKUpQClKUBjkuLajuutsOPrQgqS02UhSyB9UbiBk93MgecioX
+w9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/+
++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqU
+BA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6lX/30L8xTw9dfUq/++hfmKnqUBA+Hrr6l
+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
+IVsZZfbzsWknIyCD5fMTUxSld2LBjwrbjior4KjglOUncnYpSlalRSlKAUpSgIzUNqVc2GSy
+/wBXkxnOKw4U7gFYIII8oIJH76iPBGqvtOz/AHNz5lWlSgkFSiAB5Sax9ZjekNf1xQFa8Eaq
++07P9zc+ZTwRqr7Ts/3Nz5lWXrMb0hr+uKdZjekNf1xUUTZWvBGqvtOz/c3PmV+Ks2qFbc3K
+znaoKH8jd7wQR/rPOBVm6zG9Ia/rinWY3pDX9cUoWVrwRqr7Ts/3Nz5lPA+qfLc7Rj2RHM/2
+lWXrMb0hr+uKdZj+kNf1xShZq2C2ptVuTFDqnVlanHFn+ktRyo/vJrWvlmdmXCJdIExMO4RU
+rQ2txritqQvG5Kk5SSOQPIg5AqYBBGQcivlxaG07nFpQnzqOBUkEJ1XWH23YvhDv5mnVdYfb
+di+EO/mal+txPSmPeCnW4npTHvBQER1XWH23YvhDv5mnVdYfbdi+EO/mal+txPSmPeCnW4np
+THvBQGpYbYq2syC9JMqVKeL8l4oCQte0JGEjuASlKQOfIDmTzqRr8SpK0hSVBQPcQciv2gFK
+UoBSlKAUpSgFKUoBSlKAVB631Ta9IWZNzuvHWl2Q3FjsR0b3ZDzhwhtCcjKifOQOR51OVTul
+rSErV9ltibdKYjXKz3eNd4RfBLS3WSSELxzCSFEZGccuRoWjV8m9atXQ34Fwm3m33HTDdvcC
+JCr0lthsAjkpLoWppafJlKzg8jitmTq3SkW0MXiTqaysW2QcMTHJ7SWXT5krKtp/capet9J6
+41lYogui9ORJtsvsW6wYbDrzjDqGSSWn3VIBVuyTlLYxgcjVfY6ILqxZDIUiG7qJV9nXdh+J
+eX4KbYZO0KbZWlhZWnCRnchOefnqC6jB9WdRt+qrTcNRtWWBIYll63eEW5DEyOtC2uJw+SA5
+xSM/09mzybt3Kp2uWaA0FrK2dI1t1bqm+wLu8zpQ2eU+2FJddkdcLwUE7QnYEEJ3ZBJGdvOu
+p1JSaSfApSlCopSlAKUpQGtdp8a12uVcprqWo0VlbzqyeSUpBJP8BX84umnpRvnSHrFy4TVP
+M2kKPg6NkhCW88lEfrEcya949O1luuouh7VNlsiVLuMq3uIYQk4K1d+0ftAI/fXhRWmpUmxM
+M3SzTrZMYSG1syo6m1ZSMEpyOdUndHVpau+5XrYtwPNvsuFLiFBSfYa9KW/6UZ07p+128aF6
+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
+--------------090109030206070103090500--
+
+--------------050702060806040107070701--
+
+--------------080809000000030101030405--
diff --git a/mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml b/mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml
new file mode 100644
index 0000000..7cacfcb
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/resources/eml/emailWith3Attachments.eml
@@ -0,0 +1,50 @@
+To: Laura ROYET <la...@linagora.com>
+From: Laura Royet <la...@linagora.com>
+Subject: test
+Message-ID: <cb...@linagora.com>
+Date: Wed, 11 Jan 2017 11:52:35 +0100
+User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101
+ Thunderbird/45.6.0
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------36566F1E9D791340FFB75FF8"
+
+This is a multi-part message in MIME format.
+--------------36566F1E9D791340FFB75FF8
+Content-Type: text/plain; charset=utf-8; format=flowed
+Content-Transfer-Encoding: 7bit
+
+
+
+-- 
+Laura Royet
+
+
+--------------36566F1E9D791340FFB75FF8
+Content-Type: application/vnd.oasis.opendocument.text;
+ name="attachment1.odt"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="attachment1.odt"
+
+UEsDBBQAAAgAAGJVK0pexjIMJwAAACcAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQu
+dC54bWxQSwUGAAAAABEAEQBwBAAAjyUAAAAA
+--------------36566F1E9D791340FFB75FF8
+Content-Type: application/vnd.oasis.opendocument.text;
+ name="attachment2-nonIndexableAttachment.html"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="attachment2-nonIndexableAttachment.odt"
+
+PCFET0NUWVBFIGh0bWw+CjxodG1sIGNsYXNzPSJtb3ppbGxhIiBsYW5nPSJlbiI+PGhlYWQ+
+CI+PC9kaXY+PC9kaXY+PC9ib2R5PjwvaHRtbD4=
+--------------36566F1E9D791340FFB75FF8
+Content-Type: application/vnd.oasis.opendocument.text;
+ name="attachment3.odt"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="attachment3.odt"
+
+UEsDBBQAAAgAAG9VK0pexjIMJwAAACcAAAAIAAAAbWltZXR5cGVhcHBsaWNhdGlvbi92bmQu
+AAAAEgkAABNRVRBLUlORi9tYW5pZmVzdC54bWxQSwUGAAAAABEAEQBwBAAApyUAAAAA
+--------------36566F1E9D791340FFB75FF8--
diff --git a/mailbox/elasticsearch-v6/src/test/resources/eml/mailWithHeaders.eml b/mailbox/elasticsearch-v6/src/test/resources/eml/mailWithHeaders.eml
new file mode 100644
index 0000000..2aff55d
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/resources/eml/mailWithHeaders.eml
@@ -0,0 +1,14 @@
+Content-Type: text/plain; Charset=UTF-8
+Date: Fri, 17 Sep 2010 17:12:26 +0200
+Subject: my subject
+To: a@test, B <b...@test>
+Cc: c@test
+Bcc: dD <d...@test>
+MIME-Version: 1.0
+Message-Id: <20...@lenny>
+From: Ad Min <ad...@opush.test>
+
+Mail content
+
+-- 
+Ad Min
diff --git a/mailbox/elasticsearch-v6/src/test/resources/logback-test.xml b/mailbox/elasticsearch-v6/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..b0c305c
--- /dev/null
+++ b/mailbox/elasticsearch-v6/src/test/resources/logback-test.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+        <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
+                <resetJUL>true</resetJUL>
+        </contextListener>
+
+        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+                <encoder>
+                        <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
+                        <immediateFlush>false</immediateFlush>
+                </encoder>
+        </appender>
+
+        <root level="ERROR">
+                <appender-ref ref="CONSOLE" />
+        </root>
+
+
+        <logger name="org.apache.james" level="WARN" >
+                <appender-ref ref="CONSOLE" />
+        </logger>
+
+</configuration>
diff --git a/mailbox/pom.xml b/mailbox/pom.xml
index 449bb35..7ae95b1 100644
--- a/mailbox/pom.xml
+++ b/mailbox/pom.xml
@@ -40,6 +40,7 @@
         <module>caching</module>
         <module>cassandra</module>
         <module>elasticsearch</module>
+        <module>elasticsearch-v6</module>
 
         <module>event/event-cassandra</module>
         <module>event/event-memory</module>


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


[james-project] 02/14: JAMES-2764 Update version of ES to 6.7.2 in the mailbox es module

Posted by bt...@apache.org.
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 6da448e436a9809437b0f4174607a3cc6d57c076
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Thu May 16 15:56:15 2019 +0700

    JAMES-2764 Update version of ES to 6.7.2 in the mailbox es module
---
 mailbox/elasticsearch-v6/pom.xml | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/mailbox/elasticsearch-v6/pom.xml b/mailbox/elasticsearch-v6/pom.xml
index 6bf0500..2d8fc64 100644
--- a/mailbox/elasticsearch-v6/pom.xml
+++ b/mailbox/elasticsearch-v6/pom.xml
@@ -36,11 +36,11 @@
     <dependencies>
         <dependency>
             <groupId>${james.groupId}</groupId>
-            <artifactId>apache-james-backends-es</artifactId>
+            <artifactId>apache-james-backends-es-v6</artifactId>
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
-            <artifactId>apache-james-backends-es</artifactId>
+            <artifactId>apache-james-backends-es-v6</artifactId>
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
@@ -156,18 +156,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.elasticsearch</groupId>
-            <artifactId>elasticsearch</artifactId>
-            <version>2.2.1</version>
-        </dependency>
-        <dependency>
-            <groupId>org.elasticsearch</groupId>
-            <artifactId>elasticsearch</artifactId>
-            <version>2.2.1</version>
-            <type>test-jar</type>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter-params</artifactId>
             <scope>test</scope>


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


[james-project] 04/14: JAMES-2764 Migrate mailbox ES to ES6

Posted by bt...@apache.org.
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 b638a96d65fe117194df51e48af1998666143314
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri May 17 17:55:29 2019 +0700

    JAMES-2764 Migrate mailbox ES to ES6
---
 .../james/backends/es/v6/IndexCreationFactory.java |   9 +-
 .../v6/ElasticSearchMailboxConfiguration.java      |   8 +-
 .../v6/MailboxElasticSearchConstants.java          |   8 +-
 .../elasticsearch/v6/MailboxIndexCreationUtil.java |  26 +-
 .../elasticsearch/v6/MailboxMappingFactory.java    | 440 ++++++++++-----------
 .../ElasticSearchListeningMessageSearchIndex.java  |  13 +-
 .../elasticsearch/v6/query/CriterionConverter.java |  15 +-
 .../elasticsearch/v6/query/SortConverter.java      |  26 +-
 .../v6/search/ElasticSearchSearcher.java           |  80 ++--
 9 files changed, 303 insertions(+), 322 deletions(-)

diff --git a/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java b/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java
index 8c281d0..17c51c7 100644
--- a/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java
+++ b/backends-common/elasticsearch-v6/src/main/java/org/apache/james/backends/es/v6/IndexCreationFactory.java
@@ -173,14 +173,15 @@ public class IndexCreationFactory {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(IndexCreationFactory.class);
     private static final String INDEX_ALREADY_EXISTS_EXCEPTION_MESSAGE = "type=resource_already_exists_exception";
-    private static final String CASE_INSENSITIVE = "case_insensitive";
-    private static final String KEEP_MAIL_AND_URL = "keep_mail_and_url";
-    private static final String SNOWBALL_KEEP_MAIL_AND_URL = "snowball_keep_mail_and_token";
-    private static final String ENGLISH_SNOWBALL = "english_snowball";
 
     private final int nbShards;
     private final int nbReplica;
 
+    public static final String CASE_INSENSITIVE = "case_insensitive";
+    public static final String KEEP_MAIL_AND_URL = "keep_mail_and_url";
+    public static final String SNOWBALL_KEEP_MAIL_AND_URL = "snowball_keep_mail_and_token";
+    public static final String ENGLISH_SNOWBALL = "english_snowball";
+
     @Inject
     public IndexCreationFactory(ElasticSearchConfiguration configuration) {
         this.nbShards = configuration.getNbShards();
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
index 6ff086c..3bac6b3 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
@@ -23,9 +23,9 @@ import java.util.Objects;
 import java.util.Optional;
 
 import org.apache.commons.configuration.Configuration;
-import org.apache.james.backends.es.IndexName;
-import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.WriteAliasName;
+import org.apache.james.backends.es.v6.IndexName;
+import org.apache.james.backends.es.v6.ReadAliasName;
+import org.apache.james.backends.es.v6.WriteAliasName;
 import org.apache.james.util.OptionalUtils;
 
 public class ElasticSearchMailboxConfiguration {
@@ -114,7 +114,7 @@ public class ElasticSearchMailboxConfiguration {
     public static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
     public static final int DEFAULT_NB_SHARDS = 5;
     public static final int DEFAULT_NB_REPLICA = 1;
-    public static final int DEFAULT_PORT = 9300;
+    public static final int DEFAULT_PORT = 9200;
     public static final Optional<Integer> DEFAULT_PORT_AS_OPTIONAL = Optional.of(DEFAULT_PORT);
 
     public static final ElasticSearchMailboxConfiguration DEFAULT_CONFIGURATION = builder().build();
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
index 301127e..07a7644 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxElasticSearchConstants.java
@@ -19,10 +19,9 @@
 
 package org.apache.james.mailbox.elasticsearch.v6;
 
-import org.apache.james.backends.es.IndexName;
-import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.TypeName;
-import org.apache.james.backends.es.WriteAliasName;
+import org.apache.james.backends.es.v6.IndexName;
+import org.apache.james.backends.es.v6.ReadAliasName;
+import org.apache.james.backends.es.v6.WriteAliasName;
 
 public interface MailboxElasticSearchConstants {
 
@@ -33,5 +32,4 @@ public interface MailboxElasticSearchConstants {
     WriteAliasName DEFAULT_MAILBOX_WRITE_ALIAS = new WriteAliasName("mailboxWriteAlias");
     ReadAliasName DEFAULT_MAILBOX_READ_ALIAS = new ReadAliasName("mailboxReadAlias");
     IndexName DEFAULT_MAILBOX_INDEX = new IndexName("mailbox_v1");
-    TypeName MESSAGE_TYPE = new TypeName("message");
 }
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
index 4d320e4..8a41671 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxIndexCreationUtil.java
@@ -19,22 +19,23 @@
 
 package org.apache.james.mailbox.elasticsearch.v6;
 
-import org.apache.james.backends.es.ElasticSearchConfiguration;
-import org.apache.james.backends.es.IndexCreationFactory;
-import org.apache.james.backends.es.IndexName;
-import org.apache.james.backends.es.NodeMappingFactory;
-import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.WriteAliasName;
-import org.elasticsearch.client.Client;
+import java.io.IOException;
+
+import org.apache.james.backends.es.v6.ElasticSearchConfiguration;
+import org.apache.james.backends.es.v6.IndexCreationFactory;
+import org.apache.james.backends.es.v6.IndexName;
+import org.apache.james.backends.es.v6.NodeMappingFactory;
+import org.apache.james.backends.es.v6.ReadAliasName;
+import org.apache.james.backends.es.v6.WriteAliasName;
+import org.elasticsearch.client.RestHighLevelClient;
 
 public class MailboxIndexCreationUtil {
 
-    public static Client prepareClient(Client client,
+    public static RestHighLevelClient prepareClient(RestHighLevelClient client,
                                        ReadAliasName readAlias,
                                        WriteAliasName writeAlias,
                                        IndexName indexName,
-                                       ElasticSearchConfiguration configuration) {
-
+                                       ElasticSearchConfiguration configuration) throws IOException {
         return NodeMappingFactory.applyMapping(
             new IndexCreationFactory(configuration)
                 .useIndex(indexName)
@@ -42,15 +43,14 @@ public class MailboxIndexCreationUtil {
                 .addAlias(writeAlias)
                 .createIndexAndAliases(client),
             indexName,
-            MailboxElasticSearchConstants.MESSAGE_TYPE,
             MailboxMappingFactory.getMappingContent());
     }
 
-    public static Client prepareDefaultClient(Client client, ElasticSearchConfiguration configuration) {
+    public static RestHighLevelClient prepareDefaultClient(RestHighLevelClient client, ElasticSearchConfiguration configuration) throws IOException {
         return prepareClient(client,
             MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
             MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
             MailboxElasticSearchConstants.DEFAULT_MAILBOX_INDEX,
-                configuration);
+            configuration);
     }
 }
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
index 2ecd6d3..4021f73 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
@@ -19,25 +19,24 @@
 
 package org.apache.james.mailbox.elasticsearch.v6;
 
-import static org.apache.james.backends.es.IndexCreationFactory.CASE_INSENSITIVE;
-import static org.apache.james.backends.es.IndexCreationFactory.KEEP_MAIL_AND_URL;
-import static org.apache.james.backends.es.IndexCreationFactory.SNOWBALL_KEEP_MAIL_AND_URL;
-import static org.apache.james.backends.es.NodeMappingFactory.ANALYZER;
-import static org.apache.james.backends.es.NodeMappingFactory.BOOLEAN;
-import static org.apache.james.backends.es.NodeMappingFactory.FIELDS;
-import static org.apache.james.backends.es.NodeMappingFactory.FORMAT;
-import static org.apache.james.backends.es.NodeMappingFactory.IGNORE_ABOVE;
-import static org.apache.james.backends.es.NodeMappingFactory.INDEX;
-import static org.apache.james.backends.es.NodeMappingFactory.LONG;
-import static org.apache.james.backends.es.NodeMappingFactory.NESTED;
-import static org.apache.james.backends.es.NodeMappingFactory.NOT_ANALYZED;
-import static org.apache.james.backends.es.NodeMappingFactory.PROPERTIES;
-import static org.apache.james.backends.es.NodeMappingFactory.RAW;
-import static org.apache.james.backends.es.NodeMappingFactory.SEARCH_ANALYZER;
-import static org.apache.james.backends.es.NodeMappingFactory.SNOWBALL;
-import static org.apache.james.backends.es.NodeMappingFactory.SPLIT_EMAIL;
-import static org.apache.james.backends.es.NodeMappingFactory.STRING;
-import static org.apache.james.backends.es.NodeMappingFactory.TYPE;
+import static org.apache.james.backends.es.v6.IndexCreationFactory.CASE_INSENSITIVE;
+import static org.apache.james.backends.es.v6.IndexCreationFactory.KEEP_MAIL_AND_URL;
+import static org.apache.james.backends.es.v6.IndexCreationFactory.SNOWBALL_KEEP_MAIL_AND_URL;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.ANALYZER;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.BOOLEAN;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.FIELDS;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.FORMAT;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.IGNORE_ABOVE;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.KEYWORD;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.LONG;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.NESTED;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.NORMALIZER;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.PROPERTIES;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.RAW;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.SEARCH_ANALYZER;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.SNOWBALL;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.SPLIT_EMAIL;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.TYPE;
 import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.BCC;
 import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.CC;
 import static org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.DATE;
@@ -69,290 +68,267 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
 
 import java.io.IOException;
 
-import org.apache.james.backends.es.NodeMappingFactory;
+import org.apache.james.backends.es.v6.NodeMappingFactory;
 import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.EMailer;
-import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants.Property;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 
 public class MailboxMappingFactory {
 
     private static final int MAXIMUM_TERM_LENGTH = 4096;
     private static final String STANDARD = "standard";
+    private static final String STORE = "store";
+    private static final String FIELD_DATA = "fielddata";
+
 
     public static XContentBuilder getMappingContent() {
         try {
             return jsonBuilder()
                 .startObject()
 
-                    .startObject(MailboxElasticSearchConstants.MESSAGE_TYPE.getValue())
-                        .startObject(PROPERTIES)
+                    .startObject(PROPERTIES)
 
-                            .startObject(MESSAGE_ID)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(MESSAGE_ID)
+                            .field(TYPE, KEYWORD)
+                            .field(STORE, true)
+                        .endObject()
 
-                            .startObject(UID)
-                                .field(TYPE, LONG)
-                            .endObject()
+                        .startObject(UID)
+                            .field(TYPE, LONG)
+                            .field(STORE, true)
+                        .endObject()
 
-                            .startObject(MODSEQ)
-                                .field(TYPE, LONG)
-                            .endObject()
+                        .startObject(MODSEQ)
+                            .field(TYPE, LONG)
+                        .endObject()
 
-                            .startObject(SIZE)
-                                .field(TYPE, LONG)
-                            .endObject()
+                        .startObject(SIZE)
+                            .field(TYPE, LONG)
+                        .endObject()
 
-                            .startObject(IS_ANSWERED)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(IS_ANSWERED)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(IS_DELETED)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(IS_DELETED)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(IS_DRAFT)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(IS_DRAFT)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(IS_FLAGGED)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(IS_FLAGGED)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(IS_RECENT)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(IS_RECENT)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(IS_UNREAD)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(IS_UNREAD)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(DATE)
-                                .field(TYPE, NodeMappingFactory.DATE)
-                                .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
-                            .endObject()
+                        .startObject(DATE)
+                            .field(TYPE, NodeMappingFactory.DATE)
+                            .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
+                        .endObject()
 
-                            .startObject(SENT_DATE)
-                                .field(TYPE, NodeMappingFactory.DATE)
-                                .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
-                            .endObject()
+                        .startObject(SENT_DATE)
+                            .field(TYPE, NodeMappingFactory.DATE)
+                            .field(FORMAT, "yyyy-MM-dd'T'HH:mm:ssZ")
+                        .endObject()
 
-                            .startObject(MEDIA_TYPE)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(MEDIA_TYPE)
+                            .field(TYPE, KEYWORD)
+                        .endObject()
 
-                            .startObject(SUBTYPE)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(SUBTYPE)
+                            .field(TYPE, KEYWORD)
+                        .endObject()
 
-                            .startObject(USER_FLAGS)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(USER_FLAGS)
+                            .field(TYPE, KEYWORD)
+                        .endObject()
 
-                            .startObject(FROM)
-                                .field(TYPE, NESTED)
-                                .startObject(PROPERTIES)
-                                    .startObject(EMailer.NAME)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                        .startObject(FROM)
+                            .field(TYPE, NESTED)
+                            .startObject(PROPERTIES)
+                                .startObject(EMailer.NAME)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
-                                    .startObject(EMailer.ADDRESS)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, STANDARD)
-                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                                .endObject()
+                                .startObject(EMailer.ADDRESS)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, STANDARD)
+                                    .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(SUBJECT)
-                                .field(TYPE, STRING)
-                                .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                .startObject(FIELDS)
-                                    .startObject(RAW)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, CASE_INSENSITIVE)
-                                    .endObject()
+                        .startObject(SUBJECT)
+                            .field(TYPE, TEXT)
+                            .field(ANALYZER, KEEP_MAIL_AND_URL)
+                            .startObject(FIELDS)
+                                .startObject(RAW)
+                                    .field(TYPE, KEYWORD)
+                                    .field(NORMALIZER, CASE_INSENSITIVE)
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(TO)
-                                .field(TYPE, NESTED)
-                                .startObject(PROPERTIES)
-                                    .startObject(EMailer.NAME)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                        .startObject(TO)
+                            .field(TYPE, NESTED)
+                            .startObject(PROPERTIES)
+                                .startObject(EMailer.NAME)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
-                                    .startObject(EMailer.ADDRESS)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, STANDARD)
-                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                                .endObject()
+                                .startObject(EMailer.ADDRESS)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, STANDARD)
+                                    .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(CC)
-                                .field(TYPE, NESTED)
-                                .startObject(PROPERTIES)
-                                    .startObject(EMailer.NAME)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                        .startObject(CC)
+                            .field(TYPE, NESTED)
+                            .startObject(PROPERTIES)
+                                .startObject(EMailer.NAME)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
-                                    .startObject(EMailer.ADDRESS)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, STANDARD)
-                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                            .field(TYPE, STRING)
-                                            .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                                .endObject()
+                                .startObject(EMailer.ADDRESS)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, STANDARD)
+                                    .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(BCC)
-                                .field(TYPE, NESTED)
-                                .startObject(PROPERTIES)
-                                    .startObject(EMailer.NAME)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                        .startObject(BCC)
+                            .field(TYPE, NESTED)
+                            .startObject(PROPERTIES)
+                                .startObject(EMailer.NAME)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
-                                    .startObject(EMailer.ADDRESS)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, STANDARD)
-                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
-                                        .startObject(FIELDS)
-                                            .startObject(RAW)
-                                                .field(TYPE, STRING)
-                                                .field(ANALYZER, CASE_INSENSITIVE)
-                                            .endObject()
+                                .endObject()
+                                .startObject(EMailer.ADDRESS)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, STANDARD)
+                                    .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                    .startObject(FIELDS)
+                                        .startObject(RAW)
+                                            .field(TYPE, KEYWORD)
+                                            .field(NORMALIZER, CASE_INSENSITIVE)
                                         .endObject()
                                     .endObject()
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(MAILBOX_ID)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(MAILBOX_ID)
+                            .field(TYPE, KEYWORD)
+                            .field(STORE, true)
+                        .endObject()
 
-                            .startObject(MIME_MESSAGE_ID)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(MIME_MESSAGE_ID)
+                            .field(TYPE, KEYWORD)
+                        .endObject()
 
-                            .startObject(USERS)
-                                .field(TYPE, STRING)
-                                .field(INDEX, NOT_ANALYZED)
-                            .endObject()
+                        .startObject(USERS)
+                            .field(TYPE, KEYWORD)
+                        .endObject()
 
-                            .startObject(PROPERTIES)
-                                .field(TYPE, NESTED)
-                                .startObject(PROPERTIES)
-                                    .startObject(Property.NAMESPACE)
-                                        .field(TYPE, STRING)
-                                        .field(INDEX, NOT_ANALYZED)
-                                    .endObject()
-                                    .startObject(Property.NAME)
-                                        .field(TYPE, STRING)
-                                        .field(INDEX, NOT_ANALYZED)
-                                    .endObject()
-                                    .startObject(Property.VALUE)
-                                        .field(TYPE, STRING)
-                                        .field(INDEX, NOT_ANALYZED)
-                                    .endObject()
+                        .startObject(TEXT_BODY)
+                            .field(TYPE, TEXT)
+                            .field(ANALYZER, KEEP_MAIL_AND_URL)
+                            .startObject(FIELDS)
+                                .startObject(SPLIT_EMAIL)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, STANDARD)
+                                    .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
                                 .endObject()
-                            .endObject()
-
-                            .startObject(TEXT_BODY)
-                                .field(TYPE, STRING)
-                                .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                .startObject(FIELDS)
-                                    .startObject(SPLIT_EMAIL)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, STANDARD)
-                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
-                                    .endObject()
-                                    .startObject(RAW)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, CASE_INSENSITIVE)
-                                        .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
-                                    .endObject()
+                                .startObject(RAW)
+                                    .field(TYPE, KEYWORD)
+                                    .field(NORMALIZER, CASE_INSENSITIVE)
+                                    .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(HTML_BODY)
-                                .field(TYPE, STRING)
-                                .field(ANALYZER, KEEP_MAIL_AND_URL)
-                                .startObject(FIELDS)
-                                    .startObject(SPLIT_EMAIL)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, STANDARD)
-                                        .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
-                                    .endObject()
-                                    .startObject(RAW)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, CASE_INSENSITIVE)
-                                        .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
-                                    .endObject()
+                        .startObject(HTML_BODY)
+                            .field(TYPE, TEXT)
+                            .field(ANALYZER, KEEP_MAIL_AND_URL)
+                            .startObject(FIELDS)
+                                .startObject(SPLIT_EMAIL)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, STANDARD)
+                                    .field(SEARCH_ANALYZER, KEEP_MAIL_AND_URL)
+                                .endObject()
+                                .startObject(RAW)
+                                    .field(TYPE, KEYWORD)
+                                    .field(NORMALIZER, CASE_INSENSITIVE)
+                                    .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
                                 .endObject()
                             .endObject()
+                        .endObject()
 
-                            .startObject(HAS_ATTACHMENT)
-                                .field(TYPE, BOOLEAN)
-                            .endObject()
+                        .startObject(HAS_ATTACHMENT)
+                            .field(TYPE, BOOLEAN)
+                        .endObject()
 
-                            .startObject(TEXT)
-                                .field(TYPE, STRING)
-                                .field(ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
-                                .field(IGNORE_ABOVE, MAXIMUM_TERM_LENGTH)
-                                .startObject(FIELDS)
-                                    .startObject(SPLIT_EMAIL)
-                                        .field(TYPE, STRING)
-                                        .field(ANALYZER, SNOWBALL)
-                                        .field(SEARCH_ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
-                                    .endObject()
+                        .startObject(TEXT)
+                            .field(TYPE, TEXT)
+                            .field(ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
+                            .startObject(FIELDS)
+                                .startObject(SPLIT_EMAIL)
+                                    .field(TYPE, TEXT)
+                                    .field(ANALYZER, SNOWBALL)
+                                    .field(SEARCH_ANALYZER, SNOWBALL_KEEP_MAIL_AND_URL)
                                 .endObject()
                             .endObject()
                         .endObject()
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
index 8ee2204..f86ac35 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
@@ -20,6 +20,7 @@ package org.apache.james.mailbox.elasticsearch.v6.events;
 
 import static org.elasticsearch.index.query.QueryBuilders.termQuery;
 
+import java.io.IOException;
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Iterator;
@@ -29,8 +30,8 @@ import java.util.Optional;
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.apache.james.backends.es.ElasticSearchIndexer;
-import org.apache.james.backends.es.UpdatedRepresentation;
+import org.apache.james.backends.es.v6.ElasticSearchIndexer;
+import org.apache.james.backends.es.v6.UpdatedRepresentation;
 import org.apache.james.mailbox.MailboxManager.MessageCapabilities;
 import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
 import org.apache.james.mailbox.MailboxSession;
@@ -100,7 +101,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     @Override
     public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) throws MailboxException {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
-        Optional<Long> noLimit = Optional.empty();
+        Optional<Integer> noLimit = Optional.empty();
         return searcher
                 .search(ImmutableList.of(mailbox.getMailboxId()), searchQuery, noLimit)
                 .map(SearchResult::getMessageUid)
@@ -126,7 +127,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     }
 
     @Override
-    public void add(MailboxSession session, Mailbox mailbox, MailboxMessage message) throws JsonProcessingException {
+    public void add(MailboxSession session, Mailbox mailbox, MailboxMessage message) throws IOException {
         LOGGER.info("Indexing mailbox {}-{} of user {} on message {}",
             mailbox.getName(),
             mailbox.getMailboxId(),
@@ -152,7 +153,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     }
 
     @Override
-    public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) {
+    public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) throws IOException {
             elasticSearchIndexer.delete(expungedUids.stream()
                 .map(uid ->  indexIdFor(mailbox, uid))
                 .collect(Guavate.toImmutableList()));
@@ -167,7 +168,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     }
 
     @Override
-    public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) {
+    public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) throws IOException {
             elasticSearchIndexer.update(updatedFlagsList.stream()
                 .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
                     updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
index 19612cc..7251f27 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
@@ -19,8 +19,8 @@
 
 package org.apache.james.mailbox.elasticsearch.v6.query;
 
-import static org.apache.james.backends.es.NodeMappingFactory.RAW;
-import static org.apache.james.backends.es.NodeMappingFactory.SPLIT_EMAIL;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.RAW;
+import static org.apache.james.backends.es.v6.NodeMappingFactory.SPLIT_EMAIL;
 import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
 import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
@@ -45,6 +45,7 @@ import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.SearchQuery.Criterion;
 import org.apache.james.mailbox.model.SearchQuery.HeaderOperator;
+import org.apache.lucene.search.join.ScoreMode;
 import org.elasticsearch.index.query.BoolQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
@@ -275,10 +276,12 @@ public class CriterionConverter {
     }
 
     private QueryBuilder manageAddressFields(String headerName, String value) {
-        return nestedQuery(getFieldNameFromHeaderName(headerName), boolQuery()
-            .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.NAME, value))
-            .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS, value))
-            .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS + "." + RAW, value)));
+        return nestedQuery(getFieldNameFromHeaderName(headerName),
+            boolQuery()
+                .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.NAME, value))
+                .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS, value))
+                .should(matchQuery(getFieldNameFromHeaderName(headerName) + "." + JsonMessageConstants.EMailer.ADDRESS + "." + RAW, value)),
+            ScoreMode.Avg);
     }
 
     private String getFieldNameFromHeaderName(String headerName) {
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
index 52a2624..1a29c77 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
@@ -19,22 +19,22 @@
 
 package org.apache.james.mailbox.elasticsearch.v6.query;
 
-import org.apache.james.backends.es.NodeMappingFactory;
+import org.apache.james.backends.es.v6.NodeMappingFactory;
 import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.elasticsearch.search.sort.FieldSortBuilder;
 import org.elasticsearch.search.sort.SortBuilders;
+import org.elasticsearch.search.sort.SortMode;
 import org.elasticsearch.search.sort.SortOrder;
 
 public class SortConverter {
 
-    private static final String MIN = "min";
     private static final String PATH_SEPARATOR = ".";
 
     public static FieldSortBuilder convertSort(SearchQuery.Sort sort) {
         return getSortClause(sort.getSortClause())
             .order(getOrder(sort))
-            .sortMode(MIN);
+            .sortMode(SortMode.MIN);
     }
 
     private static FieldSortBuilder getSortClause(SearchQuery.Sort.SortClause clause) {
@@ -42,14 +42,14 @@ public class SortConverter {
             case Arrival :
                 return SortBuilders.fieldSort(JsonMessageConstants.DATE);
             case MailboxCc :
-                return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
-                    .setNestedPath(JsonMessageConstants.CC);
+                return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.CC);
             case MailboxFrom :
-                return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
-                    .setNestedPath(JsonMessageConstants.FROM);
+                return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.FROM);
             case MailboxTo :
-                return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS)
-                    .setNestedPath(JsonMessageConstants.TO);
+                return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.TO);
             case BaseSubject :
                 return SortBuilders.fieldSort(JsonMessageConstants.SUBJECT + PATH_SEPARATOR + NodeMappingFactory.RAW);
             case Size :
@@ -59,11 +59,11 @@ public class SortConverter {
             case Uid :
                 return SortBuilders.fieldSort(JsonMessageConstants.UID);
             case DisplayFrom:
-                return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME + PATH_SEPARATOR + NodeMappingFactory.RAW)
-                    .setNestedPath(JsonMessageConstants.FROM);
+                return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.FROM);
             case DisplayTo:
-                return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME + PATH_SEPARATOR + NodeMappingFactory.RAW)
-                    .setNestedPath(JsonMessageConstants.TO);
+                return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.TO);
             case Id:
                 return SortBuilders.fieldSort(JsonMessageConstants.MESSAGE_ID);
             default:
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
index 195326e..f527dd4 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
@@ -19,101 +19,103 @@
 
 package org.apache.james.mailbox.elasticsearch.v6.search;
 
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Optional;
 import java.util.stream.Stream;
 
-import org.apache.james.backends.es.AliasName;
-import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.TypeName;
-import org.apache.james.backends.es.search.ScrollIterable;
+import org.apache.james.backends.es.v6.AliasName;
+import org.apache.james.backends.es.v6.NodeMappingFactory;
+import org.apache.james.backends.es.v6.ReadAliasName;
+import org.apache.james.backends.es.v6.search.ScrollIterable;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
 import org.apache.james.mailbox.elasticsearch.v6.query.QueryConverter;
 import org.apache.james.mailbox.elasticsearch.v6.query.SortConverter;
-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.SearchQuery;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
-import org.apache.james.util.streams.Iterators;
-import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchRequest;
 import org.elasticsearch.action.search.SearchResponse;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RestHighLevelClient;
+import org.elasticsearch.common.document.DocumentField;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.search.SearchHitField;
+import org.elasticsearch.search.builder.SearchSourceBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class ElasticSearchSearcher {
+import com.google.common.collect.ImmutableList;
 
-    public static final int DEFAULT_SEARCH_SIZE = 100;
+public class ElasticSearchSearcher {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchSearcher.class);
-    private static final TimeValue TIMEOUT = new TimeValue(60000);
+    private static final TimeValue TIMEOUT = TimeValue.timeValueMinutes(1);
+    private static final ImmutableList STORED_FIELDS = ImmutableList.of(JsonMessageConstants.MAILBOX_ID,
+        JsonMessageConstants.UID, JsonMessageConstants.MESSAGE_ID);
 
-    private final Client client;
+    private final RestHighLevelClient client;
     private final QueryConverter queryConverter;
     private final int size;
     private final MailboxId.Factory mailboxIdFactory;
     private final MessageId.Factory messageIdFactory;
     private final AliasName aliasName;
-    private final TypeName typeName;
 
-    public ElasticSearchSearcher(Client client, QueryConverter queryConverter, int size,
+    public ElasticSearchSearcher(RestHighLevelClient client, QueryConverter queryConverter, int size,
                                  MailboxId.Factory mailboxIdFactory, MessageId.Factory messageIdFactory,
-                                 ReadAliasName aliasName, TypeName typeName) {
+                                 ReadAliasName aliasName) {
         this.client = client;
         this.queryConverter = queryConverter;
         this.size = size;
         this.mailboxIdFactory = mailboxIdFactory;
         this.messageIdFactory = messageIdFactory;
         this.aliasName = aliasName;
-        this.typeName = typeName;
     }
 
     public Stream<MessageSearchIndex.SearchResult> search(Collection<MailboxId> mailboxIds, SearchQuery query,
-                                                          Optional<Long> limit) throws MailboxException {
-        SearchRequestBuilder searchRequestBuilder = getSearchRequestBuilder(client, mailboxIds, query, limit);
-        Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, searchRequestBuilder).stream()
+                                                          Optional<Integer> limit) {
+        SearchRequest searchRequest = prepareSearch(mailboxIds, query, limit);
+        Stream<MessageSearchIndex.SearchResult> pairStream = new ScrollIterable(client, searchRequest).stream()
             .flatMap(this::transformResponseToUidStream);
 
         return limit.map(pairStream::limit)
             .orElse(pairStream);
     }
 
-    private SearchRequestBuilder getSearchRequestBuilder(Client client, Collection<MailboxId> users,
-                                                         SearchQuery query, Optional<Long> limit) {
-        return query.getSorts()
+    private SearchRequest prepareSearch(Collection<MailboxId> users, SearchQuery query, Optional<Integer> limit) {
+        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
+            .query(queryConverter.from(users, query))
+            .size(computeRequiredSize(limit))
+            .storedFields(STORED_FIELDS);
+
+        query.getSorts()
             .stream()
-            .reduce(
-                client.prepareSearch(aliasName.getValue())
-                    .setTypes(typeName.getValue())
-                    .setScroll(TIMEOUT)
-                    .addFields(JsonMessageConstants.UID, JsonMessageConstants.MAILBOX_ID, JsonMessageConstants.MESSAGE_ID)
-                    .setQuery(queryConverter.from(users, query))
-                    .setSize(computeRequiredSize(limit)),
-                (searchBuilder, sort) -> searchBuilder.addSort(SortConverter.convertSort(sort)),
-                (partialResult1, partialResult2) -> partialResult1);
+            .map(SortConverter::convertSort)
+            .forEach(searchSourceBuilder::sort);
+
+        return new SearchRequest(aliasName.getValue())
+            .types(NodeMappingFactory.DEFAULT_MAPPING_NAME)
+            .scroll(TIMEOUT)
+            .source(searchSourceBuilder);
     }
 
-    private int computeRequiredSize(Optional<Long> limit) {
+    private int computeRequiredSize(Optional<Integer> limit) {
         return limit.map(value -> Math.min(value.intValue(), size))
             .orElse(size);
     }
 
     private Stream<MessageSearchIndex.SearchResult> transformResponseToUidStream(SearchResponse searchResponse) {
-        return Iterators.toStream(searchResponse.getHits().iterator())
+        return Arrays.stream(searchResponse.getHits().getHits())
             .map(this::extractContentFromHit)
             .filter(Optional::isPresent)
             .map(Optional::get);
     }
 
     private Optional<MessageSearchIndex.SearchResult> extractContentFromHit(SearchHit hit) {
-        SearchHitField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
-        SearchHitField uid = hit.field(JsonMessageConstants.UID);
-        Optional<SearchHitField> id = retrieveMessageIdField(hit);
+        DocumentField mailboxId = hit.field(JsonMessageConstants.MAILBOX_ID);
+        DocumentField uid = hit.field(JsonMessageConstants.UID);
+        Optional<DocumentField> id = retrieveMessageIdField(hit);
         if (mailboxId != null && uid != null) {
             Number uidAsNumber = uid.getValue();
             return Optional.of(
@@ -127,8 +129,8 @@ public class ElasticSearchSearcher {
         }
     }
 
-    private Optional<SearchHitField> retrieveMessageIdField(SearchHit hit) {
-        if (hit.fields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
+    private Optional<DocumentField> retrieveMessageIdField(SearchHit hit) {
+        if (hit.getFields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
             return Optional.ofNullable(hit.field(JsonMessageConstants.MESSAGE_ID));
         } else {
             return Optional.empty();


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


[james-project] 05/14: JAMES-2764 Migrate mailbox ES tests to ES6

Posted by bt...@apache.org.
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 b091de9aa59e15f012712b782fed47973fe6ecd8
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri May 17 17:55:51 2019 +0700

    JAMES-2764 Migrate mailbox ES tests to ES6
---
 .../v6/ElasticSearchIntegrationTest.java           | 24 ++++++++--------------
 .../v6/ElasticSearchMailboxConfigurationTest.java  |  6 +++---
 ...asticSearchListeningMessageSearchIndexTest.java | 11 +++++-----
 3 files changed, 18 insertions(+), 23 deletions(-)

diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
index 81a4293..c838692 100644
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchIntegrationTest.java
@@ -21,14 +21,13 @@ package org.apache.james.mailbox.elasticsearch.v6;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.time.ZoneId;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
 
-import org.apache.james.backends.es.DockerElasticSearchRule;
-import org.apache.james.backends.es.ElasticSearchConfiguration;
-import org.apache.james.backends.es.ElasticSearchIndexer;
+import org.apache.james.backends.es.v6.DockerElasticSearchRule;
+import org.apache.james.backends.es.v6.ElasticSearchConfiguration;
+import org.apache.james.backends.es.v6.ElasticSearchIndexer;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MailboxSessionUtil;
 import org.apache.james.mailbox.MessageManager;
@@ -50,8 +49,7 @@ import org.apache.james.mailbox.tika.TikaHttpClientImpl;
 import org.apache.james.mailbox.tika.TikaTextExtractor;
 import org.apache.james.metrics.api.NoopMetricFactory;
 import org.apache.james.mime4j.dom.Message;
-import org.apache.james.util.concurrent.NamedThreadFactory;
-import org.elasticsearch.client.Client;
+import org.elasticsearch.client.RestHighLevelClient;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
@@ -87,15 +85,14 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest
     }
 
     @Override
-    protected void initializeMailboxManager() {
-        Client client = MailboxIndexCreationUtil.prepareDefaultClient(
+    protected void initializeMailboxManager() throws IOException {
+        RestHighLevelClient client = MailboxIndexCreationUtil.prepareDefaultClient(
             elasticSearch.clientProvider().get(),
             ElasticSearchConfiguration.builder()
-                .addHost(elasticSearch.getTcpHost())
+                .addHost(elasticSearch.getDockerElasticSearch().getHttpHost())
                 .build());
 
         InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory();
-        ThreadFactory threadFactory = NamedThreadFactory.withClassName(getClass());
 
         InMemoryIntegrationResources resources = InMemoryIntegrationResources.builder()
             .preProvisionnedFakeAuthenticator()
@@ -106,14 +103,11 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest
             .listeningSearchIndex(preInstanciationStage -> new ElasticSearchListeningMessageSearchIndex(
                 preInstanciationStage.getMapperFactory(),
                 new ElasticSearchIndexer(client,
-                    Executors.newSingleThreadExecutor(threadFactory),
                     MailboxElasticSearchConstants.DEFAULT_MAILBOX_WRITE_ALIAS,
-                    MailboxElasticSearchConstants.MESSAGE_TYPE,
                     BATCH_SIZE),
                 new ElasticSearchSearcher(client, new QueryConverter(new CriterionConverter()), SEARCH_SIZE,
                     new InMemoryId.Factory(), messageIdFactory,
-                    MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS,
-                    MailboxElasticSearchConstants.MESSAGE_TYPE),
+                    MailboxElasticSearchConstants.DEFAULT_MAILBOX_READ_ALIAS),
                 new MessageToElasticSearchJson(textExtractor, ZoneId.of("Europe/Paris"), IndexAttachments.YES),
                 preInstanciationStage.getSessionProvider()))
             .noPreDeletionHooks()
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
index 64a1cd0..6136627 100644
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
@@ -22,9 +22,9 @@ package org.apache.james.mailbox.elasticsearch.v6;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.commons.configuration.PropertiesConfiguration;
-import org.apache.james.backends.es.IndexName;
-import org.apache.james.backends.es.ReadAliasName;
-import org.apache.james.backends.es.WriteAliasName;
+import org.apache.james.backends.es.v6.IndexName;
+import org.apache.james.backends.es.v6.ReadAliasName;
+import org.apache.james.backends.es.v6.WriteAliasName;
 import org.junit.Test;
 
 public class ElasticSearchMailboxConfigurationTest {
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
index e17f6ca..2596d5c 100644
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndexTest.java
@@ -29,13 +29,14 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.io.IOException;
 import java.util.List;
 import java.util.Optional;
 
 import javax.mail.Flags;
 
-import org.apache.james.backends.es.ElasticSearchIndexer;
-import org.apache.james.backends.es.UpdatedRepresentation;
+import org.apache.james.backends.es.v6.ElasticSearchIndexer;
+import org.apache.james.backends.es.v6.UpdatedRepresentation;
 import org.apache.james.core.User;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MailboxSessionUtil;
@@ -159,7 +160,7 @@ public class ElasticSearchListeningMessageSearchIndexTest {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void deleteShouldWork() {
+    public void deleteShouldWork() throws IOException {
         //Given
         BulkResponse expectedBulkResponse = mock(BulkResponse.class);
         when(elasticSearchIndexer.delete(any(List.class)))
@@ -174,7 +175,7 @@ public class ElasticSearchListeningMessageSearchIndexTest {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void deleteShouldWorkWhenMultipleMessageIds() {
+    public void deleteShouldWorkWhenMultipleMessageIds() throws IOException {
         //Given
         MessageUid messageId2 = MessageUid.of(2);
         MessageUid messageId3 = MessageUid.of(3);
@@ -194,7 +195,7 @@ public class ElasticSearchListeningMessageSearchIndexTest {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void deleteShouldPropagateExceptionWhenExceptionOccurs() {
+    public void deleteShouldPropagateExceptionWhenExceptionOccurs() throws IOException {
         //Given
         when(elasticSearchIndexer.delete(any(List.class)))
             .thenThrow(new ElasticsearchException(""));


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


[james-project] 10/14: JAMES-2764 Add bean contracts for es6 mailbox objects

Posted by bt...@apache.org.
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 15ab767967a849eaa8b9d276a1aceed887107e95
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed May 22 17:32:35 2019 +0700

    JAMES-2764 Add bean contracts for es6 mailbox objects
---
 mailbox/elasticsearch-v6/pom.xml                   |  5 ++
 .../mailbox/elasticsearch/v6/json/EMailer.java     |  4 +-
 .../v6/ElasticSearchMailboxConfigurationTest.java  |  8 +++
 .../elasticsearch/v6/json/EMailerTest.java}        | 57 +++-------------------
 4 files changed, 22 insertions(+), 52 deletions(-)

diff --git a/mailbox/elasticsearch-v6/pom.xml b/mailbox/elasticsearch-v6/pom.xml
index 0591267..f7615c9 100644
--- a/mailbox/elasticsearch-v6/pom.xml
+++ b/mailbox/elasticsearch-v6/pom.xml
@@ -151,6 +151,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>nl.jqno.equalsverifier</groupId>
+            <artifactId>equalsverifier</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
index f827ba0..ab0194e 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
@@ -51,7 +51,7 @@ public class EMailer implements SerializableMessage {
     }
 
     @Override
-    public boolean equals(Object o) {
+    public final boolean equals(Object o) {
         if (o instanceof EMailer) {
             EMailer otherEMailer = (EMailer) o;
             return Objects.equals(name, otherEMailer.name)
@@ -61,7 +61,7 @@ public class EMailer implements SerializableMessage {
     }
 
     @Override
-    public int hashCode() {
+    public final int hashCode() {
         return Objects.hash(name, address);
     }
 
diff --git a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
index 6136627..c04eb16 100644
--- a/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfigurationTest.java
@@ -27,8 +27,16 @@ import org.apache.james.backends.es.v6.ReadAliasName;
 import org.apache.james.backends.es.v6.WriteAliasName;
 import org.junit.Test;
 
+import nl.jqno.equalsverifier.EqualsVerifier;
+
 public class ElasticSearchMailboxConfigurationTest {
     @Test
+    public void elasticSearchMailboxConfigurationShouldRespectBeanContract() {
+        EqualsVerifier.forClass(ElasticSearchMailboxConfiguration.class)
+            .verify();
+    }
+
+    @Test
     public void getIndexMailboxNameShouldReturnOldConfiguredValue() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         String name = "name";
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java
similarity index 50%
copy from mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
copy to mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java
index f827ba0..1a907fb 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
+++ b/mailbox/elasticsearch-v6/src/test/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailerTest.java
@@ -19,57 +19,14 @@
 
 package org.apache.james.mailbox.elasticsearch.v6.json;
 
-import java.util.Objects;
+import org.junit.jupiter.api.Test;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.google.common.base.Joiner;
-import com.google.common.base.MoreObjects;
+import nl.jqno.equalsverifier.EqualsVerifier;
 
-public class EMailer implements SerializableMessage {
-
-    private final String name;
-    private final String address;
-
-    public EMailer(String name, String address) {
-        this.name = name;
-        this.address = address;
-    }
-
-    @JsonProperty(JsonMessageConstants.EMailer.NAME)
-    public String getName() {
-        return name;
-    }
-
-    @JsonProperty(JsonMessageConstants.EMailer.ADDRESS)
-    public String getAddress() {
-        return address;
-    }
-
-    @Override
-    public String serialize() {
-        return Joiner.on(" ").join(name, address);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof EMailer) {
-            EMailer otherEMailer = (EMailer) o;
-            return Objects.equals(name, otherEMailer.name)
-                && Objects.equals(address, otherEMailer.address);
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(name, address);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-            .add("name", name)
-            .add("address", address)
-            .toString();
+class EMailerTest {
+    @Test
+    void eMailerShouldRespectBeanContract() {
+        EqualsVerifier.forClass(EMailer.class)
+            .verify();
     }
 }


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


[james-project] 07/14: JAMES-2764 setNestedPath is deprecated in ES6

Posted by bt...@apache.org.
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 502ce23ec686ed4188f7d392e1bb34ff07c1db57
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Tue May 21 10:53:54 2019 +0700

    JAMES-2764 setNestedPath is deprecated in ES6
---
 .../james/mailbox/elasticsearch/v6/query/SortConverter.java   | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
index 1a29c77..16a52bc 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/SortConverter.java
@@ -23,6 +23,7 @@ import org.apache.james.backends.es.v6.NodeMappingFactory;
 import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.elasticsearch.search.sort.FieldSortBuilder;
+import org.elasticsearch.search.sort.NestedSortBuilder;
 import org.elasticsearch.search.sort.SortBuilders;
 import org.elasticsearch.search.sort.SortMode;
 import org.elasticsearch.search.sort.SortOrder;
@@ -43,13 +44,13 @@ public class SortConverter {
                 return SortBuilders.fieldSort(JsonMessageConstants.DATE);
             case MailboxCc :
                 return SortBuilders.fieldSort(JsonMessageConstants.CC + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
-                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.CC);
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.CC));
             case MailboxFrom :
                 return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
-                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.FROM);
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.FROM));
             case MailboxTo :
                 return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.ADDRESS
-                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.TO);
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.TO));
             case BaseSubject :
                 return SortBuilders.fieldSort(JsonMessageConstants.SUBJECT + PATH_SEPARATOR + NodeMappingFactory.RAW);
             case Size :
@@ -60,10 +61,10 @@ public class SortConverter {
                 return SortBuilders.fieldSort(JsonMessageConstants.UID);
             case DisplayFrom:
                 return SortBuilders.fieldSort(JsonMessageConstants.FROM + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
-                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.FROM);
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.FROM));
             case DisplayTo:
                 return SortBuilders.fieldSort(JsonMessageConstants.TO + PATH_SEPARATOR + JsonMessageConstants.EMailer.NAME
-                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedPath(JsonMessageConstants.TO);
+                    + PATH_SEPARATOR + NodeMappingFactory.RAW).setNestedSort(new NestedSortBuilder(JsonMessageConstants.TO));
             case Id:
                 return SortBuilders.fieldSort(JsonMessageConstants.MESSAGE_ID);
             default:


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


[james-project] 06/14: JAMES-2764 small cleanup in mailbox ES6

Posted by bt...@apache.org.
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 fba0e418e9977f3c211f7d31c1aefa1baa4fc41f
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Tue May 21 10:38:29 2019 +0700

    JAMES-2764 small cleanup in mailbox ES6
---
 .../v6/ElasticSearchMailboxConfiguration.java      | 77 +++++++---------------
 .../elasticsearch/v6/MailboxMappingFactory.java    |  2 -
 .../ElasticSearchListeningMessageSearchIndex.java  |  6 +-
 .../v6/json/JsonMessageConstants.java              |  6 --
 4 files changed, 24 insertions(+), 67 deletions(-)

diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
index 3bac6b3..7e362a7 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/ElasticSearchMailboxConfiguration.java
@@ -36,42 +36,30 @@ public class ElasticSearchMailboxConfiguration {
         private Optional<WriteAliasName> writeAliasMailboxName;
         private Optional<IndexAttachments> indexAttachment;
 
-        public Builder() {
+        Builder() {
             indexMailboxName = Optional.empty();
             readAliasMailboxName = Optional.empty();
             writeAliasMailboxName = Optional.empty();
             indexAttachment = Optional.empty();
         }
 
-        public Builder indexMailboxName(IndexName indexMailboxName) {
-            return indexMailboxName(Optional.of(indexMailboxName));
-        }
-
-        public Builder indexMailboxName(Optional<IndexName> indexMailboxName) {
+        Builder indexMailboxName(Optional<IndexName> indexMailboxName) {
             this.indexMailboxName = indexMailboxName;
             return this;
         }
 
-        public Builder readAliasMailboxName(ReadAliasName readAliasMailboxName) {
-            return readAliasMailboxName(Optional.of(readAliasMailboxName));
-        }
-
-        public Builder readAliasMailboxName(Optional<ReadAliasName> readAliasMailboxName) {
+        Builder readAliasMailboxName(Optional<ReadAliasName> readAliasMailboxName) {
             this.readAliasMailboxName = readAliasMailboxName;
             return this;
         }
 
-        public Builder writeAliasMailboxName(WriteAliasName writeAliasMailboxName) {
-            return writeAliasMailboxName(Optional.of(writeAliasMailboxName));
-        }
-
-        public Builder writeAliasMailboxName(Optional<WriteAliasName> writeAliasMailboxName) {
+        Builder writeAliasMailboxName(Optional<WriteAliasName> writeAliasMailboxName) {
             this.writeAliasMailboxName = writeAliasMailboxName;
             return this;
         }
 
 
-        public Builder indexAttachment(IndexAttachments indexAttachment) {
+        Builder indexAttachment(IndexAttachments indexAttachment) {
             this.indexAttachment = Optional.of(indexAttachment);
             return this;
         }
@@ -91,35 +79,16 @@ public class ElasticSearchMailboxConfiguration {
         return new Builder();
     }
 
-    public static final String ELASTICSEARCH_HOSTS = "elasticsearch.hosts";
-    public static final String ELASTICSEARCH_MASTER_HOST = "elasticsearch.masterHost";
-    public static final String ELASTICSEARCH_PORT = "elasticsearch.port";
-    public static final String ELASTICSEARCH_INDEX_NAME = "elasticsearch.index.name";
-    public static final String ELASTICSEARCH_INDEX_MAILBOX_NAME = "elasticsearch.index.mailbox.name";
-    public static final String ELASTICSEARCH_NB_REPLICA = "elasticsearch.nb.replica";
-    public static final String ELASTICSEARCH_NB_SHARDS = "elasticsearch.nb.shards";
-    public static final String ELASTICSEARCH_ALIAS_READ_NAME = "elasticsearch.alias.read.name";
-    public static final String ELASTICSEARCH_ALIAS_WRITE_NAME = "elasticsearch.alias.write.name";
-    public static final String ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME = "elasticsearch.alias.read.mailbox.name";
-    public static final String ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME = "elasticsearch.alias.write.mailbox.name";
-    public static final String ELASTICSEARCH_INDEX_QUOTA_RATIO_NAME = "elasticsearch.index.quota.ratio.name";
-    public static final String ELASTICSEARCH_ALIAS_READ_QUOTA_RATIO_NAME = "elasticsearch.alias.read.quota.ratio.name";
-    public static final String ELASTICSEARCH_ALIAS_WRITE_QUOTA_RATIO_NAME = "elasticsearch.alias.write.quota.ratio.name";
-    public static final String ELASTICSEARCH_RETRY_CONNECTION_MIN_DELAY = "elasticsearch.retryConnection.minDelay";
-    public static final String ELASTICSEARCH_RETRY_CONNECTION_MAX_RETRIES = "elasticsearch.retryConnection.maxRetries";
-    public static final String ELASTICSEARCH_INDEX_ATTACHMENTS = "elasticsearch.indexAttachments";
-
-    public static final int DEFAULT_CONNECTION_MAX_RETRIES = 7;
-    public static final int DEFAULT_CONNECTION_MIN_DELAY = 3000;
-    public static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
-    public static final int DEFAULT_NB_SHARDS = 5;
-    public static final int DEFAULT_NB_REPLICA = 1;
-    public static final int DEFAULT_PORT = 9200;
-    public static final Optional<Integer> DEFAULT_PORT_AS_OPTIONAL = Optional.of(DEFAULT_PORT);
-
-    public static final ElasticSearchMailboxConfiguration DEFAULT_CONFIGURATION = builder().build();
-
-    public static ElasticSearchMailboxConfiguration fromProperties(Configuration configuration) {
+    private static final String ELASTICSEARCH_INDEX_NAME = "elasticsearch.index.name";
+    private static final String ELASTICSEARCH_INDEX_MAILBOX_NAME = "elasticsearch.index.mailbox.name";
+    private static final String ELASTICSEARCH_ALIAS_READ_NAME = "elasticsearch.alias.read.name";
+    private static final String ELASTICSEARCH_ALIAS_WRITE_NAME = "elasticsearch.alias.write.name";
+    private static final String ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME = "elasticsearch.alias.read.mailbox.name";
+    private static final String ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME = "elasticsearch.alias.write.mailbox.name";
+    private static final String ELASTICSEARCH_INDEX_ATTACHMENTS = "elasticsearch.indexAttachments";
+    private static final boolean DEFAULT_INDEX_ATTACHMENTS = true;
+
+    static ElasticSearchMailboxConfiguration fromProperties(Configuration configuration) {
         return builder()
             .indexMailboxName(computeMailboxIndexName(configuration))
             .readAliasMailboxName(computeMailboxReadAlias(configuration))
@@ -128,7 +97,7 @@ public class ElasticSearchMailboxConfiguration {
             .build();
     }
 
-    public static Optional<IndexName> computeMailboxIndexName(Configuration configuration) {
+    static Optional<IndexName> computeMailboxIndexName(Configuration configuration) {
         return OptionalUtils.or(
             Optional.ofNullable(configuration.getString(ELASTICSEARCH_INDEX_MAILBOX_NAME))
                 .map(IndexName::new),
@@ -136,7 +105,7 @@ public class ElasticSearchMailboxConfiguration {
                 .map(IndexName::new));
     }
 
-    public static Optional<WriteAliasName> computeMailboxWriteAlias(Configuration configuration) {
+    static Optional<WriteAliasName> computeMailboxWriteAlias(Configuration configuration) {
         return OptionalUtils.or(
             Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_WRITE_MAILBOX_NAME))
                 .map(WriteAliasName::new),
@@ -144,7 +113,7 @@ public class ElasticSearchMailboxConfiguration {
                 .map(WriteAliasName::new));
     }
 
-    public static Optional<ReadAliasName> computeMailboxReadAlias(Configuration configuration) {
+    static Optional<ReadAliasName> computeMailboxReadAlias(Configuration configuration) {
         return OptionalUtils.or(
             Optional.ofNullable(configuration.getString(ELASTICSEARCH_ALIAS_READ_MAILBOX_NAME))
                 .map(ReadAliasName::new),
@@ -161,8 +130,6 @@ public class ElasticSearchMailboxConfiguration {
     }
 
 
-
-
     private final IndexName indexMailboxName;
     private final ReadAliasName readAliasMailboxName;
     private final WriteAliasName writeAliasMailboxName;
@@ -177,19 +144,19 @@ public class ElasticSearchMailboxConfiguration {
     }
 
 
-    public IndexName getIndexMailboxName() {
+    IndexName getIndexMailboxName() {
         return indexMailboxName;
     }
 
-    public ReadAliasName getReadAliasMailboxName() {
+    ReadAliasName getReadAliasMailboxName() {
         return readAliasMailboxName;
     }
 
-    public WriteAliasName getWriteAliasMailboxName() {
+    WriteAliasName getWriteAliasMailboxName() {
         return writeAliasMailboxName;
     }
 
-    public IndexAttachments getIndexAttachment() {
+    IndexAttachments getIndexAttachment() {
         return indexAttachment;
     }
 
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
index 4021f73..af0bbe1 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/MailboxMappingFactory.java
@@ -77,8 +77,6 @@ public class MailboxMappingFactory {
     private static final int MAXIMUM_TERM_LENGTH = 4096;
     private static final String STANDARD = "standard";
     private static final String STORE = "store";
-    private static final String FIELD_DATA = "fielddata";
-
 
     public static XContentBuilder getMappingContent() {
         try {
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
index f86ac35..4ef7af2 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
@@ -41,7 +41,6 @@ import org.apache.james.mailbox.elasticsearch.v6.json.JsonMessageConstants;
 import org.apache.james.mailbox.elasticsearch.v6.json.MessageToElasticSearchJson;
 import org.apache.james.mailbox.elasticsearch.v6.search.ElasticSearchSearcher;
 import org.apache.james.mailbox.events.Group;
-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;
@@ -99,7 +98,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     }
     
     @Override
-    public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) throws MailboxException {
+    public Iterator<MessageUid> search(MailboxSession session, Mailbox mailbox, SearchQuery searchQuery) {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
         Optional<Integer> noLimit = Optional.empty();
         return searcher
@@ -109,8 +108,7 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
     }
     
     @Override
-    public List<MessageId> search(MailboxSession session, Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit)
-            throws MailboxException {
+    public List<MessageId> search(MailboxSession session, Collection<MailboxId> mailboxIds, SearchQuery searchQuery, long limit) {
         Preconditions.checkArgument(session != null, "'session' is mandatory");
 
         if (mailboxIds.isEmpty()) {
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
index e747c9f..21ce86c 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/JsonMessageConstants.java
@@ -74,10 +74,4 @@ public interface JsonMessageConstants {
         String FILE_EXTENSION = "fileExtension";
     }
 
-    interface Property {
-        String NAMESPACE = "namespace";
-        String NAME = "name";
-        String VALUE = "value";
-    }
-
 }


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


[james-project] 09/14: JAMES-2764 Fix checkstyle and minor stuff

Posted by bt...@apache.org.
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 45b0d88d9d4df523ab0ab3695773e7015c423c0e
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Wed May 22 17:31:20 2019 +0700

    JAMES-2764 Fix checkstyle and minor stuff
---
 .../ElasticSearchListeningMessageSearchIndex.java  | 32 ++++++++++++----------
 .../mailbox/elasticsearch/v6/json/EMailer.java     |  2 +-
 .../mailbox/elasticsearch/v6/json/EMailers.java    |  2 +-
 .../elasticsearch/v6/json/MessageUpdateJson.java   |  2 +-
 ...{Serializable.java => SerializableMessage.java} |  2 +-
 .../mailbox/elasticsearch/v6/json/Subjects.java    |  2 +-
 .../elasticsearch/v6/query/CriterionConverter.java |  6 ++--
 .../v6/search/ElasticSearchSearcher.java           |  3 +-
 8 files changed, 26 insertions(+), 25 deletions(-)

diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
index 4ef7af2..0847f45 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/events/ElasticSearchListeningMessageSearchIndex.java
@@ -152,34 +152,36 @@ public class ElasticSearchListeningMessageSearchIndex extends ListeningMessageSe
 
     @Override
     public void delete(MailboxSession session, Mailbox mailbox, Collection<MessageUid> expungedUids) throws IOException {
-            elasticSearchIndexer.delete(expungedUids.stream()
-                .map(uid ->  indexIdFor(mailbox, uid))
-                .collect(Guavate.toImmutableList()));
+            elasticSearchIndexer
+                .delete(expungedUids.stream()
+                    .map(uid ->  indexIdFor(mailbox, uid))
+                    .collect(Guavate.toImmutableList()));
     }
 
     @Override
     public void deleteAll(MailboxSession session, Mailbox mailbox) {
-            elasticSearchIndexer.deleteAllMatchingQuery(
-                termQuery(
-                    JsonMessageConstants.MAILBOX_ID,
-                    mailbox.getMailboxId().serialize()));
+            elasticSearchIndexer
+                .deleteAllMatchingQuery(
+                    termQuery(
+                        JsonMessageConstants.MAILBOX_ID,
+                        mailbox.getMailboxId().serialize()));
     }
 
     @Override
     public void update(MailboxSession session, Mailbox mailbox, List<UpdatedFlags> updatedFlagsList) throws IOException {
-            elasticSearchIndexer.update(updatedFlagsList.stream()
-                .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
-                    updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
-                    .sneakyThrow())
-                .collect(Guavate.toImmutableList()));
+            elasticSearchIndexer
+                .update(updatedFlagsList.stream()
+                    .map(Throwing.<UpdatedFlags, UpdatedRepresentation>function(
+                            updatedFlags -> createUpdatedDocumentPartFromUpdatedFlags(mailbox, updatedFlags))
+                        .sneakyThrow())
+                    .collect(Guavate.toImmutableList()));
     }
 
     private UpdatedRepresentation createUpdatedDocumentPartFromUpdatedFlags(Mailbox mailbox, UpdatedFlags updatedFlags) throws JsonProcessingException {
             return new UpdatedRepresentation(
                 indexIdFor(mailbox, updatedFlags.getUid()),
-                    messageToElasticSearchJson.getUpdatedJsonMessagePart(
-                        updatedFlags.getNewFlags(),
-                        updatedFlags.getModSeq()));
+                messageToElasticSearchJson
+                    .getUpdatedJsonMessagePart(updatedFlags.getNewFlags(), updatedFlags.getModSeq()));
     }
 
     private String indexIdFor(Mailbox mailbox, MessageUid uid) {
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
index 83ff556..f827ba0 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailer.java
@@ -25,7 +25,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.base.Joiner;
 import com.google.common.base.MoreObjects;
 
-public class EMailer implements Serializable {
+public class EMailer implements SerializableMessage {
 
     private final String name;
     private final String address;
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
index e5e8e65..06a4667 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/EMailers.java
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
 import com.fasterxml.jackson.annotation.JsonValue;
 import com.google.common.base.Preconditions;
 
-public class EMailers implements Serializable {
+public class EMailers implements SerializableMessage {
 
     public static EMailers from(Set<EMailer> emailers) {
         Preconditions.checkNotNull(emailers, "'emailers' is mandatory");
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
index f8b2510..3e42114 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/MessageUpdateJson.java
@@ -62,7 +62,7 @@ public class MessageUpdateJson {
 
     @JsonProperty(JsonMessageConstants.IS_UNREAD)
     public boolean isUnRead() {
-        return ! flags.contains(Flags.Flag.SEEN);
+        return !flags.contains(Flags.Flag.SEEN);
     }
 
 
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Serializable.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java
similarity index 97%
rename from mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Serializable.java
rename to mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java
index 5ad6334..b5d903e 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Serializable.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/SerializableMessage.java
@@ -19,7 +19,7 @@
 
 package org.apache.james.mailbox.elasticsearch.v6.json;
 
-public interface Serializable {
+public interface SerializableMessage {
 
     String serialize();
 }
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
index d962932..a6a29f4 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/json/Subjects.java
@@ -25,7 +25,7 @@ import com.fasterxml.jackson.annotation.JsonValue;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
 
-public class Subjects implements Serializable {
+public class Subjects implements SerializableMessage {
 
     public static Subjects from(Set<String> subjects) {
         Preconditions.checkNotNull(subjects, "'subjects' is mandatory");
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
index 7251f27..4e8c44d 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/query/CriterionConverter.java
@@ -52,8 +52,8 @@ import org.elasticsearch.index.query.QueryBuilders;
 
 public class CriterionConverter {
 
-    private final Map<Class<?>, Function<SearchQuery.Criterion, QueryBuilder>> criterionConverterMap;
-    private final Map<Class<?>, BiFunction<String, SearchQuery.HeaderOperator, QueryBuilder>> headerOperatorConverterMap;
+    private final Map<Class<?>, Function<Criterion, QueryBuilder>> criterionConverterMap;
+    private final Map<Class<?>, BiFunction<String, HeaderOperator, QueryBuilder>> headerOperatorConverterMap;
 
     public CriterionConverter() {
         criterionConverterMap = new HashMap<>();
@@ -118,7 +118,7 @@ public class CriterionConverter {
         headerOperatorConverterMap.put(type, (BiFunction<String, HeaderOperator, QueryBuilder>) f);
     }
 
-    public QueryBuilder convertCriterion(SearchQuery.Criterion criterion) {
+    public QueryBuilder convertCriterion(Criterion criterion) {
         return criterionConverterMap.get(criterion.getClass()).apply(criterion);
     }
 
diff --git a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
index f527dd4..16ba761 100644
--- a/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
+++ b/mailbox/elasticsearch-v6/src/main/java/org/apache/james/mailbox/elasticsearch/v6/search/ElasticSearchSearcher.java
@@ -132,9 +132,8 @@ public class ElasticSearchSearcher {
     private Optional<DocumentField> retrieveMessageIdField(SearchHit hit) {
         if (hit.getFields().keySet().contains(JsonMessageConstants.MESSAGE_ID)) {
             return Optional.ofNullable(hit.field(JsonMessageConstants.MESSAGE_ID));
-        } else {
-            return Optional.empty();
         }
+        return Optional.empty();
     }
 
 }


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


[james-project] 08/14: JAMES-2764 Reordering correctly dependencies

Posted by bt...@apache.org.
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 4db9e6b972a9fc41ae7e6cb887c8291db974e9c2
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Tue May 21 17:49:36 2019 +0700

    JAMES-2764 Reordering correctly dependencies
---
 mailbox/elasticsearch-v6/pom.xml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/mailbox/elasticsearch-v6/pom.xml b/mailbox/elasticsearch-v6/pom.xml
index 2d8fc64..0591267 100644
--- a/mailbox/elasticsearch-v6/pom.xml
+++ b/mailbox/elasticsearch-v6/pom.xml
@@ -93,13 +93,13 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
-            <artifactId>james-server-util</artifactId>
-            <type>test-jar</type>
+            <artifactId>james-server-testing</artifactId>
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.apache.james</groupId>
-            <artifactId>james-server-testing</artifactId>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-util</artifactId>
+            <type>test-jar</type>
             <scope>test</scope>
         </dependency>
         <dependency>


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