Ratings and SharePoint Search better together

SearchResultsWithRatings.png

SharePoint 2010 introduces a new feature to allow a user to rate content within the sites. Depicted by five stars the user can rate content and these ratings are collated to provide the average rating for the item. Displaying content based on ratings can assist users determining the quality of content easily. Ratings will also help content authors understand which content is considered to be higher quality by the readers.

The native rating user interface is a collection of five stars. After a user selects the desired rating it is submitted and averaged with the other ratings for the content. The ratings are processed by a timer job process so there is some small delay.

By default ratings can be configured on lists and libraries and also added to page layouts. They also get fed into the Activity Feed system as rating activities. These add some great user focused features, but in my opinion there is a missing piece.

Search….

So I got thinking having seen some articles about additional search managed properties. Seeing as the average rating and rating count are both columns added to the list/library I decided to investigate whether it was possible to index and therefore present the rating within the search results.

So here the journey of rating discovery begins…

Adding ratings to lists or libraries

 

Before we can investigate getting ratings into the search experience we need to get rating up and running on some content. So lets go ahead and create a document library as our demo content. Upload numerous documents into the library (at least five) so that we have some content to rate.

So that’s the content ready for rating so what next?

  • Browse to the library.
  • From the ‘Library’ ribbon click the ‘Library settings’ button.
  • Under the general heading click the ‘Rating settings’ link.
  • Under the ‘Allow items in this list to be rated’ click yes and ‘Ok’.

This adds two columns to the library. The ‘Rating (0-5)’ and ‘Number of Ratings’. By default the Rating column is added to the view. You can also add the number column to the view if you want (more of that idea later). It should also be noted that these columns will be added to any bound content types as well.

That’s prepared the content for rating so now go ahead and click some ratings. For my demo I logged in as several users and rated the documents with ratings to demonstrate several of the number of stars.

DocLibRating

 

Timer job

 

As mentioned earlier the ratings are calculated via a timer job called {user profile SA name} – Social Rating Synchronization Job. This job aggregates the ratings. To speed up the development you can manually execute the timer job to cause the rating aggregation.

RatingJob

 

Search Managed Properties

 

So with the native rating functionality function configured and ready for indexing it’s time to swing over to the farm Search Service Application to perform the steps needed to index our ratings.

As with any element within SharePoint content for it to get indexed it needs to be a managed property. By default most common content fields are already configured. Ratings however are not setup in this way. So the first step is to create these managed properties for the ‘Rating (0-5)’ and ‘Number of Ratings’ columns.

 

AverageRating Managed Property

 

To create the average rating property map the ‘ows_AverageRating(Decimal)’ as shown in the screenshot below.

AverageRatingProp

 

RatingCountProp

Once the properties are configured a index of the content source is required. Browse to the ‘content sources’ and start a crawl. While this is whizzing along in the background the modifications to the search results web part can be made.

 

Search Results web part

 

The standard Search Results web part provides the view of the content found matching the query term.

DefaultSearchResults

As you can see there are no ratings information displayed. So we’re going to modify the results web part to include the rating and rating count below the content description.

 

Adding the columns to fetched data

 

To be able to show the AverageRating and RatingCount results they need to be added to the ‘Fetched Properties"’ xml within the web part settings (the circled element in the screen shot below)

AddingColumns

Modify the xml to add the new columns to it. The example below lists the new columns last, you can copy this or append your existing list.

<Columns>
<Column Name="WorkId"/>
<Column Name="Rank"/>
<Column Name="Title"/>
<Column Name="Author"/>
<Column Name="Size"/>
<Column Name="Path"/>
<Column Name="Description"/>
<Column Name="Write"/>
<Column Name="SiteName"/>
<Column Name="CollapsingStatus"/>
<Column Name="HitHighlightedSummary"/>
<Column Name="HitHighlightedProperties"/>
<Column Name="ContentClass"/>
<Column Name="IsDocument"/>
<Column Name="PictureThumbnailURL"/>
<Column Name="PopularSocialTags"/>
<Column Name="PictureWidth"/>
<Column Name="PictureHeight"/>
<Column Name="DatePictureTaken"/>
<Column Name="ServerRedirectedURL"/>
<Column Name="AverageRating"/>
<Column Name="RatingCount"/>
</Columns>

