0
# Search and Filtering
1
2
Advanced search capabilities with filter construction, result processing, search controls, and result handling for LDAP directory queries.
3
4
## Capabilities
5
6
### Search Operations
7
8
#### SearchRequest
9
10
Comprehensive search request construction with filters, controls, and result processing options.
11
12
```java { .api }
13
/**
14
* Request for LDAP search operations with full configuration
15
*/
16
public class SearchRequest extends UpdatableLDAPRequest {
17
// Constructors
18
public SearchRequest(String baseDN, SearchScope scope, String filter);
19
public SearchRequest(String baseDN, SearchScope scope, String filter, String... attributes);
20
public SearchRequest(String baseDN, SearchScope scope, Filter filter);
21
public SearchRequest(String baseDN, SearchScope scope, Filter filter, String... attributes);
22
public SearchRequest(SearchResultListener searchResultListener, String baseDN, SearchScope scope, String filter, String... attributes);
23
24
// Configuration
25
public String getBaseDN();
26
public void setBaseDN(String baseDN);
27
public SearchScope getScope();
28
public void setScope(SearchScope scope);
29
public Filter getFilter();
30
public void setFilter(Filter filter);
31
public void setFilter(String filter) throws LDAPException;
32
public String[] getAttributes();
33
public void setAttributes(String... attributes);
34
35
// Search limits
36
public int getSizeLimit();
37
public void setSizeLimit(int sizeLimit);
38
public int getTimeLimitSeconds();
39
public void setTimeLimit(int timeLimitSeconds);
40
public boolean typesOnly();
41
public void setTypesOnly(boolean typesOnly);
42
43
// Alias handling
44
public DereferencePolicy getDerefPolicy();
45
public void setDerefPolicy(DereferencePolicy derefPolicy);
46
47
// Result handling
48
public SearchResultListener getSearchResultListener();
49
public void setSearchResultListener(SearchResultListener searchResultListener);
50
}
51
52
/**
53
* Search scope enumeration
54
*/
55
public enum SearchScope {
56
BASE(0, "base"),
57
ONE(1, "one"),
58
SUB(2, "sub"),
59
SUBORDINATE_SUBTREE(3, "subordinateSubtree");
60
61
public int intValue();
62
public String getName();
63
public static SearchScope valueOf(int intValue);
64
public static SearchScope definedValueOf(String name);
65
}
66
67
/**
68
* Alias dereferencing policy
69
*/
70
public enum DereferencePolicy {
71
NEVER(0, "never"),
72
IN_SEARCHING(1, "inSearching"),
73
FINDING_BASE_OBJECT(2, "findingBaseObj"),
74
ALWAYS(3, "always");
75
76
public int intValue();
77
public String getName();
78
}
79
```
80
81
#### SearchResult
82
83
Complete search result with entries, references, and metadata.
84
85
```java { .api }
86
/**
87
* Result of a search operation containing entries and metadata
88
*/
89
public class SearchResult extends LDAPResult {
90
public List<SearchResultEntry> getSearchEntries();
91
public List<SearchResultReference> getSearchReferences();
92
public int getEntryCount();
93
public int getReferenceCount();
94
95
// Result limits
96
public boolean sizeLimitExceeded();
97
public boolean timeLimitExceeded();
98
99
// Search-specific result handling
100
public SearchResultEntry getSearchEntry(String dn);
101
public boolean entryReturned(String dn);
102
}
103
104
/**
105
* Individual entry from search results
106
*/
107
public class SearchResultEntry extends Entry {
108
public SearchResultEntry(String dn, Attribute... attributes);
109
public SearchResultEntry(String dn, Collection<Attribute> attributes, Control... controls);
110
111
public Control[] getControls();
112
public boolean hasControl(String oid);
113
public <T extends Control> T getControl(Class<T> controlClass);
114
}
115
116
/**
117
* Search result reference (referral)
118
*/
119
public class SearchResultReference {
120
public SearchResultReference(String[] referralURLs, Control... controls);
121
122
public String[] getReferralURLs();
123
public Control[] getControls();
124
public boolean hasControl(String oid);
125
}
126
```
127
128
### Search Result Processing
129
130
#### SearchResultListener
131
132
Interface for processing search results as they arrive.
133
134
```java { .api }
135
/**
136
* Interface for processing search results as they arrive
137
*/
138
public interface SearchResultListener extends Serializable {
139
/**
140
* Called when a search result entry is returned
141
* @param searchEntry The search result entry
142
*/
143
void searchEntryReturned(SearchResultEntry searchEntry);
144
145
/**
146
* Called when a search result reference is returned
147
* @param searchReference The search result reference
148
*/
149
void searchReferenceReturned(SearchResultReference searchReference);
150
}
151
152
/**
153
* Convenience implementation that collects results in lists
154
*/
155
public class LDAPEntrySource implements EntrySource, Closeable {
156
public LDAPEntrySource(LDAPInterface connection, String baseDN, SearchScope scope, String filter, String... attributes);
157
public LDAPEntrySource(LDAPInterface connection, SearchRequest searchRequest);
158
159
public Entry nextEntry() throws EntrySourceException;
160
public void close();
161
}
162
```
163
164
#### Asynchronous Search
165
166
```java { .api }
167
/**
168
* Asynchronous search operations
169
* @param searchRequest The search request
170
* @param resultListener Listener for handling results
171
* @return Request ID for tracking
172
* @throws LDAPException if the operation fails
173
*/
174
public AsyncRequestID asyncSearch(SearchRequest searchRequest, AsyncSearchResultListener resultListener) throws LDAPException;
175
176
/**
177
* Cancel an asynchronous operation
178
* @param asyncRequestID The request ID to cancel
179
* @throws LDAPException if cancellation fails
180
*/
181
public void abandon(AsyncRequestID asyncRequestID) throws LDAPException;
182
183
/**
184
* Listener interface for asynchronous search results
185
*/
186
public interface AsyncSearchResultListener extends AsyncResultListener {
187
void searchEntryReturned(SearchResultEntry entry);
188
void searchReferenceReturned(SearchResultReference reference);
189
void searchResultReceived(AsyncRequestID requestID, SearchResult result);
190
}
191
```
192
193
### Advanced Filter Operations
194
195
#### Filter Construction Utilities
196
197
```java { .api }
198
/**
199
* Advanced filter construction and manipulation
200
*/
201
public class Filter implements Serializable {
202
// Comprehensive filter creation methods
203
public static Filter createEqualityFilter(String attributeName, String assertionValue);
204
public static Filter createEqualityFilter(String attributeName, byte[] assertionValue);
205
206
// Substring filters
207
public static Filter createSubstringFilter(String attributeName, String subInitial, String[] subAny, String subFinal);
208
public static Filter createSubInitialFilter(String attributeName, String subInitial);
209
public static Filter createSubAnyFilter(String attributeName, String... subAnyValues);
210
public static Filter createSubFinalFilter(String attributeName, String subFinal);
211
212
// Comparison filters
213
public static Filter createGreaterOrEqualFilter(String attributeName, String assertionValue);
214
public static Filter createLessOrEqualFilter(String attributeName, String assertionValue);
215
public static Filter createApproximateMatchFilter(String attributeName, String assertionValue);
216
217
// Presence and extensible match
218
public static Filter createPresenceFilter(String attributeName);
219
public static Filter createExtensibleMatchFilter(String attributeName, String matchingRuleID, boolean dnAttributes, String assertionValue);
220
221
// Logical combinations
222
public static Filter createANDFilter(Filter... filters);
223
public static Filter createANDFilter(Collection<Filter> filters);
224
public static Filter createORFilter(Filter... filters);
225
public static Filter createORFilter(Collection<Filter> filters);
226
public static Filter createNOTFilter(Filter filter);
227
228
// Filter analysis
229
public FilterType getFilterType();
230
public String getAttributeName();
231
public String getAssertionValue();
232
public byte[] getAssertionValueBytes();
233
public String getSubInitial();
234
public String[] getSubAny();
235
public String getSubFinal();
236
public Filter[] getComponents();
237
public Filter getNOTComponent();
238
public String getMatchingRuleID();
239
public boolean getDNAttributes();
240
241
// Filter transformation
242
public Filter toNormalizedFilter();
243
public String toNormalizedString();
244
public boolean matchesEntry(Entry entry) throws LDAPException;
245
public boolean matchesEntry(Entry entry, Schema schema) throws LDAPException;
246
}
247
```
248
249
### Search Controls
250
251
#### Paged Results Control
252
253
```java { .api }
254
/**
255
* Control for retrieving search results in pages
256
*/
257
public class PagedResultsRequestControl extends Control {
258
public PagedResultsRequestControl(int pageSize);
259
public PagedResultsRequestControl(int pageSize, ASN1OctetString cookie);
260
public PagedResultsRequestControl(int pageSize, ASN1OctetString cookie, boolean isCritical);
261
262
public int getSize();
263
public ASN1OctetString getCookie();
264
}
265
266
/**
267
* Response control for paged results
268
*/
269
public class SimplePagedResultsControl extends Control {
270
public int getSize();
271
public ASN1OctetString getCookie();
272
public boolean moreResultsToReturn();
273
}
274
```
275
276
#### Server-Side Sort Control
277
278
```java { .api }
279
/**
280
* Control for server-side sorting of search results
281
*/
282
public class ServerSideSortRequestControl extends Control {
283
public ServerSideSortRequestControl(SortKey... sortKeys);
284
public ServerSideSortRequestControl(List<SortKey> sortKeys);
285
public ServerSideSortRequestControl(boolean isCritical, SortKey... sortKeys);
286
287
public List<SortKey> getSortKeys();
288
}
289
290
/**
291
* Sort key specification for server-side sorting
292
*/
293
public class SortKey implements Serializable {
294
public SortKey(String attributeName);
295
public SortKey(String attributeName, boolean reverseOrder);
296
public SortKey(String attributeName, String matchingRuleID);
297
public SortKey(String attributeName, String matchingRuleID, boolean reverseOrder);
298
299
public String getAttributeName();
300
public String getMatchingRuleID();
301
public boolean reverseOrder();
302
}
303
304
/**
305
* Response control for server-side sort status
306
*/
307
public class ServerSideSortResponseControl extends Control {
308
public ResultCode getResultCode();
309
public String getAttributeName();
310
}
311
```
312
313
#### Virtual List View Control
314
315
```java { .api }
316
/**
317
* Control for virtual list view (VLV) searches
318
*/
319
public class VirtualListViewRequestControl extends Control {
320
// Constructor for offset-based VLV
321
public VirtualListViewRequestControl(int targetOffset, int beforeCount, int afterCount, int contentCount);
322
public VirtualListViewRequestControl(int targetOffset, int beforeCount, int afterCount, int contentCount, ASN1OctetString contextID);
323
324
// Constructor for value-based VLV
325
public VirtualListViewRequestControl(String targetValue, int beforeCount, int afterCount);
326
public VirtualListViewRequestControl(byte[] targetValue, int beforeCount, int afterCount);
327
328
public int getTargetOffset();
329
public byte[] getTargetValue();
330
public int getBeforeCount();
331
public int getAfterCount();
332
public int getContentCount();
333
public ASN1OctetString getContextID();
334
}
335
336
/**
337
* Response control for VLV result information
338
*/
339
public class VirtualListViewResponseControl extends Control {
340
public int getTargetPosition();
341
public int getContentCount();
342
public ResultCode getResult();
343
public ASN1OctetString getContextID();
344
}
345
```
346
347
#### Persistent Search Control
348
349
```java { .api }
350
/**
351
* Control for persistent search operations (change notifications)
352
*/
353
public class PersistentSearchRequestControl extends Control {
354
public PersistentSearchRequestControl();
355
public PersistentSearchRequestControl(Set<PersistentSearchChangeType> changeTypes, boolean changesOnly, boolean returnECs);
356
public PersistentSearchRequestControl(Set<PersistentSearchChangeType> changeTypes, boolean changesOnly, boolean returnECs, boolean isCritical);
357
358
public Set<PersistentSearchChangeType> getChangeTypes();
359
public boolean changesOnly();
360
public boolean returnECs();
361
}
362
363
/**
364
* Change types for persistent search
365
*/
366
public enum PersistentSearchChangeType {
367
ADD(1),
368
DELETE(2),
369
MODIFY(4),
370
MODIFY_DN(8);
371
372
public int intValue();
373
}
374
375
/**
376
* Entry change notification control (response)
377
*/
378
public class EntryChangeNotificationControl extends Control {
379
public PersistentSearchChangeType getChangeType();
380
public long getChangeNumber();
381
public String getPreviousDN();
382
}
383
```
384
385
### Search Utilities
386
387
#### Entry Source Framework
388
389
```java { .api }
390
/**
391
* Interface for iterating through entries
392
*/
393
public interface EntrySource extends Closeable {
394
Entry nextEntry() throws EntrySourceException;
395
void close();
396
}
397
398
/**
399
* LDAP-backed entry source for search results
400
*/
401
public class LDAPEntrySource implements EntrySource {
402
public LDAPEntrySource(LDAPInterface connection, String baseDN, SearchScope scope, String filter, String... attributes);
403
public LDAPEntrySource(LDAPInterface connection, SearchRequest searchRequest);
404
405
public Entry nextEntry() throws EntrySourceException;
406
public long getEntriesRead();
407
public void close();
408
}
409
410
/**
411
* Exception for entry source operations
412
*/
413
public class EntrySourceException extends Exception {
414
public EntrySourceException(String message);
415
public EntrySourceException(String message, Throwable cause);
416
417
public boolean mayContinueReading();
418
}
419
```
420
421
## Usage Examples
422
423
### Basic Search Operations
424
425
```java
426
import com.unboundid.ldap.sdk.*;
427
428
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
429
430
try {
431
connection.bind("cn=admin,dc=example,dc=com", "password");
432
433
// Simple search
434
SearchResult result = connection.search(
435
"ou=people,dc=example,dc=com", // base DN
436
SearchScope.ONE, // scope
437
"(objectClass=inetOrgPerson)", // filter
438
"cn", "mail", "telephoneNumber" // attributes to return
439
);
440
441
System.out.println("Found " + result.getEntryCount() + " entries");
442
443
// Process results
444
for (SearchResultEntry entry : result.getSearchEntries()) {
445
System.out.println("DN: " + entry.getDN());
446
System.out.println("CN: " + entry.getAttributeValue("cn"));
447
System.out.println("Mail: " + entry.getAttributeValue("mail"));
448
449
// Handle multi-valued attributes
450
String[] phones = entry.getAttributeValues("telephoneNumber");
451
if (phones != null) {
452
System.out.println("Phones: " + Arrays.toString(phones));
453
}
454
System.out.println("---");
455
}
456
457
} finally {
458
connection.close();
459
}
460
```
461
462
### Advanced Search with SearchRequest
463
464
```java
465
import com.unboundid.ldap.sdk.*;
466
import com.unboundid.ldap.sdk.controls.*;
467
468
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
469
470
try {
471
connection.bind("cn=admin,dc=example,dc=com", "password");
472
473
// Create complex filter
474
Filter complexFilter = Filter.createANDFilter(
475
Filter.createEqualityFilter("objectClass", "inetOrgPerson"),
476
Filter.createPresenceFilter("mail"),
477
Filter.createORFilter(
478
Filter.createSubstringFilter("cn", "John", null, null),
479
Filter.createSubstringFilter("cn", "Jane", null, null)
480
)
481
);
482
483
// Create search request with controls
484
SearchRequest searchRequest = new SearchRequest(
485
"dc=example,dc=com",
486
SearchScope.SUB,
487
complexFilter,
488
"cn", "mail", "telephoneNumber", "department"
489
);
490
491
// Add server-side sorting
492
SortKey[] sortKeys = {
493
new SortKey("department"),
494
new SortKey("cn")
495
};
496
searchRequest.addControl(new ServerSideSortRequestControl(sortKeys));
497
498
// Set limits
499
searchRequest.setSizeLimit(100);
500
searchRequest.setTimeLimit(30);
501
502
// Execute search
503
SearchResult result = connection.search(searchRequest);
504
505
// Check if results were truncated
506
if (result.getResultCode() == ResultCode.SIZE_LIMIT_EXCEEDED) {
507
System.out.println("Results truncated due to size limit");
508
}
509
510
// Process sorted results
511
for (SearchResultEntry entry : result.getSearchEntries()) {
512
System.out.println("Department: " + entry.getAttributeValue("department"));
513
System.out.println("Name: " + entry.getAttributeValue("cn"));
514
System.out.println("Mail: " + entry.getAttributeValue("mail"));
515
System.out.println("---");
516
}
517
518
} finally {
519
connection.close();
520
}
521
```
522
523
### Paged Search Results
524
525
```java
526
import com.unboundid.ldap.sdk.*;
527
import com.unboundid.ldap.sdk.controls.*;
528
529
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
530
531
try {
532
connection.bind("cn=admin,dc=example,dc=com", "password");
533
534
// Initial search request
535
SearchRequest searchRequest = new SearchRequest(
536
"dc=example,dc=com",
537
SearchScope.SUB,
538
"(objectClass=inetOrgPerson)",
539
"cn", "mail"
540
);
541
542
int pageSize = 10;
543
ASN1OctetString cookie = null;
544
int totalEntries = 0;
545
546
do {
547
// Add paged results control
548
searchRequest.setControls(new PagedResultsRequestControl(pageSize, cookie));
549
550
// Execute search
551
SearchResult result = connection.search(searchRequest);
552
totalEntries += result.getEntryCount();
553
554
// Process current page
555
System.out.println("Processing page with " + result.getEntryCount() + " entries");
556
for (SearchResultEntry entry : result.getSearchEntries()) {
557
System.out.println(" " + entry.getAttributeValue("cn"));
558
}
559
560
// Get cookie for next page
561
SimplePagedResultsControl responseControl =
562
SimplePagedResultsControl.get(result);
563
if (responseControl != null) {
564
cookie = responseControl.getCookie();
565
}
566
567
} while ((cookie != null) && (cookie.getValueLength() > 0));
568
569
System.out.println("Total entries processed: " + totalEntries);
570
571
} finally {
572
connection.close();
573
}
574
```
575
576
### Asynchronous Search
577
578
```java
579
import com.unboundid.ldap.sdk.*;
580
import java.util.concurrent.CountDownLatch;
581
582
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
583
584
try {
585
connection.bind("cn=admin,dc=example,dc=com", "password");
586
587
final CountDownLatch latch = new CountDownLatch(1);
588
final List<SearchResultEntry> entries = new ArrayList<>();
589
590
SearchRequest searchRequest = new SearchRequest(
591
"dc=example,dc=com",
592
SearchScope.SUB,
593
"(objectClass=inetOrgPerson)",
594
"cn", "mail"
595
);
596
597
// Perform asynchronous search
598
AsyncRequestID requestID = connection.asyncSearch(
599
searchRequest,
600
new AsyncSearchResultListener() {
601
public void searchEntryReturned(SearchResultEntry entry) {
602
entries.add(entry);
603
System.out.println("Received entry: " + entry.getAttributeValue("cn"));
604
}
605
606
public void searchReferenceReturned(SearchResultReference reference) {
607
System.out.println("Received reference: " +
608
Arrays.toString(reference.getReferralURLs()));
609
}
610
611
public void searchResultReceived(AsyncRequestID requestID, SearchResult result) {
612
System.out.println("Search completed with result code: " +
613
result.getResultCode());
614
latch.countDown();
615
}
616
}
617
);
618
619
// Wait for completion
620
latch.await();
621
System.out.println("Total entries received: " + entries.size());
622
623
} finally {
624
connection.close();
625
}
626
```
627
628
### Persistent Search (Change Notifications)
629
630
```java
631
import com.unboundid.ldap.sdk.*;
632
import com.unboundid.ldap.sdk.controls.*;
633
import java.util.EnumSet;
634
635
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
636
637
try {
638
connection.bind("cn=admin,dc=example,dc=com", "password");
639
640
// Configure persistent search
641
Set<PersistentSearchChangeType> changeTypes = EnumSet.of(
642
PersistentSearchChangeType.ADD,
643
PersistentSearchChangeType.MODIFY,
644
PersistentSearchChangeType.DELETE,
645
PersistentSearchChangeType.MODIFY_DN
646
);
647
648
SearchRequest persistentRequest = new SearchRequest(
649
"ou=people,dc=example,dc=com",
650
SearchScope.SUB,
651
"(objectClass=inetOrgPerson)",
652
"cn", "mail", "telephoneNumber"
653
);
654
655
// Add persistent search control
656
persistentRequest.addControl(new PersistentSearchRequestControl(
657
changeTypes,
658
true, // changes only (don't return existing entries)
659
true // return entry change notification control
660
));
661
662
// Start persistent search
663
AsyncRequestID requestID = connection.asyncSearch(
664
persistentRequest,
665
new AsyncSearchResultListener() {
666
public void searchEntryReturned(SearchResultEntry entry) {
667
// Check for change notification control
668
EntryChangeNotificationControl changeControl =
669
entry.getControl(EntryChangeNotificationControl.class);
670
671
if (changeControl != null) {
672
System.out.println("Change detected:");
673
System.out.println(" Type: " + changeControl.getChangeType());
674
System.out.println(" DN: " + entry.getDN());
675
System.out.println(" Change Number: " + changeControl.getChangeNumber());
676
677
if (changeControl.getPreviousDN() != null) {
678
System.out.println(" Previous DN: " + changeControl.getPreviousDN());
679
}
680
}
681
}
682
683
public void searchReferenceReturned(SearchResultReference reference) {
684
// Handle referrals if needed
685
}
686
687
public void searchResultReceived(AsyncRequestID requestID, SearchResult result) {
688
System.out.println("Persistent search ended: " + result.getResultCode());
689
}
690
}
691
);
692
693
System.out.println("Persistent search started. Monitoring for changes...");
694
695
// Keep running until interrupted
696
Thread.sleep(60000); // Monitor for 1 minute
697
698
// Cancel persistent search
699
connection.abandon(requestID);
700
701
} finally {
702
connection.close();
703
}
704
```
705
706
### Using Entry Source for Large Result Sets
707
708
```java
709
import com.unboundid.ldap.sdk.*;
710
711
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
712
713
try {
714
connection.bind("cn=admin,dc=example,dc=com", "password");
715
716
// Create entry source for memory-efficient processing
717
LDAPEntrySource entrySource = new LDAPEntrySource(
718
connection,
719
"dc=example,dc=com",
720
SearchScope.SUB,
721
"(objectClass=inetOrgPerson)",
722
"cn", "mail", "department"
723
);
724
725
try {
726
Entry entry;
727
int count = 0;
728
729
// Process entries one at a time
730
while ((entry = entrySource.nextEntry()) != null) {
731
count++;
732
733
System.out.println("Processing entry " + count + ": " +
734
entry.getAttributeValue("cn"));
735
736
// Process entry without loading all results into memory
737
String department = entry.getAttributeValue("department");
738
if ("Engineering".equals(department)) {
739
// Do something with engineering entries
740
System.out.println(" Engineering employee: " +
741
entry.getAttributeValue("mail"));
742
}
743
744
// Prevent memory issues with very large result sets
745
if (count % 1000 == 0) {
746
System.out.println("Processed " + count + " entries so far...");
747
}
748
}
749
750
System.out.println("Total entries processed: " + count);
751
752
} finally {
753
entrySource.close();
754
}
755
756
} finally {
757
connection.close();
758
}
759
```
760
761
### Complex Filter Construction
762
763
```java
764
import com.unboundid.ldap.sdk.*;
765
766
// Build complex filters programmatically
767
Filter complexFilter = Filter.createANDFilter(
768
// Must be a person
769
Filter.createEqualityFilter("objectClass", "inetOrgPerson"),
770
771
// Must have an email address
772
Filter.createPresenceFilter("mail"),
773
774
// Either in Engineering or Marketing department
775
Filter.createORFilter(
776
Filter.createEqualityFilter("department", "Engineering"),
777
Filter.createEqualityFilter("department", "Marketing")
778
),
779
780
// Name starts with A-M (first half of alphabet)
781
Filter.createORFilter(
782
Filter.createSubstringFilter("cn", "A", null, null),
783
Filter.createSubstringFilter("cn", "B", null, null),
784
Filter.createSubstringFilter("cn", "C", null, null),
785
Filter.createSubstringFilter("cn", "D", null, null),
786
Filter.createSubstringFilter("cn", "E", null, null),
787
Filter.createSubstringFilter("cn", "F", null, null),
788
Filter.createSubstringFilter("cn", "G", null, null),
789
Filter.createSubstringFilter("cn", "H", null, null),
790
Filter.createSubstringFilter("cn", "I", null, null),
791
Filter.createSubstringFilter("cn", "J", null, null),
792
Filter.createSubstringFilter("cn", "K", null, null),
793
Filter.createSubstringFilter("cn", "L", null, null),
794
Filter.createSubstringFilter("cn", "M", null, null)
795
),
796
797
// Exclude disabled accounts
798
Filter.createNOTFilter(
799
Filter.createEqualityFilter("accountStatus", "disabled")
800
)
801
);
802
803
System.out.println("Complex filter: " + complexFilter.toString());
804
805
// Use the filter in a search
806
LDAPConnection connection = new LDAPConnection("ldap.example.com", 389);
807
try {
808
connection.bind("cn=admin,dc=example,dc=com", "password");
809
810
SearchResult result = connection.search(
811
"dc=example,dc=com",
812
SearchScope.SUB,
813
complexFilter,
814
"cn", "mail", "department"
815
);
816
817
System.out.println("Found " + result.getEntryCount() + " matching entries");
818
819
} finally {
820
connection.close();
821
}
822
```