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/03/28 02:15:56 UTC

[james-project] branch master updated: JAMES-2080 Add a dedicated Subject search criterion (#1503)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new c7bf7ad6f0 JAMES-2080 Add a dedicated Subject search criterion (#1503)
c7bf7ad6f0 is described below

commit c7bf7ad6f0c2d394f608fd73c1239d98eea71f47
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Tue Mar 28 04:15:48 2023 +0200

    JAMES-2080 Add a dedicated Subject search criterion (#1503)
---
 .../apache/james/mailbox/model/SearchQuery.java    | 42 ++++++++++++++++++++++
 .../lucene/search/LuceneMessageSearchIndex.java    |  3 ++
 .../opensearch/query/CriterionConverter.java       |  9 +++++
 .../mail/SearchThreadIdGuessingAlgorithm.java      |  2 +-
 .../mailbox/store/search/MessageSearches.java      |  3 ++
 .../store/search/SimpleMessageSearchIndex.java     |  3 +-
 .../james/imap/processor/SearchProcessor.java      |  2 +-
 .../james/imap/processor/SearchProcessorTest.java  |  5 ++-
 .../james/jmap/draft/utils/FilterToCriteria.java   |  4 +--
 .../jmap/draft/utils/FilterToCriteriaTest.java     | 10 +++---
 .../james/jmap/utils/search/MailboxFilter.scala    |  4 +--
 11 files changed, 72 insertions(+), 15 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchQuery.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchQuery.java
index fb9a1f1ac0..5e84e32913 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchQuery.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/SearchQuery.java
@@ -750,6 +750,10 @@ public class SearchQuery {
         return new MimeMessageIDCriterion(messageId);
     }
 
+    public static Criterion subject(String subject) {
+        return new SubjectCriterion(subject);
+    }
+
     public static Criterion threadId(ThreadId threadId) {
         return new ThreadIdCriterion(threadId);
     }
@@ -1156,6 +1160,44 @@ public class SearchQuery {
         }
     }
 
+    public static class SubjectCriterion extends Criterion {
+        private final String subject;
+
+        public SubjectCriterion(String subject) {
+            this.subject = subject;
+        }
+
+        public String getSubject() {
+            return subject;
+        }
+
+        public HeaderCriterion asHeaderCriterion() {
+            return new HeaderCriterion("Subject", new ContainsOperator(subject));
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof SubjectCriterion) {
+                SubjectCriterion that = (SubjectCriterion) o;
+
+                return java.util.Objects.equals(this.subject, that.subject);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return java.util.Objects.hash(subject);
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this)
+                .add("subject", subject)
+                .toString();
+        }
+    }
+
     /**
      * Filters on the threadId of the messages.
      */
