0
# Search Capabilities
1
2
This document covers the comprehensive message search functionality provided by the javax.mail.search package, including search terms, logical operators, and complex query construction.
3
4
## Search Foundation
5
6
The SearchTerm class is the abstract base class for all search criteria in the Jakarta Mail API.
7
8
### Base Search Term
9
10
```java { .api }
11
abstract class SearchTerm implements Serializable {
12
public abstract boolean match(Message msg);
13
}
14
```
15
16
The `match` method tests whether a message satisfies the search criteria. All search implementations override this method to provide specific matching logic.
17
18
### Comparison Base Class
19
20
```java { .api }
21
abstract class ComparisonTerm extends SearchTerm {
22
public static final int LE = 1; // Less than or equal
23
public static final int LT = 2; // Less than
24
public static final int EQ = 3; // Equal
25
public static final int NE = 4; // Not equal
26
public static final int GT = 5; // Greater than
27
public static final int GE = 6; // Greater than or equal
28
29
protected int comparison;
30
31
public int getComparison();
32
public boolean match(Message msg);
33
}
34
```
35
36
### Usage Example
37
38
```java
39
// Search for messages and apply criteria
40
Folder folder = store.getFolder("INBOX");
41
folder.open(Folder.READ_ONLY);
42
43
SearchTerm term = new SubjectTerm("Important");
44
Message[] results = folder.search(term);
45
46
System.out.println("Found " + results.length + " messages with 'Important' in subject");
47
```
48
49
## Logical Operations
50
51
### AND Operations
52
53
```java { .api }
54
final class AndTerm extends SearchTerm {
55
public AndTerm(SearchTerm t1, SearchTerm t2);
56
public AndTerm(SearchTerm[] terms);
57
58
public SearchTerm[] getTerms();
59
public boolean match(Message msg);
60
}
61
```
62
63
### OR Operations
64
65
```java { .api }
66
final class OrTerm extends SearchTerm {
67
public OrTerm(SearchTerm t1, SearchTerm t2);
68
public OrTerm(SearchTerm[] terms);
69
70
public SearchTerm[] getTerms();
71
public boolean match(Message msg);
72
}
73
```
74
75
### NOT Operations
76
77
```java { .api }
78
final class NotTerm extends SearchTerm {
79
public NotTerm(SearchTerm t);
80
81
public SearchTerm getTerm();
82
public boolean match(Message msg);
83
}
84
```
85
86
### Logical Operations Example
87
88
```java
89
// Complex search: Messages from John OR Jane, but NOT marked as spam
90
SearchTerm fromJohn = new FromStringTerm("john@example.com");
91
SearchTerm fromJane = new FromStringTerm("jane@example.com");
92
SearchTerm fromEither = new OrTerm(fromJohn, fromJane);
93
94
SearchTerm spamTerm = new SubjectTerm("SPAM");
95
SearchTerm notSpam = new NotTerm(spamTerm);
96
97
SearchTerm finalTerm = new AndTerm(fromEither, notSpam);
98
99
Message[] results = folder.search(finalTerm);
100
```
101
102
## String-Based Searches
103
104
### Base String Term
105
106
```java { .api }
107
abstract class StringTerm extends SearchTerm {
108
protected String pattern;
109
protected boolean ignoreCase;
110
111
public StringTerm(String pattern);
112
public StringTerm(String pattern, boolean ignoreCase);
113
114
public String getPattern();
115
public boolean getIgnoreCase();
116
117
protected boolean match(String s);
118
}
119
```
120
121
### Subject Searches
122
123
```java { .api }
124
final class SubjectTerm extends StringTerm {
125
public SubjectTerm(String pattern);
126
127
public boolean match(Message msg);
128
}
129
```
130
131
### Body Content Searches
132
133
```java { .api }
134
final class BodyTerm extends StringTerm {
135
public BodyTerm(String pattern);
136
137
public boolean match(Message msg);
138
}
139
```
140
141
### Header Searches
142
143
```java { .api }
144
final class HeaderTerm extends StringTerm {
145
protected String headerName;
146
147
public HeaderTerm(String headerName, String pattern);
148
149
public String getHeaderName();
150
public boolean match(Message msg);
151
}
152
```
153
154
### Message ID Searches
155
156
```java { .api }
157
final class MessageIDTerm extends StringTerm {
158
public MessageIDTerm(String pattern);
159
160
public boolean match(Message msg);
161
}
162
```
163
164
### String Search Examples
165
166
```java
167
// Search for messages with specific subject
168
SearchTerm subjectTerm = new SubjectTerm("Meeting");
169
170
// Case-insensitive subject search
171
SearchTerm caseInsensitive = new SubjectTerm("meeting", true);
172
173
// Search in message body
174
SearchTerm bodyTerm = new BodyTerm("quarterly report");
175
176
// Search specific header
177
SearchTerm priorityTerm = new HeaderTerm("X-Priority", "1");
178
179
// Search by Message-ID
180
SearchTerm msgIdTerm = new MessageIDTerm("12345@example.com");
181
182
// Combine multiple string searches
183
SearchTerm combined = new OrTerm(
184
new SubjectTerm("urgent"),
185
new BodyTerm("deadline")
186
);
187
```
188
189
## Address-Based Searches
190
191
### Base Address Term
192
193
```java { .api }
194
abstract class AddressTerm extends SearchTerm {
195
protected Address address;
196
197
public AddressTerm(Address address);
198
199
public Address getAddress();
200
201
protected boolean match(Address a);
202
}
203
```
204
205
### From Address Searches
206
207
```java { .api }
208
final class FromTerm extends AddressTerm {
209
public FromTerm(Address address);
210
211
public boolean match(Message msg);
212
}
213
```
214
215
### Recipient Searches
216
217
```java { .api }
218
final class RecipientTerm extends AddressTerm {
219
protected Message.RecipientType type;
220
221
public RecipientTerm(Message.RecipientType type, Address address);
222
223
public Message.RecipientType getRecipientType();
224
public boolean match(Message msg);
225
}
226
```
227
228
### String-Based Address Searches
229
230
```java { .api }
231
abstract class AddressStringTerm extends StringTerm {
232
public AddressStringTerm(String pattern);
233
}
234
235
final class FromStringTerm extends AddressStringTerm {
236
public FromStringTerm(String pattern);
237
238
public boolean match(Message msg);
239
}
240
241
final class RecipientStringTerm extends AddressStringTerm {
242
protected Message.RecipientType type;
243
244
public RecipientStringTerm(Message.RecipientType type, String pattern);
245
246
public Message.RecipientType getRecipientType();
247
public boolean match(Message msg);
248
}
249
```
250
251
### Address Search Examples
252
253
```java
254
// Search by exact From address
255
Address johnAddr = new InternetAddress("john@example.com");
256
SearchTerm fromJohn = new FromTerm(johnAddr);
257
258
// Search by From address pattern (more flexible)
259
SearchTerm fromExample = new FromStringTerm("@example.com");
260
261
// Search by recipient (TO addresses)
262
SearchTerm toMary = new RecipientTerm(Message.RecipientType.TO,
263
new InternetAddress("mary@example.com"));
264
265
// Search by recipient pattern (CC addresses)
266
SearchTerm ccDomain = new RecipientStringTerm(Message.RecipientType.CC, "@company.com");
267
268
// Combine address searches
269
SearchTerm fromOrTo = new OrTerm(
270
new FromStringTerm("manager@"),
271
new RecipientStringTerm(Message.RecipientType.TO, "team@")
272
);
273
```
274
275
## Date-Based Searches
276
277
### Base Date Term
278
279
```java { .api }
280
abstract class DateTerm extends ComparisonTerm {
281
protected Date date;
282
283
public DateTerm(int comparison, Date date);
284
285
public Date getDate();
286
public int getComparison();
287
288
protected boolean match(Date d);
289
}
290
```
291
292
### Sent Date Searches
293
294
```java { .api }
295
final class SentDateTerm extends DateTerm {
296
public SentDateTerm(int comparison, Date date);
297
298
public boolean match(Message msg);
299
}
300
```
301
302
### Received Date Searches
303
304
```java { .api }
305
final class ReceivedDateTerm extends DateTerm {
306
public ReceivedDateTerm(int comparison, Date date);
307
308
public boolean match(Message msg);
309
}
310
```
311
312
### Date Search Examples
313
314
```java
315
// Create date references
316
Calendar cal = Calendar.getInstance();
317
cal.add(Calendar.DAY_OF_MONTH, -7);
318
Date oneWeekAgo = cal.getTime();
319
320
cal = Calendar.getInstance();
321
cal.add(Calendar.MONTH, -1);
322
Date oneMonthAgo = cal.getTime();
323
324
// Search for messages sent in the last week
325
SearchTerm recentSent = new SentDateTerm(ComparisonTerm.GT, oneWeekAgo);
326
327
// Search for messages received before one month ago
328
SearchTerm oldReceived = new ReceivedDateTerm(ComparisonTerm.LT, oneMonthAgo);
329
330
// Search for messages sent today
331
cal = Calendar.getInstance();
332
cal.set(Calendar.HOUR_OF_DAY, 0);
333
cal.set(Calendar.MINUTE, 0);
334
cal.set(Calendar.SECOND, 0);
335
Date startOfDay = cal.getTime();
336
337
SearchTerm sentToday = new SentDateTerm(ComparisonTerm.GE, startOfDay);
338
339
// Date range search (sent between two dates)
340
Date startDate = oneMonthAgo;
341
Date endDate = oneWeekAgo;
342
343
SearchTerm dateRange = new AndTerm(
344
new SentDateTerm(ComparisonTerm.GE, startDate),
345
new SentDateTerm(ComparisonTerm.LE, endDate)
346
);
347
```
348
349
## Numeric Searches
350
351
### Base Integer Comparison Term
352
353
```java { .api }
354
abstract class IntegerComparisonTerm extends ComparisonTerm {
355
protected int number;
356
357
public IntegerComparisonTerm(int comparison, int number);
358
359
public int getNumber();
360
public int getComparison();
361
362
protected boolean match(int i);
363
}
364
```
365
366
### Message Number Searches
367
368
```java { .api }
369
final class MessageNumberTerm extends IntegerComparisonTerm {
370
public MessageNumberTerm(int comparison, int number);
371
372
public boolean match(Message msg);
373
}
374
```
375
376
### Size-Based Searches
377
378
```java { .api }
379
final class SizeTerm extends IntegerComparisonTerm {
380
public SizeTerm(int comparison, int size);
381
382
public boolean match(Message msg);
383
}
384
```
385
386
### Numeric Search Examples
387
388
```java
389
// Search for large messages (> 1MB)
390
int oneMB = 1024 * 1024;
391
SearchTerm largeMsgs = new SizeTerm(ComparisonTerm.GT, oneMB);
392
393
// Search for small messages (< 10KB)
394
int tenKB = 10 * 1024;
395
SearchTerm smallMsgs = new SizeTerm(ComparisonTerm.LT, tenKB);
396
397
// Search for specific message number
398
SearchTerm msgNum100 = new MessageNumberTerm(ComparisonTerm.EQ, 100);
399
400
// Search for recent message numbers (> 500)
401
SearchTerm recentNums = new MessageNumberTerm(ComparisonTerm.GT, 500);
402
403
// Size range search
404
SearchTerm mediumMsgs = new AndTerm(
405
new SizeTerm(ComparisonTerm.GE, tenKB),
406
new SizeTerm(ComparisonTerm.LE, oneMB)
407
);
408
```
409
410
## Flag-Based Searches
411
412
### Flag Term Searches
413
414
```java { .api }
415
final class FlagTerm extends SearchTerm {
416
protected Flags flags;
417
protected boolean set;
418
419
public FlagTerm(Flags flags, boolean set);
420
421
public Flags getFlags();
422
public boolean getTestSet();
423
public boolean match(Message msg);
424
}
425
```
426
427
### Flag Search Examples
428
429
```java
430
// Search for unread messages
431
SearchTerm unread = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
432
433
// Search for flagged/important messages
434
SearchTerm flagged = new FlagTerm(new Flags(Flags.Flag.FLAGGED), true);
435
436
// Search for deleted messages
437
SearchTerm deleted = new FlagTerm(new Flags(Flags.Flag.DELETED), true);
438
439
// Search for messages that are both unread AND flagged
440
SearchTerm unreadFlagged = new AndTerm(
441
new FlagTerm(new Flags(Flags.Flag.SEEN), false),
442
new FlagTerm(new Flags(Flags.Flag.FLAGGED), true)
443
);
444
445
// Search for draft messages
446
SearchTerm drafts = new FlagTerm(new Flags(Flags.Flag.DRAFT), true);
447
448
// Search for messages with custom user flags
449
Flags customFlags = new Flags();
450
customFlags.add("Important");
451
SearchTerm customFlagged = new FlagTerm(customFlags, true);
452
```
453
454
## Complex Search Examples
455
456
### Multi-Criteria Search
457
458
```java
459
public class SearchExamples {
460
461
public Message[] findImportantUnreadMessages(Folder folder) throws MessagingException {
462
// Messages that are:
463
// - Unread (SEEN flag not set)
464
// - From VIP sender OR marked as high priority
465
// - Received in the last 30 days
466
// - Not marked as SPAM
467
468
Calendar cal = Calendar.getInstance();
469
cal.add(Calendar.DAY_OF_MONTH, -30);
470
Date thirtyDaysAgo = cal.getTime();
471
472
// Base criteria: unread and recent
473
SearchTerm unread = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
474
SearchTerm recent = new ReceivedDateTerm(ComparisonTerm.GT, thirtyDaysAgo);
475
476
// VIP sender or high priority
477
SearchTerm vipSender = new FromStringTerm("@vip-domain.com");
478
SearchTerm highPriority = new HeaderTerm("X-Priority", "1");
479
SearchTerm important = new OrTerm(vipSender, highPriority);
480
481
// Not spam
482
SearchTerm notSpam = new NotTerm(new SubjectTerm("[SPAM]"));
483
484
// Combine all criteria
485
SearchTerm finalCriteria = new AndTerm(new SearchTerm[]{
486
unread, recent, important, notSpam
487
});
488
489
return folder.search(finalCriteria);
490
}
491
492
public Message[] findMessagesForArchiving(Folder folder) throws MessagingException {
493
// Messages to archive:
494
// - Older than 1 year
495
// - Already read
496
// - Not flagged as important
497
// - Size less than 5MB (to avoid archiving large attachments separately)
498
499
Calendar cal = Calendar.getInstance();
500
cal.add(Calendar.YEAR, -1);
501
Date oneYearAgo = cal.getTime();
502
503
SearchTerm old = new ReceivedDateTerm(ComparisonTerm.LT, oneYearAgo);
504
SearchTerm read = new FlagTerm(new Flags(Flags.Flag.SEEN), true);
505
SearchTerm notFlagged = new FlagTerm(new Flags(Flags.Flag.FLAGGED), false);
506
507
int fiveMB = 5 * 1024 * 1024;
508
SearchTerm smallSize = new SizeTerm(ComparisonTerm.LT, fiveMB);
509
510
return folder.search(new AndTerm(new SearchTerm[]{
511
old, read, notFlagged, smallSize
512
}));
513
}
514
515
public Message[] findSuspiciousMessages(Folder folder) throws MessagingException {
516
// Potentially suspicious messages:
517
// - No subject OR subject contains suspicious keywords
518
// - From external domain (not @mycompany.com)
519
// - Contains attachments (multipart message)
520
// - Received recently (last 7 days)
521
522
Calendar cal = Calendar.getInstance();
523
cal.add(Calendar.DAY_OF_MONTH, -7);
524
Date lastWeek = cal.getTime();
525
526
// Subject criteria
527
SearchTerm noSubject = new SubjectTerm("");
528
SearchTerm suspiciousSubject = new OrTerm(
529
new SubjectTerm("urgent", true),
530
new SubjectTerm("click here", true)
531
);
532
SearchTerm badSubject = new OrTerm(noSubject, suspiciousSubject);
533
534
// External sender (not from company domain)
535
SearchTerm externalSender = new NotTerm(new FromStringTerm("@mycompany.com"));
536
537
// Recent messages
538
SearchTerm recent = new ReceivedDateTerm(ComparisonTerm.GT, lastWeek);
539
540
return folder.search(new AndTerm(new SearchTerm[]{
541
badSubject, externalSender, recent
542
}));
543
}
544
}
545
```
546
547
### Search with Custom Logic
548
549
```java
550
public class CustomSearchTerm extends SearchTerm {
551
private String[] keywords;
552
private boolean requireAll;
553
554
public CustomSearchTerm(String[] keywords, boolean requireAll) {
555
this.keywords = keywords;
556
this.requireAll = requireAll;
557
}
558
559
@Override
560
public boolean match(Message msg) {
561
try {
562
String content = getMessageContent(msg);
563
if (content == null) return false;
564
565
content = content.toLowerCase();
566
int matchCount = 0;
567
568
for (String keyword : keywords) {
569
if (content.contains(keyword.toLowerCase())) {
570
matchCount++;
571
if (!requireAll) {
572
return true; // Any match is sufficient
573
}
574
}
575
}
576
577
return requireAll ? (matchCount == keywords.length) : false;
578
} catch (Exception e) {
579
return false;
580
}
581
}
582
583
private String getMessageContent(Message msg) throws MessagingException, IOException {
584
StringBuilder content = new StringBuilder();
585
586
// Add subject
587
String subject = msg.getSubject();
588
if (subject != null) {
589
content.append(subject).append(" ");
590
}
591
592
// Add body content
593
Object msgContent = msg.getContent();
594
if (msgContent instanceof String) {
595
content.append((String) msgContent);
596
} else if (msgContent instanceof Multipart) {
597
extractMultipartContent((Multipart) msgContent, content);
598
}
599
600
return content.toString();
601
}
602
603
private void extractMultipartContent(Multipart mp, StringBuilder content)
604
throws MessagingException, IOException {
605
for (int i = 0; i < mp.getCount(); i++) {
606
BodyPart bp = mp.getBodyPart(i);
607
if (bp.isMimeType("text/plain") || bp.isMimeType("text/html")) {
608
content.append(bp.getContent().toString()).append(" ");
609
}
610
}
611
}
612
}
613
614
// Usage of custom search term
615
String[] keywords = {"project", "deadline", "budget"};
616
SearchTerm customSearch = new CustomSearchTerm(keywords, true); // Require all keywords
617
Message[] results = folder.search(customSearch);
618
```
619
620
## Search Performance Optimization
621
622
### Efficient Search Strategies
623
624
```java
625
public class SearchOptimization {
626
627
public Message[] optimizedSearch(Folder folder) throws MessagingException {
628
// Strategy 1: Use server-side search when possible
629
// Most specific criteria first to reduce result set quickly
630
631
// Start with date range (usually most selective)
632
Calendar cal = Calendar.getInstance();
633
cal.add(Calendar.MONTH, -1);
634
Date lastMonth = cal.getTime();
635
SearchTerm dateRange = new ReceivedDateTerm(ComparisonTerm.GT, lastMonth);
636
637
// Add flag criteria (often indexed on server)
638
SearchTerm unread = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
639
640
// Combine with AND (server can optimize)
641
SearchTerm serverOptimized = new AndTerm(dateRange, unread);
642
643
// Get intermediate results
644
Message[] candidates = folder.search(serverOptimized);
645
646
// Apply more complex criteria client-side if needed
647
List<Message> finalResults = new ArrayList<>();
648
for (Message msg : candidates) {
649
if (complexContentMatch(msg)) {
650
finalResults.add(msg);
651
}
652
}
653
654
return finalResults.toArray(new Message[0]);
655
}
656
657
private boolean complexContentMatch(Message msg) {
658
// Perform expensive content analysis only on pre-filtered set
659
try {
660
Object content = msg.getContent();
661
// Complex analysis logic here
662
return true; // placeholder
663
} catch (Exception e) {
664
return false;
665
}
666
}
667
668
public void searchWithPagination(Folder folder, int pageSize) throws MessagingException {
669
// For large folders, paginate through results
670
SearchTerm criteria = new SubjectTerm("report");
671
672
int totalMessages = folder.getMessageCount();
673
for (int start = 1; start <= totalMessages; start += pageSize) {
674
int end = Math.min(start + pageSize - 1, totalMessages);
675
676
Message[] pageMessages = folder.getMessages(start, end);
677
Message[] pageResults = folder.search(criteria, pageMessages);
678
679
processResults(pageResults);
680
}
681
}
682
683
private void processResults(Message[] results) {
684
// Process page of results
685
System.out.println("Processing " + results.length + " messages");
686
}
687
}
688
```
689
690
## Exception Handling
691
692
```java { .api }
693
class SearchException extends MessagingException {
694
public SearchException();
695
public SearchException(String s);
696
}
697
```
698
699
### Search Error Handling
700
701
```java
702
public Message[] safeSearch(Folder folder, SearchTerm term) {
703
try {
704
return folder.search(term);
705
} catch (SearchException e) {
706
System.err.println("Search failed: " + e.getMessage());
707
// Fallback to client-side filtering
708
return fallbackSearch(folder, term);
709
} catch (MessagingException e) {
710
System.err.println("Messaging error during search: " + e.getMessage());
711
return new Message[0];
712
}
713
}
714
715
private Message[] fallbackSearch(Folder folder, SearchTerm term) {
716
try {
717
// Get all messages and filter client-side
718
Message[] allMessages = folder.getMessages();
719
List<Message> results = new ArrayList<>();
720
721
for (Message msg : allMessages) {
722
try {
723
if (term.match(msg)) {
724
results.add(msg);
725
}
726
} catch (Exception e) {
727
// Skip problematic messages
728
continue;
729
}
730
}
731
732
return results.toArray(new Message[0]);
733
} catch (MessagingException e) {
734
System.err.println("Fallback search failed: " + e.getMessage());
735
return new Message[0];
736
}
737
}
738
```