Sorted LDAP Search Results

These routines are used to perform sorting of entries returned from the server following an LDAP search operation.

The ldap_create_sort_keylist() function builds a list of LDAPsortkey structures based on the list of attributes included in the incoming string. A sort key is made up of three possible values:

The syntax of the attributes in the sortString, [-]<attribute name>[:<matching rule OID>], specifies whether or not there is a matching rule OID that must be used for the attribute, and whether or not the attribute must be sorted in reverse order. Note that IBM® Tivoli® Directory Server does not support the use of matching rules in sorted search requests. However, this feature can be used with other LDAP servers that do support matching rules. In the following example sortString, the search results are sorted first by surname and then by given name, with the given name being sorted in reverse (descending order) as specified by the prefixed minus sign ( - ):

     sn -givenname

Thus, the syntax of the sort parameter is as follows:

     [-]<attribute name>[:<matching rule OID>]

where

The sortKeyList, output from the ldap_create_sort_keylist() function, can be used as input into the ldap_create_sort_control() function. The sortKeyList is an ordered array of LDAPsortkey structures such that the key with the highest precedence is at the front of the array. The control output form ldap_create_sort_control() function includes the criticality set based on the value of the isCritical flag. This control is added to the list of client controls sent to the server on the LDAP search request.

The ldap_free_sort_keylist() function cleans up all the memory used by the sort key list. This function must be called after the ldap_create_sort_control() function has completed.

When a sort results control is returned by the server, the ldap_parse_sort_control() function can be used to retrieve the values from the control. The function takes as input the server controls returned by the server, and returns the value of the sort control return code and possibly an attribute name if the return code is not LDAP_SUCCESS. If there was an error parsing the sort criteria for the search or there were no entries returned for the search, no sort control is returned to the client.

Server side sorting of search results

Sorted Search Results provides sort capabilities for LDAP clients that have limited or no sort functionality. Sorted Search Results enables an LDAP client to receive sorted search results based on a list of criteria, where each criteria represents a sort key. The sort criteria includes attribute types, matching rules, or descending order. The server must use this criteria to sort search results before returning them. This moves the responsibility of sorting from the client application to the server, where it might be done much more efficiently. For example, a client application might want to sort the list of employees at their Grand Cayman site by surname, common name, and telephone number. Instead of building the search list twice so it can be sorted (once at the server and then again at the client when all the results are returned), the search list is built once, and then sorted, before returning the results to the client application.

In the following example sortString, the search results are sorted first by surname (sn), then by given name (givenname), with the given name being sorted in reverse (descending) order as specified by the prefixed minus sign ( - ).

     sn -givenname

The sortKeyList output from ldap_create_sort_keylist() can be used as input to ldap_create_sort_control(). The sortKeyList is an ordered array of LDAPsortkey structures such that the key with the highest precedence is at the front of the array. ldap_create_sort_control() outputs a LDAPControl structure which can be added to the list of client controls sent to the server on the LDAP search request. The LDAPControl structure returned by the ldap_create_sort_control() API can be used as input to ldap_search_ext() or ldap_search_ext_s(), which are used to make the actual search request.

Note:
Server side sorting is an optional extension of the LDAP v3 protocol, so the server you have bound to prior to the ldap_search_ext() or ldap_search_ext_s() call might not support this function.

Now that you have created the server side control, you can free the sortKeyList output from ldap_create_sort_keylist() using ldap_free_sort_keylist().

Upon completion of the search request you submitted using ldap_search_ext() or ldap_search_ext_s(), the server returns an LDAP result message that includes a sort results control. The client application can parse this control using ldap_parse_sort_control() which takes the returned server response controls (a null terminated array of pointers to LDAPControl structures) as input. ldap_parse_sort_control() outputs a return code that indicates whether or not the sort request was successful. If the sort was not successful, the name of the attribute in error might be output from ldap_parse_sort_control(). Use ldap_controls_free() to free the memory used by the client application to hold the server controls when you are done processing all controls returned by the server for this search request.

The server returns a successful return code of LDAP_SUCCESS in the sort response control (sortKeyResponseControl) in the search result (searchResultDone) message if the server supports sorting and can sort the search results using the specified keys. If the search fails for any reason or there are no search results, then the server omits the sortKeyResponseControl from the searchResultsDone message.

If the server does not support sorting and the criticality specified on the sort control for the search request is TRUE, the server does not return any search results, and the sort response control return code is set to LDAP_UNAVAILABLE_CRITICAL_EXTENSION. If the server does not support sorting and the criticality specified on the sort control for the search request is FALSE, the server returns all search results and the sort control is ignored.

