1.4 Search Requests

eDirectory provides a powerful and useful searching capability that allows you to retrieve information based on your specified searching criteria. For example, you can perform a search that allows your application to

A search request must include not only search criteria, but also the area to be searched and the amount of information to be returned for each matching object. The sections below describe the following aspects of setting up search requests:

Remember, if you are creating a client application, you must initialize the Unicode table with either the NWCallsInit or the NWInitUnicodeTables function before starting the search.

Also, you must have a valid context handle that you have set to the container in the eDirectory tree from which you want the search to begin. The context handle's keys and flags determine how the search handles aliases and how object names are returned. For more information, see Section 1.1, Context Handles.

For a code example, see ndssearc.c.

1.4.1 Buffers Needed for eDirectory Searches

The NWDSSearch function requires three buffers: two input buffers (called NameBuffer and FilterBuffer in this example) and an output buffer (called ResultBuffer). The purpose of these buffers is summarized in the list that follows.

NameBuffer

Restricts the search results to certain attributes only. A Name Buffer is not required if you want to return all attribute information or no attribute information.

FilterBuffer

Contains the search expression. If no filtering is needed, construct a filter equivalent to objectclass=*.

ResultBuffer

Stores the search results.

The FilterBuffer determines what eDirectory searches for. The NameBuffer determines what information is returned about the objects that match the search criteria. The ResultBuffer stores this information.

You need to allocate memory space for all three buffers and verify that memory is allocated successfully. The following code segment demonstrates how this is done. The data structure for these buffers is Buf_T, which is defined in nwdsbuft.h.

  retcode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN,&NameBuffer); 
  retcode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN,&FilterBuffer); 
  retcode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN,&ResultBuffer);
  

Since NameBuffer and FilterBuffer are input buffers, they need to be initialized. The output buffer does not need to be initialized.

The following code initializes NameBuffer to the DSV_SEARCH operation.

  retcode = NWDSInitBuf (context, DSV_SEARCH, NameBuffer);
  

The requested attributes should be stored in NameBuffer. The following code makes a request to return a match when the Surname and Telephone attributes have values.

  retcode = NWDSPutAttrName (context, NameBuffer, "Surname"); 
  retcode = NWDSPutAttrName (context, NameBuffer, "Telephone");
  

FilterBuffer is initialized to the DSV_SEARCH_FILTER operation:

  retcode = NWDSInitBuf (context, DSV_SEARCH_FILTER, FilterBuffer);
  

Finally, you should allocate a filter cursor which initializes the cursor to the current insertion point. The data structure for the filter cursor is Filter_Cursor_T, which is defined in nwdsfilt.h.

  retcode = NWDSAllocFilter (&FilterCursor);
  

1.4.2 Search Filter Components

You search for information in eDirectory by first building a search filter and then putting the filter into the filter buffer. The search filter describes the set of conditions that satisfy the search. To create a search filter, use the following functions:

NWDSAllocFilter

Allocates space for the filter.

NWDSAddFilterToken

Adds search criteria to the allocated filter.

NWDSPutFilter

Places the finished search filter into an input filter buffer. This function normally frees the space that has been allocated for the filter.

NWDSFreeFilter

Frees the space allocated for the filter if the NWDSPutFilter function is not called or fails.

When you allocate a search filter by calling the NWDSAllocFilter function, you receive a filter cursor. The filter cursor is initialized to the current insertion point. It is empty until you add search filter information. The cursor is used as input as you add the search filter information.

A search filter is made up of a tree of filter nodes. Each node consists of the following three parts:

  • Token

  • Value

  • Syntax

Token. The token determines what can be in the other parts of the node. A token can be one of the following:

FTOK_ANAME

Signals that an attribute name is in the node.

FTOK_AVAL

Signals that an attribute value is in the node.

Relational Operator

Usually comes before an attribute name or value node and signals how the search treats the following node. For example, signals whether the search is looking for a value equal to or greater than the following node.

Logical Operator

Usually comes before an attribute name or value node and signals how the search treats the following node. For example, signals whether the next node is another condition that must be met or whether the condition is an either one or the other relationship.

FTOK_END

Signals the end of the search filter.

