SharePoint People Search – Lessons learned. Part 2: Programmatically running search queries

In the first part of the SharePoint People Search – Lessons learned article we’ve discussed the basics of configuring SharePoint People Search. We have also looked at how it could be used for a facebook solution and what the shortcomings are. In this part we will explore different aspects you are very likely to face while creating a custom facebook solution based on SharePoint People Search.

Introduction

If the standard search functionality provided by SharePoint is insufficient for your requirements, you are very likely to develop your own Web Parts. SharePoint supports such scenario by providing two mechanisms for communicating with the Search Service.

Searching using the SharePoint Search Object Model

If the custom search solution that you’re creating is going to be deployed on the same server that contains the information you want to present, you are very likely to use the SharePoint Search Object Model. Its benefit over the Search Web Service is that is saves you some HTTP traffic for sending queries and receiving the search results.

There is a great Visual How To by Patrick Tisseghem available on MSDN which will guide you through the process of creating search queries programmatically.

Firing search queries using the Search Web Service

If the information you want to present is remote to your server however you will have to use the Search Web Service to run the search query. There is another Visual How To also by Patrick Tisseghem available on MSDN which explains how the SharePoint Search Web Service works and how it can be incorporated in your custom code.

Presenting the Search Results

Running a search query is just the first step. After you got the results from the SharePoint Search Service you have to present them to the end user. Because you’re inside your custom solution you are free to choose how you are going to do it. Depending on the approach you choose, you will get different types of results which you have to transform into a readable output.

While working on my SharePoint facebook solution I chose to use the Data Form Web Part which is the base type of the Search Core Results Web Part. The Data Form Web Part provides all the plumbing required for transforming XML input into HTML output using XSLT allowing you to focus on retrieving the data and formatting its presentation.

Using XSLT for input transforming gives you great flexibility and manageability. It allows you to fully separate data from the presentation layer. Additionally, because it’s a custom Web Part that you’re developing, you are free to extend the standard XSLT functionality with custom parameters and XSLT functions.

While using the Data Form Web Part there are at least three different ways of how you can bind the data to the Web Part so it can be transformed using XSLT. Depending on which approach you use to query the SharePoint Search Service you might want choose one of them instead of another.

Presenting search results using the DataFormWebPart.DataSource property

One way of how you can bind the data to the DataFormWebPart is by using the DataSource property. This property expects data stored in an object which implements the IDataSource interface. This is where the things get challenging.

By default the SharePoint Search Service returns query results stored in a ResultTable object, which doesn’t implement the IDataSource interface. Neither is the DataTable, to which a ResultTable can be easily converted, a valid data source for the DataFormWebPart. What you could do is to first convert the results to a DataTable and then to an XmlDataSource which is accepted by the DataFormWebPart.DataSource property. The following code snippet illustrates that process:

// run search query
ResultTableCollection searchResults = searchQuery.Execute();  
ResultTable searchResult = searchResults[ResultType.RelevantResults];  
// store the result in a DataTable for XML serialization
DataTable result = new DataTable();  
result.TableName = "Result";  
result.Load(searchResult, LoadOption.OverwriteChanges);  
// serialize DataTable to XML
StringBuilder resultsXml = new StringBuilder();  
using (StringWriter sw = new StringWriter(resultsXml))  
{
  result.WriteXml(sw);
  sw.Flush();
}

// move XML to the XmlDataSource
XmlDataSource xds = new XmlDataSource();  
xds.Data = resultsXml.ToString();  
// bind data to DataFormWebPart DataSource
this.DataSource = xds;  
this.BindData();  

Quite some code to get things done, isn’t it? Search Core Result Web Part wraps this process in a method which serializes the ResultTable to XML. Unfortunately that method is marked internal what means that we cannot actually use it in our custom solutions.

Presenting search results using by overriding the GetXPathNavigator method

Another way of how you can make the DataFormWebPart render your data is to override the GetXPathNavigator method. This is the approach that the Search Core Results Web Part uses.

Comparing to the first approach there is not that much difference. You also have to convert the ResultTable to a DataTable and serialize it to XML. The only difference is that you’re not using an XmlDataSource but load the serialized XML results into an XmlDocument:

protected override XPathNavigator GetXPathNavigator(string viewPath)  
{
  // run search query
  ResultTableCollection searchResults = searchQuery.Execute();
  ResultTable searchResult = searchResults[ResultType.RelevantResults];
  // store the result in a DataTable for XML serialization
  DataTable result = new DataTable();
  result.TableName = "Result";
  result.Load(searchResult, LoadOption.OverwriteChanges);
  // serialize DataTable to XML
  StringBuilder resultsXml = new StringBuilder();
  using (StringWriter sw = new StringWriter(resultsXml))
  {
    result.WriteXml(sw);
    sw.Flush();
  }

  XmlDocument xd  = new XmlDocument();
  xd.InnerXml = resultsXml.ToString();
  return xd.CreateNavigator();
}

Data Form Web Part and Web Services