diff --git a/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java b/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
index 2bf8f7c9f2..c71e17ce5e 100644
--- a/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
+++ b/mailbox/lucene/src/main/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndex.java
@@ -1245,6 +1245,9 @@ public class LuceneMessageSearchIndex extends ListeningMessageSearchIndex {
         } else if (criterion instanceof SearchQuery.MimeMessageIDCriterion) {
             SearchQuery.MimeMessageIDCriterion mimeMessageIDCriterion = (SearchQuery.MimeMessageIDCriterion) criterion;
             return createHeaderQuery(mimeMessageIDCriterion.asHeaderCriterion());
+        } else if (criterion instanceof SearchQuery.SubjectCriterion) {
+            SearchQuery.SubjectCriterion subjectCriterion = (SearchQuery.SubjectCriterion) criterion;
+            return createHeaderQuery(subjectCriterion.asHeaderCriterion());
         } else if (criterion instanceof SearchQuery.ThreadIdCriterion) {
             SearchQuery.ThreadIdCriterion threadIdCriterion = (SearchQuery.ThreadIdCriterion) criterion;
             return createTermQuery(THREAD_ID_FIELD, threadIdCriterion.getThreadId().serialize());
diff --git a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/query/CriterionConverter.java b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/query/CriterionConverter.java
index 9efe66925a..5b76063a77 100644
--- a/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/query/CriterionConverter.java
+++ b/mailbox/opensearch/src/main/java/org/apache/james/mailbox/opensearch/query/CriterionConverter.java
@@ -67,6 +67,7 @@ public class CriterionConverter {
         registerCriterionConverter(SearchQuery.MessageIdCriterion.class, this::convertMessageId);
         registerCriterionConverter(SearchQuery.ConjunctionCriterion.class, this::convertConjunction);
         registerCriterionConverter(SearchQuery.HeaderCriterion.class, this::convertHeader);
+        registerCriterionConverter(SearchQuery.SubjectCriterion.class, this::convertSubject);
         registerCriterionConverter(SearchQuery.TextCriterion.class, this::convertTextCriterion);
         registerCriterionConverter(SearchQuery.CustomFlagCriterion.class, this::convertCustomFlagCriterion);
         
@@ -424,6 +425,14 @@ public class CriterionConverter {
                 headerCriterion.getOperator());
     }
 
+    private Query convertSubject(SearchQuery.SubjectCriterion headerCriterion) {
+        return new MatchQuery.Builder()
+            .field(JsonMessageConstants.SUBJECT)
+            .query(new FieldValue.Builder().stringValue(headerCriterion.getSubject()).build())
+            .build()
+            ._toQuery();
+    }
+
     private Query manageAddressFields(String headerName, String value) {
         return new BoolQuery.Builder()
             .should(new MatchQuery.Builder()
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
index 551e737b93..99c00f5a81 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/SearchThreadIdGuessingAlgorithm.java
@@ -93,7 +93,7 @@ public class SearchThreadIdGuessingAlgorithm implements ThreadIdGuessingAlgorith
         });
         SearchQuery.Criterion mimeMessageIdCriterion = SearchQuery.or(mimeMessageIdCriteriaBuilder.build());
 
-        SearchQuery.Criterion finalCriterion = subject.map(value -> SearchQuery.and(mimeMessageIdCriterion, SearchQuery.headerContains("Subject", SearchUtil.getBaseSubject(value.getValue()))))
+        SearchQuery.Criterion finalCriterion = subject.map(value -> SearchQuery.and(mimeMessageIdCriterion, SearchQuery.subject(SearchUtil.getBaseSubject(value.getValue()))))
             .orElse(mimeMessageIdCriterion);
 
         return MultimailboxesSearchQuery
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
index c1189aaff4..435be54147 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/MessageSearches.java
@@ -188,6 +188,9 @@ public class MessageSearches implements Iterable<SimpleMessageSearchIndex.Search
         } else if (criterion instanceof SearchQuery.MimeMessageIDCriterion) {
             SearchQuery.MimeMessageIDCriterion mimeMessageIDCriterion = (SearchQuery.MimeMessageIDCriterion) criterion;
             return isMatch(mimeMessageIDCriterion.asHeaderCriterion(), message, recentMessageUids);
+        } else if (criterion instanceof SearchQuery.SubjectCriterion) {
+            SearchQuery.SubjectCriterion subjectCriterion = (SearchQuery.SubjectCriterion) criterion;
+            return isMatch(subjectCriterion.asHeaderCriterion(), message, recentMessageUids);
         } else if (criterion instanceof SearchQuery.ThreadIdCriterion) {
             SearchQuery.ThreadIdCriterion threadIdCriterion = (SearchQuery.ThreadIdCriterion) criterion;
             return matches(threadIdCriterion, message);
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
index 1858d16b78..af1651e05b 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndex.java
@@ -133,7 +133,8 @@ public class SimpleMessageSearchIndex implements MessageSearchIndex {
         if (crit instanceof SearchQuery.AllCriterion || crit instanceof SearchQuery.TextCriterion) {
             return FetchType.FULL;
         }
-        if (crit instanceof SearchQuery.HeaderCriterion || crit instanceof SearchQuery.MimeMessageIDCriterion) {
+        if (crit instanceof SearchQuery.HeaderCriterion || crit instanceof SearchQuery.MimeMessageIDCriterion
+            || crit instanceof SearchQuery.SubjectCriterion) {
             return FetchType.HEADERS;
         }
         return FetchType.METADATA;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
index 3ab4073b44..6d506900c3 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
@@ -347,7 +347,7 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp
         case TYPE_SMALLER:
             return SearchQuery.sizeLessThan(key.getSize());
         case TYPE_SUBJECT:
-            return SearchQuery.headerContains(ImapConstants.RFC822_SUBJECT, key.getValue());
+            return SearchQuery.subject(key.getValue());
         case TYPE_TEXT:
             return SearchQuery.mailContains(key.getValue());
         case TYPE_TO:
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java
index 8658605fde..43389a778e 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java
@@ -77,7 +77,7 @@ import it.unimi.dsi.fastutil.longs.LongList;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
-public class SearchProcessorTest {
+class SearchProcessorTest {
     private static final int DAY = 6;
 
     private static final int MONTH = 6;
@@ -424,8 +424,7 @@ public class SearchProcessorTest {
     @Test
     void testSUBJECT() throws Exception {
         expectsGetSelectedMailbox();
-        check(SearchKey.buildSubject(SUBJECT), SearchQuery.headerContains(
-                ImapConstants.RFC822_SUBJECT, SUBJECT));
+        check(SearchKey.buildSubject(SUBJECT), SearchQuery.subject(SUBJECT));
     }
 
     @Test
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java
index 5c9edd4829..c25b01da75 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/utils/FilterToCriteria.java
@@ -56,7 +56,7 @@ public class FilterToCriteria {
                         SearchQuery.address(AddressType.To, text),
                         SearchQuery.address(AddressType.Cc, text),
                         SearchQuery.address(AddressType.Bcc, text),
-                        SearchQuery.headerContains("Subject", text),
+                        SearchQuery.subject(text),
                         SearchQuery.attachmentContains(text),
                         SearchQuery.bodyContains(text),
                         SearchQuery.attachmentFileName(text)))
@@ -65,7 +65,7 @@ public class FilterToCriteria {
         filter.getTo().ifPresent(to -> builder.add(SearchQuery.address(AddressType.To, to)));
         filter.getCc().ifPresent(cc -> builder.add(SearchQuery.address(AddressType.Cc, cc)));
         filter.getBcc().ifPresent(bcc -> builder.add(SearchQuery.address(AddressType.Bcc, bcc)));
-        filter.getSubject().ifPresent(subject -> builder.add(SearchQuery.headerContains("Subject", subject)));
+        filter.getSubject().ifPresent(subject -> builder.add(SearchQuery.subject(subject)));
         filter.getAttachments().ifPresent(attachments ->  builder.add(SearchQuery.attachmentContains(attachments)));
         filter.getBody().ifPresent(body ->  builder.add(SearchQuery.bodyContains(body)));
         filter.getAfter().ifPresent(after -> builder.add(SearchQuery.sentDateAfter(Date.from(after.toInstant()), DateResolution.Second)));
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java
index c03cbac7e8..eae8725850 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/utils/FilterToCriteriaTest.java
@@ -123,7 +123,7 @@ public class FilterToCriteriaTest {
                 .subject(subject)
                 .build());
 
-        assertThat(criteria).containsExactly(SearchQuery.headerContains("Subject", subject));
+        assertThat(criteria).containsExactly(SearchQuery.subject(subject));
     }
 
     @Test
@@ -158,7 +158,7 @@ public class FilterToCriteriaTest {
             SearchQuery.address(AddressType.To, text),
             SearchQuery.address(AddressType.Cc, text),
             SearchQuery.address(AddressType.Bcc, text),
-            SearchQuery.headerContains("Subject", text),
+            SearchQuery.subject(text),
             SearchQuery.bodyContains(text),
             SearchQuery.attachmentContains(text),
             SearchQuery.attachmentFileName(text))));
@@ -336,7 +336,7 @@ public class FilterToCriteriaTest {
         assertThat(criteria).containsExactly(SearchQuery.and(ImmutableList.of(
             SearchQuery.address(AddressType.From, from),
             SearchQuery.address(AddressType.To, to),
-            SearchQuery.headerContains("Subject", subject))));
+            SearchQuery.subject(subject))));
     }
 
     @Test
