Migrating to Exchange Web Services, Part 3: Search

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.

Topic Last Modified: 2008-08-20

By Bob Bunn, Programming Writer

Applications that work with Microsoft Office Outlook and Microsoft Exchange Server typically extend one or more of the following features:

  • Messaging
  • Calendaring (including meetings and appointments)
  • Search
  • Contact management

In the first two parts of the series, I described how to use Exchange Web Services (EWS) to handle messaging and calendaring. In this article, I will explore another common function: search.

Search Overview

The ability to search the Exchange data store is another feature that you may have to add to your application. Searching the data store involves the following steps:

  1. Choosing the content store to search.
  2. Choosing the objects within the store to search.
  3. Choosing the value(s) to search for.
  4. Choosing shallow or deep traversal.
  5. Setting restrictions on content returned.
  6. Performing the search.

The code examples in this article show how query requests in ExOLEDB compare to the new functionality in EWS. The two APIs handle data queries differently. ExOLEDB queries use SQL syntax. EWS uses strongly typed objects, which, among other benefits, enables developers to debug query requests by stepping through the code.

Using ExOLEDB to Perform a Search Query

The following code example shows how to use ExOLEDB to query the Exchange store.

Private Function AddQuotes(ByVal str As String)
AddQuotes = """" & str & """"
End Function

Dim urlQueryFld As String
Dim strSQL As String
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset

' Build the URL string of the content store to search.
urlQueryFld = " contoso.com/Exchange01/Mailbox55"

' Connect to the calendar URL.
cnn = New ADODB.Connection
With cnn
.Provider = "exoledb.datasource"
.Open(urlQueryFld)
End With

' Build the SQL SELECT statement to choose the properties
' that should be returned.
strSQL = "SELECT " & _
AddQuotes("DAV:displayname") & ", " & _
AddQuotes("DAV:contentclass") & ", " & _
AddQuotes("DAV:href")

' Indicate shallow or deep traversal.
strSQL = strSQL & " FROM SCOPE('SHALLOW traversal of " & _
AddQuotes(urlQueryFld) & "')"

' Define a filter to restrict the results to a date range.
strSQL = strSQL & " WHERE (" & AddQuotes("DAV:isfolder") & " = True)"

' Sort the results by start date with the latest appointment first.
strSQL = strSQL & " ORDER BY " & AddQuotes("DAV:creationdate") & " ASC"

' Create and open a Recordset by using the query string.
rst = New ADODB.Recordset
rst.Open(strSQL, cnn)

' Close the ADO objects.
rst.Close()
cnn.Close()

Using Exchange Web Services to Perform a Search Query

When you perform a query by using EWS, you can specify which set of properties will be returned, in addition to restrictions, or combinations of restrictions, to apply to the search.

This example shows how to search the body of a message by using EWS.

Note

The specification of the content store is implied by context.

// Form the FindItem request.
FindItemType findRequest = new FindItemType();

// Choose the traversal mode.
findRequest.Traversal = ItemQueryTraversalType.Shallow;

// Define which item properties are returned in the response.
ItemResponseShapeType itemProperties = new ItemResponseShapeType();
itemProperties.BaseShape = DefaultShapeNamesType.AllProperties;

// Add properties shape to request.
findRequest.ItemShape = itemProperties;

// Define the folders to search. In this example, the Inbox is searched.
DistinguishedFolderIdType[] folderIDArray = new DistinguishedFolderIdType[1];
folderIDArray[0] = new DistinguishedFolderIdType();
folderIDArray[0].Id = DistinguishedFolderIdNameType.inbox;
findRequest.ParentFolderIds = folderIDArray;

// Identify the field to examine. In this example,
// the "body" with the unindexed field itemBody is identified.
PathToUnindexedFieldType pathToUnindexedField =
new PathToUnindexedFieldType();
pathToUnindexedField.FieldURI = UnindexedFieldURIType.itemBody;

// Define the type of search filter to apply. In this example,
// "itemBody CONTAINS SearchedTextPassedIn" is the search filter.
ContainsExpressionType containsExpression = new ContainsExpressionType();
containsExpression.Item = pathToUnindexedField;

// Specify how the search expression is evaluated.
containsExpression.ContainmentComparison =
ContainmentComparisonType.IgnoreCase;
containsExpression.ContainmentComparisonSpecified = true;
containsExpression.ContainmentMode = ContainmentModeType.Substring;
containsExpression.ContainmentModeSpecified = true;

// Identify the value to compare to the examined field (ItemClass).
ConstantValueType constantValue = new ConstantValueType();
constantValue.Value = SearchTextPassedIn;

// Add the value to the search expression.
containsExpression.Constant = constantValue;

// Create a restriction.
RestrictionType restriction = new RestrictionType();

// Add the search expression to the restriction.
restriction.Item = containsExpression;

// Add the restriction to the request.
findRequest.Restriction = restriction;

// Perform the search by calling the FindItem method.
FindItemResponseType findResponse =serviceBinding.FindItem(findRequest);

// Get the response message.
ResponseMessageType responseMessage = findResponse.ResponseMessages.Items[0];

// Cast to the correct response message type.
FindItemResponseMessageType findItemResponseMessage =
responseMessage as FindItemResponseMessageType;

if (findItemResponseMessage.ResponseClass == ResponseClassType.Success)
{
// Get the actual payload of items.
ArrayOfRealItemsType realItems =
findItemResponseMessage.RootFolder.Item as ArrayOfRealItemsType;

if (realItems.Items != null)
{
return new List<ItemType>(realItems.Items);
}
else
{
return null;
}
}
else
{
// Handle any errors here.
}

In this example, searching the store involves calling the ExchangeServiceBinding.FindItem Web service method with a RestrictionType object, which in turn contains a SearchExpressionType object.

In this case, the restriction is defined as a single ContainsExpressionType object, but it could include an array of ContainsExpressionType objects to solve a more complex search scenario. For example, you can include "chris" or "mike" in the To or From fields, and exclude items before a certain date.

In the code previous code example, we are setting properties in the ContainsExpressionType object (here named containsExpression). The following code example sets whether the search for this particular value is case-sensitive, whether it includes substrings or full strings, the field the search is against, and the values for which to search.

containsExpression.Item = pathToUnindexedField;
// ...
containsExpression.ContainmentComparison =
ContainmentComparisonType.IgnoreCase;
containsExpression.ContainmentComparisonSpecified = true;
containsExpression.ContainmentMode = ContainmentModeType.Substring;
containsExpression.ContainmentModeSpecified = true;
// ...
containsExpression.Constant = constantValue;

When the ContainsExpressionType object is completely formed, its value is added to the Item property of a RestrictionType object (here named restriction), as follows:

restriction.Item = containsExpression;

After all the search parameters and restrictions are set, the Restriction property of the FindItemType object (here named findRequest) is set to the previously defined restriction object, as follows:

findRequest.Restriction = restriction;

Finally, the FindItemRequest is passed to the FindItem method of the ExchangeServiceBinding object (here named serviceBinding) to perform the search, as follows:

FindItemResponseType findResponse =serviceBinding.FindItem(findRequest);

Note

The FindItem operation only retrieves summary information for most properties. A long message subject, for example, might be truncated to 512 characters. To obtain actual (that is, non-summary) data for an item, use the GetItem operation.

More to Come

In the fourth article in this series, I will provide examples that will show how to use EWS to manager your contacts by updating properties of the contacts. And, of course, the fifth and final article will include fully functioning sample applications that illustrate all the scenarios described in the series.

Additional Information