Value. Value only applies when the token is FTOK_ANAME or FTOK_AVAL. When one of these is the token, the value specifies the name of the attribute or the value, respectively. For all other tokens, value must be set to NULL.

Syntax. Syntax only applies where the token is FTOK_ANAME or FTOK_AVAL. Otherwise, it must be set to 0 (zero). When the node contains an attribute value or name, the syntax contains the syntax ID of the attribute. Use the NWDSGetSyntaxID function to obtain the syntax ID.

Logical Operators. The logical operator tokens express logical relationships among attribute value assertions. The following table shows the logical operators and the conditions that test TRUE for each.

Token

Value

Comment

FTOK_OR

1

TRUE if either subordinate node is true.

FTOK_AND

2

TRUE only if both subordinate nodes are true.

FTOK_NOT

3

TRUE if the node is false.

FTOK_LPAREN

4

Left parenthesis.

FTOK_RPAREN

5

Right parenthesis.

You can control precedence among the logical operators by inserting tokens that act as parentheses. In the absence of parentheses, the FTOK_AND operator takes precedence over the FTOK_OR operator, and the FTOK_NOT operator takes precedence over both.

Relational Operators. A relational operator asserts something about an attribute (for example, the attribute is present or its value is greater than 100). The truth of a relational operator is evaluated with the matching rules associated with the attribute’s syntax. A relational operator must be followed by a node that asserts the test case, for example an FTOK_ANAME or FTOK_AVAL.

The following table shows the relational operators and the conditions that test TRUE for each.

Token

Value

Comment

FTOK_EQ

7

TRUE only if the attribute’s value is equal to the asserted value. Must be followed by a FTOK_AVAL node that contains the value.

For example, to set up a search where the attribute must equal an integer value of 5, use the following node expressions:

  • FTOK_EQ, NULL, 0
  • FTOK_AVAL, "5", SYN_INTEGER

FTOK_GE

8

TRUE only if the attribute’s value is greater than or equal to the asserted value. Must be followed by a FTOK_AVAL node that contains the value. See FTOK_EQ for a sample syntax.

FTOK_LE

9

TRUE only if the relative ordering places the asserted value before any of the attribute’s values. Must be followed by a FTOK_AVAL node that contains the value. See FTOK_EQ for a sample syntax.

FTOK_APPROX

10

TRUE only if the value of the attribute matches the asserted value. If the attribute syntax does support approximate match, this operator matches for equality. Must be followed by a FTOK_AVAL node that contains the value. See FTOK_EQ for a sample syntax.

FTOK_PRESENT

15

TRUE only if the named attribute is present in the entry. Must be followed by a FTOK_ANAME node that contains the attribute's name.

For example, to set up a search where the object must have a Given Name attribute, use the following node expressions:

  • FTOK_PRESENT, NULL, 0
  • FTOK_ANAME, "Given Name", SYN_CI_STRING

FTOK_RDN

16

TRUE only if the object’s Relative Distinguished Name matches the asserted value. Must be followed by a FTOK_ANAME node that contains the RDN.

May be used in a search without authentication.

For example, to set up a search that returns all objects that start their name with D, use the following node expressions:

  • FTOK_RDN, NULL, 0
  • FTOK_ANAME, "D*", SYN_DIST_NAME

FTOK_BASECLS

17

TRUE only if the object belongs to the asserted base class. Must be followed by a FTOK_ANAME node that contains the name of the base class.

May be used in a search without authentication.

For example, to set up a search where the object must be a Group object, use the following node expressions:

  • FTOK_BASECLS, NULL, 0
  • FTOK_ANAME, "Group", SYN_CLASS_NAME

FTOK_MODTIME

18

TRUE only if the modification time stamp is greater than or equal to the asserted value.

FTOK_VALTIME

19

TRUE only if the creation time stamp is greater than or equal to the asserted value.

You can use wildcards to create relational assertions for string values. The wildcard character is the asterisk (*). Use the back slash escape character to escape the asterisk (\*) or to escape the back slash itself (\\).

1.4.3 Sample Search Expression Trees

The nodes in a search filter combine to form an expression tree. To build the tree, add each node by calling NWDSAddFilterToken.