If the server does support sorting and the criticality specified on the sort control for the search request is TRUE, but for some reason the server cannot sort the search results, then the sort response control return code is set to LDAP_UNAVAILABLE_CRITICAL_EXTENSION and no search results are returned. If the server does support sorting and the criticality specified on the sort control for the search request is FALSE, and for some reason the server cannot sort the search results, then the sort response control return code is set to the appropriate return code and all search results are returned unsorted.

The following return codes might be returned by the server in the sortKeyResponseControl of the searchResultDone message:

There are other rules that must be taken into consideration when requesting sort from the server, These rules include the following:

When sorted search is requested along with simple paged results, the sortKeyResponseControl is returned on every searchResultsDone message, not just the last one of the paged results request. Of course, the sortKeyResponseControl might not be returned if there is an error processing the paged results request or there are no search results to return. Additionally, when sorted search is requested along with simple paged results, the server sends the search results sorted based on the entire search result set and does not simply sort each page. See Simple paged results of search results for more information.

When chasing referrals, the client application must send in a sorted search request to each of the referral servers. It is up to the application using the client's services to decide whether or not to set the criticality as to the support of sorted search results, and to handle a lack of support of this control on referral servers as appropriate based on the application. Additionally, the LDAP server does not ensure that the referral server supports the sorted search control. Multiple lists might be returned to the client application, some of which are not sorted. It is the client application's decision as to how best to present this information to the end user. Possible solutions include:

The client application must turn off referrals to get one truly sorted list; otherwise, when chasing referrals with the sorted search control specified, unpredictable results can occur.

More information about the server side sorted search control, with control OID of 1.2.840.113556.1.4.473, can be found in RFC 2891 - LDAP Control Extension for Server Side Sorting of Search Results.

Related Information


Example

The following example shows how to use the LDAP sorted search APIs.

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <ldap.h>

static char ibmid[] = "Copyright IBM Corporation 2003 LICENSED MATERIAL - PROGRAM PROPERTY OF IBM";
#define         BIND_DN "cn=administrator"
#define         BIND_PW "adminpwd"

char             *server, *base, *filter, *scopes[] = { "BASE", "ONELEVEL", "SUBTREE" };
int              scope;
LDAP             *ld;

