or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-messaging.mdevent-handling.mdfolder-operations.mdindex.mdinternet-messaging.mdsearch-capabilities.md

search-capabilities.mddocs/

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

```