Apply the changes to the web part. Now the data is coming back within the search result set.

 

Modifying the XSLT

 

Next step is to get these properties displaying.

XSLEdit

I’m not that skilled in front end coding so this will demo the concept and I’m sure those more creative design peeps will add their own flare to the visuals Winking smile

So I’m choosing to inject the rating and number of raters just after the title and description. Therefore locate the ‘<div class="srch-Metadata2">’ div to inject the new code. Below is a snippet from the section and includes the calls to the new templates.

<div class="srch-Metadata2">

<xsl:call-template name="stars">
<xsl:with-param name="starCount" select="averagerating"/>
</xsl:call-template>
<xsl:call-template name="ratingcount">
<xsl:with-param name="ratingCount" select="ratingcount"/>
</xsl:call-template>
<br/>

<xsl:call-template name="DisplayAuthors">
<xsl:with-param name="author" select="author" />
</xsl:call-template>…..

As you can see I’ve introduced two new templates, one for each property.

Before we dive into the templates there is something important to share. To improve performance most visual images displayed in css sprite format. This means that css class positions the images to display the required section from a large map of images. The rating control is no different and uses the sprite image found ‘/_layouts/Images/Ratings.png’

 

Ratings

 

So the template to render the stars needs to make use of the same native css classes.

The following is the star rating template. It contains the logic to read the rating value and generate the relevant css positioned rating image.

<!– The Stars displaying –>
<xsl:template name="stars">
<xsl:param name="starCount"/>

<span class="ms-currentRating">
<!– Set the correct css sprite for the number of stars –>
<xsl:choose>
<xsl:when test="$starCount &gt;= 4.5" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 5 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_5" alt="Current average rating is 5 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 4.5 and $starCount &lt; 5" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 4.5 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_4_5" alt="Current average rating is 4.5 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 4 and $starCount &lt; 4.5" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 4 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_4" alt="Current average rating is 4 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 3.5 and $starCount &lt; 4" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 3.5 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_3_5" alt="Current average rating is 3.5 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 3 and $starCount &lt; 3.5" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 3 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_3" alt="Current average rating is 3 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 2.5 and $starCount &lt; 3" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 2.5 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_2_5" alt="Current average rating is 2.5 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 2 and $starCount &lt; 2.5" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 2 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_2" alt="Current average rating is 2 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 1.5 and $starCount &lt; 2" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 1.5 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_1_5" alt="Current average rating is 1.5 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 1 and $starCount &lt; 1.5" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 1 star.</xsl:text>
</xsl:attribute>
<img class="ms-rating_1" alt="Current average rating is 1 star." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:when test="$starCount &gt;= 0.5 and $starCount &lt; 1" >
<xsl:attribute name="title">
<xsl:text>Current average rating is 0.5 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_0_5" alt="Current average rating is 0.5 stars." src="/_layouts/Images/Ratings.png" />
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="title">
<xsl:text>Current average rating is 0 stars.</xsl:text>
</xsl:attribute>
<img class="ms-rating_0" alt="Current average rating is 0 stars." src="/_layouts/Images/Ratings.png" />
</xsl:otherwise>
</xsl:choose>
</span>

</xsl:template>

 

As well as the rating we’re adding the number or people who have rated. This gives the consuming user a decent idea of the level of interest the item has had. The following is the rating count template.

<!– The Rating Count displaying –>
<xsl:template name="ratingcount">
<xsl:param name="ratingCount"/>

