CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-jakarta-mail--jakarta-mail-api

Jakarta Mail defines a platform-independent and protocol-independent framework to build mail and messaging applications.

Pending
Overview
Eval results
Files

message-search-filtering.mddocs/

Message Search and Filtering

Message search and filtering provides comprehensive capabilities for querying messages based on various criteria including content, headers, dates, flags, and addresses with support for logical operations.

Search Foundation

The SearchTerm abstract class forms the foundation of Jakarta Mail's search system.

public abstract class SearchTerm implements Serializable {
    // Core search method - must be implemented by all search terms
    public abstract boolean match(Message msg);
    
    // Object operations
    public boolean equals(Object obj);
    public int hashCode();
}

All search operations are performed by implementing the match() method that tests whether a message satisfies the search criteria.

String-Based Search Terms

Base String Search

public abstract class StringTerm extends SearchTerm {
    protected String pattern;
    protected boolean ignoreCase;
    
    // Constructors
    protected StringTerm(String pattern);
    protected StringTerm(String pattern, boolean ignoreCase);
    
    // Accessors
    public String getPattern();
    public boolean getIgnoreCase();
}

Content Search Terms

// Search message body content
public final class BodyTerm extends StringTerm {
    public BodyTerm(String pattern);
    public BodyTerm(String pattern, boolean ignoreCase);
}

// Search subject line
public final class SubjectTerm extends StringTerm {
    public SubjectTerm(String pattern);
    public SubjectTerm(String pattern, boolean ignoreCase);
}

// Search specific header
public final class HeaderTerm extends StringTerm {
    public HeaderTerm(String headerName, String pattern);
    public HeaderTerm(String headerName, String pattern, boolean ignoreCase);
    
    public String getHeaderName();
}

// Search Message-ID header
public final class MessageIDTerm extends StringTerm {
    public MessageIDTerm(String msgid);
}

String Search Usage Example

import jakarta.mail.search.*;
import jakarta.mail.*;

// Search for messages containing "urgent" in body (case-insensitive)
SearchTerm bodySearch = new BodyTerm("urgent", true);
Message[] urgentMessages = folder.search(bodySearch);

// Search by subject
SearchTerm subjectSearch = new SubjectTerm("Meeting");
Message[] meetingMessages = folder.search(subjectSearch);

// Search specific header
SearchTerm headerSearch = new HeaderTerm("X-Priority", "1");
Message[] highPriorityMessages = folder.search(headerSearch);

// Search by Message-ID
SearchTerm messageIdSearch = new MessageIDTerm("<12345@example.com>");
Message[] specificMessage = folder.search(messageIdSearch);

// Case-sensitive body search
SearchTerm caseSensitiveSearch = new BodyTerm("CONFIDENTIAL", false);
Message[] confidentialMessages = folder.search(caseSensitiveSearch);

Address-Based Search Terms

Address Object Search

public abstract class AddressTerm extends SearchTerm {
    protected Address address;
    
    // Constructor
    protected AddressTerm(Address address);
    
    // Accessor
    public Address getAddress();
}

// Search sender addresses (Address objects)
public final class FromTerm extends AddressTerm {
    public FromTerm(Address address);
}

// Search recipient addresses (Address objects)
public final class RecipientTerm extends AddressTerm {
    public RecipientTerm(Message.RecipientType type, Address address);
    
    public Message.RecipientType getRecipientType();
}

Address String Search

public abstract class AddressStringTerm extends StringTerm {
    // Constructor
    protected AddressStringTerm(String pattern);
    protected AddressStringTerm(String pattern, boolean ignoreCase);
}

// Search sender addresses (string pattern)
public final class FromStringTerm extends AddressStringTerm {
    public FromStringTerm(String pattern);
    public FromStringTerm(String pattern, boolean ignoreCase);
}

// Search recipient addresses (string pattern)
public final class RecipientStringTerm extends AddressStringTerm {
    public RecipientStringTerm(Message.RecipientType type, String pattern);
    public RecipientStringTerm(Message.RecipientType type, String pattern, boolean ignoreCase);
    
    public Message.RecipientType getRecipientType();
}

Address Search Usage Example

import jakarta.mail.search.*;
import jakarta.mail.internet.*;

// Search by exact sender address
InternetAddress senderAddr = new InternetAddress("boss@company.com");
SearchTerm fromSearch = new FromTerm(senderAddr);
Message[] fromBoss = folder.search(fromSearch);

// Search by sender address pattern (string)
SearchTerm fromStringSearch = new FromStringTerm("@company.com");
Message[] fromCompany = folder.search(fromStringSearch);

