Sun Directory Server Enterprise Edition 7.0 Developer's Guide

Indexing Entries According to a Matching Rule

This section covers how to provide plug-in support for a directory index that is based on a matching rule. Matching rule plug-ins are not required to enable indexing for the matching rules the plug-ins support.


Note –

Directory Server can use your plug-in for extensible match searches even if you provide no indexing capability.

Without support for indexing, however, Directory Server must generate search results from the entire directory, which severely impacts search performance.


Directory indexes speed up client searches. Directory Server can build a list of search result entries by looking through the index rather than the entire directory. When directories contain many entries, indexes offer a considerable search performance boost. Directory administrators therefore configure the directory to maintain indexes for attributes that are searched regularly.

How Directory Server Handles the Index

To maintain an index for a custom matching rule supported by your plug-in, the server requires an indexer function. The indexer function is capable of translating attribute values to index keys. Directory Server calls the indexer function to create the index, and subsequently when adding, modifying, or deleting attribute values, because such changes can affect the index.

To read an index for a custom matching rule, Directory Server requires a filter index function. The filter factory function provides a pointer to this function. Refer to Handling Extensible Match Filters and Filter Index Function for details.

Directory Server relies on the indexer factory function in your plug-in to set up an indexing object and provide a pointer to the appropriate indexer function.

The following figure shows how Directory Server performs indexing based on a matching rule.

Figure 10–4 Directory Server Maintaining an Index Using a Matching Rule

Flow diagram shows Directory Server calling the indexer
function to generate index keys.

    The following summarizes the process shown in Figure 10–4.

  1. Directory Server creates a parameter block, setting parameters to indicate the matching rule OID and attribute type to match. Directory Server then calls the indexer factory function.

  2. The indexer factory function examines the parameter block and allocates an indexer object if necessary. The function also sets pointers to the indexer and filter index functions. If necessary, the function sets a pointer to a destructor to free the indexer object before returning control to Directory Server.

  3. Directory Server sets the parameter block to indicate attribute values to translate to keys and calls the indexer function.

  4. The indexer function translates values to keys. After the indexer returns, Directory Server uses the keys to update the index.

  5. Directory Server frees the parameter block. If necessary, the server frees the indexer object by using the destructor provided.

Be aware that Directory Server can call indexer factory functions concurrently. Indexer factory functions must therefore be thread safe.

The main filter functions are as follows:

Indexer Function

An indexer function takes a parameter block from Directory Server that contains a pointer to a berval array of attribute values. The function then generates a berval array of corresponding keys from the values. Finally, the function sets a pointer in the parameter block to the keys.

The following figure shows how Directory Server uses the indexer function.

Figure 10–5 Indexer Function Context

Flow diagram shows Directory Server allocating a PBlock
for the search, finding and returning results, and freeing the PBlock.

The following example shows the indexer function for the case exact matching rule where the keys are the same as the values.


Example 10–7 Indexer Function (matchingrule.c)

#include "slapi-plugin.h"

typedef struct plg_filter_t            /* For manipulating filter obj. */
{   char          *  f_type;           /* Attribute type to match      */
    int              f_op;             /* Type of comparison
                                        * (<, <=, ==, >=, >, substr)
                                        * for the filter.              */
    struct berval ** f_values;         /* Array of values to match     */
} plg_filter_t;

static int
plg_index_entry(Slapi_PBlock * pb)
{
    int              rc     = LDAP_OPERATIONS_ERROR;
    void          *  obj    = NULL;    /* Server lets the plug-in      */
    plg_filter_t  *  fobj;             /* handle the object type.      */
    struct berval ** values;           /* Values from server           */
        
    if (slapi_pblock_get(pb, SLAPI_PLUGIN_OBJECT, &obj) == 0) {
        fobj = (plg_filter_t *)obj;
        if (
            (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_VALUES, &values) == 0) &&
            (values != NULL)
        ) {
                
            /* The case exact match builds the index keys
             * from the values by copying the values because
             * the keys and values are the same.
             *
             * Your matching rule may do something quite
             * different before setting the keys associated
             * with the values in the parameter block.                 */
            rc = slapi_pblock_set(     /* Set keys based on values.    */
                pb,
                SLAPI_PLUGIN_MR_KEYS,
                slapi_ch_bvecdup(values)
            );
        }
    }

    return rc;
}

Here, obj points to the indexer object, and values points to the berval array of attribute values. Notice that the function returns LDAP_OPERATIONS_ERROR on failure. Technically, LDAP_OPERATIONS_ERROR indicates bad sequencing of LDAP operations. For historical reasons, the error is used in this context to indicate an internal error.