int main (int argc, char *argv[])
{
   int           l_rc, l_entries, l_port, l_entry_count=0, l_search_count=0, l_errcode=0; 
   char          *sortString, sortCriticality = 'T', *l_dn, *attrInError = NULL;
   unsigned long sortRC=0;
   LDAPMessage   *l_result, *l_entry;
   LDAPsortkey   **sortKeyList = NULL;
   LDAPControl   *sortControl = NULL, *M_controls[2] = { NULL, NULL }, **returnedControls = NULL;

   /******************************************************************/
   /* Check input parameters                                         */
   /*                                                                */
   if (argc < 5)
   {
      printf("The input parameters are as follows:\n");
      printf("     1. Search base\n");
      printf("     2. Filter\n");
      printf("     3. Search Scope (0=base, 1=onelevel, OR 2=subtree)\n");
      printf("     4. Sorting Attribute  Ex: for  ascending order on the sn attribute; use  \"sn\"\n");
      printf("                               for descending order on the sn attribute; use \"-sn\"\n");
   return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Set default values: Server and Port. And then parse            */
   /* input parameters into program variables                        */
   /*                                                                */
   server   = NULL;
   l_port     = LDAP_PORT;

   base     = argv[1];
   filter   = argv[2];
   scope    = atoi(argv[3]);
   sortString = argv[4];
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Initialize an LDAP session                                     */
   /*                                                                */     
   ld = ldap_init(server, l_port);
   /* Check if connection is OK                                      */
   if (ld == NULL)
   {
      printf("==Error==");
      printf("  Init of server %s at port %d failed.\n", server, l_port);
      return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Bind as the ldap administrator                                 */
   /*                                                                */
   l_rc = ldap_simple_bind_s (ld, BIND_DN, BIND_PW);
   if ( l_rc != LDAP_SUCCESS)
   {
      printf("==Error== %s");
      printf("  Unable to Bind to the LDAP server.  Return code is %d.\n", l_rc);
      return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Create the sortKeyList structure with sort key values used     */
   /* when sorting search result. The structure is needed for the    */
   /* ldap_create_sort_control API.                                  */
   /*                                                                */
   /*                                                                */
   l_rc = ldap_create_sort_keylist(&sortKeyList, sortString);

   if (l_rc != LDAP_SUCCESS)
   {
      printf("==Error==");
      printf("  Failure during a create_sort_keylist.  Return code is %d.\n",l_rc);
      ldap_unbind(ld);
      return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Create the control for sorting to be used by the search using: */
   /* the ld pointer created by ldap_init API,                       */
   /* sortKeyList created by the ldap_create_sort_keylist API,       */
   /* sortCriticality defined to be True.                            */
   /* The return is a sortControl that is used in the                */
   /* ldap_search_ext_s API                                          */
   l_rc = ldap_create_sort_control(ld, sortKeyList, sortCriticality, &sortControl);
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Free all Memory used by the Sort Key List                      */
   /* This API must be called after the ldap_create_sort_control()   */
   /* function has completed                                         */
   ldap_free_sort_keylist(sortKeyList);
   if (l_rc != LDAP_SUCCESS)
   {
      printf("==Error==");
      printf("  Failure during a create_sort_control.  Return code is %d.\n",l_rc);
      ldap_unbind(ld);
      return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Search for entries in the directory using the parmeters        */
   /*                                                                */
   M_controls[0] = sortControl;
   l_rc = ldap_search_ext_s(ld, base, scope, filter, NULL, 0, M_controls, NULL, NULL, 0, &l_result);
   if ((l_rc != LDAP_SUCCESS) & (l_rc != LDAP_PARTIAL_RESULTS))
   {
      printf("==Error==");
      printf("  Failure during a search.  Return code is %d.\n",l_rc);
      printf("  The search parms were:\n");
      printf("       base: %s\n",base);
      printf("      scope: %s\n",scopes[scope]);
      printf("     filter: %s\n",filter);
      ldap_unbind(ld);
      return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Parse the result to get the returned controls                  */
   /*                                                                */
   l_rc = ldap_parse_result(ld, l_result, &l_errcode, NULL, NULL, NULL, &returnedControls, LDAP_FALSE);

   if ((l_rc != LDAP_SUCCESS) | (l_errcode != LDAP_SUCCESS))
   {
      printf("==Error==");
      printf("  Failure during parse result.  Return code is %d.\n",l_rc);
      printf("                                Error code is %d.\n",l_errcode);
      ldap_unbind(ld);
      return 0;
   }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Parse the sort control returned, looking for any errors        */
   /*                                                                */
   if (returnedControls != NULL)
   {
      l_rc = ldap_parse_sort_control(ld, returnedControls, &sortRC, &attrInError);

      if ((l_rc != LDAP_SUCCESS) | (sortRC != LDAP_SUCCESS))
      {
         printf("==Error==");
         printf("  Failure during parse sort control.  Return code is %d.\n", l_rc);
         printf("                                      Sort return code is %d.\n", sortRC);
         printf("                ld: %p\n", ld);
         printf("  returnedControls: %p\n", returnedControls);
         printf("       attrInError: %p\n", attrInError);
         ldap_unbind(ld);
      return 0;
      }
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Do the required cleanup                                        */
   /*                                                                */
      ldap_controls_free(returnedControls);
      returnedControls = NULL;

      if (attrInError != NULL)
      {
         free(attrInError);
         attrInError = NULL;
      }
   }

   M_controls[0] = NULL;
   ldap_control_free(sortControl);
   sortControl = NULL;
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Disply the returned result                                     */
   /*                                                                */
   /* Determine how many entries have been found.                    */
   l_entries = ldap_count_entries(ld, l_result);
    
   if (l_entries > 0)
   {
      l_entry_count = l_entry_count + l_entries;
   }

   printf("  The search parms were:\n");
   printf("        base: %s\n",base);
   printf("       scope: %s\n",scopes[scope]);
   printf("      filter: %s\n",filter);
   printf("    sortAttr: %s\n",sortString);
   printf("  The sorted entries returned were:\n");

   for ( l_entry = ldap_first_entry(ld, l_result);
         l_entry != NULL;
         l_entry = ldap_next_entry(ld, l_entry) )
   {
      l_dn = ldap_get_dn(ld, l_entry);
      printf("    %s\n",l_dn);
   }

   /* Free the search results.                                       */
   ldap_msgfree(l_result);
        
   printf("\n  %d entries found during the search",l_entry_count);
   /*                                                                */
   /******************************************************************/

   /******************************************************************/
   /* Close the LDAP session                                         */
   /*                                                                */
   ldap_unbind(ld);
   /*                                                                */
   /******************************************************************/

   return 0;
}

[ Back to top | LDAP APIs | APIs by category ]