// Search by recipient (TO addresses)
SearchTerm toSearch = new RecipientStringTerm(Message.RecipientType.TO, "team@company.com");
Message[] toTeam = folder.search(toSearch);

// Search by CC recipients
SearchTerm ccSearch = new RecipientStringTerm(Message.RecipientType.CC, "@department.com");
Message[] ccDepartment = folder.search(ccSearch);

// Case-insensitive address search
SearchTerm caseInsensitiveSearch = new FromStringTerm("MANAGER", true);
Message[] fromManager = folder.search(caseInsensitiveSearch);

Date-Based Search Terms

Base Date Search

public abstract class DateTerm extends ComparisonTerm {
    protected Date date;
    
    // Constructor
    protected DateTerm(int comparison, Date date);
    
    // Accessor
    public Date getDate();
}

Date Search Terms

// Search by received date
public final class ReceivedDateTerm extends DateTerm {
    public ReceivedDateTerm(int comparison, Date date);
}

// Search by sent date
public final class SentDateTerm extends DateTerm {
    public SentDateTerm(int comparison, Date date);
}

Comparison Operators

public abstract class ComparisonTerm extends SearchTerm {
    // Comparison constants
    public static final int LE = 1; // Less than or equal
    public static final int LT = 2; // Less than
    public static final int EQ = 3; // Equal
    public static final int NE = 4; // Not equal
    public static final int GT = 5; // Greater than
    public static final int GE = 6; // Greater than or equal
    
    protected int comparison;
    
    // Constructor
    protected ComparisonTerm(int comparison);
    
    // Accessor
    public int getComparison();
}

Date Search Usage Example

import jakarta.mail.search.*;
import java.util.Date;
import java.util.Calendar;

// Search for messages received today
Calendar today = Calendar.getInstance();
today.set(Calendar.HOUR_OF_DAY, 0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);

SearchTerm todaySearch = new ReceivedDateTerm(ComparisonTerm.GE, today.getTime());
Message[] todayMessages = folder.search(todaySearch);

// Search for messages sent last week
Calendar weekAgo = Calendar.getInstance();
weekAgo.add(Calendar.DAY_OF_YEAR, -7);
SearchTerm lastWeekSearch = new SentDateTerm(ComparisonTerm.GE, weekAgo.getTime());
Message[] lastWeekMessages = folder.search(lastWeekSearch);

// Search for messages received between two dates
Calendar startDate = Calendar.getInstance();
startDate.add(Calendar.DAY_OF_YEAR, -30);
Calendar endDate = Calendar.getInstance();
endDate.add(Calendar.DAY_OF_YEAR, -1);

SearchTerm dateRange = new AndTerm(
    new ReceivedDateTerm(ComparisonTerm.GE, startDate.getTime()),
    new ReceivedDateTerm(ComparisonTerm.LE, endDate.getTime())
);
Message[] rangeMessages = folder.search(dateRange);

// Search for messages sent exactly on a specific date
Calendar specificDate = Calendar.getInstance();
specificDate.set(2024, Calendar.JANUARY, 1);
SearchTerm exactDateSearch = new SentDateTerm(ComparisonTerm.EQ, specificDate.getTime());
Message[] exactDateMessages = folder.search(exactDateSearch);

Numeric Search Terms

Base Numeric Search

public abstract class IntegerComparisonTerm extends ComparisonTerm {
    protected int number;
    
    // Constructor
    protected IntegerComparisonTerm(int comparison, int number);
    
    // Accessor
    public int getNumber();
}

Numeric Search Terms

// Search by message number
public final class MessageNumberTerm extends IntegerComparisonTerm {
    public MessageNumberTerm(int number);
    public MessageNumberTerm(int comparison, int number);
}

// Search by message size
public final class SizeTerm extends IntegerComparisonTerm {
    public SizeTerm(int comparison, int size);
}

Numeric Search Usage Example

import jakarta.mail.search.*;

// Search for large messages (over 1MB)
SearchTerm largeMessages = new SizeTerm(ComparisonTerm.GT, 1024 * 1024);
Message[] bigMessages = folder.search(largeMessages);

// Search for small messages (under 10KB)
SearchTerm smallMessages = new SizeTerm(ComparisonTerm.LT, 10 * 1024);
Message[] tinyMessages = folder.search(smallMessages);

// Search for specific message number
SearchTerm messageNumber = new MessageNumberTerm(100);
Message[] specificNumber = folder.search(messageNumber);

// Search for messages in a range of numbers
SearchTerm numberRange = new AndTerm(
    new MessageNumberTerm(ComparisonTerm.GE, 50),
    new MessageNumberTerm(ComparisonTerm.LE, 100)
);
Message[] numberRangeMessages = folder.search(numberRange);

Flag-Based Search Terms

Flag Search

public final class FlagTerm extends SearchTerm {
    // Constructors
    public FlagTerm(Flags flags, boolean set);
    
    // Accessors
    public Flags getFlags();
    public boolean getTestSet();
}

Flag Search Usage Example

import jakarta.mail.search.*;
import jakarta.mail.*;

// Search for unread messages
Flags unreadFlags = new Flags(Flags.Flag.SEEN);
SearchTerm unreadSearch = new FlagTerm(unreadFlags, false);
Message[] unreadMessages = folder.search(unreadSearch);

// Search for flagged messages
Flags flaggedFlags = new Flags(Flags.Flag.FLAGGED);
SearchTerm flaggedSearch = new FlagTerm(flaggedFlags, true);
Message[] flaggedMessages = folder.search(flaggedSearch);

// Search for deleted messages
Flags deletedFlags = new Flags(Flags.Flag.DELETED);
SearchTerm deletedSearch = new FlagTerm(deletedFlags, true);
Message[] deletedMessages = folder.search(deletedSearch);

// Search for draft messages
Flags draftFlags = new Flags(Flags.Flag.DRAFT);
SearchTerm draftSearch = new FlagTerm(draftFlags, true);
Message[] draftMessages = folder.search(draftSearch);

// Search for answered messages
Flags answeredFlags = new Flags(Flags.Flag.ANSWERED);
SearchTerm answeredSearch = new FlagTerm(answeredFlags, true);
Message[] answeredMessages = folder.search(answeredSearch);

// Search for recent messages
Flags recentFlags = new Flags(Flags.Flag.RECENT);
SearchTerm recentSearch = new FlagTerm(recentFlags, true);
Message[] recentMessages = folder.search(recentSearch);

// Search for custom user flags
Flags customFlags = new Flags();
customFlags.add("Important");
SearchTerm customSearch = new FlagTerm(customFlags, true);
Message[] importantMessages = folder.search(customSearch);

Logical Operators

Logical AND

public final class AndTerm extends SearchTerm {
    // Constructors
    public AndTerm(SearchTerm t1, SearchTerm t2);
    public AndTerm(SearchTerm[] terms);
    
    // Accessor
    public SearchTerm[] getTerms();
}

Logical OR

public final class OrTerm extends SearchTerm {
    // Constructors
    public OrTerm(SearchTerm t1, SearchTerm t2);
    public OrTerm(SearchTerm[] terms);
    
    // Accessor
    public SearchTerm[] getTerms();
}

Logical NOT

public final class NotTerm extends SearchTerm {
    // Constructor
    public NotTerm(SearchTerm t);
    
    // Accessor
    public SearchTerm getTerm();
}

Logical Operators Usage Example

import jakarta.mail.search.*;

// Complex search: Unread messages from specific sender
SearchTerm complexSearch = new AndTerm(
    new FlagTerm(new Flags(Flags.Flag.SEEN), false),
    new FromStringTerm("important@company.com")
);
Message[] unreadFromImportant = folder.search(complexSearch);

// Multiple AND conditions
SearchTerm multipleAnd = new AndTerm(new SearchTerm[] {
    new SubjectTerm("Meeting"),
    new FromStringTerm("@company.com"),
    new ReceivedDateTerm(ComparisonTerm.GE, weekAgo.getTime())
});
Message[] complexResults = folder.search(multipleAnd);

// OR search: Messages from multiple senders
SearchTerm multiSenderSearch = new OrTerm(
    new FromStringTerm("boss@company.com"),
    new FromStringTerm("manager@company.com")
);
Message[] fromBossOrManager = folder.search(multiSenderSearch);

// NOT search: Messages not from specific domain
SearchTerm notFromSpam = new NotTerm(
    new FromStringTerm("@spam.com")
);
Message[] nonSpamMessages = folder.search(notFromSpam);

// Complex nested logic: (urgent OR important) AND not read AND from company
SearchTerm urgentOrImportant = new OrTerm(
    new SubjectTerm("urgent"),
    new SubjectTerm("important")
);

SearchTerm unreadAndFromCompany = new AndTerm(
    new FlagTerm(new Flags(Flags.Flag.SEEN), false),
    new FromStringTerm("@company.com")
);

SearchTerm finalSearch = new AndTerm(urgentOrImportant, unreadAndFromCompany);
Message[] complexNestedResults = folder.search(finalSearch);