As an example, suppose you want to search for all User objects whose surname begins with “Sm.” If you were expressing this criteria as a string, it would look like this:

  Base Class=User AND Surname=Sm*
  

To create the expression tree, think of each element as corresponding to a node in the tree. The sequence for adding the nodes is the same as if you were processing the string from left to right. You add one node for each element and add one node to signal the end:

Element

Node Expression

Object's Base Class

(FTOK_BASECLS, NULL, 0)

User

(FTOK_AVAL, "User", SYN_CLASS_NAME)

AND

(FTOK_AND, NULL, 0)

Surname

(FTOK_ANAME, "Surname", SYN_CI_STRING)

=

(FTOK_EQ, NULL, 0)

Sm*

(FTOK_AVAL, "Sm*", SYN_CI_STRING)

 

(FTOK_END, NULL, 0)

Once you form a search expression, you build the expression tree by calling the NWDSAddFilterToken function to add each node to the tree. Each token represents a node on the expression tree:

The following example demonstrates how to build a search filter that looks for user objects with a Surname not equal to “brown.” It starts with a string expression for the filter.

  Expression: 
     NOT (Surname EQ "brown") 
   
  Code Example: 
     NWDSAllocFilter(&FilterCursor); 
     NWDSAddFilterToken(FilterCursor, FTOK_NOT, NULL,0);  
     NWDSAddFilterToken(FilterCursor, FTOK_LPAREN, NULL,0); 
     NWDSGetSyntaxID(context,"surname",&syntaxID); 
     NWDSAddFilterToken(FilterCursor, FTOK_ANAME,"surname", syntaxID); 
     NWDSAddFilterToken(FilterCursor, FTOK_EQ, NULL,0); 
     NWDSAddFilterToken(FilterCursor, FTOK_AVAL,"brown", syntaxID); 
     NWDSAddFilterToken(FilterCursor, FTOK_RPAREN, NULL,0); 
     NWDSAddFilterToken(FilterCursor, FTOK_END,NULL,0);
  

After you have added all of the filter tokens to the cursor, your next task is to store the filter expression in the filter buffer.

  NWDSPutFilter (context, filterBuffer, FilterCursor, FreeValuePointer);
  

The FreeValuePointer parameter can be passed either as NULL or as a pointer to a function that frees the attribute values.

If the NWDSPutFilter function succeeds, the FilterCursor is automatically freed. If the NWDSPutFilter function fails, you should call the NWDSFreeFilter function to free the FilterCursor. Do not call the NWDSFreeFilter function when the NWDSPutFilter function succeeds or you will crash your program.

Once you have put the search filter in the filter buffer, you are ready to start the search by calling the NWDSSearch function.

1.4.4 Retrieving Information from the Result Buffer

When the search completes, you cannot just read the information. You must retrieve the information from the result buffer. Remember to pull out all the information you requested, whether you need it or not. Use the infoType, allAttrs, and attrNames parameters in the NWDSSearch function to control what is returned. The attrNames parameter has been called the NameBuffer in the previous sections.

If you requested attribute names and values, you cannot retrieve just attribute names. The failure to pull out the attribute values makes it impossible to retrieve the rest of the attribute names. Use the following functions:

NWDSGetObjectCount

Returns the number of objects whose information is stored in the result buffer.

NWDSGetObjectName

Returns the name of the current object and the count of attributes associated with the object.

NWDSGetAttrName

Returns attribute's name and the number of values associated with the attribute.

NWDSComputeAttrValSize

Returns the size of the attribute's value. This function is only required if you don't know the size of the attribute.

NWDSGetAttrVal

Returns the attribute's value. Must be called for each value associated with the attribute.

For each object in the result buffer, you must retrieve all the attributes, and all the values for each attribute, before you can retrieve information about the next object in the result buffer.

A search request can return more information than can fit in the result buffer. To retrieve all the information, you need to call the NWDSSearch function repeatedly until the iterationHandle parameter is equal to NO_MORE_ITERATIONS. For more information on iteration handles, see Controlling Iterations.

1.4.5 Search Cleanup

After you have retrieved all the information you need from the result buffer, you should clean up all the buffers you allocated. Use the following functions: