Kevin Marshall's Epic Work Blog for Awesome People

Kevin the Coder is a maverick. Almost like a team of mavericks.
in

Sharepoint Search from Tafiti

I read a post on Angus Logan's blog about using Tafiti to provide a UX on top of Enterprise Search.  I've never used the SharePoint Search SDK before so I thought I'd give it a try.

The code is pretty hacked together since I just wanted to see how it worked. 

Screenshot of Tafiti running in a page viewer web part against my dev machine.  The code is using impersonation to query our MOSS server.

 

image

 

Screenshot of the classic tree view.

image

 

To get started you need to download the Tafiti source from CodePlex and follow the setup guide.

Open the solution in VS 2008.  Although solution didn't work for me.  I had to remove the projects and re-add them.  I also changed the target framework type to 3.0.

Add a web reference to you sharepoint search web service at <server_name>/_vti_bin/search.asmx

 

image

As per Angus's blog post, you really only have to alter code in the Search.aspx.cs page to change the search engine to SharePoint.

 

   1:  private void SoapSearch(SourceType sourceType, string query, int first, int count)
   2:      {
   3:          try
   4:          {
   5:              SourceRequest[] sr = new SourceRequest[1];
   6:              sr[0] = new SourceRequest();
   7:              sr[0].Source = sourceType;
   8:              sr[0].Offset = first;
   9:              sr[0].Count = count;
  10:              sr[0].ResultFields = GetResultFieldMask(sourceType);
  11:   
  12:              //SearchRequest request = new SearchRequest();
  13:              //request.Query = query;
  14:              //request.Requests = sr;
  15:              //request.SafeSearch = SafeSearchOptions.Strict;
  16:              //request.AppID = SettingsWrapper.LiveSearchAppID;
  17:              //request.CultureInfo = "en-US";
  18:   
  19:              //if (sourceType == SourceType.PhoneBook)
  20:              //{
  21:              //    // Using Redmond as the search location.
  22:              //    request.Location = new Location();
  23:              //    request.Location.Longitude = Double.Parse("-122.33482360839846");
  24:              //    request.Location.Latitude = Double.Parse("47.6082462871061");
  25:              //    request.Location.Radius = Double.Parse("5");
  26:              //}
  27:   
  28:              //MSNSearchService searchService = new MSNSearchService();
  29:              //SearchResponse searchResponse;
  30:              //searchResponse = searchService.Search(request);
  31:   
  32:              //---------SharePoint Search Code------
  33:   
  34:              string qXMLString = "<QueryPacket xmlns='urn:Microsoft.Search.Query'>" +
  35:                  "<Query><SupportedFormats><Format revision='1'>" +
  36:                  "urn:Microsoft.Search.Response.Document:Document</Format>" +
  37:                  "</SupportedFormats><Context><QueryText language='en-US' type='STRING'>" +
  38:                  query +
  39:                  "</QueryText></Context></Query></QueryPacket>";
  40:   
  41:   
  42:              System.Security.Principal.WindowsImpersonationContext impersonationContext;
  43:              impersonationContext =
  44:                  ((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
  45:   
  46:              SPSearch.QueryService queryService = new SPSearch.QueryService();
  47:              queryService.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
  48:              System.Data.DataSet queryResults = queryService.QueryEx(qXMLString);
  49:   
  50:              impersonationContext.Undo();
  51:   
  52:              //--------------------------------
  53:   
  54:              LiveXmlSearchResults result = CreateLiveXmlFromMossSearchResults(queryResults);
  55:   
  56:              JavaScriptSerializer serializer = new JavaScriptSerializer();
  57:              string json = serializer.Serialize(result);
  58:              byte[] jsonUtf8 = System.Text.Encoding.UTF8.GetBytes(json);
  59:   
  60:              Response.StatusCode = 200;
  61:              Response.ContentType = "text/javascript";
  62:              Response.OutputStream.Write(jsonUtf8, 0, jsonUtf8.Length);
  63:          }
  64:          catch (SoapException e)
  65:          {
  66:              throw new HttpException((int)HttpStatusCode.InternalServerError, "Internal Server Error", e);
  67:          }
  68:          catch (WebException e)
  69:          {
  70:              throw new HttpException((int)HttpStatusCode.InternalServerError, "Internal Server Error", e);
  71:          }
  72:      }

 

To make a search against sharepoint you just need to call the QueryEx method off of the QueryService from your web reference.  MSDN has an overview of the QueryEx method here.

The next step is to reformat the dataset returned from MOSS into the Live search results format.  Like Angus mentioned, converting the result into the LiveXmlSearchResults allows you to reuse the JavaScriptSerializer and existing JavaScript code in Silverlight.

 

These functions are a little messy :) Not all of the results had a description so I pulled the HitHighlightedSummary from the dataset and removed some of the extra tags it added.

 

   1:      private LiveXmlSearchResults CreateLiveXmlFromMossSearchResults(System.Data.DataSet mossSearchResults)
   2:      {
   3:          LiveXmlSearchResults searchResults = new LiveXmlSearchResults();
   4:          searchResults.searchresult.documentset._source = "FEDERATOR_MONARCH";
   5:          searchResults.searchresult.documentset._count = mossSearchResults.Tables[0].Rows.Count.ToString();
   6:          searchResults.searchresult.documentset._start = "0";
   7:          searchResults.searchresult.documentset._total = mossSearchResults.Tables[0].Rows.Count.ToString();
   8:   
   9:          LiveXmlResult[] results = ConvertMOSSWebResults(mossSearchResults.Tables[0]);
  10:   
  11:          if (results != null)
  12:              searchResults.searchresult.documentset.document = (results.Length > 1) ? (object)results : (object)results[0];
  13:   
  14:          return searchResults;
  15:      }
  16:   
  17:      private static LiveXmlResult[] ConvertMOSSWebResults(System.Data.DataTable mossSearchResultsTable)
  18:      {
  19:          LiveXmlWebResult[] results = new LiveXmlWebResult[mossSearchResultsTable.Rows.Count];
  20:   
  21:          for (int i = 0; i < mossSearchResultsTable.Rows.Count; i++)
  22:          {
  23:              LiveXmlWebResult result = new LiveXmlWebResult();
  24:              string description = string.Empty;
  25:              description = 
  26:                   GetDescription(mossSearchResultsTable.RowsIdea [I]["Description"].ToString(), mossSearchResultsTable.RowsIdea [I]["HitHighlightedSummary"].ToString());
  27:              result.title = mossSearchResultsTable.RowsIdea [I]["Title"].ToString();
  28:              result.desc  = description;
  29:              result.url = mossSearchResultsTable.RowsIdea [I]["Path"].ToString();
  30:              resultsIdea [I] = result;
  31:          }
  32:          return results;
  33:      }
  34:   
  35:      private static string GetDescription(string description, string hitHighlightedSummary)
  36:      {
  37:          string strippedText = string.Empty;
  38:   
  39:          if (string.IsNullOrEmpty(description))
  40:          {
  41:              Regex regEx = new Regex("<[^>]*>", RegexOptions.IgnoreCase);
  42:              strippedText = regEx.Replace(hitHighlightedSummary, "...");
  43:              return strippedText;
  44:          }
  45:          else
  46:              return description;
  47:      }

 

Next I think I'll try integrating it into MOSS to be an actual page in the search center.  I'd also like to change the News/Phonebook/Web search option carousal to have it do some different searches like People, Office Docs, etc.   Also remove the Windows Live functionality and misc. extra hyperlinks. Oh and fix the range / start offset so it pages properly.  When I started this an hour ago I didn't really realize the difference between Query and QueryEx [Maybe I should have read those docs I linked to :) ].  Looks like I might have to use QueryEx so get some of the extra info like total number of records.  Oh maybe add in the relevance / thumbnail icons for filetypes.  Well it's a prototype so I'll probably just start over anyway.

 

Comments

Removing Software Asbestos said:

Kevin Marshall at Clarity Consulting blogged today about how he's integrated Tafiti - a Silverlight-based
# December 20, 2007 11:34 AM

Noticias externas said:

Kevin Marshall at Clarity Consulting blogged today about how he&amp;#39;s integrated Tafiti - a Silverlight
# December 20, 2007 11:45 AM

Angus Logan's Blog said:

Kudos to Kevin Marshall of Clarity Consulting for being the first to blog SharePoint &amp;amp; Tafiti . Now
# December 20, 2007 6:17 PM

Noticias externas said:

Kudos to Kevin Marshall of Clarity Consulting for being the first to blog SharePoint &amp;amp; Tafiti . Now
# December 20, 2007 6:55 PM

George Durzi said:

In this excellent post, my colleague Kevin Marshall build a prototype for integrating Tafiti search into...
# January 7, 2008 5:27 PM

George Durzi said:

 In this excellent post, my colleague Kevin Marshall build a prototype for integrating Tafiti search...
# January 9, 2008 2:49 PM

ssahmed said:

Awesome work! Thanks for posting this!

Regards

SSA
MOSS MVP
# January 11, 2008 9:41 AM

NiciOD20 said:

It supposes to be incumbent to compose a best range <a href="http://www.exclusivethesis.com">dissertation</a> when scholars are at study at the university. Thence, your fantastic information about this good topic should be a very good sample for the phd thesis creators.

# December 30, 2009 5:36 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)