Advanced Search Patterns

Time Range Searches

// Messages from last 24 hours
Calendar yesterday = Calendar.getInstance();
yesterday.add(Calendar.DAY_OF_YEAR, -1);
SearchTerm last24Hours = new ReceivedDateTerm(ComparisonTerm.GE, yesterday.getTime());

// Messages from this month
Calendar startOfMonth = Calendar.getInstance();
startOfMonth.set(Calendar.DAY_OF_MONTH, 1);
startOfMonth.set(Calendar.HOUR_OF_DAY, 0);
startOfMonth.set(Calendar.MINUTE, 0);
startOfMonth.set(Calendar.SECOND, 0);
SearchTerm thisMonth = new ReceivedDateTerm(ComparisonTerm.GE, startOfMonth.getTime());

// Messages older than 30 days
Calendar thirtyDaysAgo = Calendar.getInstance();
thirtyDaysAgo.add(Calendar.DAY_OF_YEAR, -30);
SearchTerm oldMessages = new ReceivedDateTerm(ComparisonTerm.LT, thirtyDaysAgo.getTime());

Content and Metadata Searches

// High priority messages
SearchTerm highPriority = new OrTerm(new SearchTerm[] {
    new HeaderTerm("X-Priority", "1"),
    new HeaderTerm("Priority", "urgent"),
    new SubjectTerm("URGENT"),
    new SubjectTerm("IMPORTANT")
});

// Attachment searches (size-based heuristic)
SearchTerm hasAttachments = new SizeTerm(ComparisonTerm.GT, 50 * 1024); // > 50KB

// Meeting-related messages
SearchTerm meetingMessages = new OrTerm(new SearchTerm[] {
    new SubjectTerm("meeting"),
    new SubjectTerm("conference"),
    new SubjectTerm("call"),
    new BodyTerm("calendar")
});

Mailbox Management Searches

// Messages to clean up (old, read, not flagged)
SearchTerm cleanupCandidates = new AndTerm(new SearchTerm[] {
    new ReceivedDateTerm(ComparisonTerm.LT, thirtyDaysAgo.getTime()),
    new FlagTerm(new Flags(Flags.Flag.SEEN), true),
    new FlagTerm(new Flags(Flags.Flag.FLAGGED), false)
});

// Important messages to preserve
SearchTerm importantToKeep = new OrTerm(new SearchTerm[] {
    new FlagTerm(new Flags(Flags.Flag.FLAGGED), true),
    new SubjectTerm("contract"),
    new SubjectTerm("invoice"),
    new FromStringTerm("legal@")
});

// Spam-like messages
SearchTerm potentialSpam = new OrTerm(new SearchTerm[] {
    new SubjectTerm("free"),
    new SubjectTerm("urgent action required"),
    new BodyTerm("click here"),
    new FromStringTerm("noreply@")
});

Search Performance Optimization

Using Search with Fetch Profiles

// Optimize search performance with fetch profiles
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(FetchProfile.Item.ENVELOPE);
fetchProfile.add(FetchProfile.Item.FLAGS);

// Perform search
Message[] results = folder.search(searchTerm);

// Fetch needed data in bulk
folder.fetch(results, fetchProfile);

// Now access is optimized
for (Message message : results) {
    // These accesses are now efficient
    System.out.println("From: " + Arrays.toString(message.getFrom()));
    System.out.println("Subject: " + message.getSubject());
    System.out.println("Flags: " + message.getFlags());
}

Search on Subsets

// Search within a specific range first
Message[] recentMessages = folder.getMessages(folder.getMessageCount() - 100, folder.getMessageCount());

// Then search within that subset
Message[] searchResults = folder.search(searchTerm, recentMessages);

Search Exception Handling

public class SearchException extends MessagingException {
    public SearchException();
    public SearchException(String message);
    public SearchException(String message, Exception e);
}

Search Error Handling Example

try {
    Message[] results = folder.search(complexSearchTerm);
    System.out.println("Found " + results.length + " messages");
} catch (SearchException e) {
    System.err.println("Search failed: " + e.getMessage());
    // Fallback to simpler search or manual filtering
} catch (MessagingException e) {
    System.err.println("Messaging error during search: " + e.getMessage());
}

Install with Tessl CLI

npx tessl i tessl/maven-jakarta-mail--jakarta-mail-api

docs

core-mail-operations.md

event-handling-system.md

index.md

internet-mail-mime.md

message-search-filtering.md

store-folder-management.md

utility-classes-streams.md

tile.json