As I have mentioned before, one of the ways of how you can query the SharePoint Search Service is by using the Search Web Service. When used in custom code you could of course call the service yourself and bind the retrieved data to the DataFormWebPart using of one the approaches I presented above. But why bother? Wouldn’t it be easier to let the DataFormWebPart make the call to the Web Service and retrieve that data for you and just focus on the XSLT instead?

DataFormWebPart ships with some hidden gems: the DataSources and DataSourcesString properties. Both of them contain the information about the data sources bound to the Web Part. The difference is that the DataSources property contains objects while DataSourcesString contains exactly the same information as DataSources but then serialized to a string. Which one you choose is up to you.

If you choose for the DataSourcesString property there is a CodePlex project you should check out. Phil Wicklund has created a template class for using Web Services as data sources for the DataFormWebPart. Using the template allows you to focus on the properties and settings specific to your Web Service.

No matter which approach you choose, one of the information you have to provide is the search query wrapped in the QueryPacket element. The problem with the query is that the Web Service expects it to be encoded as follows:

Encoded Search Query as expected by the SharePoint Search Service

As far as I know there are no publicly available methods which would encode a string to resemble the one you can see on the screenshot above. I have found some internal methods but none of them encoded the query string completely. Seems like magic is going on in the background.

If you will be working with the SharePoint Search Web Service you will be needing that query. The good news is that there is a workaround to get it in the right format. All you need is the query XML and SharePoint Designer (SPD).

Building the search query

The first thing to do is to build the search query that you will use for retrieving items. SharePoint Search Service uses yet-another SQL-like query syntax (probably different than any other query syntax you know). The good news is that there is a tool which can help you build and test your SharePoint Search queries. You should check out the zevenseas MOSS SQL SearchCoder by Daniel McPherson.

zevenseas MOSS SQL SearchCoder screenshot

The downside of the zevenseas MOSS SQL SearchCoder is that it doesn’t display all of the User Profile properties which you can include in your search query. The easiest way to obtain the complete list is to use the following method:

public static Dictionary<string, string> GetUserProfileProperties(string webApplicationUrl)  
{
  Dictionary<string, string> userProfileProperties = null;
  SPWebApplication webApp = SPWebApplication.Lookup(new Uri(webApplicationUrl));
  if (webApp != null)
  {
    UserProfileConfigManager upcm = new UserProfileConfigManager(ServerContext.GetContext(webApp));
    PropertyCollection properties = upcm.GetProperties();
    userProfileProperties = new Dictionary<string, string>(properties.Count);
    foreach (Property p in properties)
    {
      userProfileProperties.Add(p.ManagedPropertyName ?? p.Name, p.DisplayName);
    }
  }

  return userProfileProperties;
}

…and then print the retrieved values on the screen using either a Console Application or like me - a Visual Studio Macro:

Overview of all available User Profile Properties printed out using Visual Studio Macro

As soon as you have the search query ready you have to construct the complete QueryPacket element which you will to test the query and later on to generate the encoded string to include in your Web Part. The MOSS SDK provides information on what settings are available and how to configure them.

Now your query XML string is complete, you can move to SharePoint Designer. First of all create a new ASPX page and open it in SharePoint Designer:

Creating new ASPX page using SharePoint Designer

In the Data Source Library tool pane click on the Data Source Library tab. Click on Connect to a web service…:

Connecting to a web service data source using SharePoint Designer

Enter the URL of your web service (Search Service in this example) and click on Connect now. Choose QueryEx as the Operation.

The last piece of the web service configuration is providing the value for the queryXml element. That’s where your QueryPacket element needs to be put:

Providing the value for the SharePoint Search Service's queryXml parameter in SharePoint Designer

Now the web service has been configured you can confirm the changes and click on Show Data to check whether everything has been setup correctly:

Showing Data returned by a web service in SharePoint Designer

If everything has been configured correctly you should see some data in the Data Source Details tab.

We configured the Web Service in SharePoint Designer in order to get the escaped Search query. To make SharePoint Designer render that value you have to drop the data returned by the Web Service onto the page you have created earlier. To do this, be sure that you are in the Data Source Details tab, click with the right mouse button on RelevantResults and choose for example Insert as Table.

Insert data retrieved by a web service as table in SharePoint Designer

As soon as the data has been inserted, go to the Code view of your page and copy the escaped query string (the whole queryXml element):

Encoded search query generated by SharePoint Designer

Adding that query to your Web Part is the last piece required by your Web Part to work. You should be able to get some results at this point. If everything works correctly you can proceed with modifying XSLT to display your results as desired.

Summary

Using all the things I’ve discussed in this and the previous article you should be able to create the basic foundation of a SharePoint People Search solution. SharePoint Search is really powerful but requires an additional set of knowledge you have to have in order to make it work and to really benefit of it. Leveraging it in your custom solutions allows you to achieve some cool and very well performing results.

Screenshot of Imtech facebook solution 

Technorati Tags: SharePoint, SharePoint 2007, MOSS 2007, Search, People Search

Comments

comments powered by Disqus