You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2023/12/06 08:13:01 UTC

(james-project) branch postgresql updated (68e778ac7b -> db9d39b27d)

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

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


    from 68e778ac7b JAMES-2586 Fully drop JPA within mailbox-postgresql
     new 6b75683248 JAMES-2586 [PGSQL] Implement correctly FetchType
     new 28a9895a9b JAMES-2586 [PGSQL] Optimize getMailboxCounter method
     new db9d39b27d JAMES-2586 [PGSQL] Improve PostresMessageManager::getMetadata method

The 3 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/postgres/PostgresCommons.java   |   7 +-
 .../mailbox/postgres/mail/PostgresMailbox.java     |  42 +++---
 .../postgres/mail/PostgresMailboxMapper.java       |  18 ++-
 .../postgres/mail/PostgresMessageManager.java      |  49 +++++--
 .../postgres/mail/PostgresMessageMapper.java       |  57 ++++----
 .../postgres/mail/dao/PostgresMailboxDAO.java      |  43 +++---
 .../mail/dao/PostgresMailboxMessageDAO.java        |  69 ++++++----
 .../mail/dao/PostgresMailboxMessageDAOUtils.java   |  37 +++---
 .../dao/PostgresMailboxMessageFetchStrategy.java   | 147 +++++++++++++++++++++
 .../postgres/mail/PostgresMailboxMapperTest.java   |  42 ++++++
 10 files changed, 369 insertions(+), 142 deletions(-)
 copy backends-common/opensearch/src/main/java/org/apache/james/backends/opensearch/IndexName.java => mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailbox.java (66%)
 create mode 100644 mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageFetchStrategy.java


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


(james-project) 03/03: JAMES-2586 [PGSQL] Improve PostresMessageManager::getMetadata method

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit db9d39b27d609facfda6e9689ec123d4d5c10354
Author: Tung Tran <vt...@linagora.com>
AuthorDate: Mon Dec 4 16:06:17 2023 +0700

    JAMES-2586 [PGSQL] Improve PostresMessageManager::getMetadata method
---
 .../mailbox/postgres/mail/PostgresMailbox.java}    | 41 +++++++++++-------
 .../postgres/mail/PostgresMailboxMapper.java       | 18 +++++---
 .../postgres/mail/PostgresMessageManager.java      | 49 +++++++++++++++-------
 .../postgres/mail/dao/PostgresMailboxDAO.java      | 43 +++++++++++--------
 .../postgres/mail/PostgresMailboxMapperTest.java   | 42 +++++++++++++++++++
 5 files changed, 141 insertions(+), 52 deletions(-)

diff --git a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailbox.java
similarity index 59%
copy from mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java
copy to mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailbox.java
index 3b134b5bb9..0485f5f49b 100644
--- a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailbox.java
@@ -19,25 +19,36 @@
 
 package org.apache.james.mailbox.postgres.mail;
 