<xsl:choose>
<xsl:when test="$ratingCount = 1" >
<xsl:text>Rated by 1 person.</xsl:text>
</xsl:when>
<xsl:when test="$ratingCount &gt;= 1" >
<xsl:text>Rated by </xsl:text>
<xsl:value-of select="$ratingCount" />
<xsl:text> people.</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>Not rated.</xsl:text>
</xsl:otherwise>
</xsl:choose>

</xsl:template>

 

With all these changes applied to the web part settings, save and publish the page and you should find the results now look something like the following screenshot.

SearchResultsWithRatings

Ok so that is pretty cool, users can now see content and peoples ratings of them to assist in finding the right things. It’s not quite the whole story on all the cool things to display, refiners are the other.

 

Refining by Ratings

 

With the ratings being displayed in the results it got me thinking that having a refiner for the rating value and number of people who had rated.

So the native refinement web part allows the customisation of the refiners via its settings. One important point to note is that the web part settings will have no effect unless the following check box is unchecked.

RefinerCheckBox

The Rating refiner is below.

<Category Title="Rating"
          Description="The average rating for the item." 
          Type="Microsoft.Office.Server.Search.WebControls.ManagedPropertyFilterGenerator"
          MetadataThreshold="5"
          NumberOfFiltersToDisplay="4"
          MaxNumberOfFilters="0"
          SortBy="Frequency"
          SortDirection="Ascending"
          SortByForMoreFilters="Name"
          SortDirectionForMoreFilters="Ascending"
          ShowMoreLink="True"
          MappedProperty="AverageRating"
          MoreLinkText="show more"
          LessLinkText="show fewer"/>

The Number of Ratings refiner is below.

<Category Title="Number of ratings"
          Description="The number of ratings from people for the item."
          Type="Microsoft.Office.Server.Search.WebControls.ManagedPropertyFilterGenerator"
          MetadataThreshold="5"
          NumberOfFiltersToDisplay="4"
          MaxNumberOfFilters="0"
          SortBy="Frequency"
          SortDirection="Ascending"
          SortByForMoreFilters="Name"
          SortDirectionForMoreFilters="Ascending"
          ShowMoreLink="True"
          MappedProperty="RatingCount"
          MoreLinkText="show more"
          LessLinkText="show fewer"/>

Add these to the XML property of the web part.

RefinerXml

Save the page and you end up with the refiners for ratings and number of ratings available.

RefinersAndSearch

Going the extra step with the refiners

So the initial refiners display any found values. A nice proof of concept would be to add some filter groups to group rating values together into Bronze, Silver, Gold instead of 0-5. That is possible with the refiner property grouping using ranges.

Wrap-up

Hopefully this no-code solution adds some extra sweetness to the use of ratings and search in equal measure.

18 comments

  1. Any way to change the ratings in the refinement panel so that it doesnt show 5, 4 , 3 , 2 , 1
    one under each other.
    Id like to have them either on the same line horizontaly or change the 5 to 5 stars; 4 to 4 stars, etc to make it a bit more visual.
    I checked the css but it’s an xsl so it would apply to all, kinda stuck looking for ideas

  2. I implemnented your solution on a custom list web part and the stars displayed just perfect with the number of people who have rated the list item. However, the stars do not respond on roll-over. Is there a different hack for custom list web parts?

    Anticipating your response.
    Thanks,
    Basil

    • Hi Basil,

      The stars would only respond to roll over if the ‘rating’ functionality is available. The rating in the article is not ‘enabled’ but merely a display of that articles current rating. The reason for not enabling the rating in the search results is a simple one that a user is not likely to have read the content enough to be capable to truely rate it. So not lettting them rate from the results seems sensible.

      To implement the functionality in your custom web part i suggest taking a close look at the OOTB implementation. I suspect it’s fired by client side script so should be relatively simple to replicate.

      Cheers,

      Wes

  3. Hi, Wes!
    Nice post. I am having one problem, however: the ows_AverageRating(Decimal) field does not appear when I try to add a managed property. I have rated some pages on my site, and I have run the job you mentioned. Any idea as to what may be causing this?
    Thanks!
    RP

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>