@@ -357,7 +357,7 @@ public class FilterToCriteriaTest {
         assertThat(criteria).containsExactly(SearchQuery.or(ImmutableList.of(
             SearchQuery.address(AddressType.From, from),
             SearchQuery.address(AddressType.To, to),
-            SearchQuery.headerContains("Subject", subject))));
+            SearchQuery.subject(subject))));
     }
 
     @Test
@@ -378,7 +378,7 @@ public class FilterToCriteriaTest {
         assertThat(criteria).containsExactly(SearchQuery.not(ImmutableList.of(
             SearchQuery.address(AddressType.From, from),
             SearchQuery.address(AddressType.To, to),
-            SearchQuery.headerContains("Subject", subject))));
+            SearchQuery.subject(subject))));
     }
 
     @Test
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
index 43e7fa523a..7ac016aafd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/utils/search/MailboxFilter.scala
@@ -249,7 +249,7 @@ object MailboxFilter {
               SearchQuery.address(AddressType.Cc, text.value),
               SearchQuery.address(AddressType.Bcc, text.value),
               SearchQuery.address(AddressType.From, text.value),
-              SearchQuery.headerContains("Subject", text.value),
+              SearchQuery.subject(text.value),
               SearchQuery.bodyContains(text.value))
             .asJava)))
         case None => Right(Nil)
@@ -287,7 +287,7 @@ object MailboxFilter {
   case object Subject extends ConditionFilter {
     override def toQuery(filterCondition: FilterCondition): Either[UnsupportedFilterException, List[Criterion]] =
       filterCondition.subject match {
-        case Some(subject) => Right(List(SearchQuery.headerContains("Subject", subject.value)))
+        case Some(subject) => Right(List(SearchQuery.subject(subject.value)))
         case None => Right(Nil)
       }
   }


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