-import org.apache.james.backends.postgres.PostgresExtension;
-import org.apache.james.mailbox.model.MailboxId;
-import org.apache.james.mailbox.postgres.PostgresMailboxId;
-import org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxDAO;
-import org.apache.james.mailbox.store.mail.MailboxMapper;
-import org.apache.james.mailbox.store.mail.model.MailboxMapperTest;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-public class PostgresMailboxMapperTest extends MailboxMapperTest {
-    @RegisterExtension
-    static PostgresExtension postgresExtension = PostgresExtension.withoutRowLevelSecurity(PostgresMailboxModule.MODULE);
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.model.Mailbox;
+
+public class PostgresMailbox extends Mailbox {
+    private final ModSeq highestModSeq;
+    private final MessageUid lastUid;
+
+    public PostgresMailbox(Mailbox mailbox, ModSeq highestModSeq, MessageUid lastUid) {
+        super(mailbox);
+        this.highestModSeq = highestModSeq;
+        this.lastUid = lastUid;
+    }
+
+
+    public ModSeq getHighestModSeq() {
+        return highestModSeq;
+    }
+
+    public MessageUid getLastUid() {
+        return lastUid;
+    }
 
     @Override
-    protected MailboxMapper createMailboxMapper() {
-        return new PostgresMailboxMapper(new PostgresMailboxDAO(postgresExtension.getPostgresExecutor()));
+    public final boolean equals(Object o) {
+        return super.equals(o);
     }
 
     @Override
-    protected MailboxId generateId() {
-        return PostgresMailboxId.generate();
+    public final int hashCode() {
+        return super.hashCode();
     }
 }
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapper.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapper.java
index f44a4fb0c5..a1da33a11e 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapper.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapper.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.mailbox.postgres.mail;
 
+import java.util.function.Function;
+
 import javax.inject.Inject;
 
 import org.apache.james.core.Username;
@@ -62,17 +64,20 @@ public class PostgresMailboxMapper implements MailboxMapper {
 
     @Override
     public Mono<Mailbox> findMailboxByPath(MailboxPath mailboxName) {
-        return postgresMailboxDAO.findMailboxByPath(mailboxName);
+        return postgresMailboxDAO.findMailboxByPath(mailboxName)
+            .map(Function.identity());
     }
 
     @Override
     public Mono<Mailbox> findMailboxById(MailboxId mailboxId) {
-        return postgresMailboxDAO.findMailboxById(mailboxId);
+        return postgresMailboxDAO.findMailboxById(mailboxId)
+            .map(Function.identity());
     }
 
     @Override
     public Flux<Mailbox> findMailboxWithPathLike(MailboxQuery.UserBound query) {
-        return postgresMailboxDAO.findMailboxWithPathLike(query);
+        return postgresMailboxDAO.findMailboxWithPathLike(query)
+            .map(Function.identity());
     }
 
     @Override
@@ -82,12 +87,14 @@ public class PostgresMailboxMapper implements MailboxMapper {
 
     @Override
     public Flux<Mailbox> list() {
-        return postgresMailboxDAO.getAll();
+        return postgresMailboxDAO.getAll()
+            .map(Function.identity());
     }
 
     @Override
     public Flux<Mailbox> findNonPersonalMailboxes(Username userName, MailboxACL.Right right) {
-        return postgresMailboxDAO.findNonPersonalMailboxes(userName, right);
+        return postgresMailboxDAO.findNonPersonalMailboxes(userName, right)
+            .map(Function.identity());
     }
 
     @Override
@@ -110,4 +117,5 @@ public class PostgresMailboxMapper implements MailboxMapper {
                 return ACLDiff.computeDiff(oldACL, updatedACL);
             });
     }
+
 }
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageManager.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageManager.java
index 39b584529d..c10700e36a 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageManager.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageManager.java
@@ -21,17 +21,20 @@ package org.apache.james.mailbox.postgres.mail;
 
 import java.time.Clock;
 import java.util.EnumSet;
+import java.util.List;
+import java.util.Optional;
 
 import javax.mail.Flags;
 
 import org.apache.james.events.EventBus;
 import org.apache.james.mailbox.MailboxPathLocker;
 import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxCounters;
 import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.quota.QuotaManager;
 import org.apache.james.mailbox.quota.QuotaRootResolver;
 import org.apache.james.mailbox.store.BatchSizes;
@@ -46,9 +49,8 @@ import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.ThreadIdGuessingAlgorithm;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
 
-import com.github.fge.lambdas.Throwing;
-
 import reactor.core.publisher.Mono;
+import reactor.util.function.Tuple2;
 
 public class PostgresMessageManager extends StoreMessageManager {
 
@@ -80,21 +82,40 @@ public class PostgresMessageManager extends StoreMessageManager {
     }
 
     public Mono<MailboxMetaData> getMetaDataReactive(MailboxMetaData.RecentMode recentMode, MailboxSession mailboxSession, EnumSet<MailboxMetaData.Item> items) throws MailboxException {
-        MailboxACL resolvedAcl = getResolvedAcl(mailboxSession);
         if (!storeRightManager.hasRight(mailbox, MailboxACL.Right.Read, mailboxSession)) {
-            return Mono.just(MailboxMetaData.sensibleInformationFree(resolvedAcl, getMailboxEntity().getUidValidity(), isWriteable(mailboxSession)));
+            return Mono.just(MailboxMetaData.sensibleInformationFree(getResolvedAcl(mailboxSession), getMailboxEntity().getUidValidity(), isWriteable(mailboxSession)));
         }
+
         Flags permanentFlags = getPermanentFlags(mailboxSession);
-        UidValidity uidValidity = getMailboxEntity().getUidValidity();
         MessageMapper messageMapper = mapperFactory.getMessageMapper(mailboxSession);
 
-        return messageMapper.executeReactive(
-            nextUid(messageMapper, items)
-                .flatMap(nextUid -> highestModSeq(messageMapper, items)
-                    .flatMap(highestModSeq -> firstUnseen(messageMapper, items)
-                        .flatMap(Throwing.function(firstUnseen -> recent(recentMode, mailboxSession)
-                            .flatMap(recents -> mailboxCounters(messageMapper, items)
-                                .map(counters -> new MailboxMetaData(recents, permanentFlags, uidValidity, nextUid, highestModSeq, counters.getCount(),
-                                    counters.getUnseen(), firstUnseen.orElse(null), isWriteable(mailboxSession), resolvedAcl))))))));
+        Mono<PostgresMailbox> postgresMailboxMetaDataPublisher = Mono.just(mapperFactory.getMailboxMapper(mailboxSession))
+            .flatMap(postgresMailboxMapper -> postgresMailboxMapper.findMailboxById(getMailboxEntity().getMailboxId())
+                .map(mailbox -> (PostgresMailbox) mailbox));
+
+        Mono<Tuple2<Optional<MessageUid>, List<MessageUid>>> firstUnseenAndRecentPublisher = Mono.zip(firstUnseen(messageMapper, items), recent(recentMode, mailboxSession));
+
+        return messageMapper.executeReactive(Mono.zip(postgresMailboxMetaDataPublisher, mailboxCounters(messageMapper, items))
+            .flatMap(metadataAndCounter -> {
+                PostgresMailbox metadata = metadataAndCounter.getT1();
+                MailboxCounters counters = metadataAndCounter.getT2();
+                return firstUnseenAndRecentPublisher.map(firstUnseenAndRecent -> new MailboxMetaData(
+                    firstUnseenAndRecent.getT2(),
+                    permanentFlags,
+                    metadata.getUidValidity(),
+                    nextUid(metadata),
+                    metadata.getHighestModSeq(),
+                    counters.getCount(),
+                    counters.getUnseen(),
+                    firstUnseenAndRecent.getT1().orElse(null),
+                    isWriteable(mailboxSession),
+                    metadata.getACL()));
+            }));
+    }
+
+    private MessageUid nextUid(PostgresMailbox mailboxMetaData) {
+        return Optional.ofNullable(mailboxMetaData.getLastUid())
+            .map(MessageUid::next)
+            .orElse(MessageUid.MIN_VALUE);
     }
 }
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
index ac5279062e..cedcfb12af 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxDAO.java
@@ -54,6 +54,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.postgres.PostgresMailboxId;
+import org.apache.james.mailbox.postgres.mail.PostgresMailbox;
 import org.apache.james.mailbox.store.MailboxExpressionBackwardCompatibility;
 import org.jooq.Record;
 import org.jooq.impl.DSL;
@@ -85,6 +86,18 @@ public class PostgresMailboxDAO {
         .map(Optional::get)
         .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)));
 
