2.3 - Searching (...)

Searching is the most important operation in LDAP. It has to be fast, very fast. On the other hand, as the server does not a lot of processing while looking for entries, the client has to provide many information in order to get some accurate results.

The idea is to define a search API which is easy to use in the simplest cases, but provides all the necessary bolts if you need to send complex search requests.

Let's first look at a simple search. What we basically need to process a search is a starting point in the tree, a filter, a scope. Here is an example :

EntryCursor cursor = connection.search( "ou=system", "(objectclass=*)", SearchScope.ONELEVEL );

while ( cursor.next() )
{
    Entry entry = cursor.getEntry();
    assertNotNull( entry );
    System.out.println( entry );
}

cursor.close();

In this example, the connection has been previously created. We just search for all the entries starting at ou=system and their children, which have an ObjectClass attribute (all the entries have such an attribute, so we should get back all the entries). The scope (ONELEVEL) says we just search one level under the starting base.

We get back a cursor on entries, which can be walked forward. Every call to the getEntry() method will return an entry.

This is pretty much it !

But this is not really enough , there are many possible options.

Note Don't forget to close the cursor, otherwise the associated data will remain in memory foreever !

Searching using a DN

In the previous sample, we used a String to define the starting point of the search. Sometime, you will find it more convenient to start a search using a DN (for instance because you got back a DN from an other operation). In this case, no need to transform the DN into a String before doing your search : simply use the DN !

DN systemDn = new Dn( "ou=system" );
...
EntryCursor cursor = connection.search( systemDn, "(objectclass=*)", SearchScope.ONELEVEL );

while ( cursor.next() )
{
    Entry entry = cursor.getEntry();
    assertNotNull( entry );
    System.out.println( entry );
}

cursor.close();

This is it !

Scope

There are three different different scopes you can use to search for data : * SearchScope.OBJECT : you will just get back the entry for the given DN, if it exists. Note that you could use a lookup if the filter is irrelevent. * SearchScope.ONELEVEL : you will get back all the elements just under the current DN, but not the element associated with the DN. * SearchScope.SUBLEVEL : you will get back all the elements starting from the given DN, including the element associated with the DN, whatever the depth of the tree.

Filter

The filter is used to define the elements that will be selected. There are various possibilities to construct a filter, using one or more connectors,a nd one or more expression nodes.

Connrectors are using a prefix notation, followed by as many expression node as necessary, like in (& (node1) (node2) ... (nodeN))

Expression nodes are always contane din parenthesis, with a left part - the attributeType and a right part - the value -.

Here is the list of possible connectors :

* & : n-ary AND connector, all the nodes must evaluate to TRUE
* | : n-ary OR connector, at least one of the node must evaluate to true
* ! : 1-ary NOT connector : the node must evaluate to false

And here is the list of possible expression nodes, assuming that an expression node :

* = Equality expression node : the selected entry matches the right part
* =* Presence expression node : tehentry has the Attribute on the left side
* >= Superior expression node : the entry should be superior to the right part
* <= Inferior expression node : the entry should be superior to the right part
* = [start][*][middle][*][final] Substring expression nose : the entry should match a usbstring

Note: As of Apache DS 2.0, we don't support approx matches not extensible matches.

More complex searches

Sometime, you way want to have more control on the search opetation, either on the parameters you want to use, or on the results you get.

A search can return something else than an entry. In fact, you can get three different kind of response

* When it's done, you will receive a SearchResultDone
* When the response is a reference to another entry, you will get a SearchResultReference
* In some cases, you may also receive an IntermediateResponse.

You may also add a Control to the searchRequest, or request some specific AttributeType to be returned. The parameter you can inject into a SearchRequest are the following :

* The base DN 
* The filter
* The Scope (one of OBJECT, ONELEVEL or SUBTREE)
* The size limit
* The time limit
* The list of attributes to return
* The alias dereferencing mode (one of DEREF_ALWAYS, DEREF_FINDING_BASE_OBJ, DEREF_IN_SEARCHING or NEVER_DEREF_ALIASES)
* The TypesOnly flag (to get the AttributeTypes but no values)
* The controls

In both case, you should fill a SearchRequest message and send it to the server :

// Create the SearchRequest object
SearchRequest req = new SearchRequestImpl();
req.setScope( SearchScope.SUBTREE );
req.addAttributes( "*" );
req.setTimeLimit( 0 );
req.setBase( new Dn( "ou=system" ) );
req.setFilter( "(cn=user1)" );

// Process the request
SearchCursor searchCursor = connection.search( req );

while ( searchCursor.next() )
{
    Response response = searchCursor.get();

    // process the SearchResultEntry
    if ( response instanceof SearchResultEntry )
    {
        Entry resultEntry = ( ( SearchResultEntry ) response ).getEntry();
        ...
    }
}

As we can see, the response is different : we get back a SearchCursor instance, and we have to check the response type before being able to process it.