Creating a Custom Federation Location for SharePoint 2010 Search

SharePoint 2010 adds a new feature set of components called the Federation Object Model. This object model provides the ability to manage the configuration, querying and rendering of search results.  The entire Federation OM consists of three core components: 

  • The Federation Location which provides the configuration information and run-time for search providers.
    • The runtime provides the necessary logic for send and returning results.
    • SharePoint 2010 comes with three run-time types out of the box
      • FAST Search
      • SharePoint Search
      • OpenSearch
  • The Query Manager which sends the query and returns the results from the various Federation Locations
  • The Search WebParts which provide the user interaction of the search experience

With the three out of the box Federation Locations types, under certain circumstances it may be necessary to create your own custom federation location run-time. The following scenarios would be possible candidates for creating a custom Federation Location run-time.

  • Querying data sources that do not support OpenSearch
  • Utilizing refiners with the search experience.
  • Querying data sources that do not support standard web protocols such as http

Creating a Custom Federation Location for SharePoint 2010 Search is overall not that hard.  The things to remember are to implement the correct interfaces and created the Open Search Definition File (OSDX).  In the example below I created a simple federation run-time that will query a SQL Server Database. 

  1. First thing to do is create a Empty SharePoint project this way your assembly can be easily deployed as a WSP. 

  2. Second create a class name it "SQLServerLocationRuntime", now you want your new class to inherit from ILocationRuntime and if you want refiners you will need to inherit from IRefinableRuntime as well.

  3. Looking at the ILocationRuntime members you will have to implement the following:

    • void Close()
    • void Dispose()
    • LocationConfiguration Config
    • String DecodeForLocationQueryFormat(String query)
    • String EncodeForLocationQueryFormat(String query)
    • Exception GetException()
    • Location Location
    • IAsyncResult SendRequest(String queryUrl, ResultCallback callback)
    • XmlDocument SendRequest(String queryUrl)
    • Boolean SupportAsync
  4. For the purposes of this demo, you are going to effectively ignore the "IAsyncResult SendRequest" method.

  5. the "DecodeForLocationQueryFormat", and "EncodeForLocationQueryFormat" methods will simply return the string that was passed in as the returned value.

  6. Default the SupportAsync property to return false.

  7. Create a private SqlConnection instance within the class and have the dispose method check to see if it is open and close it if necessary.

  8. The SendRequest method that returns the XmlDocument will parse the connection info, query the SQL Server database and populate the refiners. I get the connection string from the MappedQueryTemplateUrl property of the Location property that gets set in the Federation OM. Something similar to this :

    String connUrl = this.Location.MappedQueryTemplateUrl

  9. I formated the URL to look like this : mssql://<ConnectionString>?q=<sql query>&rc=<refinement categories>

  10. I then parsed the connection string, sql query and refinement categories. You can look in the attached file at the source code to see how I did it.

  11. Next I perform the query and I return the results and output them into the same xml format as a FAST search results which is something similar to this:

          <All_Results><Results><Result>...</Result><Result>...</Result></Results></All_Results>

  12. The reason why I did this will be apparent in my next posting, but one benefit is I can re-use the XSLT that is used with other search results

  13. Now with the Refiners, I simply hacked off the select statement and did a grouping by each refiner i specified in the url. I appended the group by to the from and where clauses of the query and returned the counts. 

  14. Adding refiners to the RefinementManager is easy, when you implement the IRefinableRuntimeinterface. It adds a property called RefinemenntManager of type RefinementManager.

  15. The RefinementManager class has a few Add methods for adding refiners. All you do is specify the filter category, refinement name, value, count (optional), and percentage (optional)

  16. Last thing is to create the OSDX file. The easiest way to do that a export the FAST Search or SharePoint Search location definition, you can do this in the Locations section of your Search Service Application.

  17. You will notice the LocationType has a token like "FASTSearch", you will want to change that to type definition of your class (i.e. MyNamespace.MyClass, MyAssembly, Version=1.0.0.0 PublicToken=6a5bc2...)

  18. The Visualization elements of your OSDX define the default XSLT to utilize to display the core results of your search.

  19. Make sure you give it an internal name that is unique

  20. Now deploy, your WSP, import the OSDX (Note if you edit it, never touch the three OOB location types as this will override your type) and you should be good to go.

That is all there is to it. I included a sample OSDX and C# class file that you can use as a guide.

SQLServerLocationRuntime.zip

Comments

  • Anonymous
    June 18, 2010
    When would you run this procedure and how will you know it is successful? Good point, I just edited the posting.  

  • Anonymous
    November 07, 2010
    I tried your sample (modified it with dummy data) but I get a NullReferenceException in the refinement webpart in the search UI. I also tried to call RefinementManager.GetRefinementXml() in my code, but it always returns null, which is the cause of the exception in the webpart, If I omit IRefinableRuntimeinterface the sample is great, but of course, no refiners. Any idea what could be wrong?

  • Anonymous
    November 07, 2010
    The RefinementManager is unforgiving if you omit whats required.  Since obviously no refinement values can exist (in the case of a zero search results returned), more than likely you are missing one of the required parameters.  Check to make sure you have a filter category, refinement name, and value specified.  If that doesn't work try adding a count as well. Without seeing the error message or your source its hard to see where it breaks down.  Let em know how it goes and I will update the post appropriately.