Input Parameters for Indexers

An indexer function can get values for the following from the parameter block:

Output Parameter for Indexers

An indexer function should generate the berval array of keys. The function should set SLAPI_PLUGIN_MR_KEYS in the parameter block before returning control to Directory Server. Refer to Part II, Directory Server Plug-In API Reference for details.

Thread Safety and Indexers

Directory Server never calls an indexer for the same indexer object concurrently. Directory Server can, however, call the function concurrently for different indexer objects. If you use global variables, ensure that your function handles such variables safely.

Indexer Factory Function

The indexer factory function takes a parameter block from Directory Server. The function sets parameters such that Directory Server can update an index or sort results based on a matching rule.

The following figure shows how Directory Server uses the indexer factory function.

Figure 10–6 Indexer Factory Function Context

Flow diagram shows Directory Server calling the indexer
factory function to create and indexer object.

The following example shows the indexer factory function for the case exact matching rule.


Example 10–8 Indexer Factory Function (matchingrule.c)

#include "slapi-plugin.h"

#define PLG_OID       "1.3.6.1.4.1.42.2.27.999.4.1"
#define SUBSYS        "CaseExactMatching Plugin"

/* Functions to obtain connection information for logging.             */
static long mypblock_get_msgid( Slapi_PBlock * pb);
static int  mypblock_get_connid(Slapi_PBlock * pb);
static int  mypblock_get_opid(  Slapi_PBlock * pb);

static int
plg_indexer_create(Slapi_PBlock * pb)
{
    int    rc     = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; /* Init failed*/
    char * mr_oid = NULL;              /* MR OID from the server       */
    
    if (slapi_pblock_get(pb, SLAPI_PLUGIN_MR_OID, mr_oid) ||
        (mr_oid == NULL)) {
        slapi_log_error_ex(
            -1, /* errorId */
            mypblock_get_msgid(pb),
            mypblock_get_connid(pb),
            mypblock_get_opid(pb),
            SUBSYS,
            SLAPI_INTERNAL_ERROR,
            "plg_indexer_create failed: NULL OID values are invalid.\n"
        );
    } else if (strcmp(mr_oid, PLG_OID) == 0) {
        if ( /* The MR OID from the server is handled by this plug-in. */
            (slapi_pblock_set(         /* This is for completeness.    */
                pb,                    /* Your plug-in may set a       */
                SLAPI_PLUGIN_MR_OID,   /* different OID than the one   */
                PLG_OID                /* provided by the server.      */
            ) == 0) &&
            (slapi_pblock_set(         /* Provide an appropriate       */
                pb,                    /* indexer function pointer.    */
                SLAPI_PLUGIN_MR_INDEX_FN,
                (void *)plg_index_entry
            ) == 0) &&
            (slapi_pblock_set(         /* Set the object destructor.   */
                pb,
                SLAPI_PLUGIN_DESTROY_FN,
                (void *)plg_filter_destroy
            ) == 0)) {
                rc = LDAP_SUCCESS;
        } else {
            slapi_log_error_ex(
                -1, /* errorId */
                mypblock_get_msgid(pb),
                mypblock_get_connid(pb),
                mypblock_get_opid(pb),
                SUBSYS,
                SLAPI_INTERNAL_ERROR,
                "plg_indexer_create failed %d \n", rc
            );
        }
    }
    
    return rc;
}

Here, PLG_OID is the object identifier for the matching rule. plg_index_entry() is the indexer. plg_filter_destroy() is the destructor. Notice that the function returns LDAP_UNAVAILABLE_CRITICAL_EXTENSION on failure.


Input Parameters for Indexer Factory Functions

An indexer factory function can read the values for the following from the parameter block:

Output Parameters for Indexer Factory Functions

An indexer factory function should set values for at least the following in the parameter block before returning control to Directory Server:

Refer to Chapter 17, Parameter Block Reference for details about the parameter block.

Thread Safety and Indexer Factory Functions

This function must be thread safe. Directory Server can call this function concurrently.

Indexer Object Destructor Function

An indexer object destructor function frees memory that was allocated for an indexer object set up by the indexer factory function. Directory Server calls the destructor after an index operation completes.

Indexer object destructors take a parameter block as their only argument, as do filter object destructors. The parameter block holds a pointer to the object in SLAPI_PLUGIN_OBJECT. Refer to the Filter Object Destructor Function for details.

Directory Server never calls a destructor for the same object concurrently.