+    private static final Function<Record, Mailbox> RECORD_TO_MAILBOX_FUNCTION = record -> {
+        Mailbox mailbox = new Mailbox(new MailboxPath(record.get(MAILBOX_NAMESPACE), Username.of(record.get(USER_NAME)), record.get(MAILBOX_NAME)),
+            UidValidity.of(record.get(MAILBOX_UID_VALIDITY)), PostgresMailboxId.of(record.get(MAILBOX_ID)));
+        mailbox.setACL(HSTORE_TO_MAILBOX_ACL_FUNCTION.apply(Hstore.hstore(record.get(MAILBOX_ACL, LinkedHashMap.class))));
+        return mailbox;
+    };
+
+    private static final Function<Record, PostgresMailbox> RECORD_TO_POSTGRES_MAILBOX_FUNCTION = record -> new PostgresMailbox(RECORD_TO_MAILBOX_FUNCTION.apply(record),
+        Optional.ofNullable(record.get(MAILBOX_HIGHEST_MODSEQ)).map(ModSeq::of).orElse(ModSeq.first()),
+        Optional.ofNullable(record.get(MAILBOX_LAST_UID)).map(MessageUid::of).orElse(null));
+
+
     private static Optional<Map.Entry<MailboxACL.EntryKey, MailboxACL.Rfc4314Rights>> deserializeMailboxACLEntry(String key, String value) {
         try {
             MailboxACL.EntryKey entryKey = MailboxACL.EntryKey.deserialize(key);
@@ -140,14 +153,14 @@ public class PostgresMailboxDAO {
             .map(record -> HSTORE_TO_MAILBOX_ACL_FUNCTION.apply(record.get(MAILBOX_ACL)));
     }
 
-    public Flux<Mailbox> findNonPersonalMailboxes(Username userName, MailboxACL.Right right) {
+    public Flux<PostgresMailbox> findNonPersonalMailboxes(Username userName, MailboxACL.Right right) {
         String mailboxACLEntryByUser = String.format("mailbox_acl -> '%s'", userName.asString());
 
         return postgresExecutor.executeRows(dslContext -> Flux.from(dslContext.selectFrom(TABLE_NAME)
                 .where(MAILBOX_ACL.isNotNull(),
                     DSL.field(mailboxACLEntryByUser).isNotNull(),
                     DSL.field(mailboxACLEntryByUser).contains(Character.toString(right.asCharacter())))))
-            .map(this::asMailbox);
+            .map(RECORD_TO_POSTGRES_MAILBOX_FUNCTION);
     }
 
     public Mono<Void> delete(MailboxId mailboxId) {
@@ -155,29 +168,29 @@ public class PostgresMailboxDAO {
             .where(MAILBOX_ID.eq(((PostgresMailboxId) mailboxId).asUuid()))));
     }
 
-    public Mono<Mailbox> findMailboxByPath(MailboxPath mailboxPath) {
+    public Mono<PostgresMailbox> findMailboxByPath(MailboxPath mailboxPath) {
         return postgresExecutor.executeRow(dsl -> Mono.from(dsl.selectFrom(TABLE_NAME)
             .where(MAILBOX_NAME.eq(mailboxPath.getName())
                 .and(USER_NAME.eq(mailboxPath.getUser().asString()))
                 .and(MAILBOX_NAMESPACE.eq(mailboxPath.getNamespace())))))
-            .map(this::asMailbox);
+            .map(RECORD_TO_POSTGRES_MAILBOX_FUNCTION);
     }
 
-    public Mono<Mailbox> findMailboxById(MailboxId id) {
+    public Mono<PostgresMailbox> findMailboxById(MailboxId id) {
         return postgresExecutor.executeRow(dsl -> Mono.from(dsl.selectFrom(TABLE_NAME)
             .where(MAILBOX_ID.eq(((PostgresMailboxId) id).asUuid()))))
-            .map(this::asMailbox)
+            .map(RECORD_TO_POSTGRES_MAILBOX_FUNCTION)
             .switchIfEmpty(Mono.error(new MailboxNotFoundException(id)));
     }
 
-    public Flux<Mailbox> findMailboxWithPathLike(MailboxQuery.UserBound query) {
+    public Flux<PostgresMailbox> findMailboxWithPathLike(MailboxQuery.UserBound query) {
         String pathLike = MailboxExpressionBackwardCompatibility.getPathLike(query);
 
         return postgresExecutor.executeRows(dsl -> Flux.from(dsl.selectFrom(TABLE_NAME)
             .where(MAILBOX_NAME.like(pathLike)
                 .and(USER_NAME.eq(query.getFixedUser().asString()))
                 .and(MAILBOX_NAMESPACE.eq(query.getFixedNamespace())))))
-            .map(this::asMailbox)
+            .map(RECORD_TO_POSTGRES_MAILBOX_FUNCTION)
             .filter(query::matches)
             .collectList()
             .flatMapIterable(Function.identity());
@@ -195,20 +208,13 @@ public class PostgresMailboxDAO {
             .hasElements();
     }
 
-    public Flux<Mailbox> getAll() {
+    public Flux<PostgresMailbox> getAll() {
         return postgresExecutor.executeRows(dsl -> Flux.from(dsl.selectFrom(TABLE_NAME)))
-            .map(this::asMailbox);
+            .map(RECORD_TO_POSTGRES_MAILBOX_FUNCTION);
     }
 
     private UUID asUUID(MailboxId mailboxId) {
-        return ((PostgresMailboxId)mailboxId).asUuid();
-    }
-
-    private Mailbox asMailbox(Record record) {
-        Mailbox mailbox = new Mailbox(new MailboxPath(record.get(MAILBOX_NAMESPACE), Username.of(record.get(USER_NAME)), record.get(MAILBOX_NAME)),
-            UidValidity.of(record.get(MAILBOX_UID_VALIDITY)), PostgresMailboxId.of(record.get(MAILBOX_ID)));
-        mailbox.setACL(HSTORE_TO_MAILBOX_ACL_FUNCTION.apply(Hstore.hstore(record.get(MAILBOX_ACL, LinkedHashMap.class))));
-        return mailbox;
+        return ((PostgresMailboxId) mailboxId).asUuid();
     }
 
     public Mono<MessageUid> findLastUidByMailboxId(MailboxId mailboxId) {
@@ -255,4 +261,5 @@ public class PostgresMailboxDAO {
                 .returning(MAILBOX_LAST_UID, MAILBOX_HIGHEST_MODSEQ)))
             .map(record -> Pair.of(MessageUid.of(record.get(MAILBOX_LAST_UID)), ModSeq.of(record.get(MAILBOX_HIGHEST_MODSEQ))));
     }
+
 }
diff --git a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java b/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java
index 3b134b5bb9..31a2ae282a 100644
--- a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java
+++ b/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMapperTest.java
@@ -19,12 +19,22 @@
 
 package org.apache.james.mailbox.postgres.mail;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import org.apache.james.backends.postgres.PostgresExtension;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MailboxSessionUtil;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.postgres.PostgresMailboxId;
 import org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxDAO;
 import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.model.MailboxMapperTest;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 public class PostgresMailboxMapperTest extends MailboxMapperTest {
@@ -40,4 +50,36 @@ public class PostgresMailboxMapperTest extends MailboxMapperTest {
     protected MailboxId generateId() {
         return PostgresMailboxId.generate();
     }
+
+    @Test
+    void retrieveMailboxShouldReturnCorrectHighestModSeqAndLastUidWhenDefault() {
+        Mailbox mailbox = mailboxMapper.create(benwaInboxPath, UidValidity.of(43)).block();
+
+        PostgresMailbox metaData = (PostgresMailbox) mailboxMapper.findMailboxById(mailbox.getMailboxId()).block();
+
+        assertThat(metaData.getHighestModSeq()).isEqualTo(ModSeq.first());
+        assertThat(metaData.getLastUid()).isEqualTo(null);
+    }
+
+    @Test
+    void retrieveMailboxShouldReturnCorrectHighestModSeqAndLastUid() {
+        Username BENWA = Username.of("benwa");
+        MailboxPath benwaInboxPath = MailboxPath.forUser(BENWA, "INBOX");
+
+        Mailbox mailbox = mailboxMapper.create(benwaInboxPath, UidValidity.of(43)).block();
+
+        // increase modSeq
+        ModSeq nextModSeq = new PostgresModSeqProvider.Factory(postgresExtension.getExecutorFactory()).create(MailboxSessionUtil.create(BENWA))
+            .nextModSeqReactive(mailbox.getMailboxId()).block();
+
+        // increase lastUid
+        MessageUid nextUid = new PostgresUidProvider.Factory(postgresExtension.getExecutorFactory()).create(MailboxSessionUtil.create(BENWA))
+            .nextUidReactive(mailbox.getMailboxId()).block();
+
+        PostgresMailbox metaData = (PostgresMailbox) mailboxMapper.findMailboxById(mailbox.getMailboxId()).block();
+
+        assertThat(metaData.getHighestModSeq()).isEqualTo(nextModSeq);
+        assertThat(metaData.getLastUid()).isEqualTo(nextUid);
+    }
+
 }


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


(james-project) 02/03: JAMES-2586 [PGSQL] Optimize getMailboxCounter method

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 28a9895a9bfe86ac3a63aae6a6514b53247c7176
Author: Tung Tran <vt...@linagora.com>
AuthorDate: Mon Dec 4 13:49:59 2023 +0700

    JAMES-2586 [PGSQL] Optimize getMailboxCounter method
    
    - Use single query to database for fetch total + total unseen
    The query looks like:
    
    select
      count(*) as "total_count",
      count(*) filter (where is_seen = $1) as "unseen_count"
    from message_mailbox
    where mailbox_id = cast($2 as uuid)
---
 .../mailbox/postgres/mail/PostgresMessageMapper.java  | 13 ++++++-------
 .../postgres/mail/dao/PostgresMailboxMessageDAO.java  | 19 ++++++++++++-------
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java
index 101118676a..6a10891a12 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java
@@ -220,13 +220,12 @@ public class PostgresMessageMapper implements MessageMapper {
 
     @Override
     public Mono<MailboxCounters> getMailboxCountersReactive(Mailbox mailbox) {
-        return mailboxMessageDAO.countTotalMessagesByMailboxId((PostgresMailboxId) mailbox.getMailboxId())
-            .flatMap(totalMessage -> mailboxMessageDAO.countUnseenMessagesByMailboxId((PostgresMailboxId) mailbox.getMailboxId())
-                .map(unseenMessage -> MailboxCounters.builder()
-                    .mailboxId(mailbox.getMailboxId())
-                    .count(totalMessage)
-                    .unseen(unseenMessage)
-                    .build()));
+        return mailboxMessageDAO.countTotalAndUnseenMessagesByMailboxId((PostgresMailboxId) mailbox.getMailboxId())
+            .map(pair -> MailboxCounters.builder()
+                .mailboxId(mailbox.getMailboxId())
+                .count(pair.getLeft())
+                .unseen(pair.getRight())
+                .build());
     }
 
     @Override
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java
index 48f93a912e..b9aa1fc89f 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java
@@ -73,6 +73,7 @@ import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
 import org.apache.james.util.streams.Limit;
 import org.jooq.Condition;
 import org.jooq.DSLContext;
+import org.jooq.Name;
 import org.jooq.Record;
 import org.jooq.Record1;
 import org.jooq.SelectFinalStep;
@@ -152,19 +153,23 @@ public class PostgresMailboxMessageDAO {
         }
     }
 
-    public Mono<Integer> countUnseenMessagesByMailboxId(PostgresMailboxId mailboxId) {
-        return postgresExecutor.executeCount(dslContext -> Mono.from(dslContext.selectCount()
-            .from(TABLE_NAME)
-            .where(MAILBOX_ID.eq(mailboxId.asUuid()))
-            .and(IS_SEEN.eq(false))));
-    }
-
     public Mono<Integer> countTotalMessagesByMailboxId(PostgresMailboxId mailboxId) {
         return postgresExecutor.executeCount(dslContext -> Mono.from(dslContext.selectCount()
             .from(TABLE_NAME)
             .where(MAILBOX_ID.eq(mailboxId.asUuid()))));
     }
 
+    public Mono<Pair<Integer, Integer>> countTotalAndUnseenMessagesByMailboxId(PostgresMailboxId mailboxId) {
+        Name totalCount = DSL.name("total_count");
+        Name unSeenCount = DSL.name("unseen_count");
+        return postgresExecutor.executeRow(dslContext -> Mono.from(dslContext.select(
+                    DSL.count().as(totalCount),
+                    DSL.count().filterWhere(IS_SEEN.eq(false)).as(unSeenCount))
+                .from(TABLE_NAME)
+                .where(MAILBOX_ID.eq(mailboxId.asUuid()))))
+            .map(record -> Pair.of(record.get(totalCount, Integer.class), record.get(unSeenCount, Integer.class)));
+    }
+
     public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxId(PostgresMailboxId mailboxId, Limit limit, MessageMapper.FetchType fetchType) {
         PostgresMailboxMessageFetchStrategy fetchStrategy = FETCH_TYPE_TO_FETCH_STRATEGY.apply(fetchType);
         Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select(fetchStrategy.fetchFields())


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


(james-project) 01/03: JAMES-2586 [PGSQL] Implement correctly FetchType

Posted by rc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 6b7568324842a18d1859d976e8f421faf53a69e7
Author: Tung Tran <vt...@linagora.com>
AuthorDate: Mon Dec 4 12:05:42 2023 +0700

    JAMES-2586 [PGSQL] Implement correctly FetchType
---
 .../james/backends/postgres/PostgresCommons.java   |   7 +-
 .../postgres/mail/PostgresMessageMapper.java       |  44 +++---
 .../mail/dao/PostgresMailboxMessageDAO.java        |  50 ++++---
 .../mail/dao/PostgresMailboxMessageDAOUtils.java   |  37 +++---
 .../dao/PostgresMailboxMessageFetchStrategy.java   | 147 +++++++++++++++++++++
 5 files changed, 217 insertions(+), 68 deletions(-)

diff --git a/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/PostgresCommons.java b/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/PostgresCommons.java
index ae4b8ebf5e..5ffb190525 100644
--- a/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/PostgresCommons.java
+++ b/backends-common/postgres/src/main/java/org/apache/james/backends/postgres/PostgresCommons.java
@@ -49,11 +49,10 @@ public class PostgresCommons {
         DataType<String[]> STRING_ARRAY = SQLDataType.CLOB.getArrayDataType();
     }
 
-    public interface SimpleTableField {
-        Field<Object> of(Table<Record> table, Field<?> field);
-    }
 
-    public static final SimpleTableField TABLE_FIELD = (table, field) -> DSL.field(table.getName() + "." + field.getName());
+    public static <T> Field<T> tableField(Table<Record> table, Field<T> field) {
+        return DSL.field(table.getName() + "." + field.getName(), field.getDataType());
+    }
 
     public static final Function<Date, LocalDateTime> DATE_TO_LOCAL_DATE_TIME = date -> Optional.ofNullable(date)
         .map(value -> LocalDateTime.ofInstant(value.toInstant(), ZoneOffset.UTC))
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java
index 1a6787d5c4..101118676a 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMessageMapper.java
@@ -128,39 +128,41 @@ public class PostgresMessageMapper implements MessageMapper {
 
     @Override
     public Flux<MailboxMessage> findInMailboxReactive(Mailbox mailbox, MessageRange messageRange, FetchType fetchType, int limitAsInt) {
+        Flux<Pair<SimpleMailboxMessage.Builder, String>> fetchMessageWithoutFullContentPublisher = fetchMessageWithoutFullContent(mailbox, messageRange, fetchType, limitAsInt);
+        if (fetchType == FetchType.FULL) {
+            return fetchMessageWithoutFullContentPublisher
+                .flatMap(messageBuilderAndBlobId -> {
+                    SimpleMailboxMessage.Builder messageBuilder = messageBuilderAndBlobId.getLeft();
+                    String blobIdAsString = messageBuilderAndBlobId.getRight();
+                    return retrieveFullContent(blobIdAsString)
+                        .map(content -> messageBuilder.content(content).build());
+                })
+                .sort(Comparator.comparing(MailboxMessage::getUid))
+                .map(message -> message);
+        } else {
+            return fetchMessageWithoutFullContentPublisher
+                .map(messageBuilderAndBlobId -> messageBuilderAndBlobId.getLeft().build());
+        }
+    }
+
+    private Flux<Pair<SimpleMailboxMessage.Builder, String>> fetchMessageWithoutFullContent(Mailbox mailbox, MessageRange messageRange, FetchType fetchType, int limitAsInt) {
         return Mono.just(messageRange)
             .flatMapMany(range -> {
                 Limit limit = Limit.from(limitAsInt);
                 switch (messageRange.getType()) {
                     case ALL:
-                        return mailboxMessageDAO.findMessagesByMailboxId((PostgresMailboxId) mailbox.getMailboxId(), limit);
+                        return mailboxMessageDAO.findMessagesByMailboxId((PostgresMailboxId) mailbox.getMailboxId(), limit, fetchType);
                     case FROM:
-                        return mailboxMessageDAO.findMessagesByMailboxIdAndAfterUID((PostgresMailboxId) mailbox.getMailboxId(), range.getUidFrom(), limit);
+                        return mailboxMessageDAO.findMessagesByMailboxIdAndAfterUID((PostgresMailboxId) mailbox.getMailboxId(), range.getUidFrom(), limit, fetchType);
                     case ONE:
-                        return mailboxMessageDAO.findMessageByMailboxIdAndUid((PostgresMailboxId) mailbox.getMailboxId(), range.getUidFrom())
+                        return mailboxMessageDAO.findMessageByMailboxIdAndUid((PostgresMailboxId) mailbox.getMailboxId(), range.getUidFrom(), fetchType)
                             .flatMapMany(Flux::just);
                     case RANGE:
-                        return mailboxMessageDAO.findMessagesByMailboxIdAndBetweenUIDs((PostgresMailboxId) mailbox.getMailboxId(), range.getUidFrom(), range.getUidTo(), limit);
+                        return mailboxMessageDAO.findMessagesByMailboxIdAndBetweenUIDs((PostgresMailboxId) mailbox.getMailboxId(), range.getUidFrom(), range.getUidTo(), limit, fetchType);
                     default:
                         throw new RuntimeException("Unknown MessageRange range " + range.getType());
                 }
-            }).flatMap(messageBuilderAndBlobId -> {
-                SimpleMailboxMessage.Builder messageBuilder = messageBuilderAndBlobId.getLeft();
-                String blobIdAsString = messageBuilderAndBlobId.getRight();
-                switch (fetchType) {
-                    case METADATA:
-                    case ATTACHMENTS_METADATA:
-                    case HEADERS:
-                        return Mono.just(messageBuilder.build());
-                    case FULL:
-                        return retrieveFullContent(blobIdAsString)
-                            .map(content -> messageBuilder.content(content).build());
-                    default:
-                        return Flux.error(new RuntimeException("Unknown FetchType " + fetchType));
-                }
-            })
-            .sort(Comparator.comparing(MailboxMessage::getUid))
-            .map(message -> message);
+            });
     }
 
     private Mono<Content> retrieveFullContent(String blobIdString) {
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java
index cbcba2943e..48f93a912e 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAO.java
@@ -22,8 +22,8 @@ package org.apache.james.mailbox.postgres.mail.dao;
 
 import static org.apache.james.backends.postgres.PostgresCommons.DATE_TO_LOCAL_DATE_TIME;
 import static org.apache.james.backends.postgres.PostgresCommons.IN_CLAUSE_MAX_SIZE;
-import static org.apache.james.backends.postgres.PostgresCommons.TABLE_FIELD;
 import static org.apache.james.backends.postgres.PostgresCommons.UNNEST_FIELD;
+import static org.apache.james.backends.postgres.PostgresCommons.tableField;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.BLOB_ID;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.INTERNAL_DATE;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.SIZE;
@@ -42,9 +42,9 @@ import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.Messa
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.THREAD_ID;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.USER_FLAGS;
 import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.BOOLEAN_FLAGS_MAPPING;
+import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.FETCH_TYPE_TO_FETCH_STRATEGY;
 import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.MESSAGE_METADATA_FIELDS_REQUIRE;
 import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_COMPOSED_MESSAGE_ID_WITH_META_DATA_FUNCTION;
-import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION;
 import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_MESSAGE_METADATA_FUNCTION;
 import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_MESSAGE_UID_FUNCTION;
 
@@ -66,6 +66,8 @@ import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.postgres.PostgresMailboxId;
 import org.apache.james.mailbox.postgres.PostgresMessageId;
 import org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable;
+import org.apache.james.mailbox.store.mail.MessageMapper;
+import org.apache.james.mailbox.store.mail.MessageMapper.FetchType;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
 import org.apache.james.util.streams.Limit;
@@ -89,7 +91,7 @@ import reactor.core.publisher.Mono;
 public class PostgresMailboxMessageDAO {
 
     private static final TableOnConditionStep<Record> MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP = TABLE_NAME.join(MessageTable.TABLE_NAME)
-        .on(TABLE_FIELD.of(TABLE_NAME, MESSAGE_ID).eq(TABLE_FIELD.of(MessageTable.TABLE_NAME, MessageTable.MESSAGE_ID)));
+        .on(tableField(TABLE_NAME, MESSAGE_ID).eq(tableField(MessageTable.TABLE_NAME, MessageTable.MESSAGE_ID)));
 
     public static final SortField<Long> DEFAULT_SORT_ORDER_BY = MESSAGE_UID.asc();
 
@@ -163,8 +165,9 @@ public class PostgresMailboxMessageDAO {
             .where(MAILBOX_ID.eq(mailboxId.asUuid()))));
     }
 
-    public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxId(PostgresMailboxId mailboxId, Limit limit) {
-        Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select()
+    public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxId(PostgresMailboxId mailboxId, Limit limit, MessageMapper.FetchType fetchType) {
+        PostgresMailboxMessageFetchStrategy fetchStrategy = FETCH_TYPE_TO_FETCH_STRATEGY.apply(fetchType);
+        Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select(fetchStrategy.fetchFields())
             .from(MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP)
             .where(MAILBOX_ID.eq(mailboxId.asUuid()))
             .orderBy(DEFAULT_SORT_ORDER_BY);
@@ -172,11 +175,12 @@ public class PostgresMailboxMessageDAO {
         return postgresExecutor.executeRows(dslContext -> limit.getLimit()
                 .map(limitValue -> Flux.from(queryWithoutLimit.andThen(step -> step.limit(limitValue)).apply(dslContext)))
                 .orElse(Flux.from(queryWithoutLimit.apply(dslContext))))
-            .map(record -> Pair.of(RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION.apply(record), record.get(BLOB_ID)));
+            .map(record -> Pair.of(fetchStrategy.toMessageBuilder().apply(record), record.get(BLOB_ID)));
     }
 
-    public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxIdAndBetweenUIDs(PostgresMailboxId mailboxId, MessageUid from, MessageUid to, Limit limit) {
-        Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select()
+    public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxIdAndBetweenUIDs(PostgresMailboxId mailboxId, MessageUid from, MessageUid to, Limit limit, FetchType fetchType) {
+        PostgresMailboxMessageFetchStrategy fetchStrategy = FETCH_TYPE_TO_FETCH_STRATEGY.apply(fetchType);
+        Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select(fetchStrategy.fetchFields())
             .from(MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP)
             .where(MAILBOX_ID.eq(mailboxId.asUuid()))
             .and(MESSAGE_UID.greaterOrEqual(from.asLong()))
@@ -186,19 +190,21 @@ public class PostgresMailboxMessageDAO {
         return postgresExecutor.executeRows(dslContext -> limit.getLimit()
                 .map(limitValue -> Flux.from(queryWithoutLimit.andThen(step -> step.limit(limitValue)).apply(dslContext)))
                 .orElse(Flux.from(queryWithoutLimit.apply(dslContext))))
-            .map(record -> Pair.of(RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION.apply(record), record.get(BLOB_ID)));
+            .map(record -> Pair.of(fetchStrategy.toMessageBuilder().apply(record), record.get(BLOB_ID)));
     }
 
-    public Mono<Pair<SimpleMailboxMessage.Builder, String>> findMessageByMailboxIdAndUid(PostgresMailboxId mailboxId, MessageUid uid) {
-        return postgresExecutor.executeRow(dslContext -> Mono.from(dslContext.select()
+    public Mono<Pair<SimpleMailboxMessage.Builder, String>> findMessageByMailboxIdAndUid(PostgresMailboxId mailboxId, MessageUid uid, FetchType fetchType) {
+        PostgresMailboxMessageFetchStrategy fetchStrategy = FETCH_TYPE_TO_FETCH_STRATEGY.apply(fetchType);
+        return postgresExecutor.executeRow(dslContext -> Mono.from(dslContext.select(fetchStrategy.fetchFields())
                 .from(MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP)
                 .where(MAILBOX_ID.eq(mailboxId.asUuid()))
                 .and(MESSAGE_UID.eq(uid.asLong()))))
-            .map(record -> Pair.of(RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION.apply(record), record.get(BLOB_ID)));
+            .map(record -> Pair.of(fetchStrategy.toMessageBuilder().apply(record), record.get(BLOB_ID)));
     }
 
-    public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxIdAndAfterUID(PostgresMailboxId mailboxId, MessageUid from, Limit limit) {
-        Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select()
+    public Flux<Pair<SimpleMailboxMessage.Builder, String>> findMessagesByMailboxIdAndAfterUID(PostgresMailboxId mailboxId, MessageUid from, Limit limit, FetchType fetchType) {
+        PostgresMailboxMessageFetchStrategy fetchStrategy = FETCH_TYPE_TO_FETCH_STRATEGY.apply(fetchType);
+        Function<DSLContext, SelectSeekStep1<Record, Long>> queryWithoutLimit = dslContext -> dslContext.select(fetchStrategy.fetchFields())
             .from(MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP)
             .where(MAILBOX_ID.eq(mailboxId.asUuid()))
             .and(MESSAGE_UID.greaterOrEqual(from.asLong()))
@@ -207,16 +213,18 @@ public class PostgresMailboxMessageDAO {
         return postgresExecutor.executeRows(dslContext -> limit.getLimit()
                 .map(limitValue -> Flux.from(queryWithoutLimit.andThen(step -> step.limit(limitValue)).apply(dslContext)))
                 .orElse(Flux.from(queryWithoutLimit.apply(dslContext))))
-            .map(record -> Pair.of(RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION.apply(record), record.get(BLOB_ID)));
+            .map(record -> Pair.of(fetchStrategy.toMessageBuilder().apply(record), record.get(BLOB_ID)));
     }
 
     public Flux<SimpleMailboxMessage.Builder> findMessagesByMailboxIdAndUIDs(PostgresMailboxId mailboxId, List<MessageUid> uids) {
-        Function<List<MessageUid>, Flux<SimpleMailboxMessage.Builder>> queryPublisherFunction = uidsToFetch -> postgresExecutor.executeRows(dslContext -> Flux.from(dslContext.select()
-                .from(MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP)
-                .where(MAILBOX_ID.eq(mailboxId.asUuid()))
-                .and(MESSAGE_UID.in(uidsToFetch.stream().map(MessageUid::asLong).toArray(Long[]::new)))
-                .orderBy(DEFAULT_SORT_ORDER_BY)))
-            .map(RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION);
+        PostgresMailboxMessageFetchStrategy fetchStrategy = PostgresMailboxMessageFetchStrategy.METADATA;
+        Function<List<MessageUid>, Flux<SimpleMailboxMessage.Builder>> queryPublisherFunction =
+            uidsToFetch -> postgresExecutor.executeRows(dslContext -> Flux.from(dslContext.select(fetchStrategy.fetchFields())
+                    .from(MESSAGES_JOIN_MAILBOX_MESSAGES_CONDITION_STEP)
+                    .where(MAILBOX_ID.eq(mailboxId.asUuid()))
+                    .and(MESSAGE_UID.in(uidsToFetch.stream().map(MessageUid::asLong).toArray(Long[]::new)))
+                    .orderBy(DEFAULT_SORT_ORDER_BY)))
+                .map(fetchStrategy.toMessageBuilder());
 
         if (uids.size() <= IN_CLAUSE_MAX_SIZE) {
             return queryPublisherFunction.apply(uids);
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAOUtils.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAOUtils.java
index f69021d3bb..65404964a4 100644
--- a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAOUtils.java
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageDAOUtils.java
@@ -20,7 +20,6 @@
 package org.apache.james.mailbox.postgres.mail.dao;
 
 import static org.apache.james.backends.postgres.PostgresCommons.LOCAL_DATE_TIME_DATE_FUNCTION;
-import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.BODY_START_OCTET;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.CONTENT_DESCRIPTION;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.CONTENT_DISPOSITION_PARAMETERS;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.CONTENT_DISPOSITION_TYPE;
@@ -30,7 +29,6 @@ import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.Messa
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.CONTENT_MD5;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.CONTENT_TRANSFER_ENCODING;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.CONTENT_TYPE_PARAMETERS;
-import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.HEADER_CONTENT;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.INTERNAL_DATE;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.SIZE;
 import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.IS_ANSWERED;
@@ -49,9 +47,7 @@ import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.Messa
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
-import java.time.LocalDateTime;
 import java.util.Arrays;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -69,9 +65,9 @@ import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.postgres.PostgresMailboxId;
 import org.apache.james.mailbox.postgres.PostgresMessageId;
 import org.apache.james.mailbox.postgres.mail.PostgresMessageModule;
+import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.impl.Properties;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
 import org.jooq.Field;
 import org.jooq.Record;
 
@@ -156,8 +152,8 @@ interface PostgresMailboxMessageDAOUtils {
         property.setContentTransferEncoding(record.get(CONTENT_TRANSFER_ENCODING));
         property.setContentLocation(record.get(CONTENT_LOCATION));
         property.setContentLanguage(Optional.ofNullable(record.get(CONTENT_LANGUAGE)).map(List::of).orElse(null));
-        property.setContentDispositionParameters(record.get(CONTENT_DISPOSITION_PARAMETERS, LinkedHashMap.class));
-        property.setContentTypeParameters(record.get(CONTENT_TYPE_PARAMETERS, LinkedHashMap.class));
+        property.setContentDispositionParameters(record.get(CONTENT_DISPOSITION_PARAMETERS).data());
+        property.setContentTypeParameters(record.get(CONTENT_TYPE_PARAMETERS).data());
         return property.build();
     };
 
@@ -173,19 +169,16 @@ interface PostgresMailboxMessageDAOUtils {
         }
     };
 
-    Function<Record, SimpleMailboxMessage.Builder> RECORD_TO_MAILBOX_MESSAGE_BUILDER_FUNCTION = record -> SimpleMailboxMessage.builder()
-        .messageId(PostgresMessageId.Factory.of(record.get(MESSAGE_ID)))
-        .mailboxId(PostgresMailboxId.of(record.get(MAILBOX_ID)))
-        .uid(MessageUid.of(record.get(MESSAGE_UID)))
-        .modseq(ModSeq.of(record.get(MOD_SEQ)))
-        .threadId(RECORD_TO_THREAD_ID_FUNCTION.apply(record))
-        .internalDate(LOCAL_DATE_TIME_DATE_FUNCTION.apply(record.get(PostgresMessageModule.MessageTable.INTERNAL_DATE, LocalDateTime.class)))
-        .saveDate(LOCAL_DATE_TIME_DATE_FUNCTION.apply(record.get(SAVE_DATE, LocalDateTime.class)))
-        .flags(RECORD_TO_FLAGS_FUNCTION.apply(record))
-        .size(record.get(PostgresMessageModule.MessageTable.SIZE))
-        .bodyStartOctet(record.get(BODY_START_OCTET))
-        .content(BYTE_TO_CONTENT_FUNCTION.apply(record.get(HEADER_CONTENT)))
-        .properties(RECORD_TO_PROPERTIES_FUNCTION.apply(record));
-
-
+    Function<MessageMapper.FetchType, PostgresMailboxMessageFetchStrategy> FETCH_TYPE_TO_FETCH_STRATEGY = fetchType -> {
+        switch (fetchType) {
+            case METADATA:
+            case ATTACHMENTS_METADATA:
+                return PostgresMailboxMessageFetchStrategy.METADATA;
+            case HEADERS:
+            case FULL:
+                return PostgresMailboxMessageFetchStrategy.FULL;
+            default:
+                throw new RuntimeException("Unknown FetchType " + fetchType);
+        }
+    };
 }
diff --git a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageFetchStrategy.java b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageFetchStrategy.java
new file mode 100644
index 0000000000..4aef7b69ef
--- /dev/null
+++ b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/dao/PostgresMailboxMessageFetchStrategy.java
@@ -0,0 +1,147 @@
+/****************************************************************
+ * 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.postgres.mail.dao;
+
+import static org.apache.james.backends.postgres.PostgresCommons.LOCAL_DATE_TIME_DATE_FUNCTION;
+import static org.apache.james.backends.postgres.PostgresCommons.tableField;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.BODY_START_OCTET;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageTable.HEADER_CONTENT;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.MAILBOX_ID;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.MESSAGE_UID;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.MOD_SEQ;
+import static org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable.SAVE_DATE;
+import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.BYTE_TO_CONTENT_FUNCTION;
+import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_FLAGS_FUNCTION;
+import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_PROPERTIES_FUNCTION;
+import static org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxMessageDAOUtils.RECORD_TO_THREAD_ID_FUNCTION;
+
+import java.time.LocalDateTime;
+import java.util.function.Function;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.model.Content;
+import org.apache.james.mailbox.postgres.PostgresMailboxId;
+import org.apache.james.mailbox.postgres.PostgresMessageId;
+import org.apache.james.mailbox.postgres.mail.PostgresMessageModule;
+import org.apache.james.mailbox.postgres.mail.PostgresMessageModule.MessageToMailboxTable;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
+import org.jooq.Field;
+import org.jooq.Record;
+
+public interface PostgresMailboxMessageFetchStrategy {
+    PostgresMailboxMessageFetchStrategy METADATA = new MetaData();
+    PostgresMailboxMessageFetchStrategy FULL = new Full();
+
+    Field<?>[] fetchFields();
+
+    Function<Record, SimpleMailboxMessage.Builder> toMessageBuilder();
+
+    static Function<Record, SimpleMailboxMessage.Builder> toMessageBuilderMetadata() {
+        return record -> SimpleMailboxMessage.builder()
+            .messageId(PostgresMessageId.Factory.of(record.get(MessageTable.MESSAGE_ID)))
+            .mailboxId(PostgresMailboxId.of(record.get(MAILBOX_ID)))
+            .uid(MessageUid.of(record.get(MESSAGE_UID)))
+            .modseq(ModSeq.of(record.get(MOD_SEQ)))
+            .threadId(RECORD_TO_THREAD_ID_FUNCTION.apply(record))
+            .internalDate(LOCAL_DATE_TIME_DATE_FUNCTION.apply(record.get(PostgresMessageModule.MessageTable.INTERNAL_DATE, LocalDateTime.class)))
+            .saveDate(LOCAL_DATE_TIME_DATE_FUNCTION.apply(record.get(SAVE_DATE, LocalDateTime.class)))
+            .flags(RECORD_TO_FLAGS_FUNCTION.apply(record))
+            .size(record.get(PostgresMessageModule.MessageTable.SIZE))
+            .bodyStartOctet(record.get(BODY_START_OCTET));
+    }
+
+    static Field<?>[] fetchFieldsMetadata() {
+        return new Field[]{
+            tableField(MessageTable.TABLE_NAME, MessageTable.MESSAGE_ID).as(MessageTable.MESSAGE_ID),
+            tableField(MessageTable.TABLE_NAME, MessageTable.INTERNAL_DATE).as(MessageTable.INTERNAL_DATE),
+            tableField(MessageTable.TABLE_NAME, MessageTable.SIZE).as(MessageTable.SIZE),
+            MessageTable.BLOB_ID,
+            MessageTable.MIME_TYPE,
+            MessageTable.MIME_SUBTYPE,
+            MessageTable.BODY_START_OCTET,
+            MessageTable.TEXTUAL_LINE_COUNT,
+            MessageToMailboxTable.MAILBOX_ID,
+            MessageToMailboxTable.MESSAGE_UID,
+            MessageToMailboxTable.MOD_SEQ,
+            MessageToMailboxTable.THREAD_ID,
+            MessageToMailboxTable.IS_DELETED,
+            MessageToMailboxTable.IS_ANSWERED,
+            MessageToMailboxTable.IS_DRAFT,
+            MessageToMailboxTable.IS_FLAGGED,
+            MessageToMailboxTable.IS_RECENT,
+            MessageToMailboxTable.IS_SEEN,
+            MessageToMailboxTable.USER_FLAGS,
+            MessageToMailboxTable.SAVE_DATE};
+    }
+
+    class MetaData implements PostgresMailboxMessageFetchStrategy {
+        public static final Field<?>[] FETCH_FIELDS = fetchFieldsMetadata();
+        public static final Content EMPTY_CONTENT = BYTE_TO_CONTENT_FUNCTION.apply(new byte[0]);
+        public static final PropertyBuilder EMPTY_PROPERTY_BUILDER = new PropertyBuilder();
+
+
+        @Override
+        public Field<?>[] fetchFields() {
+            return FETCH_FIELDS;
+        }
+
+        @Override
+        public Function<Record, SimpleMailboxMessage.Builder> toMessageBuilder() {
+            return record -> toMessageBuilderMetadata()
+                .apply(record)
+                .content(EMPTY_CONTENT)
+                .properties(EMPTY_PROPERTY_BUILDER);
+        }
+    }
+
+    class Full implements PostgresMailboxMessageFetchStrategy {
+
+        public static final Field<?>[] FETCH_FIELDS = ArrayUtils.addAll(fetchFieldsMetadata(),
+            MessageTable.HEADER_CONTENT,
+            MessageTable.TEXTUAL_LINE_COUNT,
+            MessageTable.CONTENT_DESCRIPTION,
+            MessageTable.CONTENT_LOCATION,
+            MessageTable.CONTENT_TRANSFER_ENCODING,
+            MessageTable.CONTENT_DISPOSITION_TYPE,
+            MessageTable.CONTENT_ID,
+            MessageTable.CONTENT_MD5,
+            MessageTable.CONTENT_LANGUAGE,
+            MessageTable.CONTENT_TYPE_PARAMETERS,
+            MessageTable.CONTENT_DISPOSITION_PARAMETERS);
+
+        @Override
+        public Field<?>[] fetchFields() {
+            return FETCH_FIELDS;
+        }
+
+        @Override
+        public Function<Record, SimpleMailboxMessage.Builder> toMessageBuilder() {
+            return record -> toMessageBuilderMetadata()
+                .apply(record)
+                .content(BYTE_TO_CONTENT_FUNCTION.apply(record.get(HEADER_CONTENT)))
+                .properties(RECORD_TO_PROPERTIES_FUNCTION.apply(record));
+        }
+    }
+
+}


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