Doing blended search results in SharePoint–Part 2: The Custom CoreResultsWebPart Way
In Part 1 I used two Search Core Results Web Parts and a bit of jQuery magic to achive the look of blended search results
This time we will create our own CoreResultsWebPart and inject the blended results into the result xml before it is transformed into html. In addition to blend in news results I decided to get some images as well. I did this by importing a “Federated Location” for Flickr. The location definition can be found at “Flickr Search Connector for SharePoint Server, Search Server, and FAST Search for SharePoint”.
I started off by creating an empty SharePoint 2010 project in VS2010 and added a new web part.
After deploying and activating the feature I replaced the Search Core Results Web Part with my own Search Blended Core Results Web Part.
Since I use FS4SP as the search back-end remember to copy the xslt from the original web part as the default xslt is for internal SharePoint search.
By overriding the GetXPathNavigator we have full control over the result xml. The blended results are retrieved asynchronous while we get the local results from the page’s query manager on the main thread.
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="kwrd">protected</span> <span class="kwrd">override</span> XPathNavigator GetXPathNavigator(<span class="kwrd">string</span> viewPath) { QueryManager queryManager = SharedQueryManager.GetInstance(Page).QueryManager; Func<QueryManager, StringBuilder> blend = GetBlendedResults; IAsyncResult asyncResult = blend.BeginInvoke(queryManager, <span class="kwrd">null</span>, <span class="kwrd">null</span>); <span class="rem">// Get local results</span> XmlDocument xmlDocument = queryManager.GetResults(queryManager[0]); <span class="rem">// Get blended results</span> StringBuilder sb = blend.EndInvoke(asyncResult); InsertBlendedResults(sb, xmlDocument); <span class="kwrd">return</span> xmlDocument.CreateNavigator(); } |
The blended searches have been hard coded for Bing News and Flickr, but you could very well modify the web part to include settings where you choose which federated locations to use for your blended results. In order to execute the search we create a new QueryManager object and add both search locations to it. This enables the QueryManager to execute both the news and the images search at the same time, and the results are concatenated as two channels inside the returning rss feed. The rest of the code is parsing the rss and appending it into the main xml results.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<span class="kwrd">private</span> XmlDocument GetBlendedResultsXml(QueryManager queryManager) { SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default); SearchServiceApplicationProxy searchProxy = context.GetDefaultProxy(<span class="kwrd">typeof</span> (SearchServiceApplicationProxy)) <span class="kwrd">as</span> SearchServiceApplicationProxy; QueryManager blendedQuery = <span class="kwrd">new</span> QueryManager {UserQuery = queryManager.UserQuery}; LocationList locList = <span class="kwrd">new</span> LocationList(); Location internetLocation = <span class="kwrd">new</span> Location(<span class="str">"InternetSearchResults"</span>, searchProxy) {ItemsPerPage = 3}; locList.Add(internetLocation); Location flickrLocation = <span class="kwrd">new</span> Location(<span class="str">"Flickr"</span>, searchProxy) {ItemsPerPage = 3}; locList.Add(flickrLocation); blendedQuery.Add(locList); blendedQuery.IsTriggered(locList); <span class="kwrd">return</span> blendedQuery.GetResults(locList); } |
1 |
<span class="kwrd">private</span> <span class="kwrd">static</span> Regex _imgExtract = <span class="kwrd">new</span> Regex(<span class="str">"img src=\"(?<url>.*?)\""</span>, RegexOptions.Compiled); |
1 |
<span class="kwrd">private</span> <span class="kwrd">void</span> CreateResultXmlFragment(XmlWriter writer, SyndicationItem rssItem, <span class="kwrd">string</span> positionType) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
{ writer.WriteStartElement(<span class="str">"Result"</span>); writer.WriteElementString(<span class="str">"title"</span>, rssItem.Title.Text); writer.WriteElementString(<span class="str">"description"</span>, rssItem.Summary.Text); writer.WriteElementString(<span class="str">"write"</span>, rssItem.PublishDate.Date.ToString(<span class="str">"MM/dd/yyyy"</span>)); writer.WriteElementString(<span class="str">"url"</span>, rssItem.Links.First().Uri.OriginalString); writer.WriteStartElement(<span class="str">"imageurl"</span>); writer.WriteAttributeString(<span class="str">"imageurldescription"</span>, <span class="str">"Web Page"</span>); writer.WriteString(<span class="str">"/_layouts/images/html16.png"</span>); writer.WriteEndElement(); Match m = _imgExtract.Match(rssItem.Summary.Text); <span class="kwrd">if</span> (m.Success) { writer.WriteElementString(<span class="str">"picturethumbnailurl"</span>, m.Groups[<span class="str">"url"</span>].Value); writer.WriteElementString(<span class="str">"contentclass"</span>, <span class="str">"STS_ListItem_PictureLibrary"</span>); writer.WriteElementString(<span class="str">"blendtype"</span>, <span class="str">"Images"</span>); } <span class="kwrd">else</span> { writer.WriteElementString(<span class="str">"blendtype"</span>, <span class="str">"News"</span>); } writer.WriteElementString(<span class="str">"blended"</span>, positionType); writer.WriteEndElement(); } |
1 2 3 4 5 6 |
<span class="kwrd"><</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="blended = 'first'"</span><span class="kwrd">></span> <span class="kwrd"><</span><span class="html">div</span><span class="kwrd">></span> <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="blendtype"</span> <span class="kwrd">/></span> for <span class="kwrd"><</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="$Keyword"</span><span class="kwrd">/></span> <span class="kwrd"></</span><span class="html">div</span><span class="kwrd">></span> <span class="kwrd"><</span><span class="html">xsl:text</span> <span class="attr">disable-output-escaping</span><span class="kwrd">='yes'</span><span class="kwrd">></span><span class="attr">&lt;</span>div style="margin-left: 20px;background-color:#eee;font-size: 0.8em"<span class="attr">&gt;</span><span class="kwrd"></</span><span class="html">xsl:text</span><span class="kwrd">></span> <span class="kwrd"></</span><span class="html">xsl:if</span><span class="kwrd">></span> |
And I want to thank Corey Roth for his excellent posts on how to use the QueryManager class.
This post is cross-posted from Tech and Me.