blob: 317928b75b7358abfaab4637c112df378276dde4 [file] [log] [blame]
Title: 2.3 - Searching
NavPrev: 2.2-binding-unbinding.html
NavPrevText: 2.2 - Binding and unbinding
NavUp: 2-basic-ldap-api-usage.html
NavUpText: 2 - Basic LDAP API usage
NavNext: 2.4-adding.html
NavNextText: 2.4 - Adding entries
Notice: Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
.
http://www.apache.org/licenses/LICENSE-2.0
.
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
# 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 the processing while looking for entries, the client must provide information to get accurate results.
The idea is to define a search **API** which is easy to use in the simplest cases, but provides all the capability to send complex search requests.
## Simple search
Let's first look at a simple search. To process a search we need a starting point in the tree, a filter, and a scope. Here's an example:
:::java
@Test
public void testSearch() throws Exception
{
EntryCursor cursor = connection.search( "ou=system", "(objectclass=*)", SearchScope.ONELEVEL );
for ( Entry entry : cursor )
{
assertNotNull( entry );
System.out.println( entry );
}
cursor.close();
}
In this example, the _connection_ has been previously created. We search for all entries starting at *ou=system* along with its children, which have an _ObjectClass_ attribute (all the entries have such an attribute, so we should get back all the entries). The scope (_ONELEVEL_) searches one level under the starting base.
A cursor of entries is returned, which can be iterated over. Every call to the _getEntry()_ method returns the next entry in the LDAP result set.
That's 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 remains in memory forever (causing a memory leak)! Best practice is to use try-with-resources statement.
### Searching using a DN
In the previous sample, we used a String to define the starting point of the search. Sometimes it's convenient to start a search with a DN (i.e. because you got the DN from another operation). In this case, no need to transform the DN into a String before doing your search, simply use the DN!
:::java
@Test
public void testSearchWithDn() throws Exception
{
Dn systemDn = new Dn( "ou=system" );
EntryCursor cursor = connection.search( systemDn, "(objectclass=*)", SearchScope.ONELEVEL );
for ( Entry entry : cursor )
{
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 : return the entry for a given DN, if it exists. Note that you could use `LdapConnection.lookup` if the filter is irrelevent.
* SearchScope.ONELEVEL : return all elements below the current DN, but not the element associated with the DN.
* SearchScope.SUBLEVEL : return 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 are targeted. There are various possibilities to construct a filter, using one or more connectors, and one or more expression nodes.
Connectors use a prefix notation, followed by as many expression nodes as necessary, like in (& (node1) (node2) ... (nodeN))
Expression nodes are always contained within 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 : the entry 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 node : the entry should match a substring
>**Note:** As of Apache DS 2.0, we don't support approx matches nor extensible matches.
## More complex searches
Sometimes, you want to have more control over the search operation, either with the parameters in use, or the results that are returned.
A search can return other things than entries. In fact, you can get four different kinds of responses:
* When it's a normal entry, you will receive a SearchResultEntry
* 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 search request, or request some specific AttributeType to be returned. The parameters that may be injected into a SearchRequest are as follows:
* 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 cases, you should fill the _SearchRequest_ message and send it to the server:
:::Java
@Test
public void testSearchWithSearchRequest() throws Exception
{
// 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();
System.out.println( resultEntry );
}
}
}
As we can see, the response is different, we are returned a _SearchCursor_ instance, and we must verify the response type before being able to process it.