In my last post, I described the process that I follow when customizing search results in SharePoint using the Core Search Results web part that ships with MOSS. If you haven’t seen it yet, I suggest you start there for some context on the rest of this post.
On a recent project, I ran across the requirement of grouping search results by a managed property. It seemed relatively straightforward from a functional perspective, and given SharePoint’s XML-based architecture in search, feasible from a technical standpoint, as well. I found it to be a little more difficult than I thought.
First, XSLT 2.0 has a number of features that enable result grouping, but as my colleague Bert recently noted, XSLT 2.0 is not supported in SharePoint. If you use it in a search result transform, you’ll get the friendly “An error has occurred in this web part” message.
Fortunately, there’s another approach: enter the Muenchian method. (If you’re interested in the theory behind it and some of the thought behind grouping and sorting in XSL, check out this useful site by Jeni Tennison.)
In practice, incorporating the approach into SharePoint’s search transformation isn’t difficult. The first step is to declare a key referencing the managed property that you wish to group by:
<xsl:key name="results-by-author" match="Result" use="author" />
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode, .ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode pre
{font-size:small;color:black;font-family:consolas, "Courier New", courier, monospace;background-color:#ffffff;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode pre
{margin:0em;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .rem
{color:#008000;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .kwrd
{color:#0000ff;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .str
{color:#006080;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .op
{color:#0000c0;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .preproc
{color:#cc6633;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .asp
{background-color:#ffff00;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .html
{color:#800000;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .attr
{color:#ff0000;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .alt
{background-color:#f4f4f4;width:100%;margin:0em;}
.ExternalClass0B0959307DD3486DB49CF4852DE521FE .csharpcode .lnum
{color:#606060;}
In this case, I’m specifying that I want to match on “Result” elements (the parent element of each search result item in SharePoint’s search result schema) and create a key on the values of the “author” child element.
Now that the key is created, within the XSL, I can reference results using the key:
<xsl:for-each select="Result[count(. | key('results-by-author', author)[1]) = 1]">
<xsl:sort select="author" />
<div class="searchCategory">
Author: <b><xsl:value-of select="author" /></b>
</div>
<xsl:for-each select="key('results-by-author', author)">
<xsl:variable name="id" select="id"/>
<xsl:variable name="url" select="url"/>
<!--- individual result treatment omitted -->
</xsl:for-each>
</xsl:for-each>
Note that there’s an outer for-each statement that iterates through the items I’ve grouped on (the distinct authors, in this case), and an inner for-each that iterates through all the results with that author.
The output looks something like this:
If you’re interested, the full XSL for this output is available for download here.
This approach should work effectively on any managed property, as well – simply change the key definition as desired for your situation.
Hi Matthew,
I am interested with the full XSL for this output. I can’t see a download link. Can I have the XSL file?
Thanks,
Kent
Hi Matthew,
I am also interested with the full XSL for this output. Can I have the XSL file?
